简体   繁体   English

项目的结构化日志记录

[英]Structured Logging for a project

I am using Logging on my python project. 我在我的python项目上使用Logging。 My question is about the correct and elegant way of structuring the logging in my project, for that i will first describe the structure i am using. 我的问题是关于在我的项目中构建日志记录的正确和优雅的方法,因为我将首先描述我正在使用的结构。

I have 3 main .py scripts, so acquisition.py , actuation.py and management.py, that are called as services at startup. 我有3个主要的.py脚本,所以在启动时称为服务的acquisition.py,actus.py和management.py。 The 3 scripts above import network.py and devices.py. 上面的3个脚本导入network.py和devices.py。

I wanted to organize my logging per file associated with the 3 main scripts, so i would like to have: acquisition.log , actuation.log and management.log. 我想组织与3个主要脚本相关的每个文件的日志记录,所以我想拥有:acquisition.log,actuation.log和management.log。 Inside this logs i would like to have the respective logs from the calls to network.py a device.py (using the namespace %(name) ) 在这个日志里面我希望从对network.py的调用中得到相应的日志一个device.py(使用名称空间%(name)

Example (acquisition.log): 示例(acquisition.log):

INFO acquisition.thread 2016-03-17 12:26:02,069 in Thread 70de3b66-e14b-11e5-8953-80fa5b0ae007
DEBUG acquisition 2016-03-17 12:26:02,070 Thread launched 70de3b66-e14b-11e5-8953-80fa5b0ae007
INFO acquisition.devices 2016-03-17 12:26:03,072  Variable_R read:  0013a20040c1bb0b  temperature1
ERROR acquisition.devices 2016-03-17 12:26:19,076 variable.read.DeviceConfigurationError: 0013a20040c1bb0b 
INFO acquisition.thread 2016-03-17 12:26:19,077 exit Thread 70ddfa20-e14b-11e5-8953-80fa5b0ae007
ERROR acquisition.devices 2016-03-17 12:26:25,085 variable.read.DeviceConfigurationError: 0013a20040c1bb0b 
INFO acquisition.thread 2016-03-17 12:26:25,086 exit Thread 70de3b66-e14b-11e5-8953-80fa5b0ae007

In this case you can see that for the same log file, i can have logging from different files, it can be seen in the log namespace acquisition.thread and acquisition.devices . 在这种情况下,您可以看到,对于相同的日志文件,我可以从不同的文件进行日志记录,可以在日志命名空间acquisition.threadacquisition.devices中看到。

The way i achieve this is having a logger function that i use to create a fallback logger in each file, where i am logging. 我实现这一点的方法是使用记录器功能,我用它在每个文件中创建一个回退记录器,我正在记录。 And, later if i want to log information from that file, i alter in the main script file the logger created in the imported file. 并且,稍后如果我想记录该文件中的信息,我将在主脚本文件中更改导入文件中创建的记录器。

Code example that explain the above: 代码示例解释了上述内容:

acquisition.py (main script): acquisition.py(主脚本):

imports...
import logger_sys
import logging
import xnetwork
import xdevices

# Log configuration
log_name = os.path.basename(__file__).strip(".py")
logit = logger_sys.setup_logger(log_name, log_name) #internal logger for main file
logger_thread = logging.getLogger(log_name + '.thread')

#Log configuration of external files
xnetwork.logger = logging.getLogger(log_name + '.network')
xdevices.logger = logging.getLogger(log_name + '.devices')

logit.info("START acquisition_service")

# REST OF THE CODE... 

xdevices.py: xdevices.py:

import logger_sys

# Fallback logger in case the calling script doesnt modify logger
log_name = __name__.strip(".py") + '_fallback'
logger = logger_sys.setup_logger(log_name, log_name, stream_hdlr=False)

# REST OF THE CODE...

xnetworks.py: xnetworks.py:

import logger_sys

# Fallback logger in case the calling script doesnt modify logger
log_name = __name__.strip(".py") + '_fallback'
logger = logger_sys.setup_logger(log_name, log_name, stream_hdlr=False)

# REST OF THE CODE...

logger_sys.py: logger_sys.py:

import logging, sys, os
from global_settings import RUNNING_MODE, DEBUG, STAGING, PRODUCTION

def setup_logger(namespace, filename, stream_hdlr=True):

    logger = logging.getLogger(namespace)

    handler_format = logging.Formatter("%(levelname)s %(name)s %(asctime)s %(message)s")

    log_handler = logging.FileHandler(filename + ".log")
    logger.addHandler(log_handler)
    log_handler.setFormatter(handler_format)

    if RUNNING_MODE == DEBUG:
        if stream_hdlr:
            log_handler = logging.StreamHandler(sys.stdout)
            logger.addHandler(log_handler)
            log_handler.setFormatter(handler_format)

        logger.setLevel(logging.DEBUG)

    elif RUNNING_MODE == STAGING or RUNNING_MODE == PRODUCTION:
        logger.setLevel(logging.INFO)

    return logger

Objective? 目的?

  • I would like to know if there are a more elegant solutions, that don't use the logic of passing the logger as an argument in the methods that are called. 我想知道是否有更优雅的解决方案,不使用将logger作为参数传递给被调用方法的逻辑。

  • I would like to understand how typically logging is structured in more complex cases, for example when using external modules. 我想了解在更复杂的情况下通常如何构建日志记录,例如在使用外部模块时。

  • And, i would like to read critics about this strategy of logging. 而且,我想阅读关于这种采伐策略的批评者。

Thank you in advance 先感谢您

You should study the official logging cookbook and some complex projects that do logging right. 您应该学习正式的日志记录和一些正确记录的复杂项目。 I suggest reading the requests source code to see how one fairly complex project does logging. 我建议阅读请求源代码 ,看看一个相当复杂的项目如何进行日志记录。

Perhaps the key takeaway from the cookbook for your case is: 对您的案例来说,食谱的关键点可能是:

Multiple calls to logging.getLogger('someLogger') return a reference to the same logger object. 对logging.getLogger('someLogger')的多次调用返回对同一记录器对象的引用。 This is true not only within the same module, but also across modules as long as it is in the same Python interpreter process. 这不仅在同一模块中,而且在模块之间也是如此,只要它在同一个Python解释器过程中。

A typical approach is to have something like the following at the top of a file: 一种典型的方法是在文件的顶部有类似下面的内容:

import logging
log = logging.getLogger(__name__)

This makes log global, so it can be used within functions without passing log as an argument: 这使log成为全局,因此可以在函数中使用它而不将log作为参数传递:

def add(x, y):
    log.debug('Adding {} and {}'.format(x, y))
    return x + y

If you're creating some long running service and intend to log all/most/many function calls, consider using a decorator. 如果您正在创建一些长时间运行的服务并打算记录所有/大多数/多个函数调用,请考虑使用装饰器。 I suggest this post from the Fresh Books Dev Blog as a introduction to using decorators for logging. 我建议来自Fresh Books Dev Blog的这篇文章作为使用装饰器进行日志记录的介绍。 It sounds like your program might benefit from the decorator approach. 听起来您的程序可能会受益于装饰器方法。

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

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