Que*_*ger 217 python logging config
我有一个小python项目,具有以下结构 -
Project
-- pkg01
-- test01.py
-- pkg02
-- test02.py
-- logging.conf
Run Code Online (Sandbox Code Playgroud)
我计划使用默认日志记录模块将消息打印到stdout和日志文件.要使用日志记录模块,需要进行一些初始化 -
import logging.config
logging.config.fileConfig('logging.conf')
logger = logging.getLogger('pyApp')
logger.info('testing')
Run Code Online (Sandbox Code Playgroud)
目前,我在开始记录消息之前在每个模块中执行此初始化.是否可以在一个地方只执行一次初始化,以便通过整个项目记录重复使用相同的设置?
Vin*_*jip 251
在每个模块中,最佳做法是使用如下定义的记录器:
import logging
logger = logging.getLogger(__name__)
Run Code Online (Sandbox Code Playgroud)
靠近模块的顶部,然后在模块的其他代码中做例如
logger.debug('My message with %s', 'variable data')
Run Code Online (Sandbox Code Playgroud)
如果需要在模块内细分日志活动,请使用eg
loggerA = logging.getLogger(__name__ + '.A')
loggerB = logging.getLogger(__name__ + '.B')
Run Code Online (Sandbox Code Playgroud)
和日志记录loggerA
,并loggerB
适当.
在您的主程序或程序中,例如:
def main():
"your program code"
if __name__ == '__main__':
import logging.config
logging.config.fileConfig('/path/to/logging.conf')
main()
Run Code Online (Sandbox Code Playgroud)
要么
def main():
import logging.config
logging.config.fileConfig('/path/to/logging.conf')
# your program code
if __name__ == '__main__':
main()
Run Code Online (Sandbox Code Playgroud)
请参阅此处以获取来自多个模块的日志记录,此处用于记录将由其他代码用作库模块的代码的配置.
更新:调用时fileConfig()
,您可能需要指定disable_existing_loggers=False
是否使用Python 2.6或更高版本(有关更多信息,请参阅文档).默认值是True
为了向后兼容,这会导致禁用所有现有记录器,fileConfig()
除非在配置中明确命名它们或它们的祖先.设置值后False
,现有记录器将保持不变.如果使用Python 2.7/Python 3.2或更高版本,您可能希望考虑dictConfig()
更好的API,fileConfig()
因为它可以更好地控制配置.
Sta*_*kop 104
实际上每个记录器都是父包的记录器的子节点(即package.subpackage.module
继承配置package.subpackage)
,所以你需要做的只是配置根记录器.这可以通过logging.config.fileConfig
(你自己的记录器配置)或logging.basicConfig
(设置根记录器)来实现. .在您的输入模块中设置日志记录(__main__.py
或者您想要运行的任何内容,例如main_script.py
.也__init__.py
适用)
使用basicConfig:
# package/__main__.py
import logging
import sys
logging.basicConfig(stream=sys.stdout, level=logging.INFO)
Run Code Online (Sandbox Code Playgroud)
使用fileConfig:
# package/__main__.py
import logging
import logging.config
logging.config.fileConfig('logging.conf')
Run Code Online (Sandbox Code Playgroud)
然后使用以下命令创建每个记录器:
# package/submodule.py
# or
# package/subpackage/submodule.py
import logging
log = logging.getLogger(__name__)
log.info("Hello logging!")
Run Code Online (Sandbox Code Playgroud)
有关更多信息,请参阅高级日志教程.
Yar*_*kee 19
我总是这样做.
使用单个python文件将我的日志配置为名为' log_conf.py
'的单例模式
#-*-coding:utf-8-*-
import logging.config
def singleton(cls):
instances = {}
def get_instance():
if cls not in instances:
instances[cls] = cls()
return instances[cls]
return get_instance()
@singleton
class Logger():
def __init__(self):
logging.config.fileConfig('logging.conf')
self.logr = logging.getLogger('root')
Run Code Online (Sandbox Code Playgroud)
在另一个模块中,只需导入配置.
from log_conf import Logger
Logger.logr.info("Hello World")
Run Code Online (Sandbox Code Playgroud)
这是一种简单有效的记录单例模式.
Ale*_*lig 16
对我来说,在多个模块中使用一个日志库实例的简单方法是以下解决方案:
import logging
logger = logging
logger.basicConfig(format='%(asctime)s - %(message)s', level=logging.INFO)
Run Code Online (Sandbox Code Playgroud)
from base_logger import logger
if __name__ == '__main__':
logger.info("This is an info message")
Run Code Online (Sandbox Code Playgroud)
Phi*_*hil 15
我想添加我的解决方案(它基于记录食谱和该线程中的其他文章和建议。但是我花了很长时间才弄清楚为什么它没有立即按我的预期工作。所以我创建了一个一个小测试项目来了解日志记录是如何工作的。
\n既然我已经弄清楚了,我想分享我的解决方案,也许它可以对某人有所帮助。
\n我知道我的一些代码可能不是最佳实践,但我仍在学习。我将这些print()
功能留在了那里,因为我使用了它们,而日志记录没有按预期工作。这些已在我的其他应用程序中删除。我也欢迎对代码或结构的任何部分提供任何反馈。
my_log_test 项目结构(从我从事的另一个项目克隆/简化)
\nmy_log_test\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 __init__.py\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 __main__.py\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 daemon.py\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 common\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 my_logger.py\n\xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 pkg1\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 __init__.py\n\xe2\x94\x82\xc2\xa0\xc2\xa0 \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 mod1.py\n\xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 pkg2\n \xe2\x94\x9c\xe2\x94\x80\xe2\x94\x80 __init__.py\n \xe2\x94\x94\xe2\x94\x80\xe2\x94\x80 mod2.py\n
Run Code Online (Sandbox Code Playgroud)\n要求
\n有一些不同的事情或者我在我使用的组合中没有明确提到的事情:
\ndaemon.py
由__main__.py
mod1.py
和mod2.py
basicConfig()
或FileConfig()
但保留它就像在日志记录手册中一样所以基本上,这意味着,我需要(始终)和模块中以及(仅在直接调用它们时)初始化根记录器。daemon.py
mod1.py
mod2.py
为了使几个模块中的初始化更容易,我创建了my_logger.py
它,它的作用是食谱中描述的。
我的错
\n之前,我在该模块中的错误是使用 (模块记录器) 初始化记录器,logger = logging.getLogger(__name__)
而不是使用logger = logging.getLogger()
(获取根记录器)。
第一个问题是,当从daemon.py
记录器的命名空间调用时设置为my_log_test.common.my_logger
. mod1.py
因此,具有“不匹配”命名空间的模块记录器my_log_test.pkg1.mod1
无法附加到其他记录器,并且我不会看到 mod1 的任何日志输出。
第二个“问题”是,我的主程序位于daemon.py
而不是在__main__.py
. 但对我来说毕竟不是真正的问题,但它增加了命名空间的混乱。
工作溶液
\n这是来自食谱,但位于单独的模块中。我还添加了一个logger_cleanup
可以从守护进程调用的函数,以删除早于 x 天的日志。
## my_logger.py\nfrom datetime import datetime\nimport time\nimport os\n\n## Init logging start \nimport logging\nimport logging.handlers\n\ndef logger_init():\n print("print in my_logger.logger_init()")\n print("print my_logger.py __name__: " +__name__)\n path = "log/"\n filename = "my_log_test.log"\n\n ## get logger\n #logger = logging.getLogger(__name__) ## this was my mistake, to init a module logger here\n logger = logging.getLogger() ## root logger\n logger.setLevel(logging.INFO)\n\n # File handler\n logfilename = datetime.now().strftime("%Y%m%d_%H%M%S") + f"_{filename}"\n file = logging.handlers.TimedRotatingFileHandler(f"{path}{logfilename}", when="midnight", interval=1)\n #fileformat = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s")\n fileformat = logging.Formatter("%(asctime)s [%(levelname)s]: %(name)s: %(message)s")\n file.setLevel(logging.INFO)\n file.setFormatter(fileformat)\n\n # Stream handler\n stream = logging.StreamHandler()\n #streamformat = logging.Formatter("%(asctime)s [%(levelname)s:%(module)s] %(message)s")\n streamformat = logging.Formatter("%(asctime)s [%(levelname)s]: %(name)s: %(message)s")\n stream.setLevel(logging.INFO)\n stream.setFormatter(streamformat)\n\n # Adding all handlers to the logs\n logger.addHandler(file)\n logger.addHandler(stream)\n\n\ndef logger_cleanup(path, days_to_keep):\n lclogger = logging.getLogger(__name__)\n logpath = f"{path}"\n now = time.time()\n for filename in os.listdir(logpath):\n filestamp = os.stat(os.path.join(logpath, filename)).st_mtime\n filecompare = now - days_to_keep * 86400\n if filestamp < filecompare:\n lclogger.info("Delete old log " + filename)\n try:\n os.remove(os.path.join(logpath, filename))\n except Exception as e:\n lclogger.exception(e)\n continue\n
Run Code Online (Sandbox Code Playgroud)\n运行 deamon.py (通过__main__.py
)使用python3 -m my_log_test
## __main__.py\nfrom my_log_test import daemon\n\nif __name__ == \'__main__\':\n print("print in __main__.py")\n daemon.run()\n
Run Code Online (Sandbox Code Playgroud)\n(直接)运行 deamon.py 使用python3 -m my_log_test.daemon
## daemon.py\nfrom datetime import datetime\nimport time\nimport logging\nimport my_log_test.pkg1.mod1 as mod1\nimport my_log_test.pkg2.mod2 as mod2\n\n## init ROOT logger from my_logger.logger_init()\nfrom my_log_test.common.my_logger import logger_init\nlogger_init() ## init root logger\nlogger = logging.getLogger(__name__) ## module logger\n\ndef run():\n print("print in daemon.run()")\n print("print daemon.py __name__: " +__name__)\n logger.info("Start daemon")\n loop_count = 1\n while True:\n logger.info(f"loop_count: {loop_count}")\n logger.info("do stuff from pkg1")\n mod1.do1()\n logger.info("finished stuff from pkg1")\n\n logger.info("do stuff from pkg2")\n mod2.do2()\n logger.info("finished stuff from pkg2")\n\n logger.info("Waiting a bit...")\n time.sleep(30)\n\n\nif __name__ == \'__main__\':\n try:\n print("print in daemon.py if __name__ == \'__main__\'")\n logger.info("running daemon.py as main")\n run()\n except KeyboardInterrupt as e:\n logger.info("Program aborted by user")\n except Exception as e:\n logger.info(e)\n
Run Code Online (Sandbox Code Playgroud)\n要(直接)运行 mod1.py 使用python3 -m my_log_test.pkg1.mod1
## mod1.py\nimport logging\n# mod1_logger = logging.getLogger(__name__)\nmod1_logger = logging.getLogger("my_log_test.daemon.pkg1.mod1") ## for testing, namespace set manually\n\ndef do1():\n print("print in mod1.do1()")\n print("print mod1.py __name__: " +__name__)\n mod1_logger.info("Doing someting in pkg1.do1()")\n\nif __name__ == \'__main__\':\n ## Also enable this pkg to be run directly while in development with\n ## python3 -m my_log_test.pkg1.mod1\n\n ## init root logger\n from my_log_test.common.my_logger import logger_init\n logger_init() ## init root logger\n\n print("print in mod1.py if __name__ == \'__main__\'")\n mod1_logger.info("Running mod1.py as main")\n do1()\n
Run Code Online (Sandbox Code Playgroud)\n要(直接)运行 mod2.py 使用python3 -m my_log_test.pkg2.mod2
## mod2.py\nimport logging\nlogger = logging.getLogger(__name__)\n\ndef do2():\n print("print in pkg2.do2()")\n print("print mod2.py __name__: " +__name__) # setting namespace through __name__\n logger.info("Doing someting in pkg2.do2()")\n\nif __name__ == \'__main__\':\n ## Also enable this pkg to be run directly while in development with\n ## python3 -m my_log_test.pkg2.mod2\n\n ## init root logger\n from my_log_test.common.my_logger import logger_init\n logger_init() ## init root logger\n\n print("print in mod2.py if __name__ == \'__main__\'")\n logger.info("Running mod2.py as main")\n do2()\n
Run Code Online (Sandbox Code Playgroud)\n如果有帮助的话很高兴。也很高兴收到反馈!
\n其中一些答案表明,您可以在模块的顶部进行操作
import logging
logger = logging.getLogger(__name__)
Run Code Online (Sandbox Code Playgroud)
据我所知,这被认为是非常糟糕的做法.原因是文件配置将默认禁用所有现有记录器.例如
#my_module
import logging
logger = logging.getLogger(__name__)
def foo():
logger.info('Hi, foo')
class Bar(object):
def bar(self):
logger.info('Hi, bar')
Run Code Online (Sandbox Code Playgroud)
在您的主要模块中:
#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()
Run Code Online (Sandbox Code Playgroud)
现在,logging.ini中指定的日志将为空,因为fileconfig调用已禁用现有记录器.
虽然肯定可以解决这个问题(disable_existing_Loggers = False),但实际上你的库的许多客户端都不会知道这种行为,也不会收到你的日志.通过始终在本地调用logging.getLogger,使您的客户轻松.帽子提示:我从Victor Lin的网站上了解到了这种行为.
因此,良好的做法是始终在本地调用logging.getLogger.例如
#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')
Run Code Online (Sandbox Code Playgroud)
此外,如果在main中使用fileconfig,请设置disable_existing_loggers = False,以防您的库设计者使用模块级记录器实例.
扔进另一个解决方案.
在我的模块的主init中,我有类似的东西:
# mymodule/__init__.py
import logging
def get_module_logger(mod_name):
logger = logging.getLogger(mod_name)
handler = logging.StreamHandler()
formatter = logging.Formatter(
'%(asctime)s %(name)-12s %(levelname)-8s %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.setLevel(logging.DEBUG)
return logger
Run Code Online (Sandbox Code Playgroud)
然后在每个班级我需要一个记录器,我这样做:
# mymodule/foo.py
from [modname] import get_module_logger
logger = get_module_logger(__name__)
Run Code Online (Sandbox Code Playgroud)
当错过日志时,您可以通过它们来自的模块区分它们的来源.
你也可以想出这样的东西!
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
Run Code Online (Sandbox Code Playgroud)
现在你可以在同一个模块和整个项目中使用多个记录器,如果上面是在一个单独的模块中定义的,并且需要在其他模块中导入,则需要记录。
a=get_logger("__app___")
b=get_logger("__basic_log__")
a.info("Starting logging!")
b.debug("Debug Mode")
Run Code Online (Sandbox Code Playgroud)
@ 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)
Run Code Online (Sandbox Code Playgroud)
因此LoggerManager可以插入整个应用程序.希望它有意义和有价值.
归档时间: |
|
查看次数: |
146319 次 |
最近记录: |