python logging 模块简单而又实用的记录

转自 关于 logging 的一些琐事

1. 为什么logging.info()默认不输出

因为默认生成的 root logger 的 level 是 logging.WARNING,低于该级别的就不输出了。可以进行如下设置来输出:

>>> import logging
>>> logging.info('test')
>>> root_logger = logging.getLogger()  # 或使用未公开的 logging.root
>>> root_logger.level
30
>>> logging.getLevelName(30)
'WARNING'
>>> root_logger.level = logging.NOTSET
>>> logging.info('test')
INFO:root:test

如果还没配置 handler 的话,可以用 logging.basicConfig() 来配置:

>>> root_logger.handlers
[]
>>> logging.basicConfig(level=logging.NOTSET)
>>> root_logger.handlers
[<logging.StreamHandler object at 0x108becd10>]
>>> logging.info('test')
INFO:root:test

2. 如何指定输出格式

给 logger 的 handler 设置一个 logging.Formatter 对象:

>>> root_logger.handlers[0].formatter.format
<bound method Formatter.format of <logging.Formatter object at 0x10c062d90>>
>>> root_logger.handlers[0].formatter.datefmt
>>> root_logger.handlers[0].formatter._fmt
'%(levelname)s:%(name)s:%(message)s'
>>> LOGGING_FORMAT = '[%(levelname)1.1s %(asctime)s %(module)s:%(lineno)d] %(message)s'
>>> DATE_FORMAT = '%y%m%d %H:%M:%S'
>>> formatter = logging.Formatter(LOGGING_FORMAT, DATE_FORMAT)
>>> root_logger.handlers[0].formatter = formatter
>>> logging.info('test')
[I 130221 01:58:28 <stdin>:1] test

如果还没配置 handler 的话,可以用 logging.basicConfig() 来配置:

logging.basicConfig(
    level=logging.NOTSET,
    format=LOGGING_FORMAT,
    datefmt=DATE_FORMAT
)

详细的格式介绍就查看文档吧。

3. 为什么我重定向了stdout却看不到输出

因为默认生成的 root logger 的 handler 的 stream 是 stderr,不是 stdout:

>>> root_logger.handlers[0].stream
<open file '<stderr>', mode 'w' at 0x1089cb270>

可以如下分别配置:

stdout_handler = logging.StreamHandler(sys.__stdout__)
stdout_handler.level = logging.DEBUG
stdout_handler.formatter = formatter
root_logger.addHandler(stdout_handler)

stderr_handler = logging.StreamHandler(sys.__stderr__)
stderr_handler.level = logging.WARNING
stderr_handler.formatter = formatter
root_logger.addHandler(stderr_handler)

4. 如何将日志输出到文件

使用 logging.FileHandler():

handler = logging.FileHandler('log/test.log')
root_logger.addHandler(handler)

其中文件名可以使用相对路径,但要保证文件夹存在。默认的文件打开方式是 append。 如果还没配置 handler 的话,可以用 logging.basicConfig() 来配置:

logging.basicConfig(
    level=logging.NOTSET,
    format=LOGGING_FORMAT,
    datefmt=DATE_FORMAT,
    filename='log/test.log',
    filemode='a'
)

5. 捕捉了一个异常,如何输出执行堆栈?

使用 logging.exception(),或在调用 logging.debug() 等方法时加上 exc_info=True 参数。

>>> try:
...     0 / 0
... except:
...     logging.exception('Catch an exception.')
...     print '-' * 10
...     logging.warning('Catch an exception.', exc_info=True)
... 
ERROR:root:Catch an exception.
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero
----------
WARNING:root:Catch an exception.
Traceback (most recent call last):
  File "<stdin>", line 2, in <module>
ZeroDivisionError: integer division or modulo by zero

6. 如何针对不同的模块,输出到不同的日志

可以创建多个 logger:

console_handler = logging.StreamHandler(sys. __stdout__)
console_handler.level = logging.DEBUG
console_logger = logging.getLogger('test')
console_logger.addHandler(console_handler)

file_handler = logging.FileHandler('log/test.log')
file_handler.level = logging.WARNING
file_logger = logging.getLogger('test.file')
file_logger.addHandler(file_handler)

console_logger.error('test')
file_logger.error('test')

console_logger.parent is root_logger
file_logger.parent is console_logger
console_logger.getChild('file') is file_logger

每个 logger 都有个名字,以 ‘.’ 来划分继承关系。名字为空的就是 root_logger,console_logger 的名字是 ‘test’,因此 root_logger 是 console_logger 的 parent;而 file_logger 的名字是 ‘test.file’,因此 console_logger 是 file_logger 的 parent。 如果 logger 的 propagate 属性为 True(默认值),则它的记录也会传到父 logger。因此,file_logger 在记录到文件的同时,也会在 stdout 输出日志。 建议每个模块都用自己的 logger。

7. 如何指定某些日志不输出

使用 logging.Filter 来过滤记录:

mport logging
import random


class OddFilter(logging.Filter):
    def __init__(self):
        self.count = 0

    def filter(self, record):
        self.count += 1
        if record.args[0] & 1:
            record.count = self.count  # 给 record 增加了 count 属性
            return True  # 为 True 的记录才输出
        return False


root_logger = logging.getLogger()
logging.basicConfig(level=logging.NOTSET, format='%(message)s (total: %(count)d)')  # 可以使用 record.count 来格式化
root_logger.level = logging.NOTSET
root_logger.addFilter(OddFilter())

for i in xrange(100):
    logging.error('number: %d', random.randint(0, 1000))

8. 大文件分割

可以使用 logging.handlers.RotatingFileHandler 和 logging.handlers.TimedRotatingFileHandler。前者按文件大小来分割,后者按时间来分割。 它们会在达到分割条件时(文件达到指定大小或达到指定时间),把当前的日志重命名为备份文件,然后再打开新文件来记录。 值得一提的是,如果备份文件名已存在,就会被删除。所以在多进程时不建议使用。我是将日志输出到 stdout 和 stderr,再用 supervisor 来分割日志。 此外还有一些没考虑到的特殊情况,建议使用前读读源码,然后自行实现。

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,839评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,543评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,116评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,371评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,384评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,111评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,416评论 3 400
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,053评论 0 259
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,558评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,007评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,117评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,756评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,324评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,315评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,539评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,578评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,877评论 2 345

推荐阅读更多精彩内容

  • From:Python之日志处理(logging模块) - 云游道士 - 博客园 https://www.cnbl...
    vigny的先生阅读 2,672评论 3 5
  • 本文章是我大概三年前,在上家单位使用 Python 工作时结合官方文档做的整理。现在 Python 官方文档听说已...
    好吃的野菜阅读 216,123评论 14 232
  • 在现实生活中,记录日志非常重要。银行转账时会有转账记录;飞机飞行过程中,会有黑盒子(飞行数据记录器)记录飞行过程中...
    chliar阅读 756评论 1 0
  • logging模块介绍: logging是python内置的标准库模块,模块提供不同的日志级别,并可以采用不同的方...
    4ffde5305e8f阅读 2,821评论 0 2
  • 今晚读书主要看了《自控力》里有关冥想的部分。 以前,对于冥想没有概念,不知道何为冥想。通过今晚的阅读,对冥想有大致...
    希亚阅读 113评论 0 0