使用logging模块记录执行日志需先导入模块并调用logging.basicconfig()进行基础配置,包括设置日志级别、格式、输出文件和写入模式;2. 配置后通过logging.debug()、logging.info()、logging.error()等方法在代码中记录不同级别的日志信息;3. 可创建logger、handler和formatter对象实现更高级配置,如将日志同时输出到文件和控制台,并为不同模块使用logging.getlogger(__name__)创建独立logger;4. 在多进程环境中,应使用logging.handlers.queuehandler与logging.handlers.queuelistener结合队列机制,确保日志写入的线程安全与一致性,避免多进程同时写入导致的日志混乱。
Python命令如何使用logging模块记录执行日志?简单来说,就是利用logging模块,在你的Python脚本里插入一些记录信息的代码,这样程序运行的时候,就能把关键信息保存下来,方便你排查问题或者分析程序运行情况。
logging模块提供了灵活的配置,可以控制日志级别、输出格式和存储位置。下面详细说明操作方法。
解决方案
立即学习“Python免费学习笔记(深入)”;
配置logging模块
首先,你需要导入logging模块,并进行基本配置。配置内容包括日志级别、日志格式、输出位置等。
import logging # 配置日志级别,低于DEBUG级别的日志不会被记录 logging.basicConfig(level=logging.DEBUG, # 日志格式,包括时间、级别、文件名、行号、消息 format='%(asctime)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s', # 日志输出到文件,可以指定文件名 filename='my_program.log', # 文件写入模式,'w'表示覆盖,'a'表示追加 filemode='a')
这段代码做了几件事:
-
logging.basicConfig()
: 这是最基本的配置方法。
-
level=logging.DEBUG
: 设置日志级别为DEBUG,意味着DEBUG、INFO、WARNING、ERROR、CRITICAL级别的日志都会被记录。你可以根据需要调整这个级别。
-
format='%(asctime)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s'
: 定义日志信息的格式。
%(asctime)s
是时间,
%(levelname)s
是日志级别,
%(filename)s
是文件名,
%(lineno)d
是行号,
%(message)s
是日志消息。
-
filename='my_program.log'
: 指定日志输出到文件
my_program.log
。如果不指定,默认输出到控制台。
-
filemode='a'
: 设置文件写入模式为追加。每次运行程序,新的日志信息都会追加到文件末尾。如果设置为
'w'
,每次运行都会覆盖之前的内容。
记录日志信息
配置好logging模块后,就可以在代码中插入日志记录语句了。
import logging logging.basicConfig(level=logging.DEBUG, format='%(asctime)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s', filename='my_program.log', filemode='a') def my_function(x, y): logging.debug(f"Function my_function called with x={x}, y={y}") try: result = x / y logging.info(f"Result of x / y is {result}") return result except ZeroDivisionError: logging.error("Division by zero!") return None if __name__ == '__main__': my_function(10, 2) my_function(5, 0)
这段代码演示了如何使用不同级别的日志记录:
-
logging.debug()
: 记录调试信息,通常用于开发阶段。
-
logging.info()
: 记录一般信息,用于程序正常运行时的信息。
-
logging.error()
: 记录错误信息,用于程序出现错误时的信息。
当程序运行时,日志信息会被写入到
my_program.log
文件中。
更高级的配置:Logger、Handler和Formatter
logging.basicConfig()
提供了一些基本的配置,但logging模块的功能远不止于此。你可以使用Logger、Handler和Formatter进行更高级的配置。
- Logger: 负责接收日志信息,并传递给Handler。
- Handler: 负责将日志信息输出到不同的目标,比如文件、控制台、网络等。
- Formatter: 负责格式化日志信息。
下面是一个更复杂的例子:
import logging # 创建一个logger logger = logging.getLogger('my_logger') logger.setLevel(logging.DEBUG) # 创建一个handler,用于输出到文件 file_handler = logging.FileHandler('my_program.log', mode='a') file_handler.setLevel(logging.INFO) # 创建一个handler,用于输出到控制台 console_handler = logging.StreamHandler() console_handler.setLevel(logging.DEBUG) # 创建一个formatter formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # 将formatter添加到handler file_handler.setFormatter(formatter) console_handler.setFormatter(formatter) # 将handler添加到logger logger.addHandler(file_handler) logger.addHandler(console_handler) def my_function(x, y): logger.debug(f"Function my_function called with x={x}, y={y}") try: result = x / y logger.info(f"Result of x / y is {result}") return result except ZeroDivisionError: logger.error("Division by zero!") return None if __name__ == '__main__': my_function(10, 2) my_function(5, 0)
这个例子中,我们创建了一个名为
my_logger
的logger,并添加了两个handler:一个用于输出到文件,一个用于输出到控制台。 文件handler只记录INFO级别及以上的日志,而控制台handler记录DEBUG级别及以上的日志。 这样,你就可以根据需要,将不同级别的日志输出到不同的地方。
如何自定义日志格式?
你可以通过
logging.Formatter
来自定义日志格式。
Formatter
接受一个格式字符串作为参数,格式字符串中可以使用各种占位符来表示不同的日志信息。
常用的占位符包括:
-
%(asctime)s
: 日志产生的时间
-
%(name)s
: logger的名字
-
%(levelname)s
: 日志级别
-
%(message)s
: 日志消息
-
%(filename)s
: 文件名
-
%(lineno)d
: 行号
-
%(module)s
: 模块名
-
%(funcName)s
: 函数名
-
%(process)d
: 进程ID
-
%(thread)d
: 线程ID
例如,你可以使用以下格式字符串:
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(filename)s:%(lineno)d - %(message)s')
这将生成包含时间、logger名字、日志级别、文件名、行号和消息的日志信息。
如何根据不同的模块使用不同的Logger?
在大型项目中,通常会将代码分成多个模块。 为了方便管理日志,可以为每个模块创建一个Logger。
# module1.py import logging logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) # 创建一个handler,用于输出到文件 file_handler = logging.FileHandler('my_program.log', mode='a') file_handler.setLevel(logging.INFO) # 创建一个formatter formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # 将formatter添加到handler file_handler.setFormatter(formatter) # 将handler添加到logger logger.addHandler(file_handler) def my_function(): logger.debug("my_function in module1 called") # module2.py import logging from module1 import my_function logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) # 创建一个handler,用于输出到文件 file_handler = logging.FileHandler('my_program.log', mode='a') file_handler.setLevel(logging.INFO) # 创建一个formatter formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') # 将formatter添加到handler file_handler.setFormatter(formatter) # 将handler添加到logger logger.addHandler(file_handler) def another_function(): logger.debug("another_function in module2 called") my_function() if __name__ == '__main__': another_function()
在这个例子中,
module1.py
和
module2.py
都创建了自己的Logger,logger的名字分别是
module1
和
module2
。 这样,在日志信息中就可以区分来自不同模块的日志。 使用
__name__
作为Logger的名字是一个常用的技巧,因为
__name__
会自动设置为模块的名字。
如何在多线程或多进程中使用logging模块?
在多线程或多进程环境中,需要注意logging模块的线程安全问题。 logging模块本身是线程安全的,但如果多个线程或进程同时写入同一个文件,可能会导致日志信息混乱。
为了避免这个问题,可以使用以下方法:
- 使用
logging.handlers.RotatingFileHandler
或
logging.handlers.TimedRotatingFileHandler
- 使用
logging.handlers.QueueHandler
和
logging.handlers.QueueListener
这里给出一个使用
logging.handlers.QueueHandler
和
logging.handlers.QueueListener
的例子:
import logging import logging.handlers import multiprocessing import queue import time def worker_process(queue): # 配置worker进程的logger logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) handler = logging.handlers.QueueHandler(queue) # Use QueueHandler logger.addHandler(handler) # 记录一些日志 logger.info('Worker process started') logger.debug('Debugging message from worker') logger.warning('Warning message from worker') logger.error('Error message from worker') logger.info('Worker process finished') def listener_process(queue): # 配置listener进程的logger logger = logging.getLogger(__name__) logger.setLevel(logging.DEBUG) # 创建一个FileHandler来写入日志文件 file_handler = logging.FileHandler('multiprocessing_example.log', mode='a') formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') file_handler.setFormatter(formatter) # 使用QueueListener来监听队列并将日志记录到handler listener = logging.handlers.QueueListener(queue, file_handler) listener.start() listener.join() if __name__ == '__main__': # 创建一个队列 log_queue = queue.Queue(-1) # 创建并启动listener进程 listener = multiprocessing.Process(target=listener_process, args=(log_queue,)) listener.start() # 创建并启动多个worker进程 processes = [] for i in range(3): process = multiprocessing.Process(target=worker_process, args=(log_queue,)) processes.append(process) process.start() # 等待所有worker进程完成 for process in processes: process.join() # 发送一个None到队列中,表示listener可以停止 log_queue.put(None) listener.join() print("Finished!")
这个例子中,我们创建了一个listener进程和一个或多个worker进程。 worker进程将日志信息发送到一个队列中,listener进程从队列中读取日志信息并写入文件。 这样可以避免多个进程同时写入同一个文件,保证日志信息的完整性和一致性。 注意,需要在listener进程中调用
listener.start()
和
listener.join()
,以启动和等待listener线程。 还需要在所有worker进程完成后,向队列中发送一个
None
,以通知listener进程停止。
总而言之,Python的logging模块是一个非常强大和灵活的工具,可以帮助你记录程序的运行信息,方便你排查问题和分析程序运行情况。 你可以根据自己的需要,选择合适的配置方式和日志级别,以及使用不同的Handler和Formatter,来实现各种各样的日志记录需求。
评论(已关闭)
评论已关闭