CoderLi
V1
2022/10/29阅读:42主题:极客黑
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

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

我们可以看到实现类中存储着很多属性
-
name Logger 对应的名称 -
level Logger 对应的级别 -
effectiveLevelInt 对应上面字段、数字用于比较级别的大小关系 -
parent 当前 Logger 的父级 Logger -
childrenList 当前 Logger 的子级 Logger -
aai 一个 Logger 可以有多个 Appender 、这个可以看作只 Appender 的专有集合类 -
additive 是否继承父级的 Appender。默认都是继承的
Marker
可以理解成对某条或某些日志添加一个记号、相当于我们对每个文章打上一个标签

看下 logback 对其的实现



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

再看看这个接口的实现类

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

我们看看它的实现

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
V1