Python:跨所有模块的自定义日志记录 [英] Python: custom logging across all modules

查看:80
本文介绍了Python:跨所有模块的自定义日志记录的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

任务

我有一个脚本集合,我希望它们生成统一的日志消息,同时对记录实际消息的模块进行最小的改动.

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.

我编写了一个小模块"custom_logger",计划从主应用程序调用一次,让它返回一个logger,然后继续使用.

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.

我要导入到应用程序中的子模块应该(或者我希望它们)

The submodules I'd be importing into the app should only (or rather I wish them to)

  • 仅应将日志记录作为日志导入"-这样,只要别人发现它们有用,就不需要我的网站专有的内容即可使它们运行.
  • 应该只使用log.info/error('message')记录消息,而不向其添加任何特定于站点的内容
  • 应使用已配置的"root"记录器及其所有格式和处理方式,并且不影响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

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

和主应用程序: app.py 这里没什么特别的:

And the main app: app.py Nothing special here:

#!/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

我希望能够在app.py中定义一个记录器,然后在子模块中仅使用标准的Python日志记录库来利用app.py中已配置的记录器.

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.

还有一个丑陋的解决方法:如果我将以下代码放置在submodule.py中的导入之后:

Also, an ugly workaround: if I place the below code after the imports in submodule.py:

log = custom_logger.getLogger('root')

它将在app.py中配置我的记录器之前执行,有效地使子模块(而不是我的应用)配置日志记录.

it will be executed before my logger is configured in app.py, effectively making the submodule, not my app configure logging.

我考虑过的另一种解决方法:在SubClass类的构造器中,我可以定义

Another workaround I considered: within the constuctor of the SubClass class, I could define

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

,然后使用self.log.error('some error').肯定有更好的方法-如果您可以提出有用的建议或指出我误解了文档的地方,我将不胜感激!

and then use 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.我已经花了很多时间阅读Python记录方法(基本和高级)和菜谱,所以如果我错过了那里有用的东西,请指出来.

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.

谢谢!

推荐答案

如果要更改root logger,可以在任何地方都使用getLogger(),而无需使用参数.

If you want to change root logger you could just use getLogger() everywhere, with no arguments.

关于仅在主模块中的实例设置,您可以实例化记录器,添加自己的Handler,并在所有其他子模块中使用它(就像我在下面所述).

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

我创建了一个类,该类继承了custom_logger.py中的StreamHandler:

I created a class that inherits the StreamHandler in 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)

然后,在submodule.py中,我将getLogger放在导入之后,并在方法中对其进行了注释:

Then, in submodule.py, I put the getLogger after the imports and commented it in the methods:

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

然后,在app.py中,我创建了一个Logger实例(在所有模块中都将是相同的),并添加了处理程序,该输出用于格式化输出:

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:

#!/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

输出:

./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

这篇关于Python:跨所有模块的自定义日志记录的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆