详解python之配置日志的几种方式

作为开发者,我们可以通过以下3中方式来配置logging:

1)使用Python代码显式的创建loggers, handlers和formatters并分别调用它们的配置函数;

2)创建一个日志配置文件,然后使用fileConfig()函数来读取该文件的内容;

3)创建一个包含配置信息的dict,然后把它传递个dictConfig()函数;

需要说明的是,logging.basicConfig()也属于第一种方式,它只是对loggers, handlers和formatters的配置函数进行了封装。另外,第二种配置方式相对于第一种配置方式的优点在于,它将配置信息和代码进行了分离,这一方面降低了日志的维护成本,同时还使得非开发人员也能够去很容易地修改日志配置。

一、使用Python代码实现日志配置

代码如下:

# 创建一个日志器logger并设置其日志级别为DEBUG
logger = logging.getLogger('simple_logger')
logger.setLevel(logging.DEBUG)

# 创建一个流处理器handler并设置其日志级别为DEBUG
handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.DEBUG)

# 创建一个格式器formatter并将其添加到处理器handler
formatter = logging.Formatter("%(asctime)s - %(name)s - %(levelname)s - %(message)s")
handler.setFormatter(formatter)

# 为日志器logger添加上面创建的处理器handler
logger.addHandler(handler)

# 日志输出
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')

运行输出:

2017-05-15 11:30:50,955 - simple_logger - DEBUG - debug message
2017-05-15 11:30:50,955 - simple_logger - INFO - info message
2017-05-15 11:30:50,955 - simple_logger - WARNING - warn message
2017-05-15 11:30:50,955 - simple_logger - ERROR - error message
2017-05-15 11:30:50,955 - simple_logger - CRITICAL - critical message

二、使用配置文件和fileConfig()函数实现日志配置

现在我们通过配置文件的方式来实现与上面同样的功能:

# 读取日志配置文件内容
logging.config.fileConfig('logging.conf')

# 创建一个日志器logger
logger = logging.getLogger('simpleExample')

# 日志输出
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')

配置文件logging.conf内容如下:

[loggers]
keys=root,simpleExample

[handlers]
keys=fileHandler,consoleHandler

[formatters]
keys=simpleFormatter

[logger_root]
level=DEBUG
handlers=fileHandler

[logger_simpleExample]
level=DEBUG
handlers=consoleHandler
qualname=simpleExample
propagate=0

[handler_consoleHandler]
class=StreamHandler
args=(sys.stdout,)
level=DEBUG
formatter=simpleFormatter

[handler_fileHandler]
class=FileHandler
args=('logging.log', 'a')
level=ERROR
formatter=simpleFormatter

[formatter_simpleFormatter]
format=%(asctime)s - %(name)s - %(levelname)s - %(message)s
datefmt=

运行输出:

2017-05-15 11:32:16,539 - simpleExample - DEBUG - debug message
2017-05-15 11:32:16,555 - simpleExample - INFO - info message
2017-05-15 11:32:16,555 - simpleExample - WARNING - warn message
2017-05-15 11:32:16,555 - simpleExample - ERROR - error message
2017-05-15 11:32:16,555 - simpleExample - CRITICAL - critical message

1. 关于fileConfig()函数的说明:

该函数实际上是对configparser模块的封装,关于configparser模块的介绍请参考<。

函数定义:

该函数定义在loging.config模块下:

代码如下:

logging.config.fileConfig(fname, defaults=None, disable_existing_loggers=True)

参数:

  1. fname:表示配置文件的文件名或文件对象
  2. defaults:指定传给ConfigParser的默认值
  3. disable_existing_loggers:这是一个布尔型值,默认值为True(为了向后兼容)表示禁用已经存在的logger,除非它们或者它们的祖先明确的出现在日志配置中;如果值为False则对已存在的loggers保持启动状态。

2. 配置文件格式说明:

上面提到过,fileConfig()函数是对ConfigParser/configparser模块的封装,也就是说fileConfig()函数是基于ConfigParser/configparser模块来理解日志配置文件的。换句话说,fileConfig()函数所能理解的配置文件基础格式是与ConfigParser/configparser模块一致的,只是在此基础上对文件中包含的sectionoption做了一下规定和限制,比如:

1)配置文件中一定要包含loggers、handlers、formatters这些section,它们通过keys这个option来指定该配置文件中已经定义好的loggers、handlers和formatters,多个值之间用逗号分隔;另外loggers这个section中的keys一定要包含root这个值;

2)loggers、handlers、formatters中所指定的日志器、处理器和格式器都需要在下面以单独的section进行定义。seciton的命名规则为[logger_loggerName]、[formatter_formatterName]、[handler_handlerName]

3)定义logger的section必须指定level和handlers这两个option,level的可取值为DEBUG、INFO、WARNING、ERROR、CRITICAL、NOTSET,其中NOTSET表示所有级别的日志消息都要记录,包括用户定义级别;handlers的值是以逗号分隔的handler名字列表,这里出现的handler必须出现在[handlers]这个section中,并且相应的handler必须在配置文件中有对应的section定义;

4)对于非root logger来说,除了level和handlers这两个option之外,还需要一些额外的option,其中qualname是必须提供的option,它表示在logger层级中的名字,在应用代码中通过这个名字得到logger;propagate是可选项,其默认是为1,表示消息将会传递给高层次logger的handler,通常我们需要指定其值为0,这个可以看下下面的例子;另外,对于非root logger的level如果设置为NOTSET,系统将会查找高层次的logger来决定此logger的有效level。

5)定义handler的section中必须指定class和args这两个option,level和formatter为可选option;class表示用于创建handler的类名,args表示传递给class所指定的handler类初始化方法参数,它必须是一个元组(tuple)的形式,即便只有一个参数值也需要是一个元组的形式;level与logger中的level一样,而formatter指定的是该处理器所使用的格式器,这里指定的格式器名称必须出现在formatters这个section中,且在配置文件中必须要有这个formatter的section定义;如果不指定formatter则该handler将会以消息本身作为日志消息进行记录,而不添加额外的时间、日志器名称等信息;

6)定义formatter的sectioin中的option都是可选的,其中包括format用于指定格式字符串,默认为消息字符串本身;datefmt用于指定asctime的时间格式,默认为'%Y-%m-%d %H:%M:%S';class用于指定格式器类名,默认为logging.Formatter;

说明:

配置文件中的class指定类名时,该类名可以是相对于logging模块的相对值,如:FileHandlerhandlers.TimeRotatingFileHandler;也可以是一个绝对路径值,通过普通的import机制来解析,如自定义的handler类mypackage.mymodule.MyHandler,但是mypackage需要在Python可用的导入路径中--sys.path。

3. 对于propagate属性的说明

实例1:

我们把logging.conf中simpleExample这个handler定义中的propagate属性值改为1,或者删除这个option(默认值就是1):

[logger_simpleExample]
level=DEBUG
handlers=consoleHandler
qualname=simpleExample
propagate=1

现在来执行同样的代码:

# 读取日志配置文件内容
logging.config.fileConfig('logging.conf')

# 创建一个日志器logger
logger = logging.getLogger('simpleExample')

# 日志输出
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')

我们会发现,除了在控制台有输出信息时候,在logging.log文件中也有内容输出:

2017-05-15 16:06:25,366 - simpleExample - ERROR - error message
2017-05-15 16:06:25,367 - simpleExample - CRITICAL - critical message

这说明simpleExample这个logger在处理完日志记录后,把日志记录传递给了上级的root logger再次做处理,所有才会有两个地方都有日志记录的输出。通常,我们都需要显示的指定propagate的值为0,防止日志记录向上层logger传递。

实例2:

现在,我们试着用一个没有在配置文件中定义的logger名称来获取logger:

# 读取日志配置文件内容
logging.config.fileConfig('logging.conf')

# 用一个没有在配置文件中定义的logger名称来创建一个日志器logger
logger = logging.getLogger('simpleExample1')

# 日志输出
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')

运行程序后,我们会发现控制台没有任何输出,而logging.log文件中又多了两行输出:

2017-05-15 16:13:16,810 - simpleExample1 - ERROR - error message
2017-05-15 16:13:16,810 - simpleExample1 - CRITICAL - critical message

这是因为,当一个日志器没有被设置任何处理器是,系统会去查找该日志器的上层日志器上所设置的日志处理器来处理日志记录。simpleExample1在配置文件中没有被定义,因此logging.getLogger(simpleExample1)这行代码这是获取了一个logger实例,并没有给它设置任何处理器,但是它的上级日志器--root logger在配置文件中有定义且设置了一个FileHandler处理器,simpleExample1处理器最终通过这个FileHandler处理器将日志记录输出到logging.log文件中了。

三、使用字典配置信息和dictConfig()函数实现日志配置

Python 3.2中引入的一种新的配置日志记录的方法--用字典来保存logging配置信息。这相对于上面所讲的基于配置文件来保存logging配置信息的方式来说,功能更加强大,也更加灵活,因为我们可把很多的数据转换成字典。比如,我们可以使用JSON格式的配置文件、YAML格式的配置文件,然后将它们填充到一个配置字典中;或者,我们也可以用Python代码构建这个配置字典,或者通过socket接收pickled序列化后的配置信息。总之,你可以使用你的应用程序可以操作的任何方法来构建这个配置字典。

这个例子中,我们将使用YAML格式来完成与上面同样的日志配置。

首先需要安装PyYAML模块:

pip install PyYAML

Python代码:

import logging
import logging.config
import yaml

with open('logging.yml', 'r') as f_conf:
  dict_conf = yaml.load(f_conf)
logging.config.dictConfig(dict_conf)

logger = logging.getLogger('simpleExample')
logger.debug('debug message')
logger.info('info message')
logger.warn('warn message')
logger.error('error message')
logger.critical('critical message')

logging.yml配置文件的内容:
version: 1
formatters:
 simple:
  format: '%(asctime)s - %(name)s - %(levelname)s - %(message)s'
handlers:
 console:
  class: logging.StreamHandler
  level: DEBUG
  formatter: simple
  stream: ext://sys.stdout
 console_err:
  class: logging.StreamHandler
  level: ERROR
  formatter: simple
  stream: ext://sys.stderr
loggers:
 simpleExample:
  level: DEBUG
  handlers: [console]
  propagate: yes
root:
 level: DEBUG
 handlers: [console_err]

输出结果:

2017-05-21 14:19:31,089 - simpleExample - DEBUG - debug message
2017-05-21 14:19:31,089 - simpleExample - INFO - info message
2017-05-21 14:19:31,089 - simpleExample - WARNING - warn message
2017-05-21 14:19:31,089 - simpleExample - ERROR - error message
2017-05-21 14:19:31,090 - simpleExample - CRITICAL - critical message

1. 关于dictConfig()函数的说明:

该函数实际上是对configparser模块的封装。

函数定义:

该函数定义在loging.config模块下:

logging.config.dictConfig(config)

该函数可以从一个字典对象中获取日志配置信息,config参数就是这个字典对象。关于这个字典对象的内容规则会在下面进行描述。

2. 配置字典说明

无论是上面提到的配置文件,还是这里的配置字典,它们都要描述出日志配置所需要创建的各种对象以及这些对象之间的关联关系。比如,可以先创建一个名额为“simple”的格式器formatter;然后创建一个名为“console”的处理器handler,并指定该handler输出日志所使用的格式器为"simple";然后再创建一个日志器logger,并指定它所使用的处理器为"console"。

传递给dictConfig()函数的字典对象只能包含下面这些keys,其中version是必须指定的key,其它key都是可选项:

key名称 描述
version 必选项,其值是一个整数值,表示配置格式的版本,当前唯一可用的值就是1
formatters 可选项,其值是一个字典对象,该字典对象每个元素的key为要定义的格式器名称,value为格式器的配置信息组成的dict,如format和datefmt
filters 可选项,其值是一个字典对象,该字典对象每个元素的key为要定义的过滤器名称,value为过滤器的配置信息组成的dict,如name
handlers 可选项,其值是一个字典对象,该字典对象每个元素的key为要定义的处理器名称,value为处理器的配置信息组成的dcit,如class、level、formatter和filters,其中class为必选项,其它为可选项;其他配置信息将会传递给class所指定的处理器类的构造函数,如下面的handlers定义示例中的stream、filename、maxBytes和backupCount等
loggers 可选项,其值是一个字典对象,该字典对象每个元素的key为要定义的日志器名称,value为日志器的配置信息组成的dcit,如level、handlers、filters 和 propagate(yes
root 可选项,这是root logger的配置信息,其值也是一个字典对象。除非在定义其它logger时明确指定propagate值为no,否则root logger定义的handlers都会被作用到其它logger上
incremental 可选项,默认值为False。该选项的意义在于,如果这里定义的对象已经存在,那么这里对这些对象的定义是否应用到已存在的对象上。值为False表示,已存在的对象将会被重新定义。
disable_existing_loggers 可选项,默认值为True。该选项用于指定是否禁用已存在的日志器loggers,如果incremental的值为True则该选项将会被忽略

handlers定义示例:

handlers:
 console:
  class : logging.StreamHandler
  formatter: brief
  level  : INFO
  filters: [allow_foo]
  stream : ext://sys.stdout
 file:
  class : logging.handlers.RotatingFileHandler
  formatter: precise
  filename: logconfig.log
  maxBytes: 1024
  backupCount: 3

3. 关于外部对象的访问

需要说明的是,上面所使用的对象并不限于loggging模块所提供的对象,我们可以实现自己的formatter或handler类。另外,这些类的参数也许需要包含sys.stderr这样的外部对象。如果配置字典对象是使用Python代码构造的,可以直接使用sys.stdout、sys.stderr;但是当通过文本文件(如JSON、YAML格式的配置文件)提供配置时就会出现问题,因为在文本文件中,没有标准的方法来区分sys.stderr和字符串 'sys.stderr'。为了区分它们,配置系统会在字符串值中查找特定的前缀,例如'ext://sys.stderr'中'ext://'会被移除,然后import sys.stderr

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持我们。

(0)

相关推荐

  • 详解Python中的日志模块logging

    许多应用程序中都会有日志模块,用于记录系统在运行过程中的一些关键信息,以便于对系统的运行状况进行跟踪.在.NET平台中,有非常著名的第三方开源日志组件log4net,c++中,有人们熟悉的log4cpp,而在python中,我们不需要第三方的日志组件,因为它已经为我们提供了简单易用.且功能强大的日志模块:logging.logging模块支持将日志信息保存到不同的目标域中,如:保存到日志文件中:以邮件的形式发送日志信息:以http get或post的方式提交日志到web服务器:以windows事

  • python自动化工具日志查询分析脚本代码实现

    受控节点slave.py 复制代码 代码如下: import socketimport reclass Log(object):    file_list=['access.log','C:\\access.log']    master_ip='192.168.0.103'    def __init__(self):        s=socket.socket(socket.AF_INET,socket.SOCK_STREAM)        s.bind(('',3333))      

  • Python中使用logging模块打印log日志详解

    学一门新技术或者新语言,我们都要首先学会如何去适应这们新技术,其中在适应过程中,我们必须得学习如何调试程序并打出相应的log信息来,正所谓"只要log打的好,没有bug解不了",在我们熟知的一些信息技术中,log4xxx系列以及开发Android app时的android.util.Log包等等都是为了开发者更好的得到log信息服务的.在Python这门语言中,我们同样可以根据自己的程序需要打出log. log信息不同于使用打桩法打印一定的标记信息,log可以根据程序需要而分出不同的l

  • Python同时向控制台和文件输出日志logging的方法

    本文实例讲述了Python同时向控制台和文件输出日志logging的方法.分享给大家供大家参考.具体如下: python提供了非常方便的日志模块,可实现同时向控制台和文件输出日志的功能. #-*- coding:utf-8 -*- import logging # 配置日志信息 logging.basicConfig(level=logging.DEBUG, format='%(asctime)s %(name)-12s %(levelname)-8s %(message)s', datefmt

  • python标准日志模块logging的使用方法

    最近写一个爬虫系统,需要用到python的日志记录模块,于是便学习了一下.python的标准库里的日志系统从Python2.3开始支持.只要import logging这个模块即可使用.如果你想开发一个日志系统, 既要把日志输出到控制台, 还要写入日志文件,只要这样使用: 复制代码 代码如下: import logging# 创建一个loggerlogger = logging.getLogger('mylogger')logger.setLevel(logging.DEBUG)# 创建一个ha

  • python根据文件大小打log日志

    本文实例讲述了python根据文件大小打log日志的方法,分享给大家供大家参考.具体方法如下: import glob import logging import logging.handlers LOG_FILENAME='logging_rotatingfile_example.out' # Set up a specific logger with our desired output level my_logger = logging.getLogger('MyLogger') my_l

  • 使用Python编写提取日志中的中文的脚本的方法

    由于工作需要在一大堆日志里面提取相应的一些固定字符,如果单纯靠手工取提取,数据量大,劳心劳力,于是自然而然想到了用Python做一个对应的提取工具,代替手工提取的繁杂,涉及中文字符,正则表达式不好匹配,但不是不可以实现,这个以后优化时再说. 需求描述: 一个父目录中存在多个子文件夹,子文件夹下有多个txt形式化的Log日志,要求从所有地方Log日志中找出CardType=9, CardNo=0时的CardID的值,并将其统计存储到一个文本文件中,要求CardID不能够重复. 需求解析: 首先获取

  • Python中内置的日志模块logging用法详解

    logging模块简介 Python的logging模块提供了通用的日志系统,可以方便第三方模块或者是应用使用.这个模块提供不同的日志级别,并可以采用不同的方式记录日志,比如文件,HTTP GET/POST,SMTP,Socket等,甚至可以自己实现具体的日志记录方式. logging模块与log4j的机制是一样的,只是具体的实现细节不同.模块提供logger,handler,filter,formatter. logger:提供日志接口,供应用代码使用.logger最长用的操作有两类:配置和发

  • 详解python之配置日志的几种方式

    作为开发者,我们可以通过以下3中方式来配置logging: 1)使用Python代码显式的创建loggers, handlers和formatters并分别调用它们的配置函数: 2)创建一个日志配置文件,然后使用fileConfig()函数来读取该文件的内容: 3)创建一个包含配置信息的dict,然后把它传递个dictConfig()函数: 需要说明的是,logging.basicConfig()也属于第一种方式,它只是对loggers, handlers和formatters的配置函数进行了封

  • 详解Python实现进度条的4种方式

    这里只列举了部分方法,其他方法或python库暂时还没使用到 1.不用库,直接打印: 代码样例: import time #demo1 def process_bar(percent, start_str='', end_str='', total_length=0): bar = ''.join(["\033[31m%s\033[0m"%' '] * int(percent * total_length)) + '' bar = '\r' + start_str + bar.ljus

  • 详解python里使用正则表达式的分组命名方式

    详解python里使用正则表达式的分组命名方式 分组匹配的模式,可以通过groups()来全部访问匹配的元组,也可以通过group()函数来按分组方式来访问,但是这里只能通过数字索引来访问,如果某一天产品经理需要修改需求,让你在它们之中添加一个分组,这样一来,就会导致匹配的数组的索引的变化,作为开发人员的你,必须得一行一行代码地修改.因此聪明的开发人员又想到一个好方法,把这些分组进行命名,只需要对名称进行访问分组,不通过索引来访问了,就可以避免这个问题.那么怎么样来命名呢?可以采用(?P<nam

  • 详解Python中list[::-1]的几种用法

    本文主要介绍了Python中list[::-1]的几种用法,分享给大家,具体如下: s = "abcde" list的[]中有三个参数,用冒号分割 list[param1:param2:param3] param1,相当于start_index,可以为空,默认是0 param2,相当于end_index,可以为空,默认是list.size param3,步长,默认为1.步长为-1时,返回倒序原序列 举例说明 param1 = -1,只有一个参数,作用是通过下标访问数据,-1为倒数第一个

  • 详解Python实现图像分割增强的两种方法

    方法一 import random import numpy as np from PIL import Image, ImageOps, ImageFilter from skimage.filters import gaussian import torch import math import numbers import random class RandomVerticalFlip(object): def __call__(self, img): if random.random()

  • 详解Python实现字典合并的四种方法

    目录 1.用for循环把一个字典合并到另一个字典 2.用dict(b, **a)方法构造一个新字典 3.用b.update(a)的方法,更新字典 4.把字典转换成列表合并后,再转换成字典 (1)利用a.items().b.items()把a.b两个字典转换成元组键值对列表 (2)合并列表并且把合并后的列表转换成字典 5.实例,netmiko使用json格式的数据进行自动化操作 (1)json格式的处理 (2)json格式的设备信息列表 (3)netmiko读取json类型信息示例 1.用for循

  • 一文详解Python中实现单例模式的几种常见方式

    目录 Python 中实现单例模式的几种常见方式 元类(Metaclass): 装饰器(Decorator): 模块(Module): new 方法: Python 中实现单例模式的几种常见方式 元类(Metaclass): class SingletonType(type): """ 单例元类.用于将普通类转换为单例类. """ _instances = {} # 存储单例实例的字典 def __call__(cls, *args, **kwa

  • 详解Spring Boot 访问Redis的三种方式

    目录 前言 开始准备 RedisTemplate JPA Repository Cache 总结 前言 最近在极客时间上面学习丁雪丰老师的<玩转 Spring 全家桶>,其中讲到访问Redis的方式,我专门把他们抽出来,在一起对比下,体验一下三种方式开发上面的不同, 分别是这三种方式 RedisTemplate JPA Repository Cache 开始准备 开始之前我们需要有Redis安装,我们采用本机Docker运行Redis, 主要命令如下 docker pull redis doc

  • 详解Golang开启http服务的三种方式

    前言 都说go标准库实用,Api设计简洁.这次就用go 标准库中的net/http包实现一个简洁的http web服务器,包括三种版本. v1最简单版 直接使用http.HandleFunc(partern,function(http.ResponseWriter,*http.Request){}) HandleFunc接受两个参数,第一个为路由地址,第二个为处理方法. //v1 func main() { http.HandleFunc("/", func(w http.Respon

  • 详解java连接mysql数据库的五种方式

    第一种方式:将用户名和密码封装在Properties类中 首先,导入数据库连接包这个是毋庸置疑的.创建一个jdbc驱动dirver.将数据库(以MySQL为例)的url保存在所创建的字符串url中.如果mysql版本低于8.0,则url保存形式应该为: String url = "jdbc:mysql://localhost:3306/test" 如果mysql版本为8.0版本或以上,url保存形式为: String url = "jdbc:mysql://localhost

随机推荐