CoderLi

V1

2022/10/29阅读:20主题:极客黑

Logback 的顶级接口

ILoggerFactory

介绍一下 slf4j 的一些顶级接口

public interface ILoggerFactory {
    public Logger getLogger(String name);
}

再看看 logback 对这个接口的实现 ch.qos.logback.classic.LoggerContext

final Logger root;
private Map<String, Logger> loggerCache;

@Override
public Logger getLogger(final String name) {

    if (name == null) {
        throw new IllegalArgumentException("name argument cannot be null");
    }

    // if we are asking for the root logger, then let us return it without
    // wasting time
    if (Logger.ROOT_LOGGER_NAME.equalsIgnoreCase(name)) {
        return root;
    }

    int i = 0;
    Logger logger = root;

    // check if the desired logger exists, if it does, return it
    // without further ado.
    Logger childLogger = (Logger) loggerCache.get(name);
    // if we have the child, then let us return it without wasting time
    if (childLogger != null) {
        return childLogger;
    }

    // if the desired logger does not exist, them create all the loggers
    // in between as well (if they don't already exist)
    String childName;
    while (true) {
        int h = LoggerNameUtil.getSeparatorIndexOf(name, i);
        if (h == -1) {
            childName = name;
        } else {
            childName = name.substring(0, h);
        }
        // move i left of the last point
        i = h + 1;
        synchronized (logger) {
            childLogger = logger.getChildByName(childName);
            if (childLogger == null) {
                childLogger = logger.createChildByName(childName);
                loggerCache.put(childName, childLogger);
                incSize();
            }
        }
        logger = childLogger;
        if (h == -1) {
            return childLogger;
        }
    }
}
  • 如果是 name 是 root 的话、直接返回 root 对应的 Logger
  • 如果 name 对应的 Logger 对象已经创建了、那么从缓存中取出 Logger 对象
  • 如果都没有则从 root Logger 中获取它的子 Logger、将 name 按 . 进行分割之后、按顺序创建对应的 Logger,如果已经存在则不创建。直至到创建好 name 对应的 Logger。所有创建的 Logger 都会放到缓存中保存

比如 name=com.test.de、那么会先创建 com 这个 Logger 然后到 com.test 这个 Logger 最后才到 com.test.de 这个 Logger

对应的工具类是 LoggerFactory

Logger

org.slf4j.Logger

微信公众号:CoderLi
微信公众号:CoderLi

我们再看看 logback 对这个接口的实现

微信公众号:CoderLi
微信公众号:CoderLi

我们可以看到实现类中存储着很多属性

  • name Logger 对应的名称
  • level Logger 对应的级别
  • effectiveLevelInt 对应上面字段、数字用于比较级别的大小关系
  • parent 当前 Logger 的父级 Logger
  • childrenList 当前 Logger 的子级 Logger
  • aai 一个 Logger 可以有多个 Appender 、这个可以看作只 Appender 的专有集合类
  • additive 是否继承父级的 Appender。默认都是继承的

Marker

可以理解成对某条或某些日志添加一个记号、相当于我们对每个文章打上一个标签

微信公众号:CoderLi
微信公众号:CoderLi

看下 logback 对其的实现

微信公众号:CoderLi
微信公众号:CoderLi
微信公众号:CoderLi
微信公众号:CoderLi
微信公众号:CoderLi
微信公众号:CoderLi

那么 Marker 有啥作用

  • 触发标识。当出现某个 Marker 的时候、可以对其作出一定的行为、比如说监控的报警
  • 过滤标识。根据 Marker 进行过滤、只有带有某些 Marker 的日志才会记录到对应的地方

IMarkerFactory

微信公众号:CoderLi
微信公众号:CoderLi

再看看这个接口的实现类

微信公众号:CoderLi
微信公众号:CoderLi

对应的工具类是 MarkerFactory

MDCAdapter

引入 MDC 的目的

在一个多线程程序中,不同线程处理不同客户端的请求,如果对每个客户端都实例化一个新的且独立的 logger对象,用于区分一个客户端与另一个客户端的日志输出,会导致 logger 急剧增加并且会增加维护成本。所以提出了一种轻量级的技术:给每个为客户端服务的 logger 打一个标记,用于区分不同客户端的日志输出。这个技术就是MDC(Mapped Diagnostic Context )诊断上下文映射 。

微信公众号:CoderLi
微信公众号:CoderLi

我们看看它的实现

微信公众号:CoderLi
微信公众号:CoderLi
public Map<String, String> getCopyOfContextMap() {
    Map<String, String> hashMap = copyOnThreadLocal.get();
    if (hashMap == null) {
        return null;
    } else {
        return new HashMap<String, String>(hashMap);
    }
}

多线程的时候会丢失、官方建议是先 get 然后在运行前在 set 回去

MDC 用得最多的可能就是链路追踪了、存放 traceId 和 SpanId

对应的工具类是 MDC

<appender name="STDOUT2" class="ch.qos.logback.core.ConsoleAppender">
    <encoder>
        <pattern>
            %-4relative [%thread] [%X{name}] [%mdc{address}] %-5level %logger - %msg%n
        </pattern>
    </encoder>
</appender>
public static void main(String[] args) {

    Logger logger = LoggerFactory.getLogger(MdcMain.class);
    MDC.put("name""coderLi");
    MDC.put("address""xx");
    logger.info("asdfsafsdf:{}","23");

}

SLF4JServiceProvider

微信公众号:CoderLi
微信公众号:CoderLi

提供了三个我们上面提到的接口

微信公众号:CoderLi
微信公众号:CoderLi

分类:

后端

标签:

后端

作者介绍

CoderLi
V1