PEP 282 – A Logging System
PEP 282 – A Logging System
PEP 282 – 一个日志系统
PEP: | 282 |
Title: | A Logging System |
Author: | vinay_sajip at red-dove.com (Vinay Sajip), trentm at activestate.com (Trent Mick) |
Status: | Final |
Type: | Standards Track |
Created: | 04-Feb-2002 |
Python-Version: | 2.3 |
Post-History: |
Contents
- Abstract,概述
- Motivation,动机
- Influences,影响因素
- Simple Example,简单的例子
- Control Flow,控制流
- Levels,级别
- Loggers,记录器
- Handlers,处理程序
- LogRecords,LogRecords
- Formatters,格式化器
- Filters,过滤器
- Configuration,配置
- Thread Safety,线程安全
- Module-Level Functions,模块级函数
- Implementation,实现
- Packaging,打包
- References,参考文献
- Copyright,版权声明
Abstract
This PEP describes a proposed logging package for Python’s standard library.
基本上,该系统涉及到用户创建一个或多个日志对象,在这些对象上调用方法来记录调试说明、一般信息、警告、错误等。不同的日志 "级别 "可以用来区分重要的信息和不重要的信息。
A registry of named singleton logger objects is maintained so that
different logical logging streams (or ‘channels’) exist (say, one for ‘zope.zodb’ stuff and another for ‘mywebsite’-specific stuff)
存在不同的逻辑日志流(或 “通道”)(例如,一个用于 "zope.zodb "的东西,另一个用于 "mywebsite "的特定东西)。
one does not have to pass logger object references around.
我们不需要到处传递日志对象的引用。
The system is configurable at runtime. This configuration mechanism allows one to tune the level and type of logging done while not touching the application itself.
该系统在运行时是可配置的。这种配置机制允许人们在不触及应用程序本身的情况下调整日志的级别和类型。
Motivation
如果一个单一的日志机制被载入标准库中,1)日志更有可能被做得很好,2)多个库将能够被集成到更大的应用程序中,从而可以合理地进行日志记录。
Influences
This proposal was put together after having studied the following logging packages:
- java.util.logging in JDK 1.4 (a.k.a. JSR047) [1]
- log4j [2]
- the Syslog package from the Protomatter project [3]
- MAL’s mx.Log package [4]
Simple Example
这显示了一个非常简单的例子,说明如何使用logging包在stderr上生成简单的日志输出。
--------- mymodule.py -------------------------------
import logging
log = logging.getLogger("MyModule")def doIt():log.debug("Doin' stuff...")#do stuff...raise TypeError, "Bogus type error for testing"
-------------------------------------------------------------- myapp.py ----------------------------------
import mymodule, logginglogging.basicConfig()log = logging.getLogger("MyApp")log.info("Starting my app")
try:mymodule.doIt()
except Exception, e:log.exception("There was a problem.")
log.info("Ending my app")
-----------------------------------------------------% python myapp.pyINFO:MyApp: Starting my app
DEBUG:MyModule: Doin' stuff...
ERROR:MyApp: There was a problem.
Traceback (most recent call last):File "myapp.py", line 9, in ?mymodule.doIt()File "mymodule.py", line 7, in doItraise TypeError, "Bogus type error for testing"
TypeError: Bogus type error for testingINFO:MyApp: Ending my app
上面的例子显示了默认的输出格式。输出格式的所有方面都应该是可配置的,所以你可以有这样的输出格式。
2002-04-19 07:56:58,174 MyModule DEBUG - Doin' stuff...or justDoin' stuff...
Control Flow
应用程序对记录仪对象进行日志调用。记录仪被组织在一个分层的命名空间中,子记录仪从命名空间中的父记录仪继承一些记录属性。
记录仪名称适合于 "点状名称 "的命名空间,点(句号)表示子命名空间。因此,记录仪对象的命名空间对应于一个单一的树状数据结构。
"" is the root of the namespace
"Zope" would be a child node of the root
"Zope.ZODB" would be a child node of "Zope"
每个记录仪都会跟踪一组输出处理程序。默认情况下,所有的记录仪也将其输出发送到其祖先记录仪的所有处理程序。然而,记录器也可以被配置为忽略树上的处理程序。
整个记录器层次结构也可以有一个与之相关的级别,它优先于单个记录器的级别。这是通过一个模块级函数完成的。
def disable(lvl):"""Do not generate any LogRecords for requests with a severity lessthan 'lvl'."""...
Levels
The logging levels, in increasing order of importance, are:
DEBUG
INFO
WARN
ERROR
CRITICAL
这些只是整数常数,允许简单地比较重要性。经验表明,太多的级别可能会引起混淆,因为它们会导致主观地解释哪个级别应该应用于任何特定的日志请求。
尽管上述级别是强烈推荐的,但是日志系统不应该是规定性的。用户可以定义自己的级别,以及任何级别的文本表示。然而,用户定义的级别必须遵守这样的约束,即它们都是正整数,而且它们的严重程度依次递增。
User-defined logging levels are supported through two module-level functions:
def getLevelName(lvl):"""Return the text for level 'lvl'."""...def addLevelName(lvl, lvlName):"""Add the level 'lvl' with associated text 'levelName', orset the textual representation of existing level 'lvl' to be'lvlName'."""...
Loggers
每个记录器对象都会跟踪它感兴趣的日志级别(或阈值),并丢弃低于该级别的日志请求。
一个Manager类实例维护着命名的记录器对象的分层命名空间。世代用点分隔的名字来表示。记录仪 "foo "是记录仪 "foo.bar "和 "foo.baz "的父对象。
Manager类实例是一个单例,不直接暴露给用户,用户使用各种模块级函数与之交互。
The general logging method is:
class Logger:def log(self, lvl, msg, *args, **kwargs):"""Log 'str(msg) % args' at logging level 'lvl'."""...
However, convenience functions are defined for each logging level:
class Logger:def debug(self, msg, *args, **kwargs): ...def info(self, msg, *args, **kwargs): ...def warn(self, msg, *args, **kwargs): ...def error(self, msg, *args, **kwargs): ...def critical(self, msg, *args, **kwargs): ...
class Logger:def exception(self, msg, *args): ...
class Message:"""Represents a message"""def __init__(self, id):"""Initialize with the message ID"""def __str__(self):"""Return an appropriate localized message text"""...logger.info(Message("abc"), ...)
为一条日志消息收集和格式化数据可能会很昂贵,而且如果记录器无论如何都会丢弃该消息的话,那就是一种浪费。要想知道一个请求是否会被记录仪接受,可以使用isEnabledFor()方法。
class Logger:def isEnabledFor(self, lvl):"""Return true if requests at level 'lvl' will NOT bediscarded."""...
so instead of this expensive and possibly wasteful DOM to XML conversion:
...
hamletStr = hamletDom.toxml()
log.info(hamletStr)
...
if log.isEnabledFor(logging.INFO):hamletStr = hamletDom.toxml()log.info(hamletStr)
当新的日志记录器被创建时,它们被初始化为一个表示 "无级别 "的级别。可以使用setLevel()
方法来明确设置级别:
class Logger:def setLevel(self, lvl): ...
def getEffectiveLevel(self): ...
Loggers are never instantiated directly. Instead, a module-level function is used:
def getLogger(name=None): ...
如果没有指定名称,将返回根记录器。否则,如果存在一个具有该名称的日志记录器,则会返回。如果没有,就会初始化并返回一个新的日志记录器。这里,"name "是 "channel name "的同义词。
用户可以指定一个自定义的记录器子类,以便在实例化新的记录器时被系统使用。
def setLoggerClass(klass): ...
被传递的类应该是 Logger 的子类,其__init__
方法应该调用 Logger.__init__
。
这点很让我意外,日志包居然还支持从Logger类扩展,这点值得第三方包开发者借鉴,译者注。
Handlers
处理程序负责对给定的 LogRecord 做一些有用的事情。下列核心处理程序将被实现:
StreamHandler
: A handler for writing to a file-like object.StreamHandler
:一个用于向一个类似文件的对象写入的处理程序。FileHandler
: A handler for writing to a single file or set of rotating files.FileHandler
:一个用于向单个文件或一组轮流的文件写入的处理程序。SocketHandler
: A handler for writing to remote TCP ports.SocketHandler
:一个用于向远程TCP端口写入的处理程序。DatagramHandler
: A handler for writing to UDP sockets, for low-cost logging. Jeff Bauer already had such a system [5].DatagramHandler
:一个写入UDP套接字的处理程序,用于低成本的日志记录。Jeff Bauer已经有这样一个系统[5]。MemoryHandler
: A handler that buffers log records in memory until the buffer is full or a particular condition occurs [1].MemoryHandler
:一个处理程序,在内存中缓冲日志记录,直到缓冲区满了或出现特定条件[1]。SMTPHandler
: A handler for sending to email addresses via SMTP.SMTPHandler
:一个用于通过SMTP向电子邮件地址发送内容的处理程序。SysLogHandler
: A handler for writing to Unix syslog via UDP.SysLogHandler
:一个用于通过UDP向Unix syslog写入的处理程序。NTEventLogHandler
: A handler for writing to event logs on Windows NT, 2000 and XP.NTEventLogHandler
:一个用于向Windows NT, 2000和XP的事件日志写入的处理程序。HTTPHandler
: A handler for writing to a Web server with either GET or POST semantics.HTTPHandler
: 一个处理程序,用于以GET或POST的语义向Web服务器写入信息。
Handlers can also have levels set for them using the setLevel()
method:
处理程序也可以使用setLevel()
方法为它们设置级别。
def setLevel(self, lvl): ...
The FileHandler can be set up to create a rotating set of log files. In this case, the file name passed to the constructor is taken as a “base” file name. Additional file names for the rotation are created by appending .1, .2, etc. to the base file name, up to a maximum as specified when rollover is requested. The setRollover method is used to specify a maximum size for a log file and a maximum number of backup files in the rotation.
FileHandler可以被设置为创建一组旋转的日志文件。在这种情况下,传递给构造函数的文件名被作为一个 "基本 "文件名。通过在基本文件名上添加.1、.2等来创建用于轮换的额外文件名,最多可达到要求轮换时指定的上限。setRollover方法被用来指定日志文件的最大尺寸和轮换中备份文件的最大数量。
def setRollover(maxBytes, backupCount): ...
If maxBytes is specified as zero, no rollover ever occurs and the log file grows indefinitely. If a non-zero size is specified, when that size is about to be exceeded, rollover occurs. The rollover method ensures that the base file name is always the most recent, .1 is the next most recent, .2 the next most recent after that, and so on.
如果maxBytes被指定为零,则永远不会发生翻转,并且日志文件会无限地增长。如果指定了一个非零的大小,当该大小即将被超过时,就会发生翻转。滚动方法确保基本文件名总是最新的,.1是次新的,.2是之后次新的,以此类推。
There are many additional handlers implemented in the test/example scripts provided with [6] - for example, XMLHandler and SOAPHandler.
在[6]提供的测试/示例脚本中实现了许多额外的处理程序–例如,XMLHandler和SOAPHandler。
LogRecords
LogRecord作为一个关于日志事件信息的容器。它只不过是一个字典,尽管它确实定义了一个getMessage
方法,将一个消息与可选的运行参数合并。
Formatters
格式化器负责将 LogRecord 转换为字符串表示。处理程序可以在写入记录之前调用其格式化器。以下核心格式器将被实现。
Formatter
: Provide printf-like formatting, using the % operator.Formatter
:提供类似 printf 的格式化,使用 % 操作符。BufferingFormatter
: Provide formatting for multiple messages, with header and trailer formatting support.BufferingFormatter
:为多条信息提供格式化,支持标题和预告片的格式化。
Formatters are associated with Handlers by calling setFormatter()
on a handler:
通过在处理程序上调用setFormatter()
将格式化器与处理程序相关联。
def setFormatter(self, form): ...
Formatters use the % operator to format the logging message. The format string should contain %(name)x
and the attribute dictionary of the LogRecord is used to obtain message-specific data. The following attributes are provided:
格式化器使用%操作符来格式化日志消息。格式化字符串应该包含%(name)x
,LogRecord的属性字典被用来获取消息的具体数据。提供了以下属性:
%(name)s
|
Name of the logger (logging channel) |
记录器的名称(记录通道) | |
%(levelno)s
|
Numeric logging level for the message (DEBUG, INFO, WARN, ERROR, CRITICAL) |
消息的数字日志级别(DEBUG, INFO, WARN, ERROR, CRITICAL) | |
%(levelname)s
|
Text logging level for the message (“DEBUG”, “INFO”, “WARN”, “ERROR”, “CRITICAL”) |
消息的文本日志级别(“DEBUG”, “INFO”, “WARN”, “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 from which logging call was made |
进行日志调用的模块 | |
%(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)
|
创建日志记录的时间(time.time() 返回值)
|
|
%(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) |
创建LogRecord的时间,以毫秒为单位,相对于加载日志模块的时间(通常在应用程序启动时) | |
%(thread)d
|
Thread ID (if available) |
线程ID (如果有的话) | |
%(message)s
|
The result of record.getMessage(), computed just as the record is emitted |
record.getMessage()的结果,在记录发出的时候计算 |
If a formatter sees that the format string includes “(asctime)s”, the creation time is formatted into the LogRecord’s asctime attribute. To allow flexibility in formatting dates, Formatters are initialized with a format string for the message as a whole, and a separate format string for date/time. The date/time format string should be in time.strftime format. The default value for the message format is “%(message)s”. The default date/time format is ISO8601.
如果格式化器看到格式字符串包括"(asctime)s",创建时间就会被格式化为LogRecord的asctime属性。为了允许灵活地格式化日期,格式化器被初始化为整个消息的格式字符串,以及一个单独的日期/时间格式字符串。日期/时间格式字符串应该是time.strftime格式。消息格式的默认值是"%(message)s"。默认的日期/时间格式是ISO8601。
The formatter uses a class attribute, “converter”, to indicate how to convert a time from seconds to a tuple. By default, the value of “converter” is “time.localtime”. If needed, a different converter (e.g. “time.gmtime”) can be set on an individual formatter instance, or the class attribute changed to affect all formatter instances.
格式化器使用一个类属性 “转换器”,以表明如何将时间从秒转换为元组。默认情况下,"转换器 "的值是 “time.localtime”。如果需要,可以在单个格式化器实例上设置一个不同的转换器(例如 “time.gmtime”),或者改变类属性以影响所有格式化器实例。
Filters
当基于级别的过滤不充分时,记录仪或处理程序可以调用一个过滤器来决定是否应该输出一条日志记录。记录器和处理程序可以安装多个过滤器,它们中的任何一个都可以否决一条LogRecord的输出。
class Filter:def filter(self, record):"""Return a value indicating true if the record is to beprocessed. Possibly modify the record, if deemedappropriate by the filter.如果记录要被处理,返回一个表示 "true "的值。可能会修改记录,如果过滤器认为合适的话。"""
There are many examples of Filters provided in [6].
Configuration
Configuration includes the following:
What logging level a logger or handler should be interested in.
记录器或处理程序应该对哪一个日志级别感兴趣。
What handlers should be attached to which loggers.
哪些处理程序应该被附加到哪些记录器上。
What filters should be attached to which handlers and loggers.
哪些过滤器应附加到哪些处理程序和记录器上。
Specifying attributes specific to certain handlers and filters.
为某些处理程序和过滤器指定特定的属性。
In general each application will have its own requirements for how a user may configure logging output. However, each application will specify the required configuration to the logging system through a standard mechanism.
一般来说,每个应用程序都会对用户如何配置日志输出有自己的要求。然而,每个应用程序将通过标准机制向日志系统指定所需的配置。
The most simple configuration is that of a single handler, writing to stderr, attached to the root logger. This configuration is set up by calling the basicConfig()
function once the logging module has been imported.
最简单的配置是一个单一的处理程序,写到stderr,连接到根记录器。这种配置是在导入日志模块后通过调用basicConfig()
函数设置的。
def basicConfig(): ...
For more sophisticated configurations, this PEP makes no specific proposals, for the following reasons:
对于更复杂的配置,本PEP没有提出具体建议,原因如下:
A specific proposal may be seen as prescriptive.
具体的建议可能会被看作是规定性的。
Without the benefit of wide practical experience in the Python community, there is no way to know whether any given configuration approach is a good one. That practice can’t really come until the logging module is used, and that means until after Python 2.3 has shipped.
如果没有Python社区广泛的实践经验,就没有办法知道任何给定的配置方法是否是一个好方法。在使用日志模块之前,这种实践不可能真正到来,这意味着要等到 Python 2.3 发布之后**。
There is a likelihood that different types of applications may require different configuration approaches, so that no “one size fits all”.
不同类型的应用有可能需要不同的配置方法,所以没有 “一刀切”。
The reference implementation [6] has a working configuration file format, implemented for the purpose of proving the concept and suggesting one possible alternative. It may be that separate extension modules, not part of the core Python distribution, are created for logging configuration and log viewing, supplemental handlers and other features which are not of interest to the bulk of the community.
参考实现 [6] 有一个工作的配置文件格式,实现的目的是为了证明这个概念并提出一个可能的替代方案。可能会有单独的扩展模块,而不是Python核心版本的一部分,用于日志配置和日志查看、补充处理程序和其他对大部分社区不感兴趣的功能。
Thread Safety
日志系统应该支持线程安全的操作,而不需要用户采取任何特殊的行动。
Module-Level Functions
At application exit, all handlers can be flushed by calling the function:
def shutdown(): ...
This will flush and close all handlers.
Implementation
The reference implementation is Vinay Sajip’s logging module [6].
Packaging
参考实现是作为一个单一的模块实现的。这提供了最简单的接口–用户所要做的就是 “import logging”,他们就可以使用所有可用的功能。
References
[1] | (1, 2) java.util.logging http://java.sun.com/j2se/1.4/docs/guide/util/logging/ |
[2] | log4j: a Java logging package http://jakarta.apache.org/log4j/docs/index.html |
[3] | Protomatter’s Syslog http://protomatter.sourceforge.net/1.1.6/index.html http://protomatter.sourceforge.net/1.1.6/javadoc/com/protomatter/syslog/syslog-whitepaper.html |
[4] | MAL mentions his mx.Log logging module: https://mail.python.org/pipermail/python-dev/2002-February/019767.html |
[5] | Jeff Bauer’s Mr. Creosote http://starship.python.net/crew/jbauer/creosote/ |
[6] | (1, 2, 3, 4) Vinay Sajip’s logging module. http://www.red-dove.com/python_logging.html |
Copyright
This document has been placed in the public domain.
Source: https://github.com/python/peps/blob/master/pep-0282.txt
版权声明:本文由 icexmoon 翻译,遵循CC 4.0 BY-SA版权协议。
PEP 282 – A Logging System相关推荐
- Logging system failed to initialize using configuration from ‘classpath:logback.xml‘ 不一样的解释
Logging system failed to initialize using configuration from 'classpath:logback.xml' java.lang.Illeg ...
- springboot启动时报错Logging system failed to initialize using configuration from 'classpath:logging-conf
项目报错: Logging system failed to initialize using configuration from 'classpath:logging-config.xml' ja ...
- 【IDEA】IntelliJ IDEA:Logging system failed to initialize using configuration from 'classpath:config/l
一.报错信息 Connected to the target VM, address: '127.0.0.1:63960', transport: 'socket' Logging system fa ...
- PEP Python Enhancement Proposals(python增强提案\python改进建议书)(重点PEP8)
引用文章:python 的众多PEP 之中,除了PEP8 ,还有哪一些是值得阅读的? - 豌豆花下猫的回答 - 知乎 PEP是什么? PEP的全称是Python Enhancement Proposa ...
- python中的PEP是什么?怎么理解?(转)
PEP是什么? PEP的全称是Python Enhancement Proposals,其中Enhancement是增强改进的意思,Proposals则可译为提案或建议书,所以合起来,比较常见的翻译是 ...
- python怎么读是什么意思-python中的PEP是什么?怎么理解?(转)
PEP是什么? PEP的全称是Python Enhancement Proposals,其中Enhancement是增强改进的意思,Proposals则可译为提案或建议书,所以合起来,比较常见的翻译是 ...
- 学习Python,经常见到PEP,那么PEP是什么呢?
首先,查百度得知: PEP是Python Enhancement Proposals的缩写.一个PEP是一份为Python社区提供各种增强功能的技术规格,也是提交新特性,以便让社区指出问题,精确化技术 ...
- 学习Python,怎能不懂点PEP呢? 1
或许你是一个初入门Python的小白,完全不知道PEP是什么.又或许你是个学会了Python的熟手,见过几个PEP,却不知道这玩意背后是什么.那正好,本文将系统性地介绍一下PEP,与大家一起加深对PE ...
- Python PEP—Python增强提案
PEP的全称是Python Enhancement Proposals,Python增强提案.描述了Python的语言特性.功能.编程规范等,包括了技术规范和功能的基本原理说明,是了解Python语言 ...
最新文章
- 2022-2028年中国汽车修理行业市场前瞻与投资规划分析报告
- java编程题库下载_Java习题
- excel导入数据库的简单方法
- 输出多个重复字符或字符串
- 计算机二级c语言考试模拟试题,计算机二级C语言考前模拟试题及答案2016
- 我的 8 年投资心路历程
- linux通过操作界面和命令行的方式查看ip地址、mac地址
- 计算机中答案没有小数怎么办,嵌入式编程中计算机是如何存储小数的
- spss进行偏相关分析
- VMI的两种库存管理模式
- 创建VSIX项目模板
- 整理网线接水晶头步骤
- MapReduce的核心资料索引
- MySQL数据库与身份认证(鉴权)学习、复习笔记
- C#专用集合类StringCollection与StringDictionary
- 设置状态栏颜色、沉浸式状态栏
- AES种子秘钥构造一个完整的秘钥编排方案
- Matlab数值分析实例:三次样条插值
- 【点宽专栏】破解波动性突破实盘系统
- 微博只显示来自android,修改手机发新浪微博显示的来源