文章目录
情景
之所以想写这篇文章是因为经常看到一些相关联的问题:
- 怎么有这么多非本项目的log出现? 譬如引入了其他的sdk,他们又很无节操的打了很多日志。
- 怎么去除不必要的包的日志?
- 为什么logger的名字能对应到指定的package或者类呢?
以上这些问题都是因为没搞明白,log4j是怎么去获取logger的,本文将通过 slf4j-log4j-impl 根据class去查找logger的过程来解答上述的疑问。
解析
从例子说起
- 打印日志的例子
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class HelloWorld {
public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger(HelloWorld.class);
logger.info("Hello World");
}
}
- 深入到LoggerFactory.getLogger方法
public static Logger getLogger(Class<?> clazz) {
Logger logger = getLogger(clazz.getName());
if (DETECT_LOGGER_NAME_MISMATCH) {
//省略部分打印信息的代码...
}
return logger;
}
这个DETECT_LOGGER_NAME_MISMATCH是什么意思呢,就是第一步中的参数并不是当前这个类的时候, 如果这个配置为true,则会打印一条类似下面的信息:
SLF4J: Detected logger name mismatch. Given name: "com.keven.demos.log.TestNameMismatch"; computed name: "com.keven.demos.log.AsyncLoggerDemo".
SLF4J: See http://www.slf4j.org/codes.html#loggerNameMismatch for an explanation
从LoggerFactory获取logger
- 我们接着看getLogger方法:
//此处变成通过class的全名(package name + class name)获取logger
public static Logger getLogger(Class<?> clazz) {
Logger logger = getLogger(clazz.getName());
//省略非关键部分代码...
}
//这里就是说通过slf4j的实现类的loggerFactory获取logger
//如何获取实现类的logger factory,这个在之前的文章中已经分析过了,有兴趣的同学可以看看
//https://blog.csdn.net/sweetyi/article/details/104633321
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
从LoggerContext中获取logger
- 在这里ILoggerFactory的实现类是:org.apache.logging.slf4j.Log4jLoggerFactory, 这个工厂类中没有getLogger方法,它会调用父类AbstractLoggerAdapter的getLogger方法,如下:
public L getLogger(final String name) {
//获取logger配置的上下文,这里不展开
final LoggerContext context = getContext();
//这里是做一层logger的缓存
final ConcurrentMap<String, L> loggers = getLoggersInContext(context);
//根据名字获取logger
final L logger = loggers.get(name);
if (logger != null) {
return logger;
}
//没找到现成的logger,重新生成一个
loggers.putIfAbsent(name, newLogger(name, context));
return loggers.get(name);
}
- getLoggersInContext方法是用来获取context所对应的logger的内存缓存, 以下省略了用读写锁加解锁的代码,不是重点。
/**
* A map to store loggers for their given LoggerContexts.
* context可能有多个,所需要找出的是对应context的缓存
*/
protected final Map<LoggerContext, ConcurrentMap<String, L>> registry = new WeakHashMap<>();
public ConcurrentMap