【Python系列】python 如何打印带时间的日志

'# 【Python系列】python 如何打印带时间的日志

一、背景与问题

在软件开发中,日志系统是调试和维护程序的核心工具。传统print()函数虽然简单,但存在以下严重缺陷:

  • 时间信息缺失:无法确定日志事件发生的具体时间
  • 结构化缺失:难以进行日志分析和归档
  • 性能隐患:频繁调用print()可能阻塞主线程
  • 可维护性差:无法控制日志级别和输出目的地

在Python中,标准库logging模块提供了完整的日志系统,但其时间戳机制常被误用。本文将深入解析日志时间戳的实现原理,探讨不同场景下的实现方案,并分析常见错误和性能优化方法。

二、基本原理

Python的logging模块通过Formatter对象控制日志格式,其核心机制如下:

  1. 日志记录器(Logger):负责生成日志消息
  2. 处理器(Handler):负责将日志消息发送到指定位置(控制台/文件等)
  3. 格式器(Formatter):负责格式化日志消息内容

时间戳的生成依赖于time模块,其核心函数包括:

time.time()  # 返回当前时间戳(浮点数)
time.strftime()  # 格式化时间字符串

logging模块内部通过LogRecord对象保存日志信息,其asctime属性包含时间戳。

三、环境准备

确保以下依赖安装:

python -m venv env
source env/bin/activate
pip install python-dotenv

四、核心实现

示例1:基础时间戳日志

import logging

# 配置日志
logging.basicConfig(
    level=logging.INFO,
    format='%(asctime)s - %(levelname)s - %(message)s'
)

# 记录日志
logging.info("This is an info message")
logging.warning("This is a warning message")

关键点解释:

  • asctime字段由logging模块自动添加
  • 默认时间格式为YYYY-MM-DD HH:MM:SS,mmm(毫秒)
  • 日志级别通过level参数控制

示例2:自定义时间格式

import logging
import time

# 自定义时间格式
formatter = logging.Formatter(
    fmt='%(asctime)s - %(levelname)s - %(message)s',
    datefmt='%Y-%m-%d %H:%M:%S'
)

# 创建控制台处理器
console_handler = logging.StreamHandler()
console_handler.setFormatter(formatter)

# 配置日志器
logger = logging.getLogger('my_logger')
logger.setLevel(logging.DEBUG)
logger.addHandler(console_handler)

# 记录日志
logger.debug("Debug message")
logger.info("Info message")

关键点解释:

  • datefmt参数控制时间格式
  • %(asctime)s格式符支持多种时间格式化选项
  • 可通过time.strptime()进行时间解析

示例3:手动添加时间戳

import logging
import time

def log_with_timestamp(logger, level, msg, *args):
    timestamp = time.strftime('%Y-%m-%d %H:%M:%S', time.localtime())
    logger.log(level, f"[{timestamp}] {msg}", *args)

# 配置日志
logging.basicConfig(level=logging.DEBUG)

# 使用自定义日志函数
log_with_timestamp(logging, logging.INFO, "This is an info message")

关键点解释:

  • 手动控制时间戳格式
  • 避免使用logging内置的时间戳可能带来的性能影响
  • 需要自己处理时区转换

五、完整案例

项目结构

log_demo/
├── main.py
├── config.py
├── utils/
│   └── logger.py
└── logs/
    └── app.log

1. logger.py

import logging
import os
from datetime import datetime

class Logger:
    def __init__(self, name='app_logger'):
        self.logger = logging.getLogger(name)
        self.logger.setLevel(logging.DEBUG)
        
        # 创建文件处理器
        log_file = os.path.join(os.path.dirname(__file__), '..', 'logs', 'app.log')
        file_handler = logging.FileHandler(log_file, encoding='utf-8')
        
        # 创建控制台处理器
        console_handler = logging.StreamHandler()
        
        # 自定义格式器
        formatter = logging.Formatter(
            fmt='%(asctime)s - %(levelname)s - [%(module)s:%(lineno)d] - %(message)s',
            datefmt='%Y-%m-%d %H:%M:%S'
        )
        
        # 设置格式器
        file_handler.setFormatter(formatter)
        console_handler.setFormatter(formatter)
        
        # 添加处理器
        self.logger.addHandler(file_handler)
        self.logger.addHandler(console_handler)
    
    def info(self, msg, *args):
        self.logger.info(msg, *args)
    
    def debug(self, msg, *args):
        self.logger.debug(msg, *args)
    
    def error(self, msg, *args):
        self.logger.error(msg, *args)

2. main.py

from logger import Logger
import time

logger = Logger()

def simulate_processing():
    for i in range(5):
        logger.info(f"Processing step {i}")
        time.sleep(0.5)
        logger.debug(f"Debug info for step {i}")

simulate_processing()

3. config.py

import os

# 设置日志目录
LOG_DIR = os.path.join(os.path.dirname(__file__), 'logs')
os.makedirs(LOG_DIR, exist_ok=True)

关键点说明:

  • 使用FileHandler和StreamHandler实现日志分发
  • 通过%(module)s和%(lineno)d定位日志源
  • 在main.py中模拟了异步处理场景

六、源码解析

以logging模块的Formatter类为例,其核心代码如下:

class Formatter:
    def format(self, record):
        # 处理时间戳
        record.asctime = self.formatTime(record, self.datefmt)
        # 处理其他字段
        return self.formatString % record.__dict__
    
    def formatTime(self, record, datefmt=None):
        ct = self.converter(record.created)
        if datefmt:
            s = time.strftime(datefmt, ct)
        else:
            t = time.strftime("%H:%M:%S", ct)
            s = "%s.%03d" % (t, record.msecs)
        return s

关键点分析:

  1. formatTime方法生成时间戳
  2. datefmt参数控制时间格式
  3. 使用time.strftime进行格式化
  4. record.created字段记录日志创建时间

七、进阶使用

1. 异步日志记录

import logging
import threading

class AsyncLogger:
    def __init__(self):
        self.logger = logging.getLogger('async_logger')
        self.logger.setLevel(logging.INFO)
        
        # 创建线程安全的日志处理器
        handler = logging.FileHandler('async.log', encoding='utf-8')
        handler.setFormatter(logging.Formatter('%(asctime)s - %(message)s'))
        
        # 使用线程安全的队列
        self.queue = logging.handlers.QueueHandler(handler)
        self.logger.addHandler(self.queue)
    
    def log(self, message):
        self.logger.info(message)

2. 日志轮转

import logging
from logging.handlers import RotatingFileHandler

handler = RotatingFileHandler('rotating.log', maxBytes=1024*1024, backupCount=5)
handler.setFormatter(logging.Formatter('%(asctime)s - %(message)s'))

3. 线程安全处理

import logging
import threading

# 创建线程安全的日志器
logger = logging.getLogger('thread_safe')
logger.setLevel(logging.INFO)
handler = logging.FileHandler('thread.log')
formatter = logging.Formatter('%(asctime)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)

# 线程安全的日志记录
def thread_safe_log(message):
    logger.info(message)

八、性能与工程实践

1. 性能优化策略

场景优化方案说明
高频日志使用logging.basicConfig避免重复创建处理器
分布式系统使用UUID标记区分不同服务实例日志
高并发异步日志使用QueueHandler避免阻塞
大量日志日志轮转控制日志文件大小

2. 安全实践

  • 敏感信息过滤:使用Filter类过滤敏感字段
  • 日志级别控制:生产环境使用INFO级别
  • 日志加密:使用cryptography模块加密敏感数据
  • 访问控制:限制日志文件的读写权限

3. 异常处理

import logging

def safe_log(logger, message):
    try:
        logger.info(message)
    except Exception as e:
        logger.error(f"Log error: {str(e)}", exc_info=True)

九、常见问题与踩坑

1. 时间戳不准确

错误示例:

logging.basicConfig(
    format='%(asctime)s - %(message)s'
)

问题:asctime默认包含毫秒,可能造成时间戳混乱

解决办法:显式指定格式

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

2. 日志丢失

错误场景:未配置FileHandler导致日志未写入文件

解决方案:确保配置了文件处理器

file_handler = logging.FileHandler('app.log')
logger.addHandler(file_handler)

3. 性能瓶颈

错误示例:频繁调用logging.info()影响性能

优化方案:使用logging.basicConfig一次配置

logging.basicConfig(level=logging.INFO)

十、最佳实践

  1. 统一日志配置:使用logging.basicConfig统一配置
  2. 合理设置日志级别:生产环境使用INFO级别
  3. 使用结构化日志:通过%(asctime)s等字段获取完整信息
  4. 日志轮转配置:使用RotatingFileHandler控制日志文件大小
  5. 安全处理:过滤敏感信息,限制日志访问权限
  6. 异步处理:高并发场景使用QueueHandler避免阻塞
  7. 日志分发:通过FileHandler和StreamHandler实现日志分发

十一、总结

带时间戳的日志系统是软件开发中不可或缺的工具,其核心在于合理配置Formatter和Handler。本文深入分析了logging模块的实现原理,探讨了不同场景下的实现方案,并指出常见错误和优化方法。在实际开发中,应根据具体需求选择合适的日志策略,既要保证日志信息的完整性,又要避免性能损耗。通过合理配置日志系统,可以显著提升调试效率和系统可维护性。

最后修改于:2026年09月24日 15:05

评论已关闭

推荐阅读

AIGC实战——Transformer模型
2024年12月01日
Socket TCP 和 UDP 编程基础(Python)
2024年11月30日
python , tcp , udp
如何使用 ChatGPT 进行学术润色?你需要这些指令
2024年12月01日
AI
最新 Python 调用 OpenAi 详细教程实现问答、图像合成、图像理解、语音合成、语音识别(详细教程)
2024年11月24日
ChatGPT 和 DALL·E 2 配合生成故事绘本
2024年12月01日
omegaconf,一个超强的 Python 库!
2024年11月24日
【视觉AIGC识别】误差特征、人脸伪造检测、其他类型假图检测
2024年12月01日
[超级详细]如何在深度学习训练模型过程中使用 GPU 加速
2024年11月29日
Python 物理引擎pymunk最完整教程
2024年11月27日
MediaPipe 人体姿态与手指关键点检测教程
2024年11月27日
深入了解 Taipy:Python 打造 Web 应用的全面教程
2024年11月26日
基于Transformer的时间序列预测模型
2024年11月25日
Python在金融大数据分析中的AI应用(股价分析、量化交易)实战
2024年11月25日
AIGC Gradio系列学习教程之Components
2024年12月01日
Python3 `asyncio` — 异步 I/O,事件循环和并发工具
2024年11月30日
llama-factory SFT系列教程:大模型在自定义数据集 LoRA 训练与部署
2024年12月01日
Python 多线程和多进程用法
2024年11月24日
Python socket详解,全网最全教程
2024年11月27日
python之plot()和subplot()画图
2024年11月26日
理解 DALL·E 2、Stable Diffusion 和 Midjourney 工作原理
2024年12月01日