Differences between revisions 1 and 2
Revision 1 as of 2009-02-02 22:34:02
Size: 8124
Editor: twotwo
Comment:
Revision 2 as of 2019-08-05 22:13:47
Size: 9054
Editor: twotwo
Comment: Switching from standard logging to loguru
Deletions are marked like this. Additions are marked like this.
Line 169: Line 169:
== Switch to loguru ==
[[https://loguru.readthedocs.io/en/stable/resources/migration.html||target="_blank"]]

最根本的差别:减少配置代码。默认日志输出到 `sys.stderr`。

Install: `pip install loguru`

{{{#!highlight python numbers=disable
from loguru import logger
fmt = "{time} - {name} - {level} - {message}"
logger.add("spam.log", level="DEBUG", format=fmt)
logger.add(sys.stderr, level="ERROR", format=fmt)
# Loguru only supports {}-style formatting.
logger.debug("Some variable: {}", var)
# The formatted exception will include the whole stacktrace and variables.
logger.opt(exception=True).debug("Debug error:")
# Replacing extra argument
context = {"clientip": "192.168.0.1", "user": "fbloggs"}
logger.bind(**context).info("Protocol problem") # Loguru
}}}

* Doc [[https://loguru.readthedocs.io/||target="_blank"]]
* Code [[https://github.com/Delgan/loguru||target="_blank"]]
Line 175: Line 199:

Back to Python#Operating_System_Services

See Also Python Standard Library

Python Library - logging

Source code: Lib/logging/__init__.py

1. Basic Conceptions

  • Logger 记录器,暴露了应用程序代码能直接使用的接口;

  • Handler 处理器,将(记录器产生的)日志记录发送至合适的目的地;

  • Formatter 格式化器,指明了最终输出中日志记录的布局;

  • Filter 过滤器,提供了更好的粒度控制,它可以决定输出哪些日志记录;

FORMAT = '%(asctime)s - %(process)d - %(levelname)s - %(message)s'
    logging.basicConfig(level=logging.DEBUG, format=FORMAT, datefmt='%y%m%d %H:%M:%S')
logger = logging.getLogger('myserver')
logger.info('Some action: %s' % 'make a connection')

1.1. Logger Objects

代码:

FORMAT = '%(asctime)-15s %(clientip)s %(user)-8s %(message)s'
logging.basicConfig(format=FORMAT)
d = {'clientip': '192.168.0.1', 'user': 'fbloggs'}
logger = logging.getLogger('tcpserver')
logger.warning('Protocol problem: %s', 'connection reset', extra=d)

输出:

2009-02-08 22:20:02,165 192.168.0.1 fbloggs  Protocol problem: connection reset

1.2. Logging Levels

级别

何时使用

DEBUG

详细信息,典型地调试问题时会感兴趣。

INFO

证明事情按预期工作。

WARNING

表明发生了一些意外,或者不久的将来会发生问题(如‘磁盘满了’)。软件还是在正常工作。

ERROR

由于更严重的问题,软件已不能执行一些功能了。

CRITICAL

严重错误,表明软件已不能继续运行了。

1.3. Formatter Objects

创建方法: formatter = logging.Formatter(fmt=None, datefmt=None)

其中,fmt是消息的格式化字符串,datefmt是日期字符串。如果不指明fmt,将使用'%(message)s'。如果不指明datefmt,将使用ISO8601日期格式。

1.3.1. LogRecord attributes

https://docs.python.org/2/library/logging.html#logrecord-attributes

FORMAT = '%(asctime)s - %(process)d - %(levelname)s - %(filename)s@%(lineno)d - %(message)s'

  • ->

`09/02/02 22:30:50 - 61736 - INFO - grpc_transfer.py@78 - begin gRPC call ...

  • `

%(name)s The name of the logger
%(levelname)s ('DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL')
%(pathname)s The full pathname of the source file
%(lineno)s The line number in the source file
%(asctime)s ‘2003-07-08 16:49:45,231’
%(funcName)s
%(message)s

1.4. Logging handlers

https://docs.python.org/3/howto/logging.html#useful-handlers

1.4.1. StreamHandler

sends logging output to streams such as sys.stdout, sys.stderr or any file-like object

# create console handler and set level to debug
ch = logging.StreamHandler()
ch.setLevel(logging.DEBUG)
# add formatter to ch
ch.setFormatter(logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s'))
# add ch to logger
logger.addHandler(ch)

1.4.2. FileHandler

1.4.3. NullHandler

1.4.4. WatchedFileHandler

1.4.5. RotatingFileHandler

1.4.6. TimedRotatingFileHandler

1.4.7. SocketHandler

1.4.8. DatagramHandler

1.4.9. SysLogHandler

1.4.10. NTEventLogHandler

1.4.11. SMTPHandler

1.4.12. MemoryHandler

1.4.13. HTTPHandler

1.5. Filter Objects

Handlers和Loggers可以使用Filters来完成比级别更复杂的过滤。Filter基类只允许特定Logger层次以下的事件。例如用‘A.B’初始化的Filter允许Logger ‘A.B’, ‘A.B.C’, ‘A.B.C.D’, ‘A.B.D’等记录的事件,logger‘A.BB’, ‘B.A.B’ 等就不行。 如果用空字符串来初始化,所有的事件都接受。

2. Logging workflow

logging_flow.png

  1. 判断日志的等级是否大于Logger对象的等级,如果大于,则往下执行,否则,流程结束。
  2. 产生日志。第一步,判断是否有异常,如果有,则添加异常信息。第二步,处理日志记录方法(如debug,info等)中的占位符,即一般的字符串格式化处理。
  3. 使用注册到Logger对象中的Filters进行过滤。如果有多个过滤器,则依次过滤;只要有一个过滤器返回假,则过滤结束,且该日志信息将丢弃,不再处理,而处理流程也至此结束。否则,处理流程往下执行。
  4. 在当前Logger对象中查找Handlers,如果找不到任何Handler,则往上到该Logger对象的父Logger中查找;如果找到一个或多个Handler,则依次用Handler来处理日志信息。但在每个Handler处理日志信息过程中,会首先判断日志信息的等级是否大于该Handler的等级,如果大于,则往下执行(由Logger对象进入Handler对象中),否则,处理流程结束。
  5. 执行Handler对象中的filter方法,该方法会依次执行注册到该Handler对象中的Filter。如果有一个Filter判断该日志信息为假,则此后的所有Filter都不再执行,而直接将该日志信息丢弃,处理流程结束。
  6. 使用Formatter类格式化最终的输出结果。 注:Formatter同上述第2步的字符串格式化不同,它会添加额外的信息,比如日志产生的时间,产生日志的源代码所在的源文件的路径等等。
  7. 真正地输出日志信息(到网络,文件,终端,邮件等)。至于输出到哪个目的地,由Handler的种类来决定。

3. Logging Configuration

  1. 显式创建记录器Logger、处理器Handler和格式化器Formatter,并进行相关设置;
  2. 通过简单方式进行配置,使用basicConfig()函数直接进行配置;
  3. 通过配置文件进行配置,使用fileConfig()函数读取配置文件;
  4. 通过配置字典进行配置,使用dictConfig()函数读取配置信息;
  5. 通过网络进行配置,使用listen()函数进行网络配置

4. Examples

4.1. Simple Log

>>> import logging
>>> logging.basicConfig(filename='/tmp/myapp.log', level=logging.INFO, format='%(asctime)s %(levelname)s %(message)s')
>>> logging.info('Started')
>>> logging.info('Finished')

4.2. Logging from multiple modules

# myapp.py
import logging
import mylib

def main():
    logging.basicConfig(filename='myapp.log', level=logging.INFO)
    logging.info('Started')
    mylib.do_something()
    logging.info('Finished')

if __name__ == '__main__':
    main()

# mylib.py
import logging

def do_something():
    logging.info('Doing something')

myapp.log中的输出:

INFO:root:Started
INFO:root:Doing something
INFO:root:Finished

4.3. File Config

https://docs.python.org/2/howto/logging.html#configuring-logging

https://github.com/twotwo/ci_scripts/ logging.conf, console_log and file_log

def main():

    # ...

    import logging.config
    logging.config.fileConfig('logging.conf')

5. Switch to loguru

https://loguru.readthedocs.io/en/stable/resources/migration.html

最根本的差别:减少配置代码。默认日志输出到 sys.stderr

Install: pip install loguru

from loguru import logger
fmt = "{time} - {name} - {level} - {message}"
logger.add("spam.log", level="DEBUG", format=fmt)
logger.add(sys.stderr, level="ERROR", format=fmt)
# Loguru only supports {}-style formatting.
logger.debug("Some variable: {}", var)
# The formatted exception will include the whole stacktrace and variables.
logger.opt(exception=True).debug("Debug error:")
# Replacing extra argument
context = {"clientip": "192.168.0.1", "user": "fbloggs"}
logger.bind(**context).info("Protocol problem")  # Loguru

* Doc https://loguru.readthedocs.io/ * Code https://github.com/Delgan/loguru

6. Reference

MainWiki: python_lib_logging (last edited 2019-08-22 15:14:47 by twotwo)