您的位置:首页 > 编程语言 > Python开发

python-logging.config【日志模块】

2012-09-03 10:25 561 查看

1,logging模块简介

Python的logging模块提供了通用的日志系统,熟练使用longgong模块可以方便开发者开发第三方模块或者是自己的Python应用。同样这个模块提供不同的日志级别,并可以采用不同的方式记录日志,比如文件,HTTP、GET/POST,SMTP,Socket等,甚至可以自己实现具体的日志记录方式。下文我将主要介绍如何使用文件方式记录log。

logging模块包括loggerhandlerfilterformatter这四个基本概念。

logger:提供日志接口,供应用代码使用。logger最长用的操作有两类:配置和发送日志消息。可以通过logging.getLogger(name)获取logger对象,如果不指定name则返回root对象,多次使用相同的name调用getLogger方法返回同一个logger对象。

handler:将日志记录(log record)发送到合适的目的地(destination),比如文件,socket等。一个logger对象可以通过addHandler方法添加0到多个handler,每个handler又可以定义不同日志级别,以实现日志分级过滤显示。Handler 是处理器, 可以把log记录到文件系统中,
输出到终端设备, 或者网络sockt或者邮件等。

StreamHandler
FileHandler
RotatingFileHandler
TimedRotatingFileHandler
SocketHandler
DatagramHandler
SysLogHandler
NTEventLogHandler
SMTPHandler
MemoryHandler
HTTPHandler
filter:提供一种优雅的方式决定一个日志记录是否发送到handler。

formatter:指定日志记录输出的具体格式。使用这个可以更容易的根据错误检错。formatter的构造方法需要两个参数:消息的格式字符串和日期字符串,这两个参数都是可选的。

%(name)s            Name of the logger (logging channel)
%(levelno)s         Numeric logging level for the message (DEBUG, INFO,
WARNING, ERROR, CRITICAL)
%(levelname)s       Text logging level for the message ("DEBUG", "INFO",
"WARNING", "ERROR", "CRITICAL")
%(pathname)s        Full pathname of the source file where the logging
call was issued (if available)
%(filename)s        Filename portion of pathname
%(module)s          Module (name portion of filename)
%(lineno)d          Source line number where the logging call was issued
(if available)
%(created)f         Time when the LogRecord was created (time.time()
return value)
%(asctime)s         Textual time when the LogRecord was created
%(msecs)d           Millisecond portion of the creation time
%(relativeCreated)d Time in milliseconds when the LogRecord was created,
relative to the time the logging module was loaded
(typically at application startup time)
%(thread)d          Thread ID (if available)
%(process)d         Process ID (if available)
%(message)s         The result of record.getMessage(), computed just as
the record is emitted


2,基本方法

事实上,不管用最简单的logging模块的basicConfig函数,还是用配置文件形式,只要log文件一样,不过多少程序,都会以增加的方式往里写。

2.1,程序1

一些小型的程序我们不需要构造太复杂的log系统,可以直接使用logging模块的basicConfig函数即可,代码如下:

#test.py
import logging
LOG_FILENAME="./mylog.log"
logging.basicConfig(filename=LOG_FILENAME,level=logging.DEBUG)
logging.debug("this is a debugmsg!")
logging.info("this is a infomsg!")
logging.warn("this is a warn msg!")
logging.error("this is a error msg!")
logging.critical("this is a critical msg!")
这样会生成一个mylog.log的日志文件,每运行一次,里面都增加如下内容:

DEBUG:root:this is a debugmsg!
INFO:root:this is a infomsg!
WARNING:root:this is a warn msg!
ERROR:root:this is a error msg!
CRITICAL:root:this is a critical msg!

需要说明的是我将level设定为DEBUG级别,所以log日志中只显示了包含该级别及该级别以上的log信息。信息级别依次是:notset、debug、info、warn、error、critical。如果在多个模块中使用这个配置的话,只需在主模块中配置即可,其他模块会有相同的使用效果。

2.2,程序2

上述的基础配置比较简单,下面我们看一个更标准的程序,我们需要依次设置logger、handler、formatter等配置。

# -* - coding: UTF-8 -* -
import logging

log_file = "./nomal_logger.log"
log_level = logging.DEBUG

#建立【logger】
#logger = logging.getLogger()
logger = logging.getLogger("loggingmodule.NomalLogger")
logger.setLevel(log_level)

#建立【文件句柄】和【流句柄】并分别设置级别
filehandler = logging.FileHandler(log_file)
filehandler.setLevel(logging.ERROR)
streamhandler = logging.StreamHandler()
streamhandler.setLevel(logging.ERROR)
#建立【格式串】
formatter = logging.Formatter("[%(levelname)s][%(funcName)s][%(asctime)s]%(message)s")
#【文件句柄】和【流句柄】使用【格式串】
filehandler.setFormatter(formatter)
streamhandler.setFormatter(formatter)
#【logger】连接【句柄】
logger.addHandler(filehandler)
logger.addHandler(streamhandler)

#test
logger.debug("this is a debug msg!")
logger.info("this is a info msg!")
logger.warn("this is a warn msg!")
logger.error("this is a error msg!")
logger.critical("this is a error msg!")
这样会生成一个Log_test.txt的日志文件,每运行一次,里面都增加2行:

[ERROR][<module>][2012-09-04 22:41:19,546]this is a error msg!
[CRITICAL][<module>][2012-09-04 22:41:19,562]this is a error msg!
上面程序首先生成一个日志对象logger,用它来控制日志的输入。然后生成了一个handler,logging支持很多种Handler,像FileHandler,SocketHandler等待,这里由于我们要写文件,所以用了FileHandler,它的参数就是filename,默认当前路径,当然我们可以自己指定路径。后面还设置日志信息输出的级别。

2.3,程序3

可以用“python test.py debug”的方式来控制输入级别,代码如下:

#test.py
import logging
import sys
LEVELS={'debug':logging.DEBUG,
'info':logging.INFO,
'warning':logging.WARNING,
'error':logging.ERROR,
'critical':logging.CRITICAL}

if len(sys.argv)>1:
level_name=sys.argv[1]
level=LEVELS.get(level_name,logging.NOTSET)
logging.basicConfig(level=level)

logging.debug("This is a debug message")
logging.info("This is an info message")
logging.warning("This is a warning message")
logging.error("This is an error message")


2.4,程序4

使用配置文件形式:Python的logging.config.

Python的logging模块接口仿log4j,概念上一致,使用上相当方便。利用logging.config.fileConfig(),可以将日志的配置用文件来描述,简化了日志的初始化。

例程:

# test.py

import logging

import logging.config

logging.config.fileConfig("logging.conf")

#create logger

logger = logging.getLogger("example")

#"application" code

logger.debug("debug message")

logger.info("info message")

logger.warn("warn message")

logger.error("error message")

logger.critical("critical message")

logHello = logging.getLogger("hello")

logHello.info("Hello world!")

配置文件示例如下:

# logging.conf

[loggers]

keys=root,example

[handlers]

keys=consoleHandler,rotateFileHandler

[formatters]

keys=simpleFormatter

[formatter_simpleFormatter]

format=[%(asctime)s](%(levelname)s)%(name)s : %(message)s

[logger_root]

level=DEBUG

handlers=consoleHandler,rotateFileHandler

[logger_example]

level=DEBUG

handlers=consoleHandler,rotateFileHandler

qualname=example

propagate=0

[handler_consoleHandler]

class=StreamHandler

level=DEBUG

formatter=simpleFormatter

args=(sys.stdout,)

[handler_rotateFileHandler]

class=handlers.RotatingFileHandler

level=DEBUG

formatter=simpleFormatter

args=('test.log', 'a', 200000, 9)

这样,会生成test.log.

2.5,程序5【多文件共用一个log文件】

这里叙述的是我工作中遇到的问题,需要将多个文件的log信息输入到一个文件中去。我的理解是,在A文件中设置了譬如log文件位置、log文件名字、格式化信息等等,然后在A文件末尾可能用os.system("python B.py")这样又调用了B文件,那么在B文件中要也输出到之前指定的那个log文件,必须重新设置一遍刚才的log信息。这是因为,os.system()相当于系统调用了。与import不一样,import可以理解为将代码插到这个位置。

A.py

import os,sys,time

def xixi():
gLogger.info("xixi")

def mylog():
import logging.config
gLogger = logging.getLogger()
logdir = "log/"
logfile = "verify.log"
os.system("mkdir -p " + logdir)
log_file = "./%s/%s"%(logdir,logfile)
formatter = logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s')
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(formatter)
gLogger.addHandler(handler)
formatter = logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s')
handler = logging.handlers.RotatingFileHandler(log_file)
handler.setFormatter(formatter)
gLogger.addHandler(handler)
gLogger.setLevel(logging.INFO)
return gLogger

if __name__ == "__main__":
gLogger = mylog()
xixi()
gLogger.info("A.py")
os.system("python B.py")


B.py

import os,sys,time
import logging.config

gLogger = logging.getLogger()
logdir = "log/"
logfile = "verify.log"
os.system("mkdir -p " + logdir)
log_file = "./%s/%s"%(logdir,logfile)
formatter = logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s')
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(formatter)
gLogger.addHandler(handler)
formatter = logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s')
handler = logging.handlers.RotatingFileHandler(log_file)
handler.setFormatter(formatter)
gLogger.addHandler(handler)
gLogger.setLevel(logging.INFO)

gLogger.info("B1.py")
if __name__ == "__main__":
gLogger.info("B2.py")


命令

[admin@r42h06016.xy2.aliyun.com]$python A.py


输出

[2012-09-18 23:13:28,301][INFO] xixi
[2012-09-18 23:13:28,301][INFO] A.py
[2012-09-18 23:13:28,344][INFO] B1.py
[2012-09-18 23:13:28,344][INFO] B2.py
分析

首先,python A.py 这时,运行到main,调用函数mylog初始化log信息。这时可以用gLogger来输出信息。调用xixi,之所以可以用,因为函数相当于将代码贴过来,这时gLogger已经定义过了。然后再输出A.py的信息。然后系统调用B,B已经事先初始化了。这时gLogger必然处处可用。

2.6 我的习惯

我一般是将logging等相关内容写在一个文件里,如下所示:

module_mylog.py

import os,sys
def mylog():
import logging.config
gLogger = logging.getLogger()
logdir = "log/"
logfile = "load.log"
os.system("mkdir -p " + logdir)
log_file = "./%s/%s"%(logdir,logfile)
formatter = logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s')
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(formatter)
gLogger.addHandler(handler)
formatter = logging.Formatter('[%(asctime)s][%(levelname)s] %(message)s')
handler = logging.handlers.RotatingFileHandler(log_file)
handler.setFormatter(formatter)
gLogger.addHandler(handler)
gLogger.setLevel(logging.INFO)
return gLogger
gLogger = mylog()
其他任何文件要公用这个log时,都只需import就可以:
A.py

from module_myverifylog import gLogger

if __name__ == "__main__":
gLogger.info("A.py")


B.py

from module_myverifylog import gLogger

if __name__ == "__main__":
gLogger.info("B.py")


不用担心其他文件都import它都会重新运行,实际上只会运行一次,这是当做全局变量来做的。这样A.py和B.py的log都会在一个日志里了。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: