Python:日志记录模块 - 全局

发布于 2024-12-07 13:39:14 字数 350 浏览 0 评论 0原文

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

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

class customLogger(logging.Logger):
   ...

该类位于一个单独的文件中,其中包含一些格式化程序和其他内容。 记录器本身可以完美工作。

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

self.log = logModule.customLogger(arguments)

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

I was wondering how to implement a global logger that could be used everywhere with your own settings:

I currently have a custom logger class:

class customLogger(logging.Logger):
   ...

The class is in a separate file with some formatters and other stuff.
The logger works perfectly on its own.

I import this module in my main python file and create an object like this:

self.log = logModule.customLogger(arguments)

But obviously, I cannot access this object from other parts of my code.
Am i using a wrong approach? Is there a better way to do this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

浅听莫相离 2024-12-14 13:39:14

使用 logging.getLogger(name) 创建一个命名的全局记录器。

ma​​in.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

子模块.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

Use logging.getLogger(name) to create a named global logger.

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')

Output

2011-10-01 20:08:40,049 - DEBUG - main - main message
2011-10-01 20:08:40,050 - DEBUG - submodule - submodule message
缱倦旧时光 2024-12-14 13:39:14

由于我还没有找到满意的答案,我想详细说明一下问题的答案,以便深入了解 logging 库的工作原理和意图,该库附带Python 的标准库。

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

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

这意味着您不应该创建自定义记录器类并通过添加任何配置或其他方式在该类中配置记录器。

logging 库引入了四个组件:记录器处理程序过滤器格式化程序

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

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

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

在代码中(例如在 module.py 中),您引用模块的记录器实例来记录特定级别的事件。

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

logger = logging.getLogger(__name__)

特殊变量 __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!')

每个模块中的记录器都会将任何事件传播到父记录器,而父记录器又将信息传递给其附加的处理程序嗯>!与 python 包/模块结构类似,父记录器由使用“点​​模块名称”的命名空间确定。这就是为什么使用特殊的 __name__ 变量初始化记录器是有意义的(在上面的示例中,name 匹配字符串“project.package.module”)。

有两个选项可以全局配置记录器:

  • project.py 中实例化一个记录器,名称为 __package__,等于“project”在此示例中,因此是所有子模块的记录器的父记录器。只需要向此记录器添加适当的处理程序和格式化程序即可。

  • 在执行脚本(如 ma​​in.py)中使用最顶层包的名称设置带有处理程序和格式化程序的记录器。

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

执行脚本(例如 ma​​in.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(logger.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 库提供了一种结构化和模块化的方法,只需根据需要进行利用即可。

日志记录文档

replit.com 上的完整可执行示例

Since I haven't found a satisfactory answer, I would like to elaborate on the answer to the question a little bit in order to give some insight into the workings and intents of the logging library, that comes with Python's standard library.

In contrast to the approach of the OP (original poster) the library clearly separates the interface to the logger and configuration of the logger itself.

The configuration of handlers is the prerogative of the application developer who uses your library.

That means you should not create a custom logger class and configure the logger inside that class by adding any configuration or whatsoever.

The logging library introduces four components: loggers, handlers, filters, and formatters.

  • Loggers expose the interface that application code directly uses.
  • Handlers send the log records (created by loggers) to the appropriate destination.
  • Filters provide a finer grained facility for determining which log records to output.
  • Formatters specify the layout of log records in the final output.

A common project structure looks like this:

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

Inside your code (like in module.py) you refer to the logger instance of your module to log the events at their specific levels.

A good convention to use when naming loggers is to use a module-level logger, in each module which uses logging, named as follows:

logger = logging.getLogger(__name__)

The special variable __name__ refers to your module's name and looks something like project.package.module depending on your application's code structure.

module.py (and any other class) could essentially look like this:

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

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

The logger in each module will propagate any event to the parent logger which in return passes the information to its attached handler! Analogously to the python package/module structure, the parent logger is determined by the namespace using "dotted module names". That's why it makes sense to initialize the logger with the special __name__ variable (in the example above name matches the string "project.package.module").

There are two options to configure the logger globally:

  • Instantiate a logger in project.py with the name __package__ which equals "project" in this example and is therefore the parent logger of the loggers of all submodules. It is only necessary to add an appropriate handler and formatter to this logger.

  • Set up a logger with a handler and formatter in the executing script (like main.py) with the name of the topmost package.

When developing a library which uses logging, you should take care to document how the library uses logging - for example, the names of loggers used.

The executing script, like main.py for example, might finally look something like this:

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(logger.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()

The method call log.setLevel(...) specifies the lowest-severity log message a logger will handle but not necessarily output! It simply means the message is passed to the handler as long as the message's severity level is higher than (or equal to) the one that is set. But the handler is responsible for handling the log message (by printing or storing it for example).

Hence the logging library offers a structured and modular approach which just needs to be exploited according to one's needs.

Logging documentation

Full executable example on replit.com

层林尽染 2024-12-14 13:39:14

python 日志记录模块作为全局记录器已经足够好了,您可能只是寻找这个:

ma​​in.py

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

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

module.py

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

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

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

The python logging module is already good enough as global logger, you might simply looking for this:

main.py

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

Put the codes above into your executing script, then you can use this logger with the same configs anywhere in your projects:

module.py

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

For more complicated configs you may use a config file logging.conf with logging

logging.config.fileConfig("logging.conf")
江城子 2024-12-14 13:39:14

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

Create an instance of customLogger in your log module and use it as a singleton - just use the imported instance, rather than the class.

旧情勿念 2024-12-14 13:39:14

您只需向其传递一个在第一个句点之前带有公共子字符串的字符串即可。字符串中用句点(“.”)分隔的部分可用于不同的类/模块/文件/等。如下所示(特别是 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)

You can just pass it a string with a common sub-string before the first period. The parts of the string separated by the period (".") can be used for different classes / modules / files / etc. Like so (specifically the logger = logging.getLogger(loggerName) part):

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)
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文