我想添加我的解决方案(基于记录食谱和该线程的其他文章和建议。但是我花了很长时间才弄清楚为什么它没有立即按我的预期工作。所以我创建了一个测试项目以了解日志记录是如何工作的。
既然我已经弄清楚了,我想分享我的解决方案,也许它可以对某人有所帮助。
我知道我的一些代码可能不是最佳实践,但我仍在学习。当我使用它们时,我将这些功能留print()
在了那里,而日志记录没有按预期工作。这些已在我的其他应用程序中删除。我也欢迎对代码或结构的任何部分提供任何反馈。
my_log_test 项目结构(从我从事的另一个项目中克隆/简化)
my_log_test
├── __init__.py
├── __main__.py
├── daemon.py
├── common
│ ├── my_logger.py
├── pkg1
│ ├── __init__.py
│ └── mod1.py
└── pkg2
├── __init__.py
└── mod2.py
要求
在我使用的组合中,有一些不同的或我没有明确提到的东西:
- 主模块是
daemon.py
由__main__.py
- 我希望能够在开发/测试中单独
mod1.py
调用模块mod2.py
- 在这一点上,我不想使用
basicConfig()
或FileConfig()
保留它,就像在日志记录食谱中一样
所以基本上,这意味着,我需要在(总是)和模块中以及(仅在直接调用它们时)初始化根记录器。daemon.py
mod1.py
mod2.py
为了使几个模块中的这个初始化更容易,我创建了my_logger.py
哪个,这在食谱中有所描述。
我的错
logger = logging.getLogger(__name__)
之前,我在该模块中的错误是使用(module logger) 而不是使用logger = logging.getLogger()
(获取根记录器)来初始化记录器。
第一个问题是,当从daemon.py
记录器的命名空间调用时被设置为my_log_test.common.my_logger
. mod1.py
因此,具有“不匹配”命名空间的模块记录器my_log_test.pkg1.mod1
无法附加到另一个记录器,并且我看不到 mod1 的日志输出。
第二个“问题”是,我的主程序是 indaemon.py
而不是__main__.py
. 但毕竟对我来说不是一个真正的问题,但它增加了命名空间的混乱。
工作解决方案
这是来自食谱,但在一个单独的模块中。我还添加了一个logger_cleanup
可以从守护进程调用的函数,以删除超过 x 天的日志。
## my_logger.py
from datetime import datetime
import time
import os
## Init logging start
import logging
import logging.handlers
def logger_init():
print("print in my_logger.logger_init()")
print("print my_logger.py __name__: " +__name__)
path = "log/"
filename = "my_log_test.log"
## get logger
#logger = logging.getLogger(__name__) ## this was my mistake, to init a module logger here
logger = logging.getLogger() ## root logger
logger.setLevel(logging.INFO)
# File handler
logfilename = datetime.now().strftime("%Y%m%d_%H%M%S") + f"_{filename}"
file = logging.handlers.TimedRotatingFileHandler(f"{path}{logfilename}", when="midnight", interval=1)
#fileformat = logging.Formatter("%(asctime)s [%(levelname)s] %(message)s")
fileformat = logging.Formatter("%(asctime)s [%(levelname)s]: %(name)s: %(message)s")
file.setLevel(logging.INFO)
file.setFormatter(fileformat)
# Stream handler
stream = logging.StreamHandler()
#streamformat = logging.Formatter("%(asctime)s [%(levelname)s:%(module)s] %(message)s")
streamformat = logging.Formatter("%(asctime)s [%(levelname)s]: %(name)s: %(message)s")
stream.setLevel(logging.INFO)
stream.setFormatter(streamformat)
# Adding all handlers to the logs
logger.addHandler(file)
logger.addHandler(stream)
def logger_cleanup(path, days_to_keep):
lclogger = logging.getLogger(__name__)
logpath = f"{path}"
now = time.time()
for filename in os.listdir(logpath):
filestamp = os.stat(os.path.join(logpath, filename)).st_mtime
filecompare = now - days_to_keep * 86400
if filestamp < filecompare:
lclogger.info("Delete old log " + filename)
try:
os.remove(os.path.join(logpath, filename))
except Exception as e:
lclogger.exception(e)
continue
运行 deamon.py(通过__main__.py
)使用python3 -m my_log_test
## __main__.py
from my_log_test import daemon
if __name__ == '__main__':
print("print in __main__.py")
daemon.run()
运行 deamon.py (直接)使用python3 -m my_log_test.daemon
## daemon.py
from datetime import datetime
import time
import logging
import my_log_test.pkg1.mod1 as mod1
import my_log_test.pkg2.mod2 as mod2
## init ROOT logger from my_logger.logger_init()
from my_log_test.common.my_logger import logger_init
logger_init() ## init root logger
logger = logging.getLogger(__name__) ## module logger
def run():
print("print in daemon.run()")
print("print daemon.py __name__: " +__name__)
logger.info("Start daemon")
loop_count = 1
while True:
logger.info(f"loop_count: {loop_count}")
logger.info("do stuff from pkg1")
mod1.do1()
logger.info("finished stuff from pkg1")
logger.info("do stuff from pkg2")
mod2.do2()
logger.info("finished stuff from pkg2")
logger.info("Waiting a bit...")
time.sleep(30)
if __name__ == '__main__':
try:
print("print in daemon.py if __name__ == '__main__'")
logger.info("running daemon.py as main")
run()
except KeyboardInterrupt as e:
logger.info("Program aborted by user")
except Exception as e:
logger.info(e)
要(直接)运行 mod1.py,请使用python3 -m my_log_test.pkg1.mod1
## mod1.py
import logging
# mod1_logger = logging.getLogger(__name__)
mod1_logger = logging.getLogger("my_log_test.daemon.pkg1.mod1") ## for testing, namespace set manually
def do1():
print("print in mod1.do1()")
print("print mod1.py __name__: " +__name__)
mod1_logger.info("Doing someting in pkg1.do1()")
if __name__ == '__main__':
## Also enable this pkg to be run directly while in development with
## python3 -m my_log_test.pkg1.mod1
## init root logger
from my_log_test.common.my_logger import logger_init
logger_init() ## init root logger
print("print in mod1.py if __name__ == '__main__'")
mod1_logger.info("Running mod1.py as main")
do1()
要(直接)运行 mod2.py,请使用python3 -m my_log_test.pkg2.mod2
## mod2.py
import logging
logger = logging.getLogger(__name__)
def do2():
print("print in pkg2.do2()")
print("print mod2.py __name__: " +__name__) # setting namespace through __name__
logger.info("Doing someting in pkg2.do2()")
if __name__ == '__main__':
## Also enable this pkg to be run directly while in development with
## python3 -m my_log_test.pkg2.mod2
## init root logger
from my_log_test.common.my_logger import logger_init
logger_init() ## init root logger
print("print in mod2.py if __name__ == '__main__'")
logger.info("Running mod2.py as main")
do2()
如果有帮助,我很高兴。也很高兴收到反馈!