前言:
对slf4j和log4j、logback的关系不太了解的少年们可以先看下这篇文章
https://blog.csdn.net/qq_26323323/article/details/80883760
具体来说,slf4j是一系列的接口规范定义,而Log4j、logback则是其具体实现,在写法上,我们可以直接针对接口编程,底层实现可任意切换
1.基于log4j的日志功能实现
1)maven导入依赖
org.slf4j
slf4j-log4j12
1.7.25
2)log4j.xml配置(直接配置在src/main/resources下)
3)测试类
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class TestLog4j {
private static final Logger logger = LoggerFactory.getLogger(TestLog4j.class);
public static void main(String[] args) {
logger.debug("debug msg");
logger.info("info msg");
logger.error("error msg");
}
}
4)测试结果
2018-07-02 14:11:04,298 [log4j.TestLog4j]-[DEBUG] debug msg
2018-07-02 14:11:04,299 [log4j.TestLog4j]-[INFO] info msg
2018-07-02 14:11:04,299 [log4j.TestLog4j]-[ERROR] error msg
2.获取Logger源码分析
可以看到上过程3)中,测试类TestLog4j所引入的Logger和LoggerFactory都属于slf4j包下,真正的实现都在log4j包下,下面请跟着笔者看下源码的流程
1)分析LoggerFactory.getLogger(TestLog4j.class)
public static Logger getLogger(String name) {
// 获取ILoggerFactory接口
ILoggerFactory iLoggerFactory = getILoggerFactory();
// 获取Logger,都是面向接口的方式
return iLoggerFactory.getLogger(name);
}
// 获取ILoggerFactory
public static ILoggerFactory getILoggerFactory() {
// 由代码 static volatile int INITIALIZATION_STATE = UNINITIALIZED;可知,INITIALIZATION_STATE的默认状态是UNINITIALIZED,
// 故默认会先实现该if功能,而且是串行实现的
if (INITIALIZATION_STATE == UNINITIALIZED) {
synchronized (LoggerFactory.class) {
if (INITIALIZATION_STATE == UNINITIALIZED) {
// 更改状态为正在执行
INITIALIZATION_STATE = ONGOING_INITIALIZATION;
// 初始化信息
performInitialization();
}
}
}
switch (INITIALIZATION_STATE) {
// 当performInitialization()成功执行完成后,INITIALIZATION_STATE的状态为SUCCESSFUL_INITIALIZATION,故可从此获取LoggerFactory的具体实现
case SUCCESSFUL_INITIALIZATION:
// 关键代码在这里
return StaticLoggerBinder.getSingleton().getLoggerFactory();
case NOP_FALLBACK_INITIALIZATION:
return NOP_FALLBACK_FACTORY;
case FAILED_INITIALIZATION:
throw new IllegalStateException(UNSUCCESSFUL_INIT_MSG);
case ONGOING_INITIALIZATION:
// support re-entrant behavior.
// See also http://jira.qos.ch/browse/SLF4J-97
return SUBST_FACTORY;
}
throw new IllegalStateException("Unreachable code");
}
2)初始化信息performInitialization()方法
private final static void performInitialization() {
bind();
if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
versionSanityCheck();
}
}
private final static void bind() {
try {
Set staticLoggerBinderPathSet = null;
// skip check under android, see also
// http://jira.qos.ch/browse/SLF4J-328
if (!isAndroid()) {
// 获取 org/slf4j/impl/StaticLoggerBinder.class 资源
staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
}
// 参考getSingleton()方法,可看到StaticLoggerBinder默认初始化一个SINGLETON为new StaticLoggerBinder()
StaticLoggerBinder.getSingleton();
INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
reportActualBinding(staticLoggerBinderPathSet);
fixSubstituteLoggers();
replayEvents();
// release all resources in SUBST_FACTORY
SUBST_FACTORY.clear();
}catch{...}
}
3)StaticLoggerBinder.getSingleton()
可以看到在slf4j-log4j12-1.7.25.jar下的StaticLoggerBinder类实现了ILoggerFactory接口
public class StaticLoggerBinder implements LoggerFactoryBinder {
private static final StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
public static final StaticLoggerBinder getSingleton() {
return SINGLETON;
}
private final ILoggerFactory loggerFactory;
private StaticLoggerBinder() {
// 构造的时候,直接实现了LoggerFactory,默认为Log4jLoggerFactory
loggerFactory = new Log4jLoggerFactory();
try {
@SuppressWarnings("unused")
Level level = Level.TRACE;
} catch (NoSuchFieldError nsfe) {
Util.report("This version of SLF4J requires log4j version 1.2.12 or later. See also http://www.slf4j.org/codes.html#log4j_version");
}
}
4)返回ILoggerFactory
public static ILoggerFactory getILoggerFactory() {
...
switch (INITIALIZATION_STATE) {
case SUCCESSFUL_INITIALIZATION:
// 直接从单例中获取在构造时实现的LoggerFactory,即Log4jLoggerFactory
return StaticLoggerBinder.getSingleton().getLoggerFactory();
...
}
上述即是获取ILoggerFactory具体实现的过程,下面看如何获取Logger
5)ILoggerFactory.getLogger()
目前可知,默认工厂实现是Log4jLoggerFactory,所以直接定位到该类的getLogger(String name)方法
public Log4jLoggerFactory() {
loggerMap = new ConcurrentHashMap();
// 初始化实现RootLogger
org.apache.log4j.LogManager.getRootLogger();
}
public Logger getLogger(String name) {
Logger slf4jLogger = loggerMap.get(name);
if (slf4jLogger != null) {
return slf4jLogger;
} else {
org.apache.log4j.Logger log4jLogger;
// 如果是ROOT,则默认返回RootLogger
if (name.equalsIgnoreCase(Logger.ROOT_LOGGER_NAME))
log4jLogger = LogManager.getRootLogger();
else
// 通过LogManager创建新的Logger,关键在这里
log4jLogger = LogManager.getLogger(name);
// 封装一层,再返回
Logger newInstance = new Log4jLoggerAdapter(log4jLogger);
Logger oldInstance = loggerMap.putIfAbsent(name, newInstance);
return oldInstance == null ? newInstance : oldInstance;
}
}
6)LogManager.getLogger(String name)
public static Logger getLogger(final String name) {
// Delegate the actual manufacturing of the logger to the logger repository.
return getLoggerRepository().getLogger(name);
}
// 获取LoggerRepository
public static LoggerRepository getLoggerRepository() {
// 初始化的时候repositorySelector不会为null,由LogManager的static模块代码可知,代码块代码如下所示
if (repositorySelector == null) {
repositorySelector = new DefaultRepositorySelector(new NOPLoggerRepository());
guard = null;
Exception ex = new IllegalStateException("Class invariant violation");
String msg =
"log4j called after unloading, see http://logging.apache.org/log4j/1.2/faq.html#unload.";
if (isLikelySafeScenario(ex)) {
LogLog.debug(msg, ex);
} else {
LogLog.error(msg, ex);
}
}
return repositorySelector.getLoggerRepository();
}
LogManager的static代码块
static {
// 由该代码可知,LoggerRepository默认为Hierarchy
Hierarchy h = new Hierarchy(new RootLogger((Level) Level.DEBUG));
// repositorySelector的默认实现为DefaultRepositorySelector
repositorySelector = new DefaultRepositorySelector(h);
...
}
7)LoggerRepository.getLogger(String name)
默认的LoggerRepository可知是由Hierarchy实现的,故直接定位到Hierarchy.getLogger(String name)方法
public Logger getLogger(String name) {
return getLogger(name, defaultFactory);
}
public Logger getLogger(String name, LoggerFactory factory) {
// 1.根据name封装一层对象,name相同即CategoryKey相同
CategoryKey key = new CategoryKey(name);
// Synchronize to prevent write conflicts. Read conflicts (in
// getChainedLevel method) are possible only if variable
// assignments are non-atomic.
Logger logger;
synchronized(ht) {
Object o = ht.get(key);
// 2.第一次创建时,为null,需新建
if(o == null) {
// 3.直接进行new Logger(name)操作
logger = factory.makeNewLoggerInstance(name);
// 设置Hierarchy,并放入HashTable,更新parent
logger.setHierarchy(this);
ht.put(key, logger);
updateParents(logger);
return logger;
...
}
}
}
由此可知,新创建的Logger由DefaultCategoryFactory创建,并封装一系列参数,并添加到HashTable中去
2.Logger.debug(Object message)源码分析
由上述分析可知,本例中的Logger默认实现为org.apache.log4j.Logger
1)logger.debug(Object message)
org.apache.log4j.Logger继承了Category,主要实现都在org.apache.log4j.Category中,直接定位到Category.debug(Object message)中
public void debug(Object message) {
if(repository.isDisabled(Level.DEBUG_INT))
return;
// 判断当前日志级别是否低于DEBUG,如果低于,则打印日志
if(Level.DEBUG.isGreaterOrEqual(this.getEffectiveLevel())) {
forcedLog(FQCN, Level.DEBUG, message, null);
}
}
protected void forcedLog(String fqcn, Priority level, Object message, Throwable t) {
callAppenders(new LoggingEvent(fqcn, this, level, message, t));
}
// 正在实现是这个方法
public void callAppenders(LoggingEvent event) {
int writes = 0;
// 1.依次遍历当前Logger到其父Logger,一直到null为止
for(Category c = this; c != null; c=c.parent) {
synchronized(c) {
if(c.aai != null) {
// 2.c.aai为AppenderAttachableImpl aai ,一个Appender的集合类
// 主要为了获取Appender集合后,依次执行doAppend方法
writes += c.aai.appendLoopOnAppenders(event);
}
if(!c.additive) {
break;
}
}
}
if(writes == 0) {
repository.emitNoAppenderWarning(this);
}
}
2)AppenderAttachableImpl.appendLoopOnAppenders(LoggingEvent event)
public int appendLoopOnAppenders(LoggingEvent event) {
int size = 0;
Appender appender;
if(appenderList != null) {
size = appenderList.size();
for(int i = 0; i < size; i++) {
// 依次遍历获取Appender,然后执行相应的doAppend方法
appender = (Appender) appenderList.elementAt(i);
appender.doAppend(event);
}
}
return size;
}
3)Appender.doAppend(LoggingEvent event)
由该例的log4j.xml可知,我们只定义了一个ConsoleAppender,故直接定位到ConsoleAppender.doAppend()方法
ConsoleAppender多层次继承,可知,真正实
现的是
AppenderSkeleton类
public synchronized void doAppend(LoggingEvent event) {
...
// 抽象方法,实现类为其父类,WriterAppender
this.append(event);
}
// WriterAppender默认实现
public void append(LoggingEvent event) {
if(!checkEntryConditions()) {
return;
}
subAppend(event);
}
protected void subAppend(LoggingEvent event) {
// 真正实现在这 QuietWriter qw ,默认为一个FilterWriter
// class QuietWriter extends FilterWriter
// 在输出之前进行了layout包装
this.qw.write(this.layout.format(event));
if(layout.ignoresThrowable()) {
String[] s = event.getThrowableStrRep();
if (s != null) {
int len = s.length;
for(int i = 0; i < len; i++) {
this.qw.write(s[i]);
this.qw.write(Layout.LINE_SEP);
}
}
}
if(shouldFlush(event)) {
this.qw.flush();
}
}
由上可知,最终是使用了Writer来进行写操作
3.遗留问题
通过代码分析,我们知道,所有的writer操作都是通过Appender进行的,Logger的Appender集合都存放在AppenderAttachableImpl中,那么AppenderAttachableImpl中的Appender集合是什么时候被放进去的呢?
读者可先自行分析
笔者将在下一篇博客中分析该问题。