GIScoding

虎皮猫咖

python Logging日志模块简介

15
2025-01-14

本文主体部分参考此篇文章: 标点符-python Logger 模块

使用 logging 模块,可以在应用程序中记录调试信息、错误消息和其他运行时信息。

  1. 记录调试信息:可以在代码中插入日志信息,用于追踪程序的执行过程,例如函数的输入输出、变量的值等,帮助调试程序。

  2. 记录错误信息:可以在捕获异常时记录错误信息,方便排查问题。例如,记录未捕获的异常、错误堆栈等。

  3. 监控程序运行:可以将程序的运行情况记录到日志文件中,便于在程序运行过程中或运行后分析日志,了解程序的状态和行为。

最近使用日志模块,融入自己项目代码中。
image.png

模块功能简介

Python的logging模块是一个非常强大的工具,用于在应用程序中记录和管理日志信息。它提供了灵活的功能,可以在不同的输出目标(如控制台、文件、网络等)中记录日志,并支持不同的日志级别。

  • Logger(记录器):Logger对象是应用程序可以直接使用的接口。你可以通过getLogger(name)获取一个记录器。通常,记录器会按照模块或类的名称进行命名,以便于区分不同的日志来源。

  • Handler(处理器):Handler用于定义日志消息发送到的位置。常见的处理器有StreamHandler(用于输出到控制台)和FileHandler(用于输出到文件)。每个记录器可以附加多个处理器。

  • Formatter(格式化器):Formatter用于定义日志消息的最终输出格式。你可以指定时间、日志级别、消息等的格式。通过设置格式化器,可以使日志输出更具可读性。

image.png

- Logger 是日志记录的入口。

- Handler 决定日志消息发送到哪里,例如控制台或文件。

- Formatter 决定日志消息的具体格式。

基本用法

import logging

# 创建一个logger
logger = logging.getLogger('example_logger')
logger.setLevel(logging.DEBUG)  # 设置最低日志级别

# 创建一个handler,用于写入日志文件
file_handler = logging.FileHandler('example.log')
file_handler.setLevel(logging.DEBUG)

# 创建一个handler,用于输出到控制台
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.INFO)

# 创建一个格式化器,并设置给处理器
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
file_handler.setFormatter(formatter)
console_handler.setFormatter(formatter)

# 给logger添加handler
logger.addHandler(file_handler)
logger.addHandler(console_handlerwrei
logger.info('This is an info message')
logger.warning('This is a warning message')
logger.error('This is an error message')
logger.critical('This is a critical message')

Logger 模块

核心组件,用于记录日志消息。每个 Logger 对象对应一个日志记录器。

常用方法:

  • getLogger(name):获取指定名称的 Logger 对象。如果 Logger 对象不存在,则创建一个新的。大型工程中常有不同的 logger 模块,需要分别记录不同的消息。

  • debug(msg, *args, **kwargs):记录调试级别的日志消息。

  • info(msg, *args, **kwargs):记录信息级别的日志消息。

  • warning(msg, *args, **kwargs):记录警告级别的日志消息。

  • error(msg, *args, **kwargs):记录错误级别的日志消息。

  • exception(msg, *args, **kwargs):记录异常级别的日志消息(包含异常信息)。

  • critical(msg, *args, **kwargs):记录严重错误级别的日志消息。

  • setLevel(level):设置 Logger 对象的日志级别。

  • addHandler(handler):添加日志处理器。

  • removeHandler(handler):移除日志处理器。

logging 模块支持不同的日志级别(从低到高):

  1. DEBUG:用于调试目的,记录详细的日志信息,通常只在开发阶段使用。
  2. INFO:用于记录程序正常运行时的信息,如启动、配置等。
  3. WARNING:用于记录潜在的问题或异常情况,但程序仍能正常运行。
  4. ERROR:用于记录错误信息,程序可能无法执行某些功能。
  5. CRITICAL:用于记录严重的错误,程序可能无法继续运行。

通过设置不同的日志级别,可以控制哪些信息需要记录,哪些可以忽略。

import logging

logger = logging.getLogger('my_logger')
logger.setLevel(logging.DEBUG)

logger.debug('This is a debug message')
logger.info('This is an info message')
logger.warning('This is a warning message')
logger.error('This is an error message')
logger.critical('This is a critical message')

Handler 模块

功能:处理日志消息并决定如何将其输出到不同的目标(例如控制台、文件、网络)。

常用处理器:

  • StreamHandler:将日志消息输出到流(如stdout 或 sys.stderr)。
  • FileHandler:将日志消息写入文件。
  • RotatingFileHandler:将日志消息写入文件,并在文件达到指定大小时轮换。
  • TimedRotatingFileHandler:根据时间间隔轮换日志文件。
  • SocketHandler:将日志消息发送到网络套接字。
  • SMTPHandler:通过电子邮件发送日志消息。
  • HTTPHandler:通过 HTTP 请求发送日志消息。

self.logger = logging.getLogger(log_type.value)
self.logger.setLevel(logging.DEBUG)  # 设置为最低级别,允许所有日志

if not self.logger.handlers:
    
    # 控制台处理器 - INFO级别
    console_handler = logging.StreamHandler()
    console_handler.setLevel(logging.INFO)
    
    # 文件处理器 - DEBUG级别
    file_handler = logging.FileHandler(self.log_path)
    file_handler.setLevel(logging.DEBUG)
    
    # 定义日志格式
    formatter = logging.Formatter('%(asctime)s - %(levelname)s : %(message)s')
    
    console_handler.setFormatter(formatter)
    file_handler.setFormatter(formatter)
    
    self.logger.addHandler(console_handler)
    self.logger.addHandler(file_handler)
    
    # 存储handlers以便清理
    self.handlers = [console_handler, file_handler]

这里 file_handler = logging.FileHandler('app.log'),文件默认存储在当前工作同级目录中。

Formatter 模块

功能:定义日志消息的格式,包括时间戳、日志级别、消息内容等。

常用格式化选项:

  • %(asctime)s’:时间戳。
  • %(name)s’:Logger 名称。
  • %(levelname)s’:日志级别名称。
  • %(message)s’:日志消息内容。

示例:

import logging

logger = logging.getLogger('my_logger')
logger.setLevel(logging.DEBUG)

# Create a console handler
handler = logging.StreamHandler()

# Create a formatter
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s',# 日志格式
							 datefmt='%Y/%m/%d %H:%M:%S') # 自定义时间格式
							 
handler.setFormatter(formatter)

# Add handler to logger
logger.addHandler(handler)

logger.info('This is an info message')

格式说明

%(asctime)s:
当前时间,默认格式为 YYYY-MM-DD HH:MM:SS,MS(精确到毫秒)。示例:2024-11-19 14:30:45,123

%(name)s:
日志记录器的名称,即 logging.getLogger('my_logger') 中的 'my_logger'。示例:my_logger

%(levelname)s:
日志级别名称,如 DEBUGINFOWARNINGERROR 或 CRITICAL。示例:INFO

%(message)s:
实际的日志消息内容,即 logger.info('This is an info message') 中的字符串。示例:This is an info message

常用时间格式代码

格式符含义示例
%Y4 位年份2024
%m两位月份(01-12)11
%d两位日期(01-31)19
%H小时(24 小时制,00-23)14
%I小时(12 小时制,01-12)02
%M分钟(00-59)30
%S秒(00-59)45
%f微秒(精确到百万分之一秒)123456
%pAM 或 PM(与本地设置有关)PM

自定义格式示例

  1. 默认格式:
  • '%Y-%m-%d %H:%M:%S,%f'
  • 输出:2024-11-19 14:30:45,123
  1. 日期与时间分隔更明显:
  • '%Y/%m/%d %H:%M:%S'
  • 输出:2024/11/19 14:30:45
  1. 加入时区:
  • '%Y-%m-%d %H:%M:%S %Z'
  • 输出:2024-11-19 14:30:45 UTC
  1. 简洁格式:
  • '%H:%M:%S'
  • 输出:14:30:45
  1. 带 AM/PM 的 12 小时制:

    • '%I:%M:%S %p'
    • 输出:02:30:45 PM

全局配置写法 (不推荐)

  • 快速设置: 一行代码完成基本配置,适合快速开发或简单需求。

  • 全局影响basicConfig 配置的是根记录器(root logger),对所有通过 logging 调用的日志生效。

  • 默认处理器: 自动创建一个 StreamHandler,输出到控制台或文件(取决于是否设置了 filename 参数)。

  • 小型项目或临时脚本,不需要复杂的日志管理。

  • 日志需求简单,只有一个输出目标(例如写入单个文件)。

  1. 导入模块
import logging
  1. 基本配置
logging.basicConfig(level=logging.DEBUG)
  1. 记录信息
logging.debug('This is a debug message')
logging.info('This is an info message')
logging.warning('This is a warning message')
logging.error('This is an error message')
logging.critical('This is a critical message')

配置日志格式

您可以自定义日志消息的格式,包括时间戳、日志级别和消息内容。

logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

logging.info('This is an info message with custom format')

将日志写入文件

logging.basicConfig(
    level=logging.DEBUG,
    format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S',
    filename='app.log',
    filemode='w'  # 'w' 表示写入模式,会覆盖文件。'a' 表示追加模式。
)

logging.info('This message will be written to app.log')
# 创建一个自定义的Logger
logger = logging.getLogger('my_logger')
logger.setLevel(logging.DEBUG)

# 创建一个控制台处理器
console_handler = logging.StreamHandler()
console_handler.setLevel(logging.DEBUG)

# 创建一个文件处理器
file_handler = logging.FileHandler('app.log')
file_handler.setLevel(logging.ERROR)

# 创建一个格式器
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')

# 将格式器添加到处理器
console_handler.setFormatter(formatter)
file_handler.setFormatter(formatter)

# 将处理器添加到Logger
logger.addHandler(console_handler)
logger.addHandler(file_handler)

# 使用Logger记录信息
logger.debug('This is a debug message')
logger.info('This is an info message')
logger.warning('This is a warning message')
logger.error('This is an error message')
logger.critical('This is a critical message')

image.png

日志抽象类写法


import logging
from datetime import datetime
from enum import Enum
from pathlib import Path

from isort import file


class LoggerType(Enum):
    GPS = "gps"
    DETECT = "detect"
    WEBCAM = "webCam"


class BasicLogger:
    def __init__(self, log_type: LoggerType) -> None:
        
        self.log_path = self._create_log_file(log_type)
        self.logger = None
        self.handlers = []  # 存储handlers以便清理
        
        if self.log_path:
            self._setup_logger(log_type)

    def _create_log_file(self, log_type: LoggerType)-> Path:
        current_dir = Path(__file__).resolve().parent.parent
        current_time = datetime.now().strftime("%Y-%m-%d")

        log_dir_path = current_dir.joinpath("logs",log_type.value)
        if not log_dir_path.exists():
            log_dir_path.mkdir(parents=True)
        log_path = log_dir_path.joinpath(f"{current_time}_{log_type.value}.log")
        
        return log_path
    
    def _setup_logger(self, log_type: LoggerType)-> None:
        
        self.logger = logging.getLogger(log_type.value)
        self.logger.setLevel(logging.DEBUG)  # 设置为最低级别,允许所有日志

        # handler处理日志消息并决定如何将其输出到不同的目标(例如控制台、文件、网络)

        if not self.logger.handlers:
            # 控制台设置INFO级别
            console_handler = logging.StreamHandler()
            console_handler.setLevel(logging.INFO)

            # 日志文件设置DEBUG级别
            file_handler = logging.FileHandler(self.log_path)
            file_handler.setLevel(logging.DEBUG)
            
            # 定义日志消息的格式,包括时间戳、日志级别、消息内容等
            formatter = logging.Formatter('%(asctime)s - %(levelname)s  : %(message)s')
            
            console_handler.setFormatter(formatter)
            file_handler.setFormatter(formatter)
            
            self.logger.addHandler(console_handler)
            self.logger.addHandler(file_handler)
            
            # 存储handlers以便清理
            self.handlers = [console_handler, file_handler]
    
    def cleanup(self):
        """清理日志处理器"""
        if self.logger:
            for handler in self.handlers:
                handler.close()  
                self.logger.removeHandler(handler)  
            self.handlers.clear()


class GPSLogger(BasicLogger):
    def __init__(self):
        super().__init__(LoggerType.GPS)



class WebCAMLogger(BasicLogger):
    def __init__(self):
        super().__init__(LoggerType.WEBCAM)

这样在其他代码中直接引入日志类即可。