Python3自定义日志类教程
一、说明
1.1 背景说明
Python的logging功能是比较丰富的支持不同层次的日志输出,但或是我们想在日志前输出时间、或是我们想要将日志输入到文件,我们还是想要自定义日志类。
之前自己也尝试写过但感觉文档太乱看不懂怎么写,今天有人拿个半成品来问为什么代码报错,在其基础上改造了一下。
1.2 logging级别说明
logging日志级别及对应值如下,默认情况下直接运行只有INFO及以上级别才会输出(本质上是大于等于20才会输出),调试模式运行DEBUG日志才会输出。
可以通过自定义输出日志级别,指定直接运行输出什么级别的日志;不过调试模式打印的日志应该是不可以修改的。
|
Level |
Numeric value |
|---|---|
|
|
50 |
|
|
40 |
|
|
30 |
|
|
20 |
|
|
10 |
|
|
0 |
二、实现代码
2.1 Python2实现代码
# -*- coding: utf-8 -*-
import os
import datetime
import logging class LogConfig:
def __init__(self,log_type="console"):
# 指定日志输出到控制台时的初始化
if log_type == "console":
logging.basicConfig(level=logging.INFO,
format='%(asctime)s %(levelname)s %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
)
# 指定日志输出到文件的初始化
elif log_type == "file":
# 创建存放日志的目录
if not os.path.exists('./log'):
os.mkdir('./log') # 操作系统本身不允许文件名包含:等特殊字符,所以这里也不要用,不然赋给filename时会报错
nowTime = datetime.datetime.now().strftime('%Y-%m-%d')
file_name = './log/%s.log' % nowTime # python2.7也有logging.basicConfig(),但只直接用logging.basicConfig(),写中文时会报错
# 所以为风格统一,我们这里不使用logging.basicConfig(),全通过set设置
root_logger = logging.getLogger()
root_logger.setLevel(logging.INFO)
handler = logging.FileHandler(filename=file_name, encoding='utf-8', mode='a')
formatter = logging.Formatter('%(asctime)s %(levelname)s %(message)s')
handler.setFormatter(formatter)
root_logger.addHandler(handler) def getLogger(self):
logger = logging.getLogger()
return logger if __name__ == "__main__":
# log_type = "console"
log_type = "file"
logger = LogConfig(log_type).getLogger()
logger.debug('print by debug')
logger.info('print by info')
logger.warning('print by warning')
2.2 Python3实现代码
python3.3 之后logging.basicConfig()中提供了handlers参数,我们可借助handlers参数来指定编码。
python3.3之前的python3版本写法得和python2一样。另外python3.9之后logging.basicConfig()会直接提供encoding参数,到时可以更方便。
import os
import datetime
import logging class LogConfig:
def __init__(self,log_type="console"):
# 指定日志输出到控制台时的初始化
if log_type == "console":
logging.basicConfig(level=logging.INFO,
format='%(asctime)s %(levelname)s %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
)
# 指定日志输出到文件的初始化
elif log_type == "file":
# 创建存放日志的目录
if not os.path.exists('./log'):
os.mkdir('./log') # 操作系统本身不允许文件名包含:等特殊字符,所以这里也不要用,不然赋给filename时会报错
nowTime = datetime.datetime.now().strftime('%Y-%m-%d') file_name = './log/%s.log' % nowTime
file_handler = logging.FileHandler(filename=file_name,encoding='utf-8', mode='a')
# level----指定打印的日志等级;默认为WARNING;可为NOTSET、DEBUG、INFO、WARNING、ERROR、CRITICAL
# format----指定整条日志的格式;这里设置为“时间-等级-日志内容”
# datefmt----format中时间的格式;
# filename----日志输出到的文件;默认打印到控制台
# filemode----日志文件读写形式;默认为“a”;配合filename使用,如果不用filename该参数也可不用
# 本来输出到文件使用filename和filemode两个参数就可以了,不需要handlers
# 但是logging将日志输出到文件时中文会乱码,而logging.basicConfig又没有提供指定编码的参数(python3.9之后才提供有直接的encoding参数)
# 要指定编码只能使用handlers。另外handlers还是python3.3 之后才提供的参数,在此之前的版本请参考python2的写法
logging.basicConfig(level=logging.INFO,
format='%(asctime)s %(levelname)s %(message)s',
datefmt='%Y-%m-%d %H:%M:%S',
# filename=file_name,
# filemode='a',
handlers=[file_handler],
) def getLogger(self):
logger = logging.getLogger()
return logger if __name__ == "__main__":
# log_type = "console"
log_type = "file"
logger = LogConfig(log_type).getLogger()
logger.debug('print by debug')
logger.info('print by info')
logger.warning('print by warning')
三、日志截图

四、更科学的日志定义方式(20200310更新)
通过近段时间的使用发现原先的方法就自己用用没问题,但与别人产生调用及上生产时就会存在几个问题:
第一个问题是,直接通过logging.basicConfig()进行配置,会同时影响被调用库的日志设置。
第二个问题是,原现的文件日志形式只能输出到一个给定的文件,不能实现不同的日志类型输出到不同的日志文件。
第三个问题是,原现的文件日志形式使用"w"模式则上次日志会被清除,使用"a"模式则日志又会无限增长需要注意清理。
前两个问题通过getLogger时给定一个名称而不是直接获取根logger进行处理;第三个问题通过使用RotatingFileHandler等替换FileHandler进行处理。更新代码如下:
import inspect
import logging
import logging.handlers
import os class LogConfig:
def __init__(self, log_level=logging.INFO):
if __name__ != "__main__":
self.logger = self.get_file_logger(log_level=log_level)
pass def get_console_logger(self):
def _gen_file_logger_handler():
_handler = logging.StreamHandler()
formatter = logging.Formatter(
"[%(asctime)s %(msecs)03d][%(process)d][tid=%(thread)d][%(name)s][%(levelname)s] %(message)s [%(filename)s"
" %(funcName)s %(lineno)s] ", datefmt="%Y-%m-%d %H:%M:%S")
_handler.setLevel(logging.INFO)
_handler.setFormatter(formatter)
return _handler
def _gen_console_logger():
_console_logger = logging.getLogger("console_logger")
_console_logger.addHandler(handler)
return _console_logger handler = _gen_file_logger_handler()
console_logger = _gen_console_logger()
return console_logger # 允许同时存在不同的logger,即可以传入不同log_file_name实例化不同logger即可实现不同类型日志写入不同文件
# logger自带了线程锁,是线程安全的,所以多线程不用自己实现日志文件锁
def get_file_logger(self, logger_name=None, log_file_name=None, log_level=logging.INFO):
def _get_log_file_name():
# 如果已定义有日志文件则直接原样返回
if log_file_name:
return log_file_name
# 如果是直接运行的,那取当前文件名
if __name__ == "__main__":
caller_file_name = __file__
# 如果是被调用的,则取上层调用文件文件名
else:
frame = inspect.stack()[3]
module = inspect.getmodule(frame[0])
caller_file_name = module.__file__
inner_log_file_name = f"{os.path.basename(caller_file_name)[:-3]}.log"
return inner_log_file_name
def _make_sure_log_dir_exist():
if not os.path.isdir(log_file_dir):
os.mkdir(log_file_dir)
def _gen_file_logger_handler():
# 操作系统本身不允许文件名包含:等特殊字符,所以这里也不要用,不然赋给filename时会报错
# nowTime = datetime.datetime.now().strftime('%Y-%m-%d')
file_path = f'{log_file_dir}/{log_file_name}'
# formatter = logging.Formatter(
# "[%(asctime)s %(msecs)03d][%(process)d][tid=%(thread)d][%(name)s][%(levelname)s] %(message)s [%(filename)s"
# " %(funcName)s %(lineno)s] ", datefmt="%Y-%m-%d %H:%M:%S")
formatter = logging.Formatter(
"[%(asctime)s %(msecs)03d][%(name)s][%(levelname)s] %(message)s", datefmt="%Y-%m-%d %H:%M:%S")
# filename----日志文件
# when----更换日志文件的时间单位
# interval----更换日志文件的时间单位个数;这里是7天换一个文件
# backupCount----保存的旧日志文件个数;这里即只保留上一个日志文件
# encoding----日志文件编码
# 实际使用感觉,如果程序是crontab这样定时运行而不是一直运行着,那这个按时间滚动并不生效
# _handler = logging.handlers.TimedRotatingFileHandler(
# filename=file_path,
# when='D',
# interval=7,
# backupCount=1,
# encoding='utf-8',
# )
# filename--日志文件
# mode--日志文件打开模式
# maxBytes--日志文件最大大小。每次调用打印日志时logging去检测日志大小是否达到设定的上限,如果达到则更换日志文件
# backupCount--保存的旧日志文件个数。xxx表示当前日志文件,则xxx.1表示上一份日志文件、xxx.2表示上上份日志文件...
# encoding----日志文件编码
_handler = logging.handlers.RotatingFileHandler(
filename=file_path,
mode='a',
maxBytes=1024 * 1024 * 100,
backupCount=1,
encoding='utf-8',
)
# 实际发现这里setLevel并不起作用
# _handler.setLevel(logging.DEBUG)
_handler.setFormatter(formatter)
return _handler
def _gen_file_logger():
# 如果指定了logger_name那直接采用,如果没有使用日志文件名为logger_name
nonlocal logger_name
if not logger_name:
logger_name = log_file_name
_file_logger = logging.getLogger(logger_name)
_file_logger.addHandler(handler)
_file_logger.setLevel(log_level)
return _file_logger
log_file_name = _get_log_file_name()
log_file_dir = "log"
_make_sure_log_dir_exist()
handler = _gen_file_logger_handler()
file_logger = _gen_file_logger()
return file_logger if __name__ == "__main__":
# logger = LogConfig().get_console_logger()
# log_file_name不同,返回的是不同的logger,这样就可以方便地定义多个logger
log_file_name = "random_file_name.log"
logger = LogConfig().get_file_logger(log_file_name=log_file_name)
logger.debug('print by debug')
logger.info('print by info')
logger.warning('print by warning')
参考:
https://docs.python.org/3/library/logging.html#levels
https://www.jianshu.com/p/2ec5187a953e
https://docs.python.org/2/library/logging.html#logging.basicConfig
https://docs.python.org/3/library/logging.html#logging.basicConfig
https://stackoverflow.com/questions/10706547/add-encoding-parameter-to-logging-basicconfig
Python3自定义日志类教程的更多相关文章
- Python3自定义日志类 mylog
#encoding=utf-8 import os, sysimport datetimeimport time class Mylog(object): # 根文件夹 root_dir = s ...
- flask中自定义日志类
一:项目架构 二:自定义日志类 1. 建立log.conf的配置文件 log.conf [log] LOG_PATH = /log/ LOG_NAME = info.log 2. 定义日志类 LogC ...
- 《手把手教你》系列基础篇(八十五)-java+ selenium自动化测试-框架设计基础-TestNG自定义日志-下篇(详解教程)
1.简介 TestNG为日志记录和报告提供的不同选项.现在,宏哥讲解分享如何开始使用它们.首先,我们将编写一个示例程序,在该程序中我们将使用 ITestListener方法进行日志记录. 2.Test ...
- c#自定义日志记录
废话不多说,直接上代码: 很简单:将类复制到项目中,最后在配置文件上配置一下:logUrl即可. 默认保存在:项目/temp/log /// <summary> /// 日志类 /// & ...
- 《手把手教你》系列基础篇(九十五)-java+ selenium自动化测试-框架之设计篇-java实现自定义日志输出(详解教程)
1.简介 前面宏哥一连几篇介绍如何通过开源jar包Log4j.jar.log4j2.jar和logback实现日志文件输出,Log4j和logback确实很强大,能生成三种日志文件,一种是保存到磁盘的 ...
- python3+selenium框架设计03-封装日志类
首先我们先来实现日志的功能,日志可以使用python3自带logging模块,不会的可以百度一下相关文章,也可以看我另外一篇文章Python3学习笔记24-logging模块 在封装日志类前,我们需要 ...
- python3+requests库框架设计02-封装日志类
首先我们先来实现日志的功能,日志可以使用python3自带logging模块,不会的可以百度一下相关文章,也可以看我另外一篇文章Python3学习笔记24-logging模块 在封装日志类前,我们需要 ...
- python3.4中自定义数组类(即重写数组类)
'''自定义数组类,实现数组中数字之间的四则运算,内积运算,大小比较,数组元素访问修改及成员测试等功能''' class MyArray: '''保证输入值为数字元素(整型,浮点型,复数)''' de ...
- python3 配置logging日志类
配置类config_file: from configparser import ConfigParser class config_file: def __init__(self,conf_file ...
随机推荐
- Python数据基础--列表、元组、字典、函数
一.数据结构 列表(List)和元组 序列是Python中最基本的数据结构.序列中的每个元素都分配一个数字 - 它的位置,或索引,第一个索引是0,第二个索引是1,依此类推. Python有6个序列的内 ...
- AbstractMethodError:
AbstractMethodError: This java.lang.AbstractMethodError is usually thrown when we try to invoke the ...
- vue图片onerror加载路径写法
vue里,img加载错误的时候,onerror属性可以加载错误图片的默认图片写法如下: <img class=avator' :src="data.picture" :one ...
- MB SD Connect 5 vs 2017 FVDI2 Commander
Both MB SD C5 and FVDI II are diagnostic and Programmer tools for Mercedes Benz Cars & Trucks.Th ...
- 码云和git
第一步: 码云上注册: 第二部: 创建项目;根据需求,哒哒哒全部填完 第三部: 设置公钥(重点来了,头晕) 1. 打开git终端 git Bash 2.进入.ssh目录 输入命令 cd ~/.ssh ...
- Apache Maven 3.6.1配置安装
Apache Maven 3.6.1配置安装 一.下载 maven下载地址:http://maven.apache.org/download.cgi 二.安装 1,解压即可用 2,环境变量配置 MAV ...
- autoit脚本-从基本的函数用法开始
适配浏览器:目前了解的有ie浏览器 MsgBox 显示可选提示超时的消息框 _ArrayDisplay _arraydisplay($aArray) ;$aArra一般为数组,方法用于展示表格展示数 ...
- Linux 搭建DNS
Linux 搭建DNS 使用yum源安装 yum -y install bind* 修改主配置文件 [root@localhost ~]# cp /etc/named.conf /etc/named. ...
- 在Linux(Debian)环境下搭建并运行GPU
首先通过以下命令查看是否GPU驱动成功: 注意:需要在bash终端输入 import tensorflow as tf hello = tf.constant('Hello, TensorFlow!' ...
- 剑指offer 06:旋转数组的最小数字
题目描述把一个数组最开始的若干个元素搬到数组的末尾,我们称之为数组的旋转. 输入一个非减排序的数组的一个旋转,输出旋转数组的最小元素. 例如数组{3,4,5,1,2}为{1,2,3,4,5}的一个旋转 ...