美文网首页项目
logback(二)—layout和MDC机制

logback(二)—layout和MDC机制

作者: 小胖学编程 | 来源:发表于2019-07-02 10:01 被阅读3次

如何根据日志文件快速定位到应用的行为。

1. layout

Layout组件:([累奥特] 布局),将日志事件进行格式化,返回一个字符串

1.1 常用的转换词

转换词 备注
C{length},class{length} 输出发出日志请求调用者的完整类名。注:类名最右边部分不会省略,即使长度超过了设定了length长度。
contextName 日志记录器上下文的名字
d{pattern},date{pattern},d{pattern,timezone},date{pattern,timezone} 日志事件的日期,日期转换词需要一个格式化字符串作为参数,格式化字符串语义与java.text.SimpleDateFormat相同。
F/file 日志请求的java源文件名,一般生成文件信息比较慢,因此一般不使用这个转换词
caller{depth} 输出日志调用者位置信息
L/line 日志请求的行号,由于生成行号信息比较慢,一般不使用这个转换词
m/msg/message 日志事件相关的应用提供信息
M/method 输出与日志事件相关的应用提供的信息
n 输出与平台独立的行分割符,等价于"\n"或者"\r\n"
p/le/level 输出日志级别
r/relative(相对的) 输出日志事件的相对时间
t/thread[斯乱德] 输出产生日志事件的线程名
X{key:-defaultVal} 输出与产生日志事件线程相关的MDC(mapped diagnosis context)。如果mdc转换词后面花括号中有key,那么value就会输出,如果值为空,那么输出默认值;若没有默认值,则输出空字符串。
ex{depth}[带婆斯] 若发生异常时,指定输出异常的行数。
replace(p){r,t} 用't'替换p中正则表达式r的内容,例如:%replace('%m'){'\s',''}会去替换事件消息中包含的所有空格

详细请见:https://blog.csdn.net/lingbomanbu_lyl/article/details/89852037

1.2 自定义layout

  1. 继承自LayoutBase接口即可:
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.LayoutBase;

/**
 * @ClassName MySampleLayout
 * @Description 日志布局器
 * @Date 2019/7/1
 * @Version 1.0
 **/
public class MySampleLayout extends LayoutBase<ILoggingEvent> {
    @Override
    public String doLayout(ILoggingEvent event) {
        StringBuffer sb=new StringBuffer();
        sb.append(event.getTimeStamp()-event.getLoggerContextVO().getBirthTime());
        sb.append(" [").append(event.getThreadName()).append("] ");
        sb.append(event.getLoggerName()).append("-");
        sb.append(event.getFormattedMessage()).append(CoreConstants.LINE_SEPARATOR);
        return sb.toString();
    }
}
  1. 配置文件
<configuration>
    <springProperty scope="context" name="logPath" source="log.out.path" defalutValue="/app/test.log"/>
    <!-- %m输出的信息,%p日志级别,%t线程名,%d日期,%c类的全名,%i索引【从数字0开始递增】,,, -->
    <!-- appender是configuration的子节点,是负责写日志的组件。 -->
    <!-- ConsoleAppender:把日志输出到控制台 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="com.tellme.layout.MySampleLayout"/>
            <!--<pattern>%d %p [%r] [%t]  [%X{traceRootId}] (%file:%line\): %m%n</pattern>-->
            <!--&lt;!&ndash; 控制台也要使用UTF-8,不要使用GBK,否则会中文乱码 &ndash;&gt;-->
            <!--<charset>UTF-8</charset>-->
        </encoder>
    </appender>
</configuration>
  1. 输出格式:
1230 [restartedMain] com.MmWebApplication-The following profiles are active: test

若包含自定义参数的layout组件

  1. java实现类
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.LayoutBase;
import org.apache.commons.lang3.StringUtils;

/**
 * @ClassName CustomLayout
 * @Description 自定义Layout
 * @Date 2019/7/1
 * @Version 1.0
 **/
public class CustomLayout extends LayoutBase<ILoggingEvent> {

    private String prefix = null;
    boolean printThreadName = true;

    public void setPrefix(String prefix) {
        this.prefix = prefix;
    }

    public void setPrintThreadName(boolean printThreadName) {
        this.printThreadName = printThreadName;
    }

    @Override
    public String doLayout(ILoggingEvent event) {

        StringBuffer sbuf = new StringBuffer(128);
        if (StringUtils.isNotBlank(prefix)) {
            sbuf.append(prefix).append(" ");
        }
        sbuf.append(event.getTimeStamp() - event.getLoggerContextVO().getBirthTime());
        sbuf.append(" ");
        sbuf.append(event.getLevel());
        if (printThreadName) {
            sbuf.append(" [");
            sbuf.append(event.getThreadName());
            sbuf.append("] ");
        } else {
            sbuf.append(" ");
        }
        sbuf.append(event.getLoggerName());
        sbuf.append(" - ");
        sbuf.append(event.getFormattedMessage());
        sbuf.append(CoreConstants.LINE_SEPARATOR);
        return sbuf.toString();
    }
}
  1. 配置文件
<configuration>
    <springProperty scope="context" name="logPath" source="log.out.path" defalutValue="/app/test.log"/>
    <!-- %m输出的信息,%p日志级别,%t线程名,%d日期,%c类的全名,%i索引【从数字0开始递增】,,, -->
    <!-- appender是configuration的子节点,是负责写日志的组件。 -->
    <!-- ConsoleAppender:把日志输出到控制台 -->
    <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
            <layout class="com.tellme.layout.CustomLayout">
                <prefix>MyPrefix</prefix>
                <printThreadName>false</printThreadName>
            </layout>
            <!--<pattern>%d %p [%r] [%t]  [%X{traceRootId}] (%file:%line\): %m%n</pattern>-->
            <!--&lt;!&ndash; 控制台也要使用UTF-8,不要使用GBK,否则会中文乱码 &ndash;&gt;-->
            <!--<charset>UTF-8</charset>-->
        </encoder>
    </appender>
</configuration>
  1. 输出格式
MyPrefix 1405 INFO com.MmWebApplication - The following profiles are active: test

2. MDC

logback内置的日志字段还是比较少的,如果我们需要打印有关业务的更多内容,包括自定义的一些数据,需要借助logback的MDC机制,(映射诊断上下文),即将一些运行时的上下文数据通过logback打印出来;此时我们需要借助org.sl4j.MDC类。

MDC类的原理:其内部持有一个InheritableThreadLocal [因海瑞特包 遗传]实例,用于保存context数据,MDC提供了put/get/clear等几个接口,用来操作ThreadLocal中的数据;ThreadLocal中的K-V,可以在logback.xml中声明,最终会打印在日志中。

MDC.put("userId",1000);

那么在logback.xml中,即可在layout中通过"%X{userId}"来打印此信息。

注意:因为使用的是ThreadLocal,在需要线程退出之前,需要清除(clear)MDC里面的数据;在线程池中使用MDC时,在子线程退出之前调用MDC.clear()方法。

2.1 MDC实践

public class LogMDCUtil {
    //接口名
    private final static String SVR_ID = "SVR_ID";

    private final static Logger logger = LoggerFactory.getLogger(LogMDCUtil.class);
/**
     * 为日志设置服务名
     *
     * @param serviceId
     */
    public static void setServiceNameToLog(String serviceId) {
        try {
            
            MDC.put(SVR_ID, serviceId);
        } catch (Exception e) {
            logger.error("【Logback设置MDC】-设置服务名失败", e);
        }
    }

    /**
     * 删除ThreadLocal中关于ServiceName的数据,防止内存溢出
     */
    public static void removeServiceNameToLog() {
        //线程执行完毕,需要删除ServiceName
        try {
            MDC.remove(SVR_ID);
        } catch (Exception e) {
            logger.error("【Logback设置MDC】-删除服务名失败", e);
        }
    }
}

配置文件:

 <appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
        <encoder>
            <pattern>%d %p [%t %X{SVR_ID}] (%C{0}:%line\): %m%n</pattern>
            <!-- 控制台也要使用UTF-8,不要使用GBK,否则会中文乱码 -->
            <charset>UTF-8</charset>
        </encoder>
    </appender>

参考文档

(译)(五)Logback中的Layout

相关文章

网友评论

    本文标题:logback(二)—layout和MDC机制

    本文链接:https://www.haomeiwen.com/subject/ibtncctx.html