在 Python 中记录到线程特定的和共享的日志文件

Jat*_*mar 7 python logging multithreading

我想使用 Python 的logging模块在多线程 Python 应用程序中实现每线程日志记录。我在主模块(创建线程)中的记录器名称中附加了一个唯一的 ID:

mylogger = logging.getLogger(str(someInt) + __name__)
Run Code Online (Sandbox Code Playgroud)

该模块使用多个也支持日志记录的模块,但它们的记录器初始化如下:

mylogger = logging.getLogger(__name__)
Run Code Online (Sandbox Code Playgroud)

由于被调用者类看不到调用者的记录器,调用者的日志是线程特定的,但被调用者的日志根据其路径放在一个全局文件中。

我们可以做些什么来避免更改传递str(someInt)给每个其他模块并保持不变地使用它们,但仍然记录到特定于线程的文件?

Sha*_* Hu 9

它可以通过 实现logging.Filter,这是一个例子:

import threading
import logging
import logging.config


class ThreadLogFilter(logging.Filter):
    """
    This filter only show log entries for specified thread name
    """

    def __init__(self, thread_name, *args, **kwargs):
        logging.Filter.__init__(self, *args, **kwargs)
        self.thread_name = thread_name

    def filter(self, record):
        return record.threadName == self.thread_name


def start_thread_logging():
    """
    Add a log handler to separate file for current thread
    """
    thread_name = threading.Thread.getName(threading.current_thread())
    log_file = '/tmp/perThreadLogging-{}.log'.format(thread_name)
    log_handler = logging.FileHandler(log_file)

    log_handler.setLevel(logging.DEBUG)

    formatter = logging.Formatter(
        "%(asctime)-15s"
        "| %(threadName)-11s"
        "| %(levelname)-5s"
        "| %(message)s")
    log_handler.setFormatter(formatter)

    log_filter = ThreadLogFilter(thread_name)
    log_handler.addFilter(log_filter)

    logger = logging.getLogger()
    logger.addHandler(log_handler)

    return log_handler


def stop_thread_logging(log_handler):
    # Remove thread log handler from root logger
    logging.getLogger().removeHandler(log_handler)

    # Close the thread log handler so that the lock on log file can be released
    log_handler.close()


def worker():
    thread_log_handler = start_thread_logging()
    logging.info('Info log entry in sub thread.')
    logging.debug('Debug log entry in sub thread.')
    stop_thread_logging(thread_log_handler)


def config_root_logger():
    log_file = '/tmp/perThreadLogging.log'

    formatter = "%(asctime)-15s" \
                "| %(threadName)-11s" \
                "| %(levelname)-5s" \
                "| %(message)s"

    logging.config.dictConfig({
        'version': 1,
        'formatters': {
            'root_formatter': {
                'format': formatter
            }
        },
        'handlers': {
            'console': {
                'level': 'INFO',
                'class': 'logging.StreamHandler',
                'formatter': 'root_formatter'
            },
            'log_file': {
                'class': 'logging.FileHandler',
                'level': 'DEBUG',
                'filename': log_file,
                'formatter': 'root_formatter',
            }
        },
        'loggers': {
            '': {
                'handlers': [
                    'console',
                    'log_file',
                ],
                'level': 'DEBUG',
                'propagate': True
            }
        }
    })


if __name__ == '__main__':
    config_root_logger()

    logging.info('Info log entry in main thread.')
    logging.debug('Debug log entry in main thread.')

    for i in xrange(3):
        t = threading.Thread(target=worker,
                             name='Thread-{}'.format(i),
                             args=[])
        t.start()

Run Code Online (Sandbox Code Playgroud)

控制台输出:

$ python perThreadLoggingSample.py
2019-03-07 10:29:34,318| MainThread | INFO | Info log entry in main thread.
2019-03-07 10:29:34,320| Thread-0   | INFO | Info log entry in sub thread.
2019-03-07 10:29:34,320| Thread-1   | INFO | Info log entry in sub thread.
2019-03-07 10:29:34,320| Thread-2   | INFO | Info log entry in sub thread.
$
Run Code Online (Sandbox Code Playgroud)

检查日志文件:

$ cat /tmp/perThreadLogging.log
2019-03-07 10:29:34,318| MainThread | INFO | Info log entry in main thread.
2019-03-07 10:29:34,319| MainThread | DEBUG| Debug log entry in main thread.
2019-03-07 10:29:34,320| Thread-0   | INFO | Info log entry in sub thread.
2019-03-07 10:29:34,320| Thread-1   | INFO | Info log entry in sub thread.
2019-03-07 10:29:34,321| Thread-0   | DEBUG| Debug log entry in sub thread.
2019-03-07 10:29:34,320| Thread-2   | INFO | Info log entry in sub thread.
2019-03-07 10:29:34,321| Thread-1   | DEBUG| Debug log entry in sub thread.
2019-03-07 10:29:34,321| Thread-2   | DEBUG| Debug log entry in sub thread.
$
$ cat /tmp/perThreadLogging-Thread-0.log
2019-03-07 10:29:34,320| Thread-0   | INFO | Info log entry in sub thread.
2019-03-07 10:29:34,321| Thread-0   | DEBUG| Debug log entry in sub thread.
$
$ cat /tmp/perThreadLogging-Thread-1.log
2019-03-07 10:29:34,320| Thread-1   | INFO | Info log entry in sub thread.
2019-03-07 10:29:34,321| Thread-1   | DEBUG| Debug log entry in sub thread.
$
$ cat /tmp/perThreadLogging-Thread-2.log
2019-03-07 10:29:34,320| Thread-2   | INFO | Info log entry in sub thread.
2019-03-07 10:29:34,321| Thread-2   | DEBUG| Debug log entry in sub thread.
$
Run Code Online (Sandbox Code Playgroud)

  • 它最终不会默认将线程日志写入单个日志文件。它同时写入“/tmp/perThreadLogging.log”文件和“/tmp/perThreadLogging-Thread-N.log”文件。在 config_root_logger() 函数中,有 2 个根日志处理程序:console 写入控制台屏幕,log_file 写入主日志文件。当`worker()`线程启动时,`start_thread_logging()`会创建一个新的日志处理程序`FileHandler`,它只将当前线程的日志写入线程日志文件,同时,2个根日志处理程序仍在工作,他们还将线程日志写入控制台屏幕和主日志文件。 (2认同)

Mar*_*rek 3

虽然,我对我所建议的内容并没有真正的经验,但我会尝试使用线程本地存储

\n\n

线程本地存储

\n\n
\n

表示线程本地数据的类。线程本地数据是其值特定于线程的数据。要管理线程本地数据,只需创建本地实例(或子类)并在其上存储属性。对于不同的线程,instance\xe2\x80\x99s 值会有所不同。

\n
\n\n

然后,您可以通过以下方式保存和访问变量:

\n\n
import threading\nmydata = threading.local()\nmydata.x = 1\n
Run Code Online (Sandbox Code Playgroud)\n\n

我建议在创建特定于线程的记录器时将其保存到线程局部变量中,然后在必要时通过线程局部存储再次访问记录器。

\n\n

看看,它帮助我理解了线程本地:

\n\n\n