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 Mysql数据库操作 Perl操作Mysql数据库
Jan 12 Python
Python中的hypot()方法使用简介
May 18 Python
在Mac OS系统上安装Python的Pillow库的教程
Nov 20 Python
python调用自定义函数的实例操作
Jun 26 Python
详解python播放音频的三种方法
Sep 23 Python
python3 使用traceback定位异常实例
Mar 09 Python
python 错误处理 assert详解
Apr 20 Python
python中查看.db文件中表格的名字及表格中的字段操作
Jul 07 Python
Python3安装模块报错Microsoft Visual C++ 14.0 is required的解决方法
Jul 28 Python
python实现感知机模型的示例
Sep 30 Python
Python基础知识之变量的详解
Apr 14 Python
Python代码,能玩30多款童年游戏!这些有几个是你玩过的
Apr 27 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多线程异步请求的3种方法
2014/01/17 PHP
修改ThinkPHP缓存为Memcache的方法
2014/06/25 PHP
php使用CURL伪造IP和来源实例详解
2015/01/15 PHP
用PHP的socket实现客户端到服务端的通信实例详解
2017/02/04 PHP
微信接口生成带参数的二维码
2017/07/31 PHP
基于php解决json_encode中文UNICODE转码问题
2020/11/10 PHP
用JavaScript和注册表脚本实现右键收藏Web页选中文本
2007/01/28 Javascript
阻止JavaScript事件冒泡传递(cancelBubble 、stopPropagation)
2007/05/08 Javascript
JavaScript访问样式表代码
2010/10/15 Javascript
jQuery中click事件用法实例
2014/12/26 Javascript
使用angular写一个hello world
2015/01/23 Javascript
js日期范围初始化得到前一个月日期的方法
2015/05/05 Javascript
谈谈基于iframe、FormData、FileReader三种无刷新上传文件的方法
2015/12/03 Javascript
基于JavaScript实现回到页面顶部动画代码
2016/05/24 Javascript
Bootstrap插件全集
2016/07/18 Javascript
详细总结Javascript中的焦点管理
2016/09/17 Javascript
js设置和获取自定义属性的方法
2016/10/20 Javascript
JQuery异步提交表单与文件上传功能示例
2017/01/12 Javascript
hammer.js实现图片手势放大效果
2017/08/29 Javascript
Vue 莹石摄像头直播视频实例代码
2018/08/31 Javascript
vue-router之nuxt动态路由设置的两种方法小结
2018/09/26 Javascript
JS实现图片拖拽交换效果
2018/11/30 Javascript
详解Vue+ElementUI从零开始搭建自己的网站(一、环境搭建)
2019/04/30 Javascript
python ipset管理 增删白名单的方法
2019/01/14 Python
PyQt5 多窗口连接实例
2019/06/19 Python
在Windows上安装和配置 Jupyter Lab 作为桌面级应用程序教程
2020/04/22 Python
python 如何用urllib与服务端交互(发送和接收数据)
2021/03/04 Python
CSS3实现淘宝留白的方法
2020/06/05 HTML / CSS
Carter’s官方旗舰店:美国受欢迎的婴童服装品牌
2018/01/21 全球购物
医大实习自我鉴定
2013/12/07 职场文书
商超业务员岗位职责
2014/03/12 职场文书
2014年高中教师工作总结
2014/12/19 职场文书
送达通知书
2015/04/25 职场文书
国富论读书笔记
2015/06/26 职场文书
2016党员三严三实心得体会
2016/01/15 职场文书
MySQL之DML语言
2021/04/05 MySQL