Overview
自定义Converter,通过conversionRule或者layout实现注入
常规使用Logback的方式不在本篇wiki的范围内
本文旨在解决我写的common_util.log组件在包装slf4j后丢失行号的问题,这是给出的logback解决方案
LineConverter
包装logback + slf4j之后,使用会丢失行号
// 注入Converter,全局覆盖对“L”,“line”的处理
<conversionRule conversionWord="L"
converterClass="com.qunar.flight.pangolin.pay.web.SimpleConverter" />
<conversionRule conversionWord="line"
converterClass="com.qunar.flight.pangolin.pay.web.SimpleConverter" />
// 针对appender设定Layout,局部覆盖defaultConverterMap
<appender name="stdout" class="ch.qos.logback.core.ConsoleAppender" >
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="com.qunar.flight.pangolin.pay.web.SimplePatternLayout"/>
</encoder>
<encoder>
<pattern>${normal-pattern}</pattern>
<charset>${encoding}</charset>
</encoder>
</appender>
Code
package com.qunar.flight.pangolin.pay.web;
import ch.qos.logback.classic.ClassicConstants;
import ch.qos.logback.classic.pattern.LineOfCallerConverter;
import ch.qos.logback.classic.spi.CallerData;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.LoggingEvent;
/**
* @author guohang.ding on 16-11-10
*/
public class SimpleConverter extends LineOfCallerConverter {
private static final String FQCN = com.qunar.flight.log.LogService.class.getName();
private static final String FQCN_BY_UTIL = com.qunar.flight.log.LogUtil.class.getName();
@Override
public String convert(ILoggingEvent le) {
if (!(le instanceof LoggingEvent)) {
return super.convert(le);
}
if (!checkIsService(FQCN, FQCN_BY_UTIL)) {
return super.convert(le);
}
StackTraceElement[] cda = getCallerData((LoggingEvent) le);
if (cda != null && cda.length > 0) {
return Integer.toString(cda[0].getLineNumber());
} else {
return CallerData.NA;
}
}
private boolean checkIsService(String class1, String class2) {
StackTraceElement[] steArray = new Throwable().getStackTrace();
// check if LogService or LogUtil
for (StackTraceElement aSteArray : steArray) {
if (class1.equals(aSteArray.getClassName())
|| class2.equals(aSteArray.getClassName())) {
return true;
}
}
return false;
}
private StackTraceElement[] getCallerData(LoggingEvent event) {
if (event.hasCallerData()) {
StackTraceElement[] array = extract(new Throwable(), FQCN, FQCN_BY_UTIL, ClassicConstants.DEFAULT_MAX_CALLEDER_DATA_DEPTH);
event.setCallerData(array);
}
return event.getCallerData();
}
private StackTraceElement[] extract(Throwable t, String fqnOfInvokingClass, String fqnOfInvokingClass2, final int maxDepth) {
if (t == null) {
return null;
}
StackTraceElement[] steArray = t.getStackTrace();
StackTraceElement[] callerDataArray;
int found = CallerData.LINE_NA;
for (int i = 0; i < steArray.length; i++) {
if (CallerData.isDirectlyInvokingClass(steArray[i].getClassName(), fqnOfInvokingClass) || fqnOfInvokingClass.equals(fqnOfInvokingClass2)) {
// the caller is assumed to be the next stack frame, hence the +1.
found = i + 1;
} else {
if (found != CallerData.LINE_NA) {
break;
}
}
}
// we failed to extract caller data
if (found == CallerData.LINE_NA) {
return CallerData.EMPTY_CALLER_DATA_ARRAY;
}
int availableDepth = steArray.length - found;
int desiredDepth = maxDepth < (availableDepth) ? maxDepth : availableDepth;
callerDataArray = new StackTraceElement[desiredDepth];
for (int i = 0; i < desiredDepth; i++) {
callerDataArray[i] = steArray[found + i];
}
return callerDataArray;
}
}
package com.qunar.flight.pangolin.pay.web;
import ch.qos.logback.classic.PatternLayout;
/**
* @author guohang.ding on 16-11-10
*/
public class SimplePatternLayout extends PatternLayout {
static {
defaultConverterMap.put("L", SimpleConverter.class.getName());
defaultConverterMap.put("line", SimpleConverter.class.getName());
}
}