日志文件是在开发中常见的用于记录程序运行的工具,它就像飞机上的黑盒子,记录下飞行的每时每刻,这样在飞机出现故障的时候,可以很快的找到问题所在。对于应用的开发,Debug和运行来说,日志都相当重要,虽然没有日志程序仍然可以跑,但是对于一个合格的开发者来说,保存日志是一个很良好的习惯。

使用Python的日志模块

在Python中,我们可以使用Python的标准日志模块来写日志,日志模块经过社区开发者的精心设计,用起来很方便,而且使用起来相当灵活,就像这样:

1
2
3
import logging
logging.basicConfig(level=logging.INFO)
logging.info('Hello World')

在命令行中测试这段代码,会出现:

1
INFO:root:Hello World

基本元素说明

  • Logger:用于输出的日志的总对象,提供了应用程序可以直接使用的接口
  • Handlers:用来指定log的输出方式,将(logger创建的)日志记录发送到合适的输出源
  • Formatters:设置日志信息的结构和内容格式,默认的时间格式为%Y-%m-%d %H:%M:%S
  • Filter:过滤器,用来过滤的输出内容

Logger

每个程序在输出信息之前都要获得一个Logger。Logger通常对应了程序的模块名,获取Logger的方法:

1
logger = logging.getLogger(__name__)

Logger常用函数

Logger.setLevel(level): 指定最低的日志级别,低于lel的级别将被忽略
Logger.addFilter(filt): 添加指定的filter
Logger.removeFilter(filt): 删除指定的filter
Logger.addHandler(hdlr): 增加指定的handler
Logger.removeHandler(hdlr): 删除指定的handler
Logger.log(“debug”,”This is a bug”): 可以通过这个函数直接输出内容并选择对应的告警级别
Logger.DEBUG( ):可以设置的日志级别,Debug可以替换为其他级别

级别

level有以下几个级别

1
2
3
4
5
6
7
8
9
10
11
12
13
14
$ python
>>> import logging
>>> print logging.NOTSET
0
>>> print logging.DEBUG
10
>>> print logging.INFO
20
>>> print logging.WARNING
30
>>> print logging.ERROR
40
>>> print logging.CRITICAL
50

优先级从小到大一共分为6级,NOTSET < DEBUG < INFO < WARNING < ERROR < CRITICAL
如果把Logger的级别设置为DEBUG, 那么小于DEBUG级别的日志将不会输出

Handlers

Handler对象负责发送相关的信息到指定目的地。
Python的日志系统有多种Handler可以使用。有些Handler可以把信息输出到控制台,有些Logger可以把信息输出到文件,还有些 Handler可以把信息发送到网络上。如果觉得不够用,还可以编写自己的Handler。可以通过addHandler( )方法添加多个handler

Handlers常用函数

Handler.setLevel(lel):指定被处理的信息级别,低于lel级别的信息将被忽略
Handler.setFormatter():给这个handler选择一个格式
Handler.addFilter(filt):新增一个filter对象
Handler.removeFilter(filt):删除一个filter对象

Handlers常用种类

  • logging.StreamHandler
    – 使用这个Handler可以向类似与sys.stdout或者sys.stderr的任何文件对象(file object)输出信息
    – 它的构造函数是:StreamHandler(strm),其中strm参数是一个文件对象,默认是sys.stderr
  • logging.FileHandler
    – 和StreamHandler类似,用于向一个文件输出日志信息。不过FileHandler会帮你打开这个文件
    – 它的构造函数是:FileHandler(filename,mode),filename是文件名,必须指定一个文件名。mode是文件的打开方式,默认是’a’,即添加到文件末尾
  • logging.handlers.RotatingFileHandler
    – 这个Handler类似于上面的FileHandler,但是它可以管理文件大小。当文件达到一定大小之后,它会自动将当前日志文件改名,然后创建一个新的同名日志文件继续输出。比如日志文件是example.log。当example.log达到指定的大小之后,RotatingFileHandler自动把文件改名为example.log.1。不过,如果example.log.1已经存在,会先把example.log.1重命名为example.log.2。。。最后重新创建 exmaple.log,继续输出日志信息
    – 它的构造函数是:RotatingFileHandler( filename, mode, maxBytes, backupCount) 其中filename和mode两个参数和FileHandler一样。maxBytes用于指定日志文件的最大文件大小。如果maxBytes为0,意味着日志文件可以无限大,这时上面描述的重命名过程就不会发生。backupCount用于指定保留的备份文件的个数。比如,如果指定为2,当上面描述的重命名过程发生时,原有的chat.log.2并不会被更名,而是被删除
1
2
3
#example
#create a new log_file when the size is 2000 bytes and only backup 100 logfile
handler = logging.handlers.RotatingFileHandler(LOG_FILENAME, maxBytes=2000, backupCount=100)
  • logging.handlers.TimedRotatingFileHandler
    – 这个Handler和RotatingFileHandler类似,不过,它没有通过判断文件大小来决定何时重新创建日志文件,而是间隔一定时间就 自动创建新的日志文件。重命名的过程与RotatingFileHandler类似,不过新的文件不是附加数字,而是当前时间
    – 它的构造函数是:TimedRotatingFileHandler( filename ,when ,interval ,backupCount),其中filename参数和backupCount参数和RotatingFileHandler具有相同的意义,interval是时间间隔,when参数是一个字符串,表示时间间隔的单位,不区分大小写。它有以下取值:S 秒 M 分 H 小时 D 天 W 每星期(interval==0时代表星期一)midnight 每天凌晨
1
2
3
#example
#create a new log_file in the midnight
handler_1 = logging.handlers.TimedRotatingFileHandler(LOG_FILENAME,when="midnight")
  • logging.handlers.SocketHandler
  • logging.handlers.DatagramHandler
    – 以上两个Handler类似,都是将日志信息发送到网络。不同的是前者使用TCP协议,后者使用UDP协议
    – 它们的构造函数是:Handler(host, port),其中host是主机名,port是端口名
  • logging.handlers.SysLogHandler
  • logging.handlers.NTEventLogHandler
  • logging.handlers.SMTPHandler
  • logging.handlers.MemoryHandler
  • logging.handlers.HTTPHandler

最常用的就是 StreamHandler 和 FileHandler,对于产生Log数量比较大的项目,会使用 RotatingFileHandler 或者 TimedRotatingFileHandler 处理日志文件

Formatters

Formatter对象设置日志信息最后的规则、结构和内容,默认的时间格式为%Y-%m-%d %H:%M:%S,下面是Formatter常用的一些信息:

参数含义
%(name)sLogger的名字
%(levelno)s数字形式的日志级别
%(levelname)s文本形式的日志级别
%(pathname)s调用日志输出函数的模块的完整路径名,可能没有
%(filename)s调用日志输出函数的模块的文件名
%(module)s调用日志输出函数的模块名
%(funcName)s调用日志输出函数的函数名
%(lineno)d调用日志输出函数的语句所在的代码行
%(created)f当前时间,用UNIX标准的表示时间的浮 点数表示
%(relativeCreated)d输出日志信息时的,自Logger创建以 来的毫秒数
%(asctime)s字符串形式的当前时间。默认格式是 “2003-07-08 16:49:45,896”
%(thread)d线程ID。可能没有
%(threadName)s线程名。可能没有
%(process)d进程ID。可能没有
%(message)s用户输出的消息

举个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import logging
#Get logger
logger = logging.getLogger(__name__)
#Set level
logger.setLevel(logging.DEBUG)
#Create handler
handler_debug = logging.FileHandler('example.log')
#Set the level of handler
handler_debug.setLevel(logging.DEBUG)
#Create formatter add it to the handlers
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
#Add the formatter to the handlers
handler_debug.setFormatter(formatter)
#add the handlers to the logger
logger.addHandler(handler_debug)

logger.info('Hello World')
1
2
cat example.log
2015-11-21 22:15:05,578 - __main__ - INFO - Hello World