自己动手写一个可以让slf4j加载的日志系统
问题:
(一)slf4j采用什么设计模式实现的?
(二)slf4j是如何加载log4j或logback日志实现框架的?
(三)如果自己实现一个日志系统,如何让sfl4j能够加载该日志系统?
一段典型slf4j日志实例化代码:
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogMain {
public static void main(String[] args) {
// TODO Auto-generated method stub
Logger xLogger = LoggerFactory.getLogger(LogMain.class);
xLogger.debug("hello");
}
}
寻根问底,看看slf4j的Logger代码:
public interface Logger {
/**
* Case insensitive String constant used to retrieve the name of the root logger.
*
* @since 1.3
*/
final public String ROOT_LOGGER_NAME = "ROOT";
/**
* Return the name of this <code>Logger</code> instance.
* @return name of this logger instance
*/
public String getName();
/**
* Is the logger instance enabled for the TRACE level?
*
* @return True if this Logger is enabled for the TRACE level,
* false otherwise.
* @since 1.4
*/
public boolean isTraceEnabled();
/**
* Log a message at the TRACE level.
*
* @param msg the message string to be logged
* @since 1.4
*/
public void trace(String msg);
//省略若干代码
}
查看LoggerFactory代码:
public final class LoggerFactory {
private LoggerFactory() {
}
/**
* Force LoggerFactory to consider itself uninitialized.
* <p/>
* <p/>
* This method is intended to be called by classes (in the same package) for
* testing purposes. This method is internal. It can be modified, renamed or
* removed at any time without notice.
* <p/>
* <p/>
* You are strongly discouraged from calling this method in production code.
*/
static void reset() {
INITIALIZATION_STATE = UNINITIALIZED;
}
private final static void performInitialization() {
bind();
if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) {
versionSanityCheck();
}
}
private final static void bind() {
try {
Set<URL> staticLoggerBinderPathSet = null;
// skip check under android, see also
if (!isAndroid()) {
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);
}
}
/**
* Return a logger named according to the name parameter using the
* statically bound {@link ILoggerFactory} instance.
*
* @param name
* The name of the logger.
* @return logger
*/
public static Logger getLogger(String name) {
ILoggerFactory iLoggerFactory = getILoggerFactory();
return iLoggerFactory.getLogger(name);
}
/**
* Return a logger named corresponding to the class passed as parameter,
* using the statically bound {@link ILoggerFactory} instance.
*
* <p>
* In case the the <code>clazz</code> parameter differs from the name of the
* caller as computed internally by SLF4J, a logger name mismatch warning
* will be printed but only if the
* <code>slf4j.detectLoggerNameMismatch</code> system property is set to
* true. By default, this property is not set and no warnings will be
* printed even in case of a logger name mismatch.
*
* @param clazz
* the returned logger will be named after clazz
* @return logger
*
*
* @see <a
* href="http://www.slf4j.org/codes.html#loggerNameMismatch">Detected
* logger name mismatch</a>
*/
public static Logger getLogger(Class<?> clazz) {
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;
}
/**
* Return the {@link ILoggerFactory} instance in use.
* <p/>
* <p/>
* ILoggerFactory instance is bound with this class at compile time.
*
* @return the ILoggerFactory instance in use
*/
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");
}
}
slf4j实例化流程图:
代码核心:StaticLoggerBinder
结论:要让slf4j加载自己实现的日志系统,我们自己的日志系统需要实现StaticLoggerBinder的getLoggerFactory接口。
开始实现我们自己的日志系统:
-step1 新建一个工程xfli.fastlog
-step2 新建一个包org.slf4j.impl
-step3 在org.slf4j.impl新建StaticLoggerBinder类,该类实现org.slf4j.spi.LoggerFactoryBinder接口
-step4 实现getLoggerFactory接口,返回LoggerContext
-step5 新建LoggerContext类,该类实现org.slf4j.ILoggerFactory接口
-step6 实现getLogger接口,返回我们自己实现的日志类实例即可
-step7 写一个main,测试我们的类是否能工作
-step8 打完收工
工程xfli.fastlog需要引入slf4j的maven包,参考:
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.7</version>
<scope>compile</scope>
</dependency>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-ext</artifactId>
<version>1.7.7</version>
<scope>test</scope>
</dependency>
一个示例,请移步GitHub:https://github.com/cjjxfli/xfli.fastlog