当前位置: 首页 > news >正文

第二章 Logback的架构(二)

Logger, Appenders 和 Layouts

Appenders 和 Layouts

基于日志记录器选择性地启用或禁用日志记录请求只是其中的一部分功能。Logback允许将日志记录请求输出到多个目标。在Logback术语中,输出目标被称为Appender。
目前,已经存在适用于控制台、文件、远程套接字服务器、MySQL、PostgreSQL、Oracle及其他数据库、JMS以及远程Unix系统日志守护进程的Appender。

一个Logger可以添加多个appender。

addAppender方法可以将Appender添加到给定的Logger中。每个启用的日志请求都会被转发到给定的Logger的Appender中,以及Logger层次结构的更高级别的Appender中。
换句话说,Appender是通过日志记录器层次结构累加继承的。比如,一个输出到控制台的Appender被添加到根Logger中,那么所有的启用的日志请求至少都会在控制台打印输出;
此外,如果一个输出到文件的Appender被添加到一个名为L的Logger中,那么所有L的启用日志请求以及L子级的启用日志请求都会输出到文件中并在控制台打印输出。
当然,我们可以通过设置Logger的additivity标记为false来取消这种默认的累计输出模式。

下面是控制Appender可加性的规则总结。

假如我们有一个叫L的Logger,L的日志输出会被发送到L以及其父级包含的所有Appender中。然而,如果L有一个父级Appender P,P的`additivity`标记为`false`,那么L的日志输出
会定向输出到L以及其到父级P为止所包含的所有Appender中。Logger的`additivity`标记默认都是true。

下表是一个示例:

Logger NameAttached AppendersAdditivity FlagOutput TargetsComment
rootA1not applicableA1Since the root logger stands at the top of the logger hierarchy, the additivity flag does not apply to it.
xA-x1, A-x2trueA1, A-x1, A-x2Appenders of “x” and of root.
x.ynonetrueA1, A-x1, A-x2Appenders of “x” and of root.
x.y.zA-xyz1trueA1, A-x1, A-x2, A-xyz1Appenders of “x.y.z”, “x” and of root.
securityA-secfalseA-secNo appender accumulation since the additivity flag is set to false. Only appender A-sec will be used.
security.accessnonetrueA-secOnly appenders of “security” because the additivity flag in “security” is set to false.

很多时候,用户希望不仅仅是自定义日志的输出路径,同样希望自定义日志的输出格式。这可以通过配置Appender关联的Layout来完成。Layout负责根据用户的意愿格式化日志记录请求,
而Appender则负责将格式化的输出发送到其目的地。PatternLayout是Logback中标准的Layout实现,它允许用户根据类似于C语言printf函数的转换模式指定输出格式。

比如,PatternLayout设置转换参数为%-4relative [%thread] %-5level %logger{32} - %msg%n时,会有以下输出:

176  [main] DEBUG manual.architecture.HelloWorld2 - Hello world.

第一部分字段表示程序运行到现在所经过的毫秒数,第二个部分字段表示发出日志请求的线程,第三部分字段表示日志的级别,第四部分字段表示日志的输出位置,-字符后是日志的消息内容。

参数化日志记录

在logback-classic中,日志记录器实现了SLF4J的日志记录接口,一些日志记录器方法允许接收多个参数。这些打印方法变体的主要目的是提高性能并最小化对代码可读性的影响。

比如一些日志打印这样书写:

logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));

这样确实会有一些消息参数构建的成本,比如类型转换(数值和对象转字符串)以及字符串连接。无论我们是否真的需要输出这条信息,都需要消耗这个成本。

一个可能的解决方法是使用判断语句来避免参数构建的成本。以下是一个例子。

if(logger.isDebugEnabled()) {logger.debug("Entry number: " + i + " is " + String.valueOf(entry[i]));
}

如果日志没有启用DEBUG级别,那么参数构造的开销就没有了。从另一方面讲,启用DEBUG级别时,代码将会有两次判断日志是否是可用的,
第一次是在isDebugEnabled()方法中,第二次是在debug()方法中。实际上,这种开销并不重要,因为判断的时间不到实际日志请求时间的1%。

更好的替代方法

基于消息格式化存在一种更便捷的方式,假如entry是一个对象:

Object entry = new SomeObject(); 
logger.debug("The entry is {}.", entry);

只有当日志被允许输出的时候,才会开始执行消息格式化,并使用entry的字符串替换{}。换句话说,在禁用日志语句时,这种形式不会产生参数构建的成本。

以下两行将产生完全相同的输出。但是,如果禁用日志记录语句,则第二种变体的表现至少比第一种变体好30倍。

logger.debug("The new entry is "+entry+".");
logger.debug("The new entry is {}.", entry);

两个参数的同样可以,示例:

logger.debug("The new entry is {}. It replaces {}.", entry, oldEntry);

如果需要传递三个或更多参数,还可以使用varargs(Object…)变体。例如,您可以编写:

logger.debug("Value {} was inserted between {} and {}.", newVal, below, above);

请注意,varargs变体需要创建Object[]实例的开销。

相关文章:

  • [250504] Moonshot AI 发布 Kimi-Audio:开源通用音频大模型,驱动多模态 AI 新浪潮
  • Adobe卸载清理工具Creative Cloud Cleaner Tool下载
  • 学习Python的第二天之网络爬虫
  • 各国健康指标数据查询
  • P48-56 应用游戏标签
  • PCIe控制逻辑介绍(一)
  • GitHub中多个PR时,如何协同合并和管理
  • 【计算机网络】TCP为什么可靠?解决了哪些问题?
  • JPress安装(Docker)
  • iMeta | 临床研究+scRNA-seq的组合思路 | 真实世界新辅助研究,HER2⁺就一定受益?单细胞揭示真正的“疗效敏感克隆”
  • 【BUG】mmdetection ValueError: need at least one array to concatenate
  • 【Qt4】Qt4中实现PDF预览
  • 【东枫科技】代理英伟达产品:智能网卡的连接线
  • URP - 深度图
  • CSS网格布局
  • UE5 ML机械学习肌肉反应与布料反应
  • 大疆三方云平台部署
  • Linux grep 命令详解及示例大全
  • 多线程“CPU 飙高”问题:如何确保配置的线程数与CPU核数匹配(Java、GoLang、Python )中的最佳实践解决方案
  • 可检查异常与不可检查异常
  • 前瞻|美联储明晨“按兵不动”几无悬念:关税战阴霾下,会否释放降息信号
  • 特朗普称美军舰商船应免费通行苏伊士运河,外交部:反对任何霸凌言行
  • 重磅金融政策密集发布!一文梳理这场国新办发布会
  • 央行行长:未来还可以扩大结构性货币政策工具规模或创设新的政策工具
  • 博裕基金拟收购“全球店王”北京SKP最多45%股权
  • 上海国际电影节特设“走进大卫·林奇的梦境”单元