标签:扩展 结构 分享图片 导致 改进 争论 ber 常见 erb
Logback 的基本结构充分通用,可应用于各种不同环境。目前,logback 分为三个模块:Core、Classic 和 Access。
Core模块是其他两个模块的基础。Classic 模块扩展了core模块。Classic 模块相当于 log4j的显著改进版。Logback-classic 直接实现了 SLF4J API,因此你可以在 logback 与其他记录系统如 log4j 和 java.util.logging (JUL)之间轻松互相切换。Access 模块与 Servlet 容器集成,提供 HTTP 访问记录功能。本文不讲述 access 模块。
本文中,“logback”代表 logback-classic 模块。
Logback 建立于三个主要类之上:Logger、Appender 和 Layout。这三种组件协同工作,使开发者可以按照消息类型和级别来记录消息,还可以在程序运行期内控制消息的输出格式和输出目的地。
Logger类是logback-classic 模块的一部分,而Appender和Layout接口来自logback-core。
作为一个多用途模块,logback-core 不包含任何 logger。
任何比System.out.println高级的记录API的第一个也是最重要的优点便是能够在禁用特定记录语句的同时却不妨碍输出其他语句。这种能力源自记录隔离(space)——即所有各种记录语句的隔离——是根据开发者选择的条件而进行分类的。在 logback-classic 里,这种分类是 logger 固有的。各个 logger 都被关联到一个 LoggerContext,LoggerContext 负责制造logger,也负责以树结构排列各 logger。
Logger 是命名了的实体。它们的名字大小写敏感且遵从下面的层次化的命名规则:
命名层次:
如果 logger 的名称带上一个点号后是另外一个 logger 的名称的前缀,那么, 前者 就被称为 后者的祖先。 如果 logger 与代 其后代 logger 之间没有其他祖先, 那么,前者就被称为子logger 之 父。
比如,名为“com.foo"”的 logger 是名为“com.foo.Bar”之父。同理, “java”是“java.util"”之父,也是“java.util.Vector”的祖先。
根 logger 位于 logger 等级的最顶端,它的特别之处是它是每个层次等级的共同始祖。
如同其他各 logger,根 logger 可以通过其名称取得,如下所示:
Logger rootLogger = LoggerFactory.getLogger(org.slf4j.Logger.ROOT_LOGGER_NAME);
其他所有 logger 也通过 org.slf4j.LoggerFactory 类的静态方法 getLogger 取得。getLogger方法以 logger 名称为参数。Logger 接口的部分基本方法列举如下:
package org.slf4j;
public interface Logger {
// Printing methods:
public void trace(String message);
public void debug(String message);
public void info(String message);
public void warn(String message);
public void error(String message);
}
Logger 可以被分配级别。级别包括:TRACE、DEBUG、INFO、WARN 和 ERROR,定义于 ch.qos.logback.classic.Level 类。注意在 logback 里,Level 类是 final 的,不能被继承,Marker 对象提供了更灵活的方法。
如果 logger 没有被分配级别,那么它将从有被分配级别的最近的祖先那里继承级别。
更正式地说:logger L 的有效级别等于其层次等级里的第一个非 null级别,顺序是从 从 L 开始, 向上直至根 logger 。
为确保所有 logger 都能够最终继承一个级别,根 logger 总是有级别,默认情况下,这个级别是 DEBUG。
下面的四个例子包含各种分配级别值和根据级别继承规则得出的最终有效(继承)级别。
Logger名 | 分配级别 | 有效级别 | |
root | DEBUG | DEBUG | |
X | none | DEBUG | |
X.Y | none | DEBUG | |
X.Y.Z | none | DEBUG |
例 1 里,仅根 logger 被分配了级别。级别值 DEBUG 被其他 logger X、X.Y 和 X.Y.Z 继
承。
Logger名 | 分配级别 | 有效级别 | |
root | ERROR | ERROR | |
X | INFO | INFO | |
X.Y | DEBUG | DEBUG | |
X.Y.Z | WARN | WARN |
例 2 里,所有 logger 都被分配了级别。级别继承不发挥作用。
Logger名 | 分配级别 | 有效级别 | |
root | DEBUG | DEBUG | |
X | INFO | INFO | |
X.Y | none | INFO | |
X.Y.Z | ERROR | ERROR |
例 3 里,根 logger、X 和 X.Y.Z 分别被分配了 DEBUG、INFO 和 ERROR 级别。X.Y 从
其父 X 继承级别。
Logger名 | 分配级别 | 有效级别 | |
root | DEBUG | DEBUG | |
X | INFO | INFO | |
X.Y | none | INFO | |
X.Y.Z | none | none |
例 4 里,根 logger 和 X 分别被分配了 DEBUG 和 INFO 级别。X.Y 和 X.Y.Z 从其最近的
父 X 继承级别,因为 X 被分配了级别
根据定义,打印方法决定记录请求的级别。例如,如果 L 是一个 logger 实例,那么,语句 L.info("..")是一条级别为 INFO 的记录语句。
记录请求的级别在高于或等于其 logger 的有效级别时被称为被启用,否则,称为被禁用。如前所述,没有被分配级别的 logger 将从其最近的祖先继承级别。该规则总结如下:
基本选择规则
记录为 请求级别为 p ,其 logger 的有效级别为 为 q, , 只有当 则当 p>=q, 时, 该请求才会被执行。
该规则是 logback 的核心。级别排序为:TRACE < DEBUG < INFO < WARN < ERROR。
下表显示了选择规则是如何工作的。行头是记录请求的级别 p。列头是 logger 的有效级别 q。行(请求级别)与列(有效级别)的交叉部分是按照基本选择规则得出的布尔值。
请求级别 p | 有效级别 q | |||||
TRACE | DEBUG | INFO | WARN | ERROR | OFF | |
TRACE | YES | NO | NO | NO | NO | NO |
DEBUG | YES | YES | NO | NO | NO | NO |
INFO | YES | YES | YES | NO | NO | NO |
WARN | YES | YES | YES | YES | NO | NO |
ERROR | YES | YES | YES | YES | YES | NO |
OFF | YES | NO | NO | NO | NO | NO |
下面是基本选择规则的例子。
// 取得名为"com.foo"的 logger 实例
Logger logger = LoggerFactory.getLogger("com.foo");
// 设其级别为 INFO
logger.setLevel(Level.INFO);
Logger barlogger = LoggerFactory.getLogger("com.foo.Bar");
// 该请求有效,因为 WARN >= INFO
logger.warn("Low fuel level.");
// 该请求无效,因为 DEBUG < INFO.
logger.debug("Starting search for nearest gas station.");
// 名为"com.foo.Bar"的 logger 实例 barlogger, 从"com.foo"继承级别
// 因此下面的请求有效,因为 INFO >= INFO.
barlogger.info("Located nearest gas station.");
// 该请求无效,因为 DEBUG < INFO.
barlogger.debug("Exiting gas station search");
译者注:上例的 logger.setLevel(Level.INFO)无效。org.slf4j.Logger 没有 setLevel()方法,ch.qos.logback.classic.Logger 有此方法。
用同一名字调用 LoggerFactory.getLogger 方法所得到的永远都是同一个 logger 对象的引用。
例如:
Logger x = LoggerFactory.getLogger("wombat");
Logger y = LoggerFactory.getLogger("wombat");
x 和 y 指向同一个 logger 对象。
因此,可以配置一个 logger,然后从其他地方取得同一个实例,不需要到处传递引用。
生物学里的父母总是先于其孩子,而 logback 不同,它可以以任何顺序创建和配置 logger。
特别的是,即使“父”logger 是在其后代初始化之后才初始化的,它仍将查找并链接到其后代们。
通常是在程序初始化时对 logback 环境进行配置。推荐用读配置文件类进行配置。稍后会讲这种方法。
Logback 简化了 logger 命名,方法是在每个类里初始化 logger,以类的全限定名作为
logger 名。这种定义 logger 的方法即有用又直观。由于记录输出里包含 logger 名,这种命名
方法很容易确定记录消息来源。Logback 不限制 logger 名,你可以随意命名 logger。
然而,目前已知最好的策略是以 logger 所在类的名字作为 logger 名称。
有选择性地启用或禁用记录请求仅仅是 logback 功能的冰山一角。Logback 允许打印记录请求到多个目的地。在 logback 里,一个输出目的地称为一个 appender。目前有控制台、文件、远程套接字服务器、MySQL、PostreSQL、Oracle和其他数据库、JMS和远程UNIX Syslog守护进程等多种 appender。
一个 logger 可以被关联多个 appender。
方法 addAppender 为指定的 logger 添加一个 appender。对于 logger 的每个启用了的记录请求,都将被发送到 logger 里的全部 ppender 及更高等级的 appender。换句话说,appender叠加性地继承了 logger 的层次等级。例如,如果根 logger 有一个控制台 appender,那么所有
启用了的请求都至少会被打印到控制台。如果 logger L 有额外的文件 appender,那么,L 和L后代的所有启用了的请求都将同时打印到控制台和文件。设置 logger 的 additivity 为 false,则可以取消这种默认的 appender 累积行为。
控制 appender 叠加性的规则总结如下。
Appender 叠加性
Logger L 的记录 语句的输出会发送给 L 及其祖先的全部appender。这就是“appender叠加性”的含义。
然而,果 如果 logger L 的某个祖先 P 设置叠加性标识为 false,那么,L 的输出会发送给L 与 与 P 之间(含 P )的所有 appender ,但不会发送给 P 的任何祖先的 appender。
Logger 的叠加性默认为 true。
示例:
Logger 名 | 关联的Appender | 叠加性标识 | 输出目标 | 说明 |
root | A1 | 不可用 | A1 | 叠加性标识不适用于根 logger |
x | A-x1,A-x2 | true | A1,A-x1,A-x2 | 根和 x |
x.y | none | true | A1, A-x1, A-x2 | 根和 x |
x.y.z | A-xyz1 | true | A1, A-x1, A-x2,A-xyz1 | 根、x.y.z 和 x |
security | A-sec | false | A-sec | 因为叠加性标识为 false,所以appender 不累积。只有 A-sec |
security.access | none | true | A-sec | 只有 security,因为 security 的叠 加 性 标 识 为false。 |
有些用户希望不仅可以定制输出目的地,还可以定制输出格式。这时为 appender 关联一个 layout 即可。Layout 负责根据用户意愿对记录请求进行格式化,appender 负责将格式化化后的输出发送到目的地。PatternLayout 是标准 logback 发行包的一部分,允许用户按照类似于 C 语言的 printf 函数的转换模式设置输出格式。
例如,转换模式"%-4relative [%thread] %-5level %logger{32} - %msg%n"在 PatternLayout里会输出形如:
176 [main] DEBUG manual.architecture.HelloWorld2 - Hello world.
第一个字段是自程序启动以来的逝去时间,单位是毫秒。
第二个地段发出记录请求的线程。
第三个字段是记录请求的级别。
第四个字段是与记录请求关联的 logger 的名称。
“-”之后是请求的消息文字。
因为 logback-classic 里的 logger 实现了 SLF4J 的 Logger 接口,某些打印方法可接受多个参数。这些不同的打印方法主要是为了在提高性能的同时尽量不影响代码可读性。
对于某个 Logger,下面的代码
logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
在构造消息参数时有性能消耗,即把整数 i 和 entry[i]都转换为字符串时,还有连接多个字符串时。不管消息会不会被记录,都会造成上述消耗。
一个可行的办法是用测试语句包围记录语句以避免上述消耗,比如,
if(logger.isDebugEnabled()) {
logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
}
当 logger 的 debug 级别被禁用时,这个方法可以避免参数构造带来的性能消耗。另一方面,如果 logger 的 DEBUG 级别被启用,那么会导致两次评估 logger 是否被启用:一次是isDebugEnabled 方法,一次是 debug 方法。在实践中,这种额外开销无关紧要,因为评估 logger所消耗的时间不足实际记录请求所用时间的 1%。
还有一种基于消息格式的方便的替代方法。假设 entry 是一个 object,你可以编写:
Object entry = new SomeObject();
logger.debug("The entry is {}.", entry);
在评估是否作记录后,仅当需要作记录时,logger 才会格式化消息,用 entry 的字符串值替换"{}"。换句话说,当记录语句被禁用时,这种方法不会产生参数构造所带来的性能消耗。
介绍过 logback 的核心组件后,下面描述 logback 框架在用户调用 logger 的打印方法时
所做的事情。在本例中,用户调用名为 com.wombat 的 logger 的 info()方法。
一个关于记录的常见争论是它的计算代价。这种关心很合理,因为即使是中等大小的应
用程序也会生成数以千计的记录请求。人们花了很多精力来测算和调整记录性能。尽管如此,
用户还是需要注意下面的性能问题。
logger x,x.debug("Entry number: " + i + "is " + entry[i]);
x.debug("Entry number: {} is {}", i, entry[i]);
标签:扩展 结构 分享图片 导致 改进 争论 ber 常见 erb
原文地址:https://www.cnblogs.com/yw0219/p/9310845.html