在 用SLF4j/Logback打印日志-1 和 用SLF4j/Logback打印日志-2 中分别介绍了Logback记录日志的基本原理并重点介绍了输出源配置。本篇介绍一些性能和技巧性的东西。

性能


在查看线上业务代码的时候有时候会发现类似这样的代码:

logger.debug("This " + this + " and " + that);

在对性能有要求的系统中,这种写法是非常不利的,虽然在配置线上系统的时候不会打印 DEBUG 级别的日志,但是在进入函数之前会先计算 "This " + this + " and " + that 这个字符串造成无畏的资源浪费。

在Logback中可以采用类似这样的API来解决拼字符串问题:

logger.debug("This {} and {}", this, that);

这种语法可以解决绝大部分由于拼字符串造成的性能问题,但是在某些情况下,比如还要计算一些数值,这样方案就不行了。

logger.debug("This {} and {} with {} ", this, that, compute());

这段代码虽然解决了拼字符的问题,但是调用 compute() 方法依然会造成资源浪费。在Java8之前大多数的解决方案是这样的:

if (logger.isDebugEnabled()) {
logger.debug("This {} and {} with {} ", this, that, compute());
}

在打印日志之前先判断是否需要打印相应级别的日志,这种写法可以解决任何由于日志打印造成的性能问题。但是代码却变得不够优雅,几乎需要在每个调用logger.debug(..) 方法之前掉一次 if (logger.isDebugEnabled()) {.. ,在Java8之后可以用lambda表达式完美解决这个问题。

logger.debug("I am logging that {} happened.", () -> compute());

这种写法把compute() 封装到一个匿名类里面传递给了debug方法,只有Debug方法内部执行的时候才会执行 compute() 。但是如果函数有多个参数,这种写法就变得有些怪异,因为它要求每个参数都要是lambda表达式,而写出来的代码就会变成这样:

logger.debug("This {} and {} with {} ", () -> this, () -> that, () -> compute());

Java8的lambda是非常低效的,如果方法参数较多这种写法会在每次调用的时候创建3个匿名类,反而会降低程序的性能。综合考虑,一般参数较少并且有耗时计算任务的时候考虑用java8的特性。

日志分析


通常情况下,我们会约定日志的打印格式,以便日后的分析。默认情况下,logback是用空格分隔不同的日志字段的。

%d %-5p %t %c{2} %m%n

这种约定不利于日志的机器分析,如果被打印的消息里面也包含空格,那么解析就会出错。简单的方案是重新约定日志的格式,比如用,号分隔日志,并保证打印的消息里面不再包含,号,类似这样 - %d, %-5p, %t, %c{2}, %m%n。逗号分隔只是比空格分隔略好一点,毕竟逗号出现在消息体里面的机会少一些。延续这种思路可能会进入一个误区,我们需要寻找一种尽可能稀有的分隔符来分隔日志的字段。

其实我们完全可以直接约定一种协议格式来打印日志,比如JSON。这样就不会担心消息和协议冲突的问题,但是也会带来新的新能问题。综合考虑,如果需要日志比较简单,那么可以采用简单的分隔符分隔日志,如果日志较多比较复杂,那么可以封装一些API来打印特有协议的日志,而性能问题可以考虑上一节的方案。

MDC


MDCMapped Diagnostic Context 的缩写,“映射诊断上下文”看起来高大上的样子,其实是非常简单的,就是一个临时存放k-v对的容器。和普通Map的区别是它是基于ThreadLocal实现的,所以不存在资源竞争问题,可以放心的往里面放东西。

假如我们有一个类似网关的应用,同一时间有很多的请求会发送到本系统。一般情况下,为了追踪每个请求的处理情况我们会在请求中加一个字段叫“TraceId” - 一个普通的UID用来区别每一个请求。那么怎么在日志中打印出TraceId呢,一般可以在打印消息的时候,把TraceId作为参数拼凑到日志消息中,这样有两个不好的地方:

  1. 需要写额外的代码拼凑消息
  2. 需要在每个打印消息的地方维护一个全局的TraceId变量

如果使用MDC问题就可以简化很多,在接收到请求后,解析出TraceId然后放入MDC,在配置文件中配置打印MDC,之后所有调用打印消息的日志都会自动包含TraceId了。

// 解析请求,取出TraceId放入MDC
MDC.put("traceid", xxxxx);
//配置XML
%d, %-5p, %t, %c{2}, %X{traceid}, %m%n

非常的简单而且自然有没有!!!

PS:如果使用的是log4j需要1.2版本以上。

用SLF4j/Logback打印日志-3的更多相关文章

  1. 用SLF4j/Logback打印日志-1

    在 浅谈后端日志系统 中已经写了很多日志方面的零散的非技术的东西.本篇更像一份入门说明,讲解一下SLF4j/Logback.SLF4J是一套抽象的日志API接口,logback它是的底层实现,所以在这 ...

  2. 用SLF4j/Logback打印日志-2

    本篇主要介绍logback的输出源配置,logback默认提供了很多输出源,但是用的最多的是这几种: OutputStreamAppender 日志输出到一个二进制流,可以通过 <encoder ...

  3. 日志框架之2 slf4j+logback实现日志架构 · 远观钱途

    如何从缤纷复杂的日志系统世界筛选出适合自己的日志框架以及slf4j+logback的组合美妙之处?此文可能有帮助 logback介绍 Logback是由log4j创始人设计的另一个开源日志组件,官方网 ...

  4. slf4j/logback: logging日志的配置

    slf4j/logback: logging日志的配置 import依赖: import org.slf4j.Logger;import org.slf4j.LoggerFactory;private ...

  5. 使用 SLF4J + LogBack 构建日志系统(转)

    转载自:http://www.cnblogs.com/mailingfeng/p/3499436.html 上次我们讨论了如何选择一个好的开源日志系统方案,其中的结论是:使用 SLF4J + LogB ...

  6. Spring Boot(三):logback打印日志

    springboot对logback的支持是非常好的,不需要任何配置,只需要在resource下加logback.xml就可以实现功能直接贴代码: <?xml version="1.0 ...

  7. springboot中logback打印日志(转)

    springboot对logback的支持是非常好的,不需要任何配置,只需要在resource下加logback.xml就可以实现功能 直接贴代码: <?xml version="1. ...

  8. java IDE 中安装 lombok plugin 插件,并使用 @Slf4j 注解打印日志初体验

    lombok 插件介绍: IntelliJ IDEA官方插件页面:https://plugins.jetbrains.com/plugin/6317-lombok-plugin 使用lombok之后, ...

  9. logback打印日志时添加上下文

    尝试上述特性, 配置如下: 效果:

随机推荐

  1. 007.SMB其他应用

    一 目录权限 系统权限需要对共享目录生效 samba服务器权限也会对共享目录生效 当用户过多权限交叉的时候,建议使用系统权限控制共享目录权限,而不建议采用samba权限来控制 例如: [work] p ...

  2. 大数据技术之_16_Scala学习_02_变量

    第二章 变量2.1 变量是程序的基本组成单位2.2 Scala 变量的介绍2.2.1 概念2.2.2 Scala 变量使用的基本步骤2.3 Scala 变量的基本使用2.4 Scala 变量使用说明2 ...

  3. 网络爬虫中Fiddler抓取PC端网页数据包与手机端APP数据包

    1 引言 在编写网络爬虫时,第一步(也是极为关键一步)就是对网络的请求(request)和回复(response)进行分析,寻找其中的规律,然后才能通过网络爬虫进行模拟.浏览器大多也自带有调试工具可以 ...

  4. oracle中listagg()和wmsys.wm_concat()基本用法

    一.LISTAGG() 简介 介绍:其函数在Oracle 11g 版本中推出,对分组后的数据按照一定的排序进行字符串连接. 其中,“[,]”表示字符串连接的分隔符,如果选择使用[over (parti ...

  5. python 字典相关操作

    字典 字典的增删改查 字典的创建方式: # 创建字典类型 info = { 'name':'李白', 'age':'25', 'sex':'男' } msg = { 'user01':'Longzel ...

  6. 懒人的福利?教你用set维护斜率优化凸包

    斜率优化题目大家肯定都做得不少了,有一些题目查询插入点的x坐标和查询斜率都不单调,这样就需要维护动态凸包并二分斜率.(例如bzoj1492) 常规的做法是cdq分治或手写平衡树维护凸包,然而如果我不愿 ...

  7. nginx 编译参数详解(运维必看)

    nginx参数: –prefix= 指向安装目录 –sbin-path 指向(执行)程序文件(nginx) –conf-path= 指向配置文件(nginx.conf) –error-log-path ...

  8. FireDAC 下的 Sqlite [1] - 前言

    很长时间没静下心来写博客了, 现在回来, 是 Delphi 不断地进步让我感动.振奋. Delphi XE5 并入了 FireDAC, 第一印象非常好, 恐怕 dbExpress 等等都要靠边站了. ...

  9. Visual Studio 2019 preview中体验C# 8.0新语法

    准备工作: Visual Studio 2019 Preview版本中并没有包含所有的C# 8.0的新功能,但目前也有一些可以试用了.在开始之前,需要进行入两项设置: 将Framework设置为.ne ...

  10. How Visual Studio 2012 Avoids Prompts for Source

    [原文地址]:http://blogs.msdn.com/b/heaths/archive/2012/07/26/how-visual-studio-2012-avoids-prompts-for-s ...