Python:logging模块 – 全局

嘿,我想知道如何实现一个全球logging器,可以在任何地方使用自己的设置:

我有

class customLogger(logging.Logger): ... 

在与格式化程序和其他东西的文件。 logging器完全依靠自己的工作。

我在我的main.py文件中导入这个模块并创build一个像这样的对象:

 self.log = log.customLogger(arguments) 

但显然我不能从我的代码的其他部分访问这个对象。 我用错了吗? 有一个更好的方法吗?

使用logging.getLogger(name)来创build一个命名的全局logging器。

main.py

 import log logger = log.setup_custom_logger('root') logger.debug('main message') import submodule 

log.py

 import logging def setup_custom_logger(name): formatter = logging.Formatter(fmt='%(asctime)s - %(levelname)s - %(module)s - %(message)s') handler = logging.StreamHandler() handler.setFormatter(formatter) logger = logging.getLogger(name) logger.setLevel(logging.DEBUG) logger.addHandler(handler) return logger 

submodule.py

 import logging logger = logging.getLogger('root') logger.debug('submodule message') 

产量

 2011-10-01 20:08:40,049 - DEBUG - main - main message 2011-10-01 20:08:40,050 - DEBUG - submodule - submodule message 

由于我还没有find满意的答案,为了深入了解Python标准库附带的logging库的工作原理和意图,我想详细解释一下这个问题的答案。

与OP(原始海报)的方法相反,库清楚地将接口与logging器本身的logging器和configuration分开。

处理程序的configuration是使用您的库的应用程序开发人员的特权。

这意味着你不应该创build一个自定义的logging器类,并通过添加任何configuration或任何configuration该类内的logging器。

logging库引入了四个组件: logging器处理程序filter格式化程序

  • logging器公开应用程序代码直接使用的接口。
  • 处理程序将日志logging(由logging器创build)发送到相应的目标。
  • filter提供了更好的粒度设施来确定要输出的日志logging。
  • 格式化程序指定最终输出中日志logging的布局。

一个常见的项目结构如下所示:

 Project/ |-- .../ | |-- ... | |-- project/ | |-- package/ | | |-- __init__.py | | |-- module.py | | | |-- __init__.py | |-- project.py | |-- ... |-- ... 

在你的代码里面(比如在module.py中 ),你可以引用模块的logging器实例来logging事件的特定级别。

命名logging器时使用的一个很好的约定是使用模块级别的logging器,在每个使用logging的模块中,命名如下:

 logger = logging.getLogger(__name__) 

特殊variables__name__引用你的模块的名字,看起来像project.package.module取决于你的应用程序的代码结构。

module.py (和任何其他类)可以看起来像这样:

 import logging ... log = logging.getLogger(__name__) class ModuleClass: def do_something(self): log.debug('do_something() has been called!') 

每个模块中的logging器都会将任何事件传播给父logging器,然后将这些信息传递给附加的处理器 ! 与python包/模块结构类似,父logging器由名称空间使用“虚线模块名称”确定。 这就是为什么使用特殊的__name__variables初始化logging器是__name__ (在上面的例子中, 名称与string“project.package.module”匹配)。

有两个选项来全局configurationlogging器:

  • project.py中使用名称__package__实例化一个logging器,该logging器在本例中等于“project” ,因此是所有子模块的logging器的父logging器。 只需要添加一个适当的处理程序和格式化程序到这个logging器。

  • 在执行脚本(如main.py )中使用处理程序和格式化程序设置logging器,并使用最顶层包的名称。

开发使用日志logging的库时,应注意logging库如何使用日志logging,例如logging器的名称。

执行脚本,比如main.py ,最终可能是这样的:

 import logging from project import App def setup_logger(): # create logger logger = logging.getLogger('project') logger.setLevel(logging.DEBUG) # create console handler and set level to debug ch = logging.StreamHandler() ch.setLevel(level) # create formatter formatter = logging.Formatter('%(asctime)s [%(levelname)s] %(name)s: %(message)s') # add formatter to ch ch.setFormatter(formatter) # add ch to logger logger.addHandler(ch) if __name__ == '__main__' and __package__ is None: setup_logger() app = App() app.do_some_funny_stuff() 

方法调用log.setLevel(...)指定logging器将处理但不一定输出的最低严重性日志消息! 它只是意味着只要消息的严重级别高于(或等于)已设置的消息就会将消息传递给处理程序。 但处理程序负责处理日志消息(通过打印或存储)。

因此, logging库提供了一个结构化和模块化的方法,只需要根据需要进行开发。

logging文件

在你的日志模块中创build一个customLogger实例, customLogger其作为一个单例使用 – 只需使用导入的实例,而不是类。

你可以在第一个时间段之前传递一个带有公共子串的string。 用句号(“。”)分隔的string部分可以用于不同的类/模块/文件/等。像这样(特别是logger = logging.getLogger(loggerName)部分):

 def getLogger(name, logdir=LOGDIR_DEFAULT, level=logging.DEBUG, logformat=FORMAT): base = os.path.basename(__file__) loggerName = "%s.%s" % (base, name) logFileName = os.path.join(logdir, "%s.log" % loggerName) logger = logging.getLogger(loggerName) logger.setLevel(level) i = 0 while os.path.exists(logFileName) and not os.access(logFileName, os.R_OK | os.W_OK): i += 1 logFileName = "%s.%s.log" % (logFileName.replace(".log", ""), str(i).zfill((len(str(i)) + 1))) try: #fh = logging.FileHandler(logFileName) fh = RotatingFileHandler(filename=logFileName, mode="a", maxBytes=1310720, backupCount=50) except IOError, exc: errOut = "Unable to create/open log file \"%s\"." % logFileName if exc.errno is 13: # Permission denied exception errOut = "ERROR ** Permission Denied ** - %s" % errOut elif exc.errno is 2: # No such directory errOut = "ERROR ** No such directory \"%s\"** - %s" % (os.path.split(logFileName)[0], errOut) elif exc.errno is 24: # Too many open files errOut = "ERROR ** Too many open files ** - Check open file descriptors in /proc/<PID>/fd/ (PID: %s)" % os.getpid() else: errOut = "Unhandled Exception ** %s ** - %s" % (str(exc), errOut) raise LogException(errOut) else: formatter = logging.Formatter(logformat) fh.setLevel(level) fh.setFormatter(formatter) logger.addHandler(fh) return logger class MainThread: def __init__(self, cfgdefaults, configdir, pidfile, logdir, test=False): self.logdir = logdir logLevel = logging.DEBUG logPrefix = "MainThread_TEST" if self.test else "MainThread" try: self.logger = getLogger(logPrefix, self.logdir, logLevel, FORMAT) except LogException, exc: sys.stderr.write("%s\n" % exc) sys.stderr.flush() os._exit(0) else: self.logger.debug("-------------------- MainThread created. Starting __init__() --------------------") def run(self): self.logger.debug("Initializing ReportThreads..") for (group, cfg) in self.config.items(): self.logger.debug(" ------------------------------ GROUP '%s' CONFIG ------------------------------ " % group) for k2, v2 in cfg.items(): self.logger.debug("%s <==> %s: %s" % (group, k2, v2)) try: rt = ReportThread(self, group, cfg, self.logdir, self.test) except LogException, exc: sys.stderr.write("%s\n" % exc) sys.stderr.flush() self.logger.exception("Exception when creating ReportThread (%s)" % group) logging.shutdown() os._exit(1) else: self.threads.append(rt) self.logger.debug("Threads initialized.. \"%s\"" % ", ".join([t.name for t in self.threads])) for t in self.threads: t.Start() if not self.test: self.loop() class ReportThread: def __init__(self, mainThread, name, config, logdir, test): self.mainThread = mainThread self.name = name logLevel = logging.DEBUG self.logger = getLogger("MainThread%s.ReportThread_%s" % ("_TEST" if self.test else "", self.name), logdir, logLevel, FORMAT) self.logger.info("init database...") self.initDB() # etc.... if __name__ == "__main__": # ..... MainThread(cfgdefaults=options.cfgdefaults, configdir=options.configdir, pidfile=options.pidfile, logdir=options.logdir, test=options.test)