我有一个小的python项目,它有以下结构-

Project 
 -- pkg01
   -- test01.py
 -- pkg02
   -- test02.py
 -- logging.conf

我计划使用默认日志记录模块将消息打印到标准输出和日志文件。 要使用日志记录模块,需要进行一些初始化

import logging.config

logging.config.fileConfig('logging.conf')
logger = logging.getLogger('pyApp')

logger.info('testing')

目前,在开始记录消息之前,我在每个模块中执行此初始化。是否可以只在一个地方执行一次初始化,以便通过记录整个项目来重用相同的设置?


当前回答

刚接触python,所以我不知道这是否可取,但它对于不重写样板文件非常有效。

你的项目必须有一个init.py,这样它才能作为一个模块加载

# Put this in your module's __init__.py
import logging.config
import sys

# I used this dictionary test, you would put:
# logging.config.fileConfig('logging.conf')
# The "" entry in loggers is the root logger, tutorials always 
# use "root" but I can't get that to work
logging.config.dictConfig({
    "version": 1,
    "formatters": {
        "default": {
            "format": "%(asctime)s %(levelname)s %(name)s %(message)s"
        },
    },
    "handlers": {
        "console": {
            "level": 'DEBUG',
            "class": "logging.StreamHandler",
            "stream": "ext://sys.stdout"
        }
    },
    "loggers": {
        "": {
            "level": "DEBUG",
            "handlers": ["console"]
        }
    }
})

def logger():
    # Get the name from the caller of this function
    return logging.getLogger(sys._getframe(1).f_globals['__name__'])

Sys._getframe(1)的建议来自这里

然后在任何其他文件中使用您的记录器:

from [your module name here] import logger

logger().debug("FOOOOOOOOO!!!")

警告:

你必须将你的文件作为模块运行,否则import [your module]将不起作用: Python -m[你的模块名]。[你的文件名没有。py] 程序入口点的记录器名称将是__main__,但任何使用__name__的解决方案都会有这个问题。

其他回答

这些答案中有几个建议在模块的顶部

import logging
logger = logging.getLogger(__name__)

据我所知,这是非常糟糕的做法。原因是文件配置默认情况下将禁用所有现有的记录器。如。

#my_module
import logging

logger = logging.getLogger(__name__)

def foo():
    logger.info('Hi, foo')

class Bar(object):
    def bar(self):
        logger.info('Hi, bar')

在主模块中:

#main
import logging

# load my module - this now configures the logger
import my_module

# This will now disable the logger in my module by default, [see the docs][1] 
logging.config.fileConfig('logging.ini')

my_module.foo()
bar = my_module.Bar()
bar.bar()

现在logging.ini中指定的日志将为空,因为现有的记录器已被fileconfig调用禁用。

虽然这当然是可能的(disable_existing_Loggers=False),但实际上你库的许多客户端不会知道这个行为,也不会接收到你的日志。通过始终调用logging使您的客户更容易。在本地getlog。温馨提示:我是从Victor Lin的网站上了解到这种行为的。

因此,好的做法是总是调用日志记录。在本地getlog。如。

#my_module
import logging

logger = logging.getLogger(__name__)

def foo():
    logging.getLogger(__name__).info('Hi, foo')

class Bar(object):
    def bar(self):
        logging.getLogger(__name__).info('Hi, bar')    

同样,如果你在main中使用fileconfig,设置disable_existing_loggers=False,以防你的库设计人员使用模块级记录器实例。

@Yarkee的解决方案似乎更好。我想再加一些

class Singleton(type):
    _instances = {}

    def __call__(cls, *args, **kwargs):
        if cls not in cls._instances.keys():
            cls._instances[cls] = super(Singleton, cls).__call__(*args, **kwargs)
        return cls._instances[cls]


class LoggerManager(object):
    __metaclass__ = Singleton

    _loggers = {}

    def __init__(self, *args, **kwargs):
        pass

    @staticmethod
    def getLogger(name=None):
        if not name:
            logging.basicConfig()
            return logging.getLogger()
        elif name not in LoggerManager._loggers.keys():
            logging.basicConfig()
            LoggerManager._loggers[name] = logging.getLogger(str(name))
        return LoggerManager._loggers[name]    


log=LoggerManager().getLogger("Hello")
log.setLevel(level=logging.DEBUG)

所以LoggerManager可以插入到整个应用程序中。 希望它有意义和价值。

有几个答案。我最终得到了一个类似但不同的解决方案,对我来说有意义,也许对你也有意义。 我的主要目标是能够按级别将日志传递给处理程序(调试级别的日志传递给控制台,警告和以上级别的日志传递给文件):

from flask import Flask
import logging
from logging.handlers import RotatingFileHandler

app = Flask(__name__)

# make default logger output everything to the console
logging.basicConfig(level=logging.DEBUG)

rotating_file_handler = RotatingFileHandler(filename="logs.log")
rotating_file_handler.setLevel(logging.INFO)

app.logger.addHandler(rotating_file_handler)

创建了一个名为logger.py的util文件:

import logging

def get_logger(name):
    return logging.getLogger("flask.app." + name)

长颈瓶。App是flask中硬编码的值。应用程序记录器总是从flask开始。App作为它的模块名。

现在,在每个模块中,我能够在以下模式中使用它:

from logger import get_logger
logger = get_logger(__name__)

logger.info("new log")

这将为“app.flask”创建一个新的日志。MODULE_NAME”。

你也可以想出这样的东西!

def get_logger(name=None):
    default = "__app__"
    formatter = logging.Formatter('%(levelname)s: %(asctime)s %(funcName)s(%(lineno)d) -- %(message)s',
                              datefmt='%Y-%m-%d %H:%M:%S')
    log_map = {"__app__": "app.log", "__basic_log__": "file1.log", "__advance_log__": "file2.log"}
    if name:
        logger = logging.getLogger(name)
    else:
        logger = logging.getLogger(default)
    fh = logging.FileHandler(log_map[name])
    fh.setFormatter(formatter)
    logger.addHandler(fh)
    logger.setLevel(logging.DEBUG)
    return logger

现在你可以在同一个模块和整个项目中使用多个记录器,如果上面的定义在一个单独的模块中,并在其他模块中导入日志记录是必需的。

a=get_logger("__app___")
b=get_logger("__basic_log__")
a.info("Starting logging!")
b.debug("Debug Mode")

最佳实践是,在每个模块中定义一个记录器,如下所示:

import logging
logger = logging.getLogger(__name__)

在模块的顶部附近,然后在模块中的其他代码中执行例如。

logger.debug('My message with %s', 'variable data')

如果你需要在一个模块中细分日志活动,使用例如。

loggerA = logging.getLogger(__name__ + '.A')
loggerB = logging.getLogger(__name__ + '.B')

和log到loggerA和loggerB。

在你的主程序中,执行以下操作:

def main():
    "your program code"

if __name__ == '__main__':
    import logging.config
    logging.config.fileConfig('/path/to/logging.conf')
    main()

or

def main():
    import logging.config
    logging.config.fileConfig('/path/to/logging.conf')
    # your program code

if __name__ == '__main__':
    main()

在这里可以查看来自多个模块的日志记录,在这里可以查看将被其他代码用作库模块的代码的日志记录配置。

Update: When calling fileConfig(), you may want to specify disable_existing_loggers=False if you're using Python 2.6 or later (see the docs for more information). The default value is True for backward compatibility, which causes all existing loggers to be disabled by fileConfig() unless they or their ancestor are explicitly named in the configuration. With the value set to False, existing loggers are left alone. If using Python 2.7/Python 3.2 or later, you may wish to consider the dictConfig() API which is better than fileConfig() as it gives more control over the configuration.