当前位置:网站首页>logback自定义MessageConverter
logback自定义MessageConverter
2022-07-26 22:39:00 【everyD_struggle】
import ch.qos.logback.classic.pattern.MessageConverter;
import ch.qos.logback.classic.spi.ILoggingEvent;
import com.alibaba.fastjson.JSON;
import com.didi.utils.log.LogUtils;
import org.apache.commons.lang3.StringUtils;
import org.slf4j.helpers.MessageFormatter;
import java.util.stream.Stream;
public class ArgumentJsonFormatLayout extends MessageConverter {
@Override
public String convert(ILoggingEvent event) {
try {
String cs = event.getMDCPropertyMap().get(LogUtils.CSPANID);
if (StringUtils.isBlank(cs)) {
return "";
} else {
return "cspanid=" + cs;
}
} catch (Exception e) {
return event.getMessage();
}
}
}
注意conversionRule内的conversionWord,这是我们可以提取Pattern内咱们自己设定的转义符号,converterClass则填自定义的全限定类名。
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true">
<conversionRule conversionWord="cspanid" converterClass="com.xxx.log.ArgumentJsonFormatLayout"/>
<property name="logPattern"
value="%cspanid[%level][%d{YYYY-MM-dd'T'HH:mm:ss.SSS+0800}][%file:%line]%X{DL_TAG:-_com_request_in}||traceid=%X{TRACE_ID:-init}||spanid=%X{SPAN_ID:-init}||uri=%X{CUR_URL:-init}||cur_service=%X{CUR_SERVICE:-init}||msg=%m%n"/>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<Pattern>${logPattern}</Pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<root level="info">
<appender-ref ref="STDOUT"/>
</root>
</configuration>
其实现原理在于:
从图中可以看出,整个过程中起关键作用的几个类为:AppenderAttatchableImpl,OutputStreamAppender、Encoder、Layout、OutputStream。这几类和配置文件中相应元素一一对应。logback通过类的继承和组合层层封装方法,最后通过OutputStream写入到控制台(ConsoleAppender)或者是文件(FileAppender)中。
Encode接口
public void doEncode(E event) throws IOException {
String txt = layout.doLayout(event);
outputStream.write(convertToBytes(txt));
outputStream.flush();
}
public String doLayout(ILoggingEvent event) {
if (!isStarted()) {
return CoreConstants.EMPTY_STRING;
}
return writeLoopOnConverters(event);
}
protected String writeLoopOnConverters(E event) {
StringBuilder buf = new StringBuilder(128);
Converter<E> c = head;
while (c != null) {
c.write(buf, event);
c = c.getNext();
}
return buf.toString();
}
public void start() {
if(pattern == null || pattern.length() == 0) {
addError("Empty or null pattern.");
return;
}
try {
Parser<E> p = new Parser<E>(pattern);
if (getContext() != null) {
p.setContext(getContext());
}
Node t = p.parse();
this.head = p.compile(t, getEffectiveConverterMap());
if (postCompileProcessor != null) {
postCompileProcessor.process(head);
}
setContextForConverters(head);
ConverterUtil.startConverters(this.head);
super.start();
} catch (ScanException sce) {
StatusManager sm = getContext().getStatusManager();
sm.add(new ErrorStatus("Failed to parse pattern \"" + getPattern()
+ "\".", this, sce));
}
}
static {
defaultConverterMap.put("d", DateConverter.class.getName());
defaultConverterMap.put("date", DateConverter.class.getName());
defaultConverterMap.put("r", RelativeTimeConverter.class.getName());
defaultConverterMap.put("relative", RelativeTimeConverter.class.getName());
defaultConverterMap.put("level", LevelConverter.class.getName());
defaultConverterMap.put("le", LevelConverter.class.getName());
defaultConverterMap.put("p", LevelConverter.class.getName());
defaultConverterMap.put("t", ThreadConverter.class.getName());
defaultConverterMap.put("thread", ThreadConverter.class.getName());
defaultConverterMap.put("lo", LoggerConverter.class.getName());
defaultConverterMap.put("logger", LoggerConverter.class.getName());
defaultConverterMap.put("c", LoggerConverter.class.getName());
defaultConverterMap.put("m", MessageConverter.class.getName());
defaultConverterMap.put("msg", MessageConverter.class.getName());
defaultConverterMap.put("message", MessageConverter.class.getName());
defaultConverterMap.put("C", ClassOfCallerConverter.class.getName());
defaultConverterMap.put("class", ClassOfCallerConverter.class.getName());
defaultConverterMap.put("M", MethodOfCallerConverter.class.getName());
defaultConverterMap.put("method", MethodOfCallerConverter.class.getName());
defaultConverterMap.put("L", LineOfCallerConverter.class.getName());
defaultConverterMap.put("line", LineOfCallerConverter.class.getName());
defaultConverterMap.put("F", FileOfCallerConverter.class.getName());
defaultConverterMap.put("file", FileOfCallerConverter.class.getName());
defaultConverterMap.put("X", MDCConverter.class.getName());
defaultConverterMap.put("mdc", MDCConverter.class.getName());
defaultConverterMap.put("ex", ThrowableProxyConverter.class.getName());
defaultConverterMap.put("exception", ThrowableProxyConverter.class
.getName());
defaultConverterMap.put("throwable", ThrowableProxyConverter.class
.getName());
defaultConverterMap.put("xEx", ExtendedThrowableProxyConverter.class.getName());
defaultConverterMap.put("xException", ExtendedThrowableProxyConverter.class
.getName());
defaultConverterMap.put("xThrowable", ExtendedThrowableProxyConverter.class
.getName());
defaultConverterMap.put("nopex", NopThrowableInformationConverter.class
.getName());
defaultConverterMap.put("nopexception",
NopThrowableInformationConverter.class.getName());
defaultConverterMap.put("cn", ContextNameAction.class.getName());
defaultConverterMap.put("contextName", ContextNameConverter.class.getName());
defaultConverterMap.put("caller", CallerDataConverter.class.getName());
defaultConverterMap.put("marker", MarkerConverter.class.getName());
defaultConverterMap.put("property", PropertyConverter.class.getName());
defaultConverterMap.put("n", LineSeparatorConverter.class.getName());
}
输出日志的最终任务会落到Enconder#doEncode身上,第一段代码位于LayoutWrappingEncoder.java中,doEncode会最终调用Layout.doLayout得到格式化的文本,然后使用outputStream输出。第二段代码位于PatternLayout.java中,doLayout会调用writeLoopOnConverters,而writeLoopOnConverters会将日志文件通过Converter链进行格式化。Converter链是怎么产生的呢?请看第三段代码,看来是通过配置的pattern得到的,大体上是通过Parser去解析我们配置的pattern("%d %-4relative [%thread] %-5level %logger{35} - %msg%n"),parser通过识别"%"来判决是关键词还是普通文本,例如[]就是普通文件,relative/msg/n等都是关键字。Node t = p.parse();将pattern转化成Node链,Node有多种例如KeywordNode。通过p.compile(t, getEffectiveConverterMap())得到Convert链,主要是将Node链转化成Convert链,PatternLayout中列出了所有的Convert与pattern中名字的对应关系(最后一段代码),例如d代表DateConverter,level 代码LevelConverter等。
其实我们的日志信息在经过Converter链格式化之前还经过了一次格式化。我们通常会使用logger.info(“somemsg:{}”,{}),使用"{}"作为占位符,logback会将其替换成后面出现的参数,这个过程是怎么发生的呢?
public LoggingEvent(String fqcn, Logger logger, Level level, String message,
Throwable throwable, Object[] argArray) {
this.fqnOfLoggerClass = fqcn;
this.loggerName = logger.getName();
this.loggerContext = logger.getLoggerContext();
this.loggerContextVO = loggerContext.getLoggerContextRemoteView();
this.level = level;
this.message = message;
FormattingTuple ft = MessageFormatter.arrayFormat(message, argArray);
formattedMessage = ft.getMessage();
if (throwable == null) {
argumentArray = ft.getArgArray();
throwable = ft.getThrowable();
} else {
this.argumentArray = argArray;
}
if (throwable != null) {
this.throwableProxy = new ThrowableProxy(throwable);
LoggerContext lc = logger.getLoggerContext();
if (lc.isPackagingDataEnabled()) {
this.throwableProxy.calculatePackagingData();
}
}
timeStamp = System.currentTimeMillis();
// ugly but under the circumstances acceptable
LogbackMDCAdapter logbackMDCAdapter = (LogbackMDCAdapter) MDC
.getMDCAdapter();
mdcPropertyMap = logbackMDCAdapter.getPropertyMap();
}
FormattingTuple ft = MessageFormatter.arrayFormat(message, argArray);
formattedMessage = ft.getMessage();
IloggingEvent非常重要,是log信息的携带者,其实现类为LoggingEvent,在实例化LoggingEvent时,使用MessageFormatter和FormattingTuple将{}占位符进行了替换。
原文链接:https://www.jianshu.com/p/5616844a5f68
边栏推荐
- Detailed explanation of this point in JS
- Input a string of letters and output the vowels inside. I hope you guys can give guidance
- [CTF 真题] 2018-网鼎杯-Web-Unfinish
- Dynamic binding, static binding, and polymorphism
- 2020-12-20 九九乘法表
- [SQL注入] 扩展注入手法
- Looking for the real murderer
- [b01lers2020]Welcome to Earth
- [acwing game 61]
- 八皇后 N皇后
猜你喜欢

Two methods of automated testing XSS vulnerabilities using burpsuite

ArcGIS and CASS realize elevation points of cross-section Exhibition
![[Network Research Institute] attackers scan 1.6 million WordPress websites to find vulnerable plug-ins](/img/91/4d6e7d46599a67e3d7c73afb375abd.png)
[Network Research Institute] attackers scan 1.6 million WordPress websites to find vulnerable plug-ins

JSCORE day_ 03(7.4)

Reduced dimension mean dot product matrix multiplicative norm probability normal distribution square loss
![[ciscn2019 North China division Day1 web2]ikun](/img/80/53f8253a80a80931ff56f4e684839e.png)
[ciscn2019 North China division Day1 web2]ikun
![[By Pass] WAF 的绕过方式](/img/dd/7204b2401a9f18c02c8b9897258905.png)
[By Pass] WAF 的绕过方式

Visual studio C cs0006 C failed to find metadata file

Dynamic binding, static binding, and polymorphism

Search engine realizes keyword highlighting
随机推荐
DOM day_ 02 (7.8) web page production process, picture SRC attribute, carousel chart, custom attribute, tab bar, input box event, check operation, accessor syntax
[qt] solve the problem of Chinese garbled code
【AcWing第61场周赛】
[By Pass] 文件上传的绕过方式
[HarekazeCTF2019]encode_and_encode
【3. 基础搜索与图论初识】
[ciscn2019 North China division Day1 web2]ikun
JS screen detection method summary 2021-10-05
程序员必做50题
关于Thymeleaf的表达式
[RootersCTF2019]I_< 3_ Flask
[漏洞实战] 逻辑漏洞挖掘
[BJDCTF2020]EzPHP
Yolo of Darknet_ Forward of layer_ yolo_ Layer comments
Promise basic usage 20211130
DOM day_03(7.11) 事件冒泡机制、事件委托、待办事项、阻止默认事件、鼠标坐标、页面滚动事件、创建DOM元素、DOM封装操作
JSCORE day_02(7.1)
ES6中的export和import
Install redis-7.0.4 in Linux system
js中this指向详解