Python: 日志模块-全局

我想知道如何实现一个全局日志记录器,可以在任何地方使用您自己的设置:

我目前有一个自定义日志记录器类:

class customLogger(logging.Logger):
...

该类位于一个单独的文件中,其中包含一些格式化程序和其他内容。 这个记录器自己就能很好地工作。

我在主 python 文件中导入这个模块并创建一个如下对象:

self.log = logModule.customLogger(arguments)

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

100501 次浏览

在日志模块中创建一个 customLogger实例,并将其用作单例实例——只使用导入的实例,而不是类。

只需要在第一个句点之前传递一个带有公共子字符串的字符串。由句点(“ .”)分隔的字符串部分可以用于不同的类/模块/文件等。像这样(特别是 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)

使用 logging.getLogger(name)创建命名全局日志记录器。

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

由于我还没有找到一个令人满意的答案,所以我想对这个问题的答案稍加阐述,以便深入了解 Python 标准库附带的 logging库的工作原理和意图。

与 OP (原始海报)的方法相反,库清楚地将与日志记录器的接口和日志记录器本身的配置分离开来。

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

这意味着 没有应该创建一个自定义日志记录器类,并通过添加任何配置或其他方式在该类中配置日志记录器。

logging库引入了四个组件: 伐木工负责人过滤器格式化程序

  • 日志记录器公开应用程序代码直接使用的接口。
  • 处理程序将日志记录(由记录器创建)发送到适当的目标。
  • 筛选器为确定输出哪些日志记录提供了更细粒度的设施。
  • 格式化程序在最终输出中指定日志记录的布局。

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

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

在代码中(如 模块 Py中) ,引用模块的日志记录器实例来记录特定级别的事件。

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

logger = logging.getLogger(__name__)

特殊变量 __name__引用模块的名称,它看起来像 project.package.module,这取决于应用程序的代码结构。

Py (和任何其他类)基本上可以是这样的:

import logging
...
log = logging.getLogger(__name__)


class ModuleClass:
def do_something(self):
log.debug('do_something() has been called!')

每个模块中的日志记录器将把任何事件传播给父日志记录器,而父日志记录器则将信息传递给附加的 联络人!与 python 包/模块结构类似,父日志记录器由使用“虚线模块名称”的命名空间确定。这就是为什么使用特殊的 __name__变量初始化日志记录器是有意义的(在上面的例子中,姓名匹配字符串 “ project.package.module”)。

有两个全局配置日志记录器的选项:

  • Project py中实例化一个日志记录器,名称为 __package__,在本例中等于 “计划”,因此是所有子模块的日志记录器的父日志记录器。只需要向 这个日志记录器添加适当的处理程序和格式化程序。

  • 在执行脚本(如 Main Py)中使用最顶层包的名称设置带有处理程序和格式化程序的日志记录器。

在开发使用日志记录的库时,您应该注意记录库如何使用日志记录-例如,使用的日志记录器的名称。

正在执行的脚本,例如 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库提供了一种结构化和模块化的方法,只需要根据需要进行开发。

日志文档

Python 日志记录模块作为全局日志记录器已经足够好了,您可以简单地查找以下内容:

Main Py

import logging
logging.basicConfig(level = logging.DEBUG,format = '[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s')

将上面的代码放入正在执行的脚本中,然后您就可以在项目的任何地方使用具有相同配置的日志记录器:

模块 Py

import logging
logger = logging.getLogger(__name__)
logger.info('hello world!')

对于更复杂的配置,可以使用带日志记录的配置文件 Logging.conf

logging.config.fileConfig("logging.conf")