社区所有版块导航
Python
python开源   Django   Python   DjangoApp   pycharm  
DATA
docker   Elasticsearch  
aigc
aigc   chatgpt  
WEB开发
linux   MongoDB   Redis   DATABASE   NGINX   其他Web框架   web工具   zookeeper   tornado   NoSql   Bootstrap   js   peewee   Git   bottle   IE   MQ   Jquery  
机器学习
机器学习算法  
Python88.com
反馈   公告   社区推广  
产品
短视频  
印度
印度  
Py学习  »  Python

Logbook:Python 快速日志记录实践

编程派 • 7 年前 • 1649 次点击  

作者:东东 yasking

来源:https://blog.yasking.org/a/python-logbook.html

Python 本身有logging日志记录模块,之前发现了logbook这个包,介绍说是替代logging,索性整理一下,方便之后使用

  1. >>> from logbook import Logger, StreamHandler

  2. >>> import sys

  3. >>> StreamHandler(sys.stdout).push_application()

  4. >>> log = Logger('Logbook')

  5. >>> log.info('Hello, World!')

  6. [2015-10-05 18:55:56.937141] INFO: Logbook: Hello, World!

上边这是文档中给出的例子,它定义了许多的Handler,可以把日志记录到标准输出,文件,E-MAIL,甚至Twitter

StreamHandler

使用 StreamHandler记录的日志会以流输出,这里指定 sys.stdout也就是记录到标准输出,与 print一样

(一)可以使用 with来在一定作用域内记录日志

  1. # -*- coding: utf-8 -*-

  2. from logbook import Logger, StreamHandler

  3. import logbook

  4. import sys

  5. handler = StreamHandler(sys.stdout)

  6. log = Logger('test')

  7. def main():

  8.    log.info('something logging')

  9. if __name__ == '__main__':

  10.    with handler.applicationbound():

  11.        main()

(二)也可以指定作用于整个应用

  1. # -*- coding: utf-8 -*-

  2. from logbook import Logger, StreamHandler

  3. import logbook

  4. import sys

  5. handler = StreamHandler(sys.stdout)

  6. handler.push_application()

  7. log = Logger('test')

  8. def main():

  9.    log.info('something logging')

  10. if __name__ == '__main__':

  11.    main()

FileHandler

使用 FileHandler可以把日志记录到文件,这也是最常见的方式

  1. # -*- coding: utf-8 -*-

  2. from logbook import Logger, FileHandler

  3. import logbook

  4. import sys

  5. handler = FileHandler('app.log')

  6. handler.push_application()

  7. log = Logger('test')

  8. def main():

  9.    log.info('something logging')

  10. if __name__ == '__main__':

  11.    main()

日志就写到了 app.log文件

同时输出到文件与STDOUT

同时把记录输出到多个地方可以方便查阅和记录,初始化 Handler的时候设置 bubble参数就可以使得其它 Handler也可以接收到记录

  1. from logbook import Logger, StreamHandler, FileHandler

  2. import logbook

  3. import sys

  4. '''

  5. 记录日志到文件和STDOUT

  6. '''

  7. StreamHandler(sys.stdout, level='DEBUG').push_application()

  8. FileHandler('app.log', bubble=True , level='INFO').push_application()

  9. log = Logger('test')

  10. def main():

  11.    log.info('hello world')

  12. if __name__ == '__main__':

  13.    main()

另外,通过 level可以设置日志级别,级别如下,从下到上级别越来越高,如 level设置为 INFO, 则除了 DEBUG外都会记录,设置不同的级别,搭配各种 Handler可以让日志的记录更加灵活,上边使用的 log.info可以使用不同的记录级别

级别说明
critical严重错误,需要退出程序
error错误,但在可控范围内
warning警告
notice大多数情况下希望看到的记录
info大多数情况不希望看到的记录
debug调试程序的时候详细输出的记录

MailHandler

和日志文件同样重要的就是 MailHandler了,当出现了比较严重错误的时候就要发送邮寄进行通知

详细的文档见:

分别使用了 163和 qq邮箱发送邮件测试,使用的邮箱需要开启SMTP权限,代码如下(163和qq发送参数稍有不同)

163 Mail

  1. # -*- coding: utf-8 -*-

  2. from logbook import Logger, MailHandler

  3. import logbook

  4. import sys

  5. sender = 'Logger'

  6. recipients = ['dongdong@qq.com']

  7. email_user = 'dongdong@163.com'

  8. email_pass = 'password'

  9. mail_handler = MailHandler(sender, recipients,

  10.        server_addr='smtp.163.com',

  11.        starttls=True,

  12.        secure = False,

  13.        credentials=(email_user, email_pass),

  14.        format_string=u'''

  15.            Subject: {record.level_name} on My Application

  16.            Message type: {record.level_name}

  17.            Location: {record.filename}:{record.lineno}

  18.            Module: {record.module}

  19.            Function: {record.func_name}

  20.            Time: {record.time:%Y-%m-%d %H:%M:%S}

  21.            Remote IP: {record.extra[ip]}

  22.            Request: {record.extra[url]} [{record.extra[method]}]

  23.            Message: {record.message}

  24.            ''',

  25.        bubble=True)

  26. log = Logger('test')

  27. def main():

  28.    log.info('something logging')

  29. if __name__ == '__main__':

  30.    with mail_handler.threadbound():

  31.        main()

QQ Mail

  1. # -*- coding: utf-8 -*-

  2. from logbook import Logger, MailHandler

  3. import logbook

  4. import sys

  5. sender = 'Logger'

  6. recipients = ['dongdong@163.com']

  7. email_user = 'dongdong@qq.com'

  8. email_pass = 'password'

  9. mail_handler = MailHandler(sender, recipients,

  10.        server_addr='smtp.qq.com',

  11.        starttls=False,

  12.        secure = True,

  13.        credentials=(email_user, email_pass),

  14.        format_string=u'''

  15.            Subject: {record.level_name} on My Application

  16.            Message type: {record.level_name}

  17.            Location: {record.filename}:{record.lineno}

  18.            Module: {record.module}

  19.            Function: {record.func_name}

  20.            Time: {record.time:%Y-%m-%d %H:%M:%S}

  21.            Remote IP: {record.extra[ip]}

  22.            Request: {record.extra[url]} [{record.extra[method]}]

  23.            Message: {record.message}

  24.            ''',

  25.        bubble=True)

  26. log = Logger('test')

  27. def main():

  28.    log.info('something logging')

  29. if __name__ == '__main__':

  30.    with mail_handler.threadbound():

  31.        main()

内容 format_string中的用大括号的会进行数值替换, Subject字段上边的``和下边需要空一行,这样解析参数收到的邮件才会正确的显示标题

Record Processors

上边发送邮件的例子,参数里面有一个 record.extra[url],这个参数是可以自己指定的,比如编写WSGI的程序,处理URL,每一条记录都希望记录到访问者的IP,可以这样做:

  1. # -*- coding: utf-8 -*-

  2. from logbook import Logger, StreamHandler , Processor

  3. import logbook

  4. import sys

  5. handler = StreamHandler(sys.stdout)

  6. handler.format_string = '[{{record.time:%Y-%m-%d %H:%M:%S}}] IP:{record.extra[ip]} {record.level_name}: {record.channel}: {record.message}'

  7. handler.formatter

  8. log = Logger('test')

  9. def inject_ip(record):

  10.    record.extra['ip'] = '127.0.0.1'

  11. with handler.applicationbound():

  12.    with Processor(inject_ip).applicationbound():

  13.        log.error('something error')

使用自定义参数,需要重新设置 format_string,才能进行记录,record类可以在这里找到,详细参数见 logbook.LogRecord

Output:

  1. [2016-12-13 12:20 :46] IP:127.0.0.1 ERROR: test: something error

日期格式

上边在介绍的自定义日志格式的时候使用的时间是虽然指定了格式但是是 UTC格式,跟北京时间是差了8个小时的。所以需要设置让它记录本地的时间

在刚才的例子前面加上如下代码即可

  1. logbook.set_datetime_format('local') # <= 新加入行

  2. handler = StreamHandler(sys.stdout)

  3. handler.format_string = '[{record.time:%Y-%m-%d %H:%M:%S}] IP:{record.extra[ip]} {record.level_name}: {record.channel}: {record.message}'

  4. handler.formatter

更过日期格式化的设置参看:api/utilities - logbook.set_datetime_format(datetime_format)

threadbound与applicationbound

从最开始的例子来看,可以使用两种方式来记录日志,一种是在最开始使用 push_*来激活,另一种是使用的时候用 with构造上下文,现在进行一些补充

pushwithpop
push_application()applicationbound()pop_application()
push_thread()threadbound()pop_threadbound()
push_greenlet()greenletbound()pop_greenlet()

  • 使用 pop_* 可以取消记录的上下文

  • application作用于整个应用,thread只针对当前线程


  1. handler = MyHandler()

  2. handler.push_application()

  3. # all here goes to that handler

  4. handler.pop_application()

消除嵌套

使用多个Handler的时候,使用 push_方式启动的上下文不会形成嵌套,但是使用 with启动的上下文会形成嵌套,可以使用 nested handler

  1. import os

  2. from logbook import NestedSetup, NullHandler, FileHandler,

  3. MailHandler, Processor

  4. def inject_information(record):

  5.    record.extra['cwd'] = os.getcwd()

  6. setup = NestedSetup([

  7.    # NullHandler避免stderr接受消息

  8.    NullHandler(),

  9.    FileHandler('application.log', level='WARNING'),

  10.    MailHandler ('servererrors@example.com', ['admin@example.com'],

  11.        level='ERROR', bubble=True),

  12.    Processor(inject_information)

  13. ])

使用的时候就只需要一个 with来启动

  1. with setup.threadbound():

  2.    log.info('something logging')

logbook是个不错的包,记录日志灵活方便,比自己包装发送邮件方便了不少,整理了一些基本用法,还有不少值得学习的功能,暂时能用到的基本上就这么多,之后用到高级的功能再继续研究补充。


题图:pexels,CC0 授权。

点击阅读原文,查看更多 Python 教程和资源。



今天看啥 - 高品质阅读平台
本文地址:http://www.jintiankansha.me/t/6fHsP59FQn
Python社区是高质量的Python/Django开发社区
本文地址:http://www.python88.com/topic/5460
 
1649 次点击