简体   繁体   English

Python:跨所有模块的自定义日志记录

[英]Python: custom logging across all modules

Task任务

I have a collection of scripts and I'd like them to produce unified logging messages with minimum alterations to modules doing logging the actual messages.我有一组脚本,我希望它们能够生成统一的日志消息,并对记录实际消息的模块进行最少的改动。

I've written a small module 'custom_logger' which I plan to call from the main application once, have it return a logger, which I'd then continue using.我已经编写了一个小模块“custom_logger”,我计划从主应用程序调用一次,让它返回一个记录器,然后我会继续使用它。

The submodules I'd be importing into the app should only (or rather I wish them to)我要导入应用程序的子模块应该只(或者我希望它们)

  • should only "import logging as log" - so that nothing specific to my site is required to make them just run if someone else finds them useful.应该只“将日志记录导入为日志” - 这样如果其他人发现它们有用,就不需要任何特定于我的站点的东西来使它们运行。
  • should just log messages with log.info/error('message') without adding anything site-specific to them应该只用 log.info/error('message') 记录消息而不添加任何特定于它们的站点
  • should use the already configured 'root' logger with all its formatting and handers and not affect the root logger's configuration应该使用已配置的“root”记录器及其所有格式和处理程序,而不影响根记录器的配置

*custom_logger.py* *custom_logger.py*

import logging
import logging.handlers
import os
import sys


def getLogger(name='root', loglevel='INFO'):
  logger = logging.getLogger(name)

  # if logger 'name' already exists, return it to avoid logging duplicate
  # messages by attaching multiple handlers of the same type
  if logger.handlers:
    return logger
  # if logger 'name' does not already exist, create it and attach handlers
  else:
    # set logLevel to loglevel or to INFO if requested level is incorrect
    loglevel = getattr(logging, loglevel.upper(), logging.INFO)
    logger.setLevel(loglevel)
    fmt = '%(asctime)s %(filename)-18s %(levelname)-8s: %(message)s'
    fmt_date = '%Y-%m-%dT%T%Z'
    formatter = logging.Formatter(fmt, fmt_date)
    handler = logging.StreamHandler()
    handler.setFormatter(formatter)
    logger.addHandler(handler)

    if logger.name == 'root':
      logger.warning('Running: %s %s',
                     os.path.basename(sys.argv[0]),
                     ' '.join(sys.argv[1:]))
    return logger

Then comes the submodule which has a few test messages with examples of what works and what doesn't.然后是子模块,它有一些测试消息,其中包含有效和无效的示例。

submodule.py子模块.py

import sys
import custom_logger
import logging


class SubClass(object):

  def __init__(self):
    # NOK (no idea why since by default (no name parameter), it should return the root logger)
    #log = logging.getLogger()
    #log.info('message from SubClass / __init__')

    # OK (works as expected)
    #log = logging.getLogger('root')
    #log.info('message from SubClass / __init__')

    # OK (works as expected)
    log = custom_logger.getLogger('root')
    log.info('message from SubClass / __init__')


  def SomeMethod(self):
    # OK but I'd have to define `log` for every method, which is unacceptable
    # Please see question below all code snippets
    log = custom_logger.getLogger('root')
    log.info('message from SubClass / SomeMethod')

And the main app: app.py Nothing special here:和主应用程序: app.py这里没什么特别的:

#!/usr/bin/python

import custom_logger
import submodule

log = custom_logger.getLogger('root', loglevel='DEBUG')

log.debug('debug message')
log.info('info message')
log.warning('warning message')
log.error('error message')

a = submodule.SubClass() # this should produce a log message
a.SomeMethod()           # so should this

Output that I'm after and that I'm getting, just in an exteremely ugly way:我追求的和我得到的输出,只是以一种非常丑陋的方式:

% ./app.py 
2013-04-08T03:07:46BST custom_logger.py   WARNING : Running: app.py 
2013-04-08T03:07:46BST app.py             DEBUG   : debug message
2013-04-08T03:07:46BST app.py             INFO    : info message
2013-04-08T03:07:46BST app.py             WARNING : warning message
2013-04-08T03:07:46BST app.py             ERROR   : error message
2013-04-08T03:07:46BST submodule.py       INFO    : message from SubClass / __init__
2013-04-08T03:07:46BST submodule.py       INFO    : message from SubClass / SomeMethod

I want to be able to define a logger in the app.py and then in the submodules only use the standard Python logging library to make use of an already configured logger in the app.py.我希望能够在 app.py 中定义一个记录器,然后在子模块中仅使用标准 Python 日志记录库来使用 app.py 中已配置的记录器。

Also, an ugly workaround: if I place the below code after the imports in submodule.py:另外,一个丑陋的解决方法:如果我将下面的代码放在 submodule.py 中的导入之后:

log = custom_logger.getLogger('root')

it will be executed before my logger is configured in app.py, effectively making the submodule, not my app configure logging.它将在 app.py 中配置我的记录器之前执行,从而有效地创建子模块,而不是我的应用程序配置日志记录。

Another workaround I considered: within the constuctor of the SubClass class, I could define我考虑的另一种解决方法:在 SubClass 类的构造函数中,我可以定义

self.log = custom_logger.getLogger('root')

and then use self.log.error('some error').然后使用 self.log.error('some error')。 There must be a nicer way - if you can suggest something useful or point out where I misunderstood the documentation, I'd be very grateful!一定有更好的方法 - 如果您能提出有用的建议或指出我误解文档的地方,我将不胜感激!

PS.附注。 I've spent quite a bit reading the Python logging howto (basic and advanced) and the cookbook so if I've missed something useful there, please point it out.我花了很多时间阅读 Python logging howto(基本和高级)和食谱,所以如果我错过了一些有用的东西,请指出。

Thank you!谢谢!

If you want to change root logger you could just use getLogger() everywhere, with no arguments.如果你想改变根记录器,你可以在任何地方使用getLogger() ,不带参数。

Regarding the instance setup only in the main module, you can instantiate your logger, add your own Handler, and use it in all the other submodules (as I did bellow).关于仅在主模块中设置的实例,您可以实例化您的记录器,添加您自己的处理程序,并在所有其他子模块中使用它(就像我在下面所做的那样)。

I created a class that inherits the StreamHandler in custom_logger.py :我在custom_logger.py创建了一个继承 StreamHandler 的custom_logger.py

class MyHandler(logging.StreamHandler):

    def __init__(self):
        logging.StreamHandler.__init__(self)
        fmt = '%(asctime)s %(filename)-18s %(levelname)-8s: %(message)s'
        fmt_date = '%Y-%m-%dT%T%Z'
        formatter = logging.Formatter(fmt, fmt_date)
        self.setFormatter(formatter)

Then, in submodule.py , I put the getLogger after the imports and commented it in the methods:然后,在submodule.py ,我将 getLogger 放在导入之后并在方法中对其进行注释:

import sys
import logging

log = logging.getLogger('root')

class SubClass(object):

    def __init__(self):
         log.info('message from SubClass / __init__')

    def SomeMethod(self):
        log.info('message from SubClass / SomeMethod')

Then, in app.py I created a Logger instance (that will be the same in all modules) and added my handler, which formats the output:然后,在 app.py 中,我创建了一个 Logger 实例(在所有模块中都相同)并添加了我的处理程序,用于格式化输出:

#!/usr/bin/python

import logging
import custom_logger
import submodule

log = logging.getLogger('root')
log.setLevel('DEBUG')
log.addHandler(custom_logger.MyHandler())

log.debug('debug message') 
log.info('info message')
log.warning('warning message')
log.error('error message')

a = submodule.SubClass() # this should produce a log message
a.SomeMethod()           # so should this

Output:输出:

./app.py 
2013-04-08T15:20:05EEST app.py             DEBUG   : debug message
2013-04-08T15:20:05EEST app.py             INFO    : info message
2013-04-08T15:20:05EEST app.py             WARNING : warning message
2013-04-08T15:20:05EEST app.py             ERROR   : error message
2013-04-08T15:20:05EEST submodule.py       INFO    : message from SubClass / __init__
2013-04-08T15:20:05EEST submodule.py       INFO    : message from SubClass / SomeMethod

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM