文章11 | 阅读 5960 | 点赞0
之所以想写这篇文章是因为经常看到一些相关联的问题:
以上这些问题都是因为没搞明白,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");
}
}
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
//此处变成通过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);
}
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);
}
/** * A map to store loggers for their given LoggerContexts. * context可能有多个,所需要找出的是对应context的缓存 */
protected final Map<LoggerContext, ConcurrentMap<String, L>> registry = new WeakHashMap<>();
public ConcurrentMap<String, L> getLoggersInContext(final LoggerContext context) {
ConcurrentMap<String, L> loggers;
//为了方便阅读分析,省略加锁代码...
loggers = registry.get (context);
//为了方便阅读分析,省略解锁代码...
if (loggers != null) {
return loggers;
} else {
//为了方便阅读分析,省略加锁代码...
loggers = registry.get (context);
if (loggers == null) {
loggers = new ConcurrentHashMap<> ();
registry.put (context, loggers);
}
return loggers;
//为了方便阅读分析,省略解锁代码...
}
}
@Override
protected Logger newLogger(final String name, final LoggerContext context) {
final String key = Logger.ROOT_LOGGER_NAME.equals(name) ? LogManager.ROOT_LOGGER_NAME : name;
//Log4jLogger是为了适配slf4j的接口做的一层适配,这里运用到了适配器的设计模式
//这里的newLogger方法也就是对context的getLogger包装了一层
return new Log4jLogger(context.getLogger(key), name);
}
//LoggerContext的getLogger方法
public Logger getLogger(final String name) {
return getLogger(name, null);
}
//LoggerContext的getLogger方法
public Logger getLogger(final String name, final MessageFactory messageFactory) {
//loggerRegistry的主要目的是对messageFactory, 和logger做了一层内存缓存,是这样一个结构:Map<factoryName, Map<classname, logger>>
//这里就不做展开了
Logger logger = loggerRegistry.getLogger(name, messageFactory);
if (logger != null) {
AbstractLogger.checkMessageFactory(logger, messageFactory);
return logger;
}
//重点生成logger的实例
logger = newInstance(this, name, messageFactory);
loggerRegistry.putIfAbsent(name, messageFactory, logger);
return loggerRegistry.getLogger(name, messageFactory);
}
protected Logger newInstance(final LoggerContext ctx, final String name, final MessageFactory messageFactory) {
return new Logger(ctx, name, messageFactory);
}
protected Logger(final LoggerContext context, final String name, final MessageFactory messageFactory) {
super(name, messageFactory);
this.context = context;
privateConfig = new PrivateConfig(context.getConfiguration(), this);
}
public PrivateConfig(final Configuration config, final Logger logger) {
this.config = config;
this.loggerConfig = config.getLoggerConfig(getName());
this.loggerConfigLevel = this.loggerConfig.getLevel();
this.intLevel = this.loggerConfigLevel.intLevel();
this.logger = logger;
}
public LoggerConfig getLoggerConfig(final String loggerName) {
//根据类的全名获取LoggerConfig
LoggerConfig loggerConfig = loggerConfigs.get(loggerName);
if (loggerConfig != null) {
return loggerConfig;
}
String substr = loggerName;
//每次去掉名字中.后面的单词,然后尝试从已有的LoggerConfigs中获取config,也就是说可能根据包名找到LoggerConfig
while ((substr = NameUtil.getSubName(substr)) != null) {
loggerConfig = loggerConfigs.get(substr);
if (loggerConfig != null) {
return loggerConfig;
}
}
//如果前面两种方法都没有找到就返回RootLoggerConfig
return root;
}
NYNNYstart根据类的全名获取LoggerConfigLoggerConfig为null?返回LoggerConfig结束根据prefix获取LoggerConfigLoggerConfig为null?返回root LoggerConfig
上面我们说到LoggerConfig才是真正的主角,我们看看打印日志是个什么调用链, 中间会省略部分过程
省略部分过程HelloWorld.log.infoLog4jLogger.logger.logIfEnabledAbstractLogger.logMessageLogger/AsyncLogger.logMessageLogger.privateConfig.loggerConfig.getReliabilityStrategyReliabilityStrategy.logloggerConfig.log结束
ps: ReliabilityStrategy只是对LoggerConfig的一层包装
代码解析完了,开篇的问题解答一下
<Logger name="your.package.name" level="${level}" additivity="false" >
</Logger>
版权说明 : 本文为转载文章, 版权归原作者所有 版权申明
原文链接 : https://blog.csdn.net/sweetyi/article/details/104653897
内容来源于网络,如有侵权,请联系作者删除!