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)