在项目中对于日志的使用,应该为slf4j+(log4j、logback等)。
应用中不可直接使用日志系统(Log4j、Logback)中的 API,而应依赖使用日志框架
SLF4J 中的 API,使用门面模式的日志框架,有利于维护和各个类的日志处理方式统一。
slf4j只是一个日志标准,为各个具体的日志框架提供统一接口。官网文档中的解释:
假设系统引入了zookeeper、dubbo而这两个jar一个使用log4j一个使用logback,我们的系统又使用另一个log框架,此时就带来了很大的维护成本。所以对于一个健壮、扩展性强的系统而言,最好的方法就是引入一个门面角色,业务系统也就是客户端角色只调用门面角色的相关日志接口,而具体的业务由facade交给真正的子系统。
org.slf4j
slf4j-api
xxx
使用log4j、logback(选一个)
log4j
org.apache.logging.log4j
log4j-api
xxx
org.apache.logging.log4j
log4j-core
xxx
org.apache.logging.log4j
log4j-slf4j-impl
xxx
logback
ch.qos.logback
logback-core
xxx
ch.qos.logback
logback-classic
xxx
2、配置文件
log4j2:spring boot默认会加载log4j2-spring.xml(官方推荐)、log4j2.xml两种命名的配置文件。
配置文件:
logback:logback-spring.xml(官方推荐)、logback.xml。
配置文件:
/home/../logs/system.log
../logs/${application.name}/${application.name}.%d{yyyy-MM-dd}.log
%-20(%d{HH:mm:ss} [%thread]) %-5level %logger{80} - %msg%n
%n]]>
3、日志打印使用方法
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(Abc.class);
调用以上接口获取Logger对象,使用该对象进行日志的打印
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
private static final Logger logger = LoggerFactory.getLogger(Abc.class);
LoggerFactory.getLogger(Abc.class)的源码如下:
public static Logger getLogger(Class clazz) {
// 再调用getLogger(String)方法
Logger logger = getLogger(clazz.getName());
if (DETECT_LOGGER_NAME_MISMATCH) {
Class autoComputedCallingClass = Util.getCallingClass();
if (autoComputedCallingClass != null && nonMatchingClasses(clazz, autoComputedCallingClass)) {
Util.report(String.format("Detected logger name mismatch. Given name: \"%s\"; computed name: \"%s\".", logger.getName(),
autoComputedCallingClass.getName()));
Util.report("See " + LOGGER_NAME_MISMATCH_URL + " for an explanation");
}
}
return logger;
}
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
getILoggerFactory方法
a、未进行初始化时,调用performInitialization()方法
public static ILoggerFactory getILoggerFactory() {
if (INITIALIZATION_STATE == UNINITIALIZED) {
synchronized (LoggerFactory.class) {
if (INITIALIZATION_STATE == UNINITIALIZED) {
INITIALIZATION_STATE = ONGOING_INITIALIZATION;
performInitialization();
}
}
}
switch (INITIALIZATION_STATE) {
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");
}
最后会调用bind()方法
private final static void bind() {
try {
Set staticLoggerBinderPathSet = null;
// skip check under android, see also
// http://jira.qos.ch/browse/SLF4J-328
if (!isAndroid()) {
// 关键是此findPossibleStaticLoggerBinderPathSet方法
staticLoggerBinderPathSet = findPossibleStaticLoggerBinderPathSet();
reportMultipleBindingAmbiguity(staticLoggerBinderPathSet);
}
// the next line does the binding
StaticLoggerBinder.getSingleton();
INITIALIZATION_STATE = SUCCESSFUL_INITIALIZATION;
reportActualBinding(staticLoggerBinderPathSet);
fixSubstituteLoggers();
replayEvents();
// release all resources in SUBST_FACTORY
SUBST_FACTORY.clear();
} catch (NoClassDefFoundError ncde) {
String msg = ncde.getMessage();
if (messageContainsOrgSlf4jImplStaticLoggerBinder(msg)) {
INITIALIZATION_STATE = NOP_FALLBACK_INITIALIZATION;
Util.report("Failed to load class \"org.slf4j.impl.StaticLoggerBinder\".");
Util.report("Defaulting to no-operation (NOP) logger implementation");
Util.report("See " + NO_STATICLOGGERBINDER_URL + " for further details.");
} else {
failedBinding(ncde);
throw ncde;
}
} catch (java.lang.NoSuchMethodError nsme) {
String msg = nsme.getMessage();
if (msg != null && msg.contains("org.slf4j.impl.StaticLoggerBinder.getSingleton()")) {
INITIALIZATION_STATE = FAILED_INITIALIZATION;
Util.report("slf4j-api 1.6.x (or later) is incompatible with this binding.");
Util.report("Your binding is version 1.5.5 or earlier.");
Util.report("Upgrade your binding to version 1.6.x.");
}
throw nsme;
} catch (Exception e) {
failedBinding(e);
throw new IllegalStateException("Unexpected initialization failure", e);
}
}
findPossibleStaticLoggerBinderPathSet方法重点是通过STATIC_LOGGER_BINDER_PATH加载资源,也就是在logback、log4j2或者其他框架中必须存在该接口
STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class"
static Set findPossibleStaticLoggerBinderPathSet() {
// use Set instead of list in order to deal with bug #138
// LinkedHashSet appropriate here because it preserves insertion order
// during iteration
Set staticLoggerBinderPathSet = new LinkedHashSet();
try {
ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader();
Enumeration paths;
if (loggerFactoryClassLoader == null) {
paths = ClassLoader.getSystemResources(STATIC_LOGGER_BINDER_PATH);
} else {
paths = loggerFactoryClassLoader.getResources(STATIC_LOGGER_BINDER_PATH);
}
while (paths.hasMoreElements()) {
URL path = paths.nextElement();
staticLoggerBinderPathSet.add(path);
}
} catch (IOException ioe) {
Util.report("Error getting resources from path", ioe);
}
return staticLoggerBinderPathSet;
}
再来看接下来的StaticLoggerBinder.getSingleton()调用。
虚拟机类加载对初始化的要求之一:当调用静态方法时如果类未进行过初始化则触发初始化。初始化时调用< clinit>(若存在则调用)方法,静态字段赋值、执行静态代码块。以logback的实现为例:
执行顺序为1-》2》3,说明在调用getSingleton()方法之前已经完成了一系列配置加载等操作
1
private static StaticLoggerBinder SINGLETON = new StaticLoggerBinder();
static {
3
SINGLETON.init();
}
2
private StaticLoggerBinder() {
defaultLoggerContext.setName(CoreConstants.DEFAULT_CONTEXT_NAME);
}
public static StaticLoggerBinder getSingleton() {
return SINGLETON;
}
b、当初始化成功后,则执行getILoggerFactory的StaticLoggerBinder.getSingleton().getLoggerFactory();
方法获取ILoggerFactory的实现