python3 配置logging日志类的操作


Posted in Python onApril 08, 2020

配置类config_file:

from configparser import ConfigParser

class config_file:

  def __init__(self,conf_filePath,encoding="utf-8"):
    #打开配置文件,实例化ConfigParser类,并以默认utf-8的编码格式读取文件
    self.cf = ConfigParser()
    self.cf.read(conf_filePath,encoding)

  def get_Int_Value(self,section,option):
    #获取整数
    return self.cf.getint(section,option)

  def get_boolValue(self,section,option):
    #获取布尔值
    return self.cf.getboolean(section,option)

  def get_strValue(self,section,option):
    # 获取字符串类型的值
    return self.cf.get(section,option)

  def get_floatValue(self,section,option):
    # 获取浮点数值
    return self.cf.getfloat(section,option)

  def get_sections(self):
    # 获取所有的section
    return self.cf.sections()

  def get_options(self,section):
    # 获取所有的option
    return self.cf.options(section)

日志类:

from configparser import ConfigParser
import logging
from config_file import config_file
class Log_Test(config_file):#继承config_file

  def logging(self):
    logger = logging.getLogger(self.get_strValue('log','logger_name')) #从配置文件读取logger名
    logger.setLevel(self.get_strValue('log', 'logger_level')) # 设置logger收集器的收集log级别
    format_logger = logging.Formatter(self.get_strValue('log','logger_format'))
    if(self.get_boolValue('log','logger_out')):
      handle = logging.StreamHandler() # 指定输出到console控制台
      handle.setLevel(self.get_strValue('log', 'logger_level')) # 读取日志等级并设定logging的级别
      handle.setFormatter(format_logger) # 指定日志格式
    else:
      handle = logging.FileHandler(self.get_strValue('log','logger_filepath'), encoding='utf-8')
      handle.setLevel(self.get_strValue('log', 'logger_level')) # 读取日志等级并设定logging的级别
      handle.setFormatter(format_logger) # 指定日志格式
    logger.addHandler(handle)
    return logger

日志配置文件logging.cfg:

[log]
#日志收集器
logger_name=TEST
#日志级别 级别需要大写 DEBUG-->INFO-->WARNING-->ERROR-->CRITICAL/FATAL
logger_level=DEBUG
#日志输出格式  注意转义
logger_format=%%(asctime)s-%%(filename)s-%%(levelname)s-日志信息:%%(message)s
#日志是否输出到控制台  True  or  False
logger_out=False
#日志输出指定文件地址
logger_filepath=logging_Test.log

将读取配置文件类进行封装,日志类继承配置类。

补充知识:Python2/Python3自定义日志类教程

一、说明

1.1 背景说明

Python的logging功能是比较丰富的支持不同层次的日志输出,但或是我们想在日志前输出时间、或是我们想要将日志输入到文件,我们还是想要自定义日志类。

之前自己也尝试写过但感觉文档太乱看不懂怎么写,今天有人拿个半成品来问为什么代码报错,在其基础上改造了一下。

1.2 logging级别说明

logging日志级别及对应值如下,默认情况下直接运行只有INFO及以上级别才会输出(本质上是大于等于20才会输出),调试模式运行DEBUG日志才会输出。

可以通过自定义输出日志级别,指定直接运行输出什么级别的日志;不过调试模式打印的日志应该是不可以修改的。

Level Numeric value
CRITICAL 50
ERROR 40
WARNING 30
INFO 20
DEBUG 10
NOTSET 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')

三、日志截图

python3 配置logging日志类的操作

四、更科学的日志定义方式(20200310更新)

通过近段时间的使用发现原先的方法就自己用用没问题,但与别人产生调用及上生产时就会存在几个问题:

第一个问题是,直接通过logging.basicConfig()进行配置,会同时影响被调用库的日志设置。

第二个问题是,原现的文件日志形式只能输出到一个给定的文件,不能实现不同的日志类型输出到不同的日志文件。

第三个问题是,原现的文件日志形式使用"w"模式则上次日志会被清除,使用"a"模式则日志又会无限增长需要注意清理。

前两个问题通过getLogger时给定一个名称而不是直接获取根logger进行处理;第三个问题通过使用TimedRotatingFileHandler等替换FileHandler进行处理。

更新代码如下:

import os
import datetime
import logging
import logging.handlers

class LogConfig:
  def __init__(self):
    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():
      # 解决第一个问题--logging.basicConfig()会影响被调用库的日志--getLogger时给定一个名称而不是直接获取根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

  def get_file_logger(self,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")
      # 解决第三个问题--日志会不断增大需要手动去清理--使用TimedRotatingFileHandler等替换FileHandler
      # filename----日志文件
      # when----更换日志文件的时间单位
      # interval----更换日志文件的时间单位个数;这里是7天换一个文件
      # backupCount----保存的旧日志文件个数;这里即只保留上一个日志文件
      # encoding----日志文件编码
      _handler = logging.handlers.TimedRotatingFileHandler(filename=file_path,when='D',interval=7,backupCount=1,encoding='utf-8')
      # 实际发现有些时候这里setLevel并不起作用
      # _handler.setLevel(logging.INFO)
      _handler.setFormatter(formatter)
      return _handler
    def _gen_file_logger():
      # 解决第二个问题--不能定义多个日志文件--getLogger时给定一个名称而不是直接获取根logger
      _file_logger = logging.getLogger(log_file_name)
      _file_logger.addHandler(handler)
      return _file_logger

    log_file_dir = "log"
    _make_sure_log_dir_exist()
    handler = _gen_file_logger_handler()
    file_logger = _gen_file_logger()
    # 实际发现有些时候handler的setLevel并不起作用,要在这里setLevel
    file_logger.setLevel(logging.INFO)
    return file_logger

if __name__ == "__main__":
  # log_type = "console"
  # logger = LogConfig().get_console_logger()
  log_type = "file"
  # 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')

以上这篇python3 配置logging日志类的操作就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持三水点靠木。

Python 相关文章推荐
python数据库操作常用功能使用详解(创建表/插入数据/获取数据)
Dec 06 Python
详解Python中的Numpy、SciPy、MatPlotLib安装与配置
Nov 17 Python
Python对象属性自动更新操作示例
Jun 15 Python
使用pandas read_table读取csv文件的方法
Jul 04 Python
python utc datetime转换为时间戳的方法
Jan 15 Python
python自动化测试之如何解析excel文件
Jun 27 Python
python opencv 读取图片 返回图片某像素点的b,g,r值的实现方法
Jul 03 Python
在python中用print()输出多个格式化参数的方法
Jul 16 Python
python mysql断开重连的实现方法
Jul 26 Python
详解Python利用random生成一个列表内的随机数
Aug 21 Python
python 使用elasticsearch 实现翻页的三种方式
Jul 31 Python
python获取本周、上周、本月、上月及本季的时间代码实例
Sep 08 Python
python3 logging日志封装实例
Apr 08 #Python
Django实现whoosh搜索引擎使用jieba分词
Apr 08 #Python
Python 输出详细的异常信息(traceback)方式
Apr 08 #Python
python上传时包含boundary时的解决方法
Apr 08 #Python
python MultipartEncoder传输zip文件实例
Apr 07 #Python
xadmin使用formfield_for_dbfield函数过滤下拉表单实例
Apr 07 #Python
Xadmin+rules实现多选行权限方式(级联效果)
Apr 07 #Python
You might like
PHP二维数组去重算法
2016/12/17 PHP
浅谈PHP5.6 与 PHP7.0 区别
2019/10/09 PHP
Laravel框架实现即点即改功能的方法分析
2019/10/31 PHP
从盛大通行证上摘下来的身份证验证js代码
2011/01/11 Javascript
分别用marquee和div+js实现首尾相连循环滚动效果,仅3行代码
2011/09/21 Javascript
JS TextArea字符串长度限制代码集合
2012/10/31 Javascript
兼容FF和IE的动态table示例自写
2013/10/21 Javascript
ExtJS4利根据登录后不同的角色分配不同的树形菜单
2014/05/02 Javascript
jquery实现个人中心导航菜单效果和美观都非常不错
2014/09/02 Javascript
基于Css3和JQuery实现打字机效果
2015/08/11 Javascript
jquery彩色投票进度条简单实例演示
2020/07/23 Javascript
使用vue制作FullPage页面滚动效果
2017/08/21 Javascript
JavaScript实现数值自动增加动画
2017/12/28 Javascript
使用vue制作探探滑动堆叠组件的实例代码
2018/03/07 Javascript
关于vue中的ajax请求和axios包问题
2018/04/19 Javascript
基于Vue-Cli 打包自动生成/抽离相关配置文件的实现方法
2018/12/09 Javascript
手把手教你使用TypeScript开发Node.js应用
2019/05/06 Javascript
JavaScript页面倒计时功能完整示例
2019/05/15 Javascript
基于Element封装一个表格组件tableList的使用方法
2020/06/29 Javascript
python网络编程学习笔记(二):socket建立网络客户端
2014/06/09 Python
微信跳一跳python辅助软件思路及图像识别源码解析
2018/01/04 Python
Django中间件实现拦截器的方法
2018/06/01 Python
Python实现多条件筛选目标数据功能【测试可用】
2018/06/13 Python
django重新生成数据库中的某张表方法
2019/08/28 Python
python 批量修改 labelImg 生成的xml文件的方法
2019/09/09 Python
将tensorflow模型打包成PB文件及PB文件读取方式
2020/01/23 Python
菲律宾酒店预订网站:Hotels.com菲律宾
2017/07/12 全球购物
优质有机椰子产品:Dr. Goerg
2019/09/24 全球购物
接受捐赠答谢词
2014/01/27 职场文书
专题组织生活会方案
2014/06/15 职场文书
永远跟党走演讲稿
2014/09/12 职场文书
教师工作失职检讨书
2014/09/18 职场文书
四风查摆问题自查报告
2014/10/10 职场文书
出差报告范文
2014/11/06 职场文书
解除劳动合同证明书模板
2014/11/20 职场文书
如何用JavaScript实现一个数组惰性求值库
2021/05/05 Javascript