Python日志按时间自动切分——基于logging

2023-05-16

1、TimedRotatingFileHandler

1. 简介

TimedRotatingFileHandler是logging内置的可设置固定时间间隔的日志记录类,直接调用进行实例化和配置就可以实现日志的按时间自动切分

关键参数

描述
filename输出日志的文件名/路径
when日志切分的间隔时间单位;可选参数如下: “S”:Second 秒 “M”:Minutes 分钟 “H”:Hour 小时 “D”:Days 天 “W”:Week day(0 = Monday) “midnight”:Roll over at midnight
interval间隔时间单位的个数,指等待多少个 when 的时间后进行分割
backupCount保留日志的文件个数

2. 实现

import logging
import time
from logging.handlers import TimedRotatingFileHandler

new_formatter = '[%(levelname)s]%(asctime)s:%(msecs)s.%(process)d,%(thread)d#>[%(funcName)s]:%(lineno)s  %(message)s'
"""
%(asctime)s 字符串形式的当前时间。默认格式是“2021-09-08 16:49:45,896”。逗号后面的是毫秒
%(created)f 时间戳, 等同于time.time()
%(relativeCreated)d 日志发生的时间相对于logging模块加载时间的相对毫秒数
%(msecs)d 日志时间发生的毫秒部分
%(levelname)s 日志级别str格式
%(levelno)s 日志级别数字形式(10, 20, 30, 40, 50)
%(name)s 日志器名称, 默认root
%(message)s 日志内容
%(pathname)s 日志全路径
%(filename)s 文件名含后缀
%(module)s 文件名不含后缀
%(lineno)d 调用日志记录函数源代码的行号
%(funcName)s 调用日志记录函数的函数名
%(process)d 进程id
%(processName)s 进程名称
%(thread)d 线程ID
%(threadName)s 线程名称
"""
fmt = logging.Formatter(new_formatter)


log_handel = TimedRotatingFileHandler('my.log', when='M')
log_handel.setFormatter(fmt)

log_info = logging.getLogger('info')
log_info.setLevel(logging.INFO)
log_info.addHandler(log_handel)


if __name__ == '__main__':
    while 1:
        log_info.error('test error')
        time.sleep(1)
        log_info.info('test info')

通过TimedRotatingFileHandler可以实现日志的按时间自动切分,但是它切分后的文件日志文件的后缀是日期结尾,如下:

在这里插入图片描述

TimedRotatingFileHandler对日志的切分是在满足设定的时间间隔后,执行doRollover方法,将my.log重命名为带有当前时间后缀(my.log.****)的文件,并新建一个my.log,继续记录后续日志。

这种方式切分出来的日志文件不是很符合我的日常习惯,所以强迫症患者的我重写了doRollover方法。

2、自定义类

1. 实现

主要是重写TimedRotatingFileHandler的doRollover方法的文件翻转块代码
在这里插入图片描述

做了以下两点改动:

  • 重定义了新文件名,将日期放在了中间而不是最后
  • 直接将将baseFilename 指向新文件

完整代码:

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
import datetime
import logging
import time
from logging.handlers import TimedRotatingFileHandler

class TimeLoggerRolloverHandler(TimedRotatingFileHandler):
    def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False,
                 atTime=None):
        super(TimeLoggerRolloverHandler, self).__init__(filename, when, interval, backupCount, encoding, delay, utc)

    def doRollover(self):
        """
        """
        if self.stream:
            self.stream.close()
            self.stream = None
        currentTime = int(time.time())
        dstNow = time.localtime(currentTime)[-1]
        dfn = f"my.{datetime.datetime.now().strftime('%Y%m%d_%H:%M')}.log"
        self.baseFilename = dfn
        if not self.delay:
            self.stream = self._open()
        newRolloverAt = self.computeRollover(currentTime)
        while newRolloverAt <= currentTime:
            newRolloverAt = newRolloverAt + self.interval
        if (self.when == 'MIDNIGHT' or self.when.startswith('W')) and not self.utc:
            dstAtRollover = time.localtime(newRolloverAt)[-1]
            if dstNow != dstAtRollover:
                if not dstNow:
                    addend = -3600
                else:
                    addend = 3600
                newRolloverAt += addend
        self.rolloverAt = newRolloverAt


new_formatter = '[%(levelname)s]%(asctime)s:%(msecs)s.%(process)d,%(thread)d#>[%(funcName)s]:%(lineno)s  %(message)s'
fmt = logging.Formatter(new_formatter)

log_handel = TimeLoggerRolloverHandler(f"my.{datetime.datetime.now().strftime('%Y%m%d_%H:%M')}.log", when='M')
log_handel.setFormatter(fmt)

log_info = logging.getLogger('info')
log_info.setLevel(logging.INFO)
log_info.addHandler(log_handel)

if __name__ == '__main__':
    while 1:
        log_info.error('test error')
        time.sleep(1)
        log_info.info('test info')

效果:
在这里插入图片描述

2. 再优化

所有类型的日志信息全在一个文件,增加了排查问题的工作量,将每一种类型的日志信息都对应输出到指定的文件中,这样大大的方便了问题排查。

实现思路:

  • 为每一种日志类型都定义一个handel
  • 添加一个filter,实现这输出对应类型日志到对应文件

完整代码:

#!/usr/bin/env python
# -*- encoding: utf-8 -*-
import datetime
import logging
import time
from logging.handlers import TimedRotatingFileHandler

class LogFilter:
    @staticmethod
    def info_filter(record):
        if record.levelname == 'INFO':
            return True
        return False

    @staticmethod
    def error_filter(record):
        if record.levelname == 'ERROR':
            return True
        return False


class TimeLoggerRolloverHandler(TimedRotatingFileHandler):
    def __init__(self, filename, when='h', interval=1, backupCount=0, encoding=None, delay=False, utc=False,
                 atTime=None):
        super(TimeLoggerRolloverHandler, self).__init__(filename, when, interval, backupCount, encoding, delay, utc)

    def doRollover(self):
        """
        """
        if self.stream:
            self.stream.close()
            self.stream = None
        currentTime = int(time.time())
        dstNow = time.localtime(currentTime)[-1]
        log_type = 'info' if self.level == 20 else 'error'
        dfn = f"my.{datetime.datetime.now().strftime('%Y%m%d')}.{log_type}.log"
        self.baseFilename = dfn
        if not self.delay:
            self.stream = self._open()
        newRolloverAt = self.computeRollover(currentTime)
        while newRolloverAt <= currentTime:
            newRolloverAt = newRolloverAt + self.interval
        if (self.when == 'MIDNIGHT' or self.when.startswith('W')) and not self.utc:
            dstAtRollover = time.localtime(newRolloverAt)[-1]
            if dstNow != dstAtRollover:
                if not dstNow:
                    addend = -3600
                else:
                    addend = 3600
                newRolloverAt += addend
        self.rolloverAt = newRolloverAt


new_formatter = '[%(levelname)s]%(asctime)s:%(msecs)s.%(process)d,%(thread)d#>[%(funcName)s]:%(lineno)s  %(message)s'
fmt = logging.Formatter(new_formatter)
log_error_file = 'my.{}.error.log'.format(datetime.datetime.now().strftime('%Y%m%d'))
log_info_file = 'my.{}.info.log'.format(datetime.datetime.now().strftime('%Y%m%d'))

error_handler = TimeLoggerRolloverHandler(log_error_file, when='midnight')
error_handler.addFilter(LogFilter.error_filter)
error_handler.setFormatter(fmt)
error_handler.setLevel(logging.ERROR)

info_handel = TimeLoggerRolloverHandler(log_info_file, when='midnight')
info_handel.setFormatter(fmt)
info_handel.addFilter(LogFilter.info_filter)
info_handel.setLevel(logging.INFO)


log_info = logging.getLogger('info')
log_info.setLevel(logging.INFO)

log_info.addHandler(info_handel)
log_info.addHandler(error_handler)

if __name__ == '__main__':
    while 1:
        log_info.error('test error')
        time.sleep(1)
        log_info.info('test info')

3、总结

通过TimedRotatingFileHandler类实现日志切分,最好的时间间隔是天,使用when='midnight'在每天凌晨切分,因为其他的秒、分、小时、天、周都是根据程序启动的时间计算的,比如按分钟划分,程序在17:25:33启动,那切分时间点是17:26:33,而不是17:26:00

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Python日志按时间自动切分——基于logging 的相关文章

随机推荐

  • CIO靠业务翻身

    身为CIO的你被降级了 CEO将把你叫进他的办公室并告诉你 xff0c 经过深思熟虑 xff0c 他决定让你向CFO做报告 每个人都知道这意味着什么 xff0c IT不必再向CEO作报告了 xff0c 但事实上IT是不应该这样的 虽然IT部
  • NIST SP800系列标准

    NIST SP800系列标准 SP800是美国NIST xff08 National Institute of Standards and Technology xff09 发布的一系列关于信息安全的指南 xff08 SP是Special
  • Python一个对象如何调用基类和继承类的方法

    main 函数 先 生成一个类 TrainOptions 的对象 然后调用 类中 的 parser 方法 但是发现 TrainOptions 没有该方法 所以去它的基类 BaseOptions 中找 并且找到了 parser 方法 此时调用
  • 滤波器和图像处理

    滤波器就是一个信号波形 xff0c 从时域看是 幅度 时间 的曲线 xff0c 从频域看是由多种不同频率成分的波形合成一个波形 xff0c 滤波就是滤除不要的频率成分 保留需要的频率成分 xff1b 高通滤波就是让高频成分通过 xff0c
  • 微信PC版的缓存文件夹

    微信PC版的缓存文件夹都装了什么 我们在PC版的微信上看到的所有东西 xff0c 都是会在硬盘里留下缓存文件的 微信缓存的路径 xff0c 默认是电脑的 我的文档 文件夹 xff0c 也就是 系统盘 Users 用户名 Documents
  • Win10子系统ubuntu20.04设置静态ip、笔记

    Win10子系统ubuntu20 04设置静态ip 笔记 简单有效的方法 一个新思路 xff1a 不改IP xff0c 加一个指定IP 在 Windows 10 中 xff0c 以管理员权限运行 CMD 或 Powershell xff0c
  • win10安装wsl2 unbuntu报错,WslRegisterDistribution failed with error: 0x80070002

    Installing this may take a few minutes WslRegisterDistribution failed with error 0x80070002 Error 0x80070002 The system
  • 什么是系统调用

    系统调用 1 内核模式与用户模式 为了保护设备 xff0c 操作系统不可能让所有的程序都能轻松地访问到任何的文件 xff0c 将处理器CPU分为两种模式 xff0c 内核模式和用户模式 xff0c 诸如一些修改寄存器内容的命令 xff0c
  • 【Docker】update-ca-certificates把JAVA_HOME抹掉了?一个镜像问题(未解决)

    今天在做镜像的时候发现一个问题 maven 3 6 3 openjdk 8 镜像是 dockerhub 拉下来的 xff0c 大家可以尝试一下 xff0c 然后在 Dockerfile 里执行了一下 update ca certificat
  • Mac里捣腾Kerberos(一)

    文章目录 1 Overview2 安装3 Summary 1 Overview 最近需要解决一个部门一个老旧问题 xff0c 问题大概是这样的 xff0c Spark on K8S 在访问 HDFS xff08 其他小组负责 xff09 的
  • Qt实战开发-目录树

    关键知识点 使用QStandardItemModel 建立标准化项目模型 xff0c 对树形控件节点操作提供用于存储自定义数据的通用模型 xff0c 每个数据项被表示为类QStandardItem的对象 xff0c 类QStandardIt
  • fdisk: cannot open /dev/sdb: Permission denied

    fdisk cannot open dev sda Permission denied fdisk cannot open dev sdb Permission denied 切换root用户就好了
  • Ubuntu安装NVIDIA驱动

    本篇教程来自我在好几台服务器 43 好几台工作站上安装驱动的实践经验 会持续更新 强烈建议 xff0c 阅读完全文后再上手实操 xff01 xff01 xff01 推荐博客 xff1a How to install Nvidia drive
  • Windows2012中安装Nginx并创建为Windows服务

    安装Nginx 下载windows版nginx xff08 http nginx org download nginx 1 10 0 zip xff09 xff0c 之后解压到需要放置的位置 xff08 D xampp nginx xff0
  • 对比两个List,区分重复数据(优化)。

    1 背景 现在日常工作中经常会遇到两组数据对比碰撞的需求 xff0c 菜菜不才 xff0c 之前写了一个不怎么优雅的代码才解决了问题 xff0c 而且还是只能对比String的 xff0c 菜菜有空后赶紧写了一个比较优雅的方式解决了list
  • Selenium-API基本操作

    一 元素定位 1 含义 xff1a 元素定位就是通过元素的信息或元素的层级结构来定位元素的 2 方式 xff1a idnameclass nametag namelink textpartial link textXPathCSS 3 定位
  • 解决Ubuntu18.04搜狗输入法无法使用的问题

    win10 43 Ubuntu18 04双系统 如果按照按照官网的教程直接安装 xff0c 大概率安装好以后是无法使用的 xff0c 可尝试如下方法修复 xff0c 已亲测多台电脑 xff0c 均成功 1 先卸载掉fcitx xff0c 及
  • 树莓派4B ubuntu mate 20.04 安装xrdp实现远程登录 可以用windows自带的mstsc远程

    树莓派4B安装Ubuntu Mate后 xff0c 开启远程桌面xrdp服务可以使用mstsc远程登录访问 xff1a 安装步骤 xff1a 控制台输入命令 sudo apt get install xrdp 安装后重启xrdp服务 xff
  • STM32F103程序设计-6-引脚输入功能-按键(查询)

    检测单片机引脚上的电平 xff0c 即使用单片机的输入功能 把上次的例程中初始化控制 LED的 GPIO 口的部分拿出来放到一个函数 LED Iint 中 注意 xff0c 初始化时先在 LED Iint 的最后点亮 LED xff0c 目
  • Python日志按时间自动切分——基于logging

    1 TimedRotatingFileHandler 1 简介 TimedRotatingFileHandler是logging内置的可设置固定时间间隔的日志记录类 xff0c 直接调用进行实例化和配置就可以实现日志的按时间自动切分 关键参