开始使用一个日志,基本上都是用的 [logging.info](http://logging.info) 或是 [logger.info](http://logger.info) 形式的 API
以 [logging.info](http://logging.info) 为例,其源码为
1 2 3 4 5 6 7 8 9
definfo(msg, *args, **kwargs): """ Log a message with severity 'INFO' on the root logger. If the logger has no handlers, call basicConfig() to add a console handler with a pre-defined format. """ iflen(root.handlers) == 0: basicConfig() root.info(msg, *args, **kwargs)
classRootLogger(Logger): """ A root logger is not that different to any other logger, except that it must have a logging level and there is only one instance of it in the hierarchy. """ def__init__(self, level): """ Initialize the logger with the name "root". """ Logger.__init__(self, "root", level)
return is_enabled defgetEffectiveLevel(self): """ Get the effective level for this logger. Loop through this logger and its parents in the logger hierarchy, looking for a non-zero logging level. Return the first one found. """ logger = self while logger: if logger.level: return logger.level logger = logger.parent return NOTSET
classManager(object): """ There is [under normal circumstances] just one Manager instance, which holds the hierarchy of loggers. """ def__init__(self, rootnode): """ Initialize the manager with the root node of the logger hierarchy. """ self.root = rootnode self.disable = 0 self.emittedNoHandlerWarning = False self.loggerDict = {} self.loggerClass = None self.logRecordFactory = None # ...
classLogger(Filterer): # ... defgetEffectiveLevel(self): """ Get the effective level for this logger. Loop through this logger and its parents in the logger hierarchy, looking for a non-zero logging level. Return the first one found. """ logger = self while logger: if logger.level: return logger.level logger = logger.parent return NOTSET
所以这个 isEnabledFor 流程就很明显了,看要写入日志的等级是否大于要求的最高等级(简单来说 ERROR > WARNING > INFO > DEBUG),只有大于才能写入成功;
classLogger(Filterer): def_log(self, level, msg, args, exc_info=None, extra=None, stack_info=False, stacklevel=1): """ Low-level logging routine which creates a LogRecord and then calls all the handlers of this logger to handle the record. """ sinfo = None if _srcfile: #IronPython doesn't track Python frames, so findCaller raises an #exception on some versions of IronPython. We trap it here so that #IronPython can use logging. try: fn, lno, func, sinfo = self.findCaller(stack_info, stacklevel) except ValueError: # pragma: no cover fn, lno, func = "(unknown file)", 0, "(unknown function)" else: # pragma: no cover fn, lno, func = "(unknown file)", 0, "(unknown function)" if exc_info: ifisinstance(exc_info, BaseException): exc_info = (type(exc_info), exc_info, exc_info.__traceback__) elifnotisinstance(exc_info, tuple): exc_info = sys.exc_info() record = self.makeRecord(self.name, level, fn, lno, msg, args, exc_info, func, extra, sinfo) self.handle(record)
classLogger(Filterer): defcallHandlers(self, record): """ Pass a record to all relevant handlers. Loop through all handlers for this logger and its parents in the logger hierarchy. If no handler was found, output a one-off error message to sys.stderr. Stop searching up the hierarchy whenever a logger with the "propagate" attribute set to zero is found - that will be the last logger whose handlers are called. """ c = self found = 0 while c: for hdlr in c.handlers: found = found + 1 if record.levelno >= hdlr.level: hdlr.handle(record) ifnot c.propagate: c = None#break out else: c = c.parent if (found == 0): if lastResort: if record.levelno >= lastResort.level: lastResort.handle(record) elif raiseExceptions andnot self.manager.emittedNoHandlerWarning: sys.stderr.write("No handlers could be found for logger" " \"%s\"\n" % self.name) self.manager.emittedNoHandlerWarning = True
def__init__(self, stream=None): """ Initialize the handler. If stream is not specified, sys.stderr is used. """ Handler.__init__(self) if stream isNone: stream = sys.stderr self.stream = stream
defemit(self, record): """ Emit a record. If a formatter is specified, it is used to format the record. The record is then written to the stream with a trailing newline. If exception information is present, it is formatted using traceback.print_exception and appended to the stream. If the stream has an 'encoding' attribute, it is used to determine how to do the output to the stream. """ try: msg = self.format(record) stream = self.stream # issue 35046: merged two stream.writes into one. stream.write(msg + self.terminator) self.flush() except RecursionError: # See issue 36272 raise except Exception: self.handleError(record)
不指定输出到的流默认是标准错误
调用的 format 方法是由 Handler 类给出的,用于将 record 记录转换为文字
其他的一些刷新缓存创建锁的代码就省略了
format 实现如下,默认是用了一个 Formatter,负责将你的各种参数合并为一条文字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
_defaultFormatter = Formatter()
classHandler(Filterer): defformat(self, record): """ Format the specified record. If a formatter is set, use it. Otherwise, use the default formatter for the module. """ if self.formatter: fmt = self.formatter else: fmt = _defaultFormatter return fmt.format(record)
classFilterer(object): deffilter(self, record): """ Determine if a record is loggable by consulting all the filters. The default is to allow the record to be logged; any filter can veto this and the record is then dropped. Returns a zero value if a record is to be dropped, else non-zero. .. versionchanged:: 3.2 Allow filters to be just callables. """ rv = True for f in self.filters: ifhasattr(f, 'filter'): result = f.filter(record) else: result = f(record) # assume callable - will raise if not ifnot result: rv = False break return rv
可以传递声明 filter,只要有一个返回 False 那么最终结果就是 False
Formatter
Formatter 其实没什么可说的,就是需要实现一个 format 方法,接收一个 LogRecord 对象,返回一个字符串,具体实现无非就是一些文本替换,有兴趣的可以自己去看代码,这里就不赘述了