前言
本篇为logback官方文档的阅读笔记的主干,主要记录具体到某个功能某个模块的笔记的索引和官方文档中对于logback以概括总览的视角进行说明的内容。
另外,我们在笔记中规定如下术语以方便我们描述。
经过学习或实践可知,一般日志记录形如logger.debug(a_string)
。我们约定,当指代其中a_string
的内容时,用消息一词。我们约定,表示形如logger.debug(a_string)
这样的方法被调用,我们认为发生了一次记录请求。当一个记录请求被批准,这个记录请求携带的消息最终会变成形如12:49:22.203 [main] DEBUG chapters.introduction.HelloWorld2 - Hello world.
这样的东西被输出,我们约定用记录指代这里形如12:49:22.203 [main] DEBUG chapters.introduction.HelloWorld2 - Hello world.
这样的东西。
我们约定以消息级别和记录请求的级别来指代一个记录请求和这个记录请求中的消息的级别,这两个级别显然总是相等,故消息级别与这个消息的记录请求的级别总是相同。而我们约定记录器级别指代一个Logger
类实例的记录器的级别。这里所谈的东西在《logback官方文档阅读笔记:Logger(记录器)组件》中的”记录器的级别“和”消息的级别“两小节以及它们所对应的官方文档中说的更仔细一些。
索引
《logback官方文档阅读笔记:调试logback》:主要涉及如何让logback输出自己被初始化被使用背后内部过程里产生的信息。
《logback官方文档阅读笔记:Appender(输出器)组件》:主要涉及logback中的一个重要组件——Appender——输出器。
《logback官方文档阅读笔记:Logger(记录器)组件》:本文主要关于logback帐下大将Logger——记录器。
正文
logback的角色定位/logback与软件开发的解耦
chapter 1
Note that the above example does not reference any logback classes. In most cases, as far as logging is concerned, your classes will only need to import SLF4J classes. Thus, the vast majority, if not all, of your classes will use the SLF4J API and will be oblivious to the existence of logback.
这一段言下之意即logback是slf4j日志系统规约的实现。所以在软件中使用的均为slf4j的接口。这样一来,我们在开发软件时遵循日志规约(slf4j)而不考虑日志规约的具体实现(比如logback),从而将日志的实现与软件解耦,使得我们可以自由地更换日志规约的实现(比如你觉得logback不好用了,想换成log4j了,在最理想的状况下,只需要把引入logback的包删去,然后引入log4j的包就行。不需要改动已写好的代码。)。
使用logback的套路模板
chapter 1
Here is a list of the three required steps in order to enable logging in your application.
- Configure the logback environment. You can do so in several more or less sophisticated ways. More on this later.
- In every class where you wish to perform logging, retrieve a
Logger
instance by invoking theorg.slf4j.LoggerFactory
class'getLogger()
method, passing the current class name or the class itself as a parameter.- Use this logger instance by invoking its printing methods, namely the debug(), info(), warn() and error() methods. This will produce logging output on the configured appenders.
讲了使用logback的基本套路。总结起来就是:第一步,配置logback的环境;第二步,在想要记录的类中得到一个记录器(logger
类实例);第三步,调用这个记录器的记录方法。
对于我们学习logback来说,最重要的就是第一步,如何配置logback的环境。
logback 的三个组成部分
chapter2
Logback's architectureLogback's basic architecture is sufficiently generic so as to apply under different circumstances. At the present time, logback is divided into three modules, logback-core, logback-classic and logback-access.
The core module lays the groundwork for the other two modules. The classic module extends core. The classic module corresponds to a significantly improved version of log4j. Logback-classic natively implements the SLF4J API so that you can readily switch back and forth between logback and other logging systems such as log4j or java.util.logging (JUL) introduced in JDK 1.4. The third module called access integrates with Servlet containers to provide HTTP-access log functionality. A separate document covers access module documentation.
In the remainder of this document, we will write "logback" to refer to the logback-classic module.
这一段谈及 Logback 的三个部分:logback-core,logback-classic 和 logback-access。其中 logback-core 为后两个部分的基石,logback-classic 为 logback-core 的加强。logback-classic 对应于有了经历重要更新版本的 log4j(应该是指 log4j2),同时它从设计之初就想好了是为 SLF4J 日志规约而生的,能良好的适配,也因此你可以轻松愉快地在它和其它 SLF4J 的具体实现如 log4j 和 JUL 间相互切换。而 logback-access 统合了 Servlet 容器(JavaWeb 开发的概念)以提供在 JavaWeb 开发过程中的日志支持。
同时做了术语约定:在本文档之后使用的 logback 均只指 logback-classic。
logback 功能实现的三个主力大将与所属模块
chapter 2
Logger, Appenders and LayoutsLogback is built upon three main classes:
Logger
,Appender
andLayout
. These three types of components work together to enable developers to log messages according to message type and level, and to control at runtime how these messages are formatted and where they are reported.The
Logger
class is part of the logback-classic module. On the other hand, theAppender
andLayout
interfaces are part of logback-core. As a general-purpose module, logback-core has no notion of loggers.
说完 logback 的三大部分,这里 logback 告知实现日志功能三个主力类分别是Appender
,Logger
,Layout
。它们协同工作,才使得使用者可以『根据消息的类型和重要级别有选择地进行记录,控制被记录的消息按照什么格式与存储到哪里』。
大将Logger
是logback-classic
帐下一员,而大将Appender
和Layout
是logback-core
帐下。
经过后续的学习或者一些实践,会发现这里漏了一员大将:过滤器Filter
。
日志系统主流设计和实现思路
chapter 2
The first and foremost advantage of any logging API over plain System.out.println
resides in its ability to disable certain log statements while allowing others to print unhindered.
这一段指明了使用日志系统代替System.out.println(...)
的根本原因——日志系统能够对消息进行选择,只有到了一定级别或特定类型的才会被输出,而sout
不能。
This capability assumes that the logging space, that is, the space of all possible logging statements, is categorized according to some developer-chosen criteria.
这一段说明了绝大部分日志系统应该做(从而使之强于使用System.out.println(...)
)的事情的实现思路——消息按照被开发者选择的规则分个重要级别。
In logback-classic, this categorization is an inherent part of loggers. Every single logger is attached to a LoggerContext
which is responsible for manufacturing loggers as well as arranging them in a tree like hierarchy.
这里其实讲了两件事。
第一件事,上述的对输出分类管理的思想是 logback 一以贯之的。但具体怎么贯之,没讲了。
第二件事,指明 logback 依靠一个关键类LoggerContext
作为实现思路的实践者。这个类在《logback官方文档阅读笔记:调试logback》也出现过。这个关键类负责生成记录器(即Logger
)并将它们安排在一个树状的层次结构中。
logback环境配置
chapter 2
Configuration of the logback environment is typically done at application initialization. The preferred way is by reading a configuration file. This approach will be discussed shortly.
软件初始化时通过读取配置文件来配置 logback 环境。但具体的配置方法,配置文件的写法,请继续阅读。
logback试图输出一条日志记录的六个过程
chapter 2
After we have introduced the essential logback components, we are now ready to describe the steps that the logback framework takes when the user invokes a logger's printing method. Let us now analyze the steps logback takes when the user invokes theinfo()
method of a logger named_com.wombat_.1. Get the filter chain decision
If it exists, the
TurboFilter
chain is invoked. Turbo filters can set a context-wide threshold, or filter out certain events based on information such asMarker
,Level
,Logger
, message, or theThrowable
that are associated with each logging request. If the reply of the filter chain isFilterReply.DENY
, then the logging request is dropped. If it isFilterReply.NEUTRAL
, then we continue with the next step, i.e. step 2. In case the reply isFilterReply.ACCEPT
, we skip the next and directly jump to step 3.2. Apply the basic selection rule
At this step, logback compares the effective level of the logger with the level of the request. If the logging request is disabled according to this test, then logback will drop the request without further processing. Otherwise, it proceeds to the next step.
3. Create a
LoggingEvent
objectIf the request survived the previous filters, logback will create a
ch.qos.logback.classic.LoggingEvent
object containing all the relevant parameters of the request, such as the logger of the request, the request level, the message itself, the exception that might have been passed along with the request, the current time, the current thread, various data about the class that issued the logging request and theMDC
. Note that some of these fields are initialized lazily, that is only when they are actually needed. TheMDC
is used to decorate the logging request with additional contextual information. MDC is discussed in a subsequent chapter.4. Invoking appenders
After the creation of a
LoggingEvent
object, logback will invoke thedoAppend()
methods of all the applicable appenders, that is, the appenders inherited from the logger context.All appenders shipped with the logback distribution extend the
AppenderBase
abstract class that implements thedoAppend
method in a synchronized block ensuring thread-safety. ThedoAppend()
method ofAppenderBase
also invokes custom filters attached to the appender, if any such filters exist. Custom filters, which can be dynamically attached to any appender, are presented in a separate chapter.5. Formatting the output
It is responsibility of the invoked appender to format the logging event. However, some (but not all) appenders delegate the task of formatting the logging event to a layout. A layout formats the
LoggingEvent
instance and returns the result as a String. Note that some appenders, such as theSocketAppender
, do not transform the logging event into a string but serialize it instead. Consequently, they do not have nor require a layout.6. Sending out the
LoggingEvent
After the logging event is fully formatted it is sent to its destination by each appender.
本段条理清晰地讲明了logback实现下,试图输出一条日志记录的六个过程。
在本段中,引入了过滤器Filter的概念,值得细读。实际上,继续学习或从使用经验来看,过滤器Filter的重要程度完全不输于之前提到的logback实现依赖的三大主力——输出器Appender,日志记录器Logger,布置器Layout。
同时,细读会发现在阶段一和阶段四均出现了过滤器Filter,目前仅能从文本中获知,一阶段的Filter筛选时权力和范围都更大,而四阶段更多的是对一个输出挑挑拣拣筛一筛。同时带来一些疑问:两者是否为同一个类?第四阶段中Filter的响应和第一阶段中的响应如何?而在第四阶段中的超链接点进去,会发现是官方文档第七篇。也就是说官方文档第七篇主要讲的是阶段四的Filter而不是阶段一的。那么关于Filter的这些疑问,让我们等着第七章解答吧。
Stopping logback-classic 关闭日志系统
chapter 3
Stopping the context will close all appenders attached to loggers defined by the context and stop any active threads in an orderly way.
关闭日志系统,并且日志系统运行所需的线程也会按序关闭。
一个场景:天猫双十一因为日志系统使用不当造成资源空耗,占用大量算力,此时来不及一一排查到底是哪里的问题,先关了再说。
那么怎么做呢?可以把如下代码写到每个Servlet的doGET方法里去,在经过一系列权限校验都通过之后的GET请求到达该Servlt出发该方法,造成关闭。
In web-applications the above code could be invoked from within the contextDestroyed method of ServletContextListener in order to stop logback-classic and release resources. Starting with version 1.1.10, the appropriate ServletContextListener is installed automatically for you (see just below).
不过从这段话来看,我举的这个场景更像是个脑洞。所谓"contextDestroyed method of ServletContextListener "其实意味着JavaWeb服务器即将停止运行,所以它就要释放所有资源,结束所有线程。因此在这个方法里调用关闭日志系统的方法。嗯。很理所应当。
然后本小节接下来就是“Starting with version 1.1.10, the appropriate ServletContextListener is installed automatically for you (see just below)”。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。