slf4j绑定log4j2日志系统的过程(源码分析)
来源:互联网 发布:捕鱼刷分软件 编辑:程序博客网 时间:2024/05/16 04:58
一、环境及介绍
slf4j和log4j2的介绍在文章http://blog.csdn.net/honghailiang888/article/details/52681777进行过介绍,源码分析版本log4j-api-2.2.jar、log4j-core-2.2.jar、log4j-slf4j-impl-2.2.jar、slf4j-api-1.7.21.jar
二、从使用入手
/** * @author HHL * * @date 2016年9月27日 * * 日志工具类类,采用slf4j方式 */public class LogUtil {// test日志public static Logger testLog = LoggerFactory.getLogger("test_logger");public static boolean isPrintStackTrace = true;// 记录test错误日志信息public static void testLogError(String errorMessage, Exception ex) {if (testLog != null) {testLog.error(errorMessage);}if (isPrintStackTrace && ex != null && testLog != null) {testLog.error(ex.getMessage(), ex);}}public static void printInfoLog(Class<?> cla, String message) {LoggerFactory.getLogger(cla).info(message);}}
配置文件内容log4j2.xml
<?xml version="1.0" encoding="UTF-8"?><Configuration status="WARN"> <Appenders> <Console name="Console" target="SYSTEM_OUT"> <PatternLayout pattern="%d{YYYY-MM-dd HH:mm:ss.SSS} [%t] %-5level %l %msg%n"/> </Console> <RollingFile name="test_logger" filename="${sys:catalina.home}/logs/test_logger.log" filepattern="${sys:catalina.home}/logs/test_logger.%d{yyyy-MM-dd-HH}.log"> <PatternLayout pattern="%d{YYYY-MM-dd HH:mm:ss.SSS} %5p [%t] %c(%F:%L) - %msg%n" /> <Policies> <!-- 文件大小配置 --> <SizeBasedTriggeringPolicy size="100 KB"/> </Policies> <!-- 共存log文件数量 --> <DefaultRolloverStrategy max="20"/> </RollingFile> </Appenders> <Loggers> <Root level="info"> <AppenderRef ref="Console"/> </Root> <!-- springMVC log配置 --> <Logger name="org.springframework.web" level="info" additivity="false"> <AppenderRef ref="Console"/> </Logger> <!-- mybatis log配置 --> <Logger name="com.mango.mapper" level="trace" additivity="false"> <AppenderRef ref="Console"/> </Logger> <!-- log文件配置 --> <Logger name="test_logger" level="info" additivity="true"> <AppenderRef ref="test_logger"/> </Logger> </Loggers></Configuration>
三、源码分析
1.从第一句入手
public static Logger testLog = LoggerFactory.getLogger("test_logger");
/** * 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 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"); }
其中初次bind的日志系统就在上述performInitialization()中
private final static void performInitialization() { bind(); if (INITIALIZATION_STATE == SUCCESSFUL_INITIALIZATION) { versionSanityCheck(); } }
直接看bind()函数:
private final static void bind() { try { Set<URL> staticLoggerBinderPathSet = null; // skip check under android, see also // http://jira.qos.ch/browse/SLF4J-328 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); } }
findPossibleStaticLoggerBinderPathSet()发现可能的binder路径,从类路径中寻找org/slf4j/impl/StaticLoggerBinder.class
类:LoggerFactory.java
// We need to use the name of the StaticLoggerBinder class, but we can't // reference // the class itself. private static String STATIC_LOGGER_BINDER_PATH = "org/slf4j/impl/StaticLoggerBinder.class"; static Set<URL> findPossibleStaticLoggerBinderPathSet() { // use Set instead of list in order to deal with bug #138 // LinkedHashSet appropriate here because it preserves insertion order // during iteration Set<URL> staticLoggerBinderPathSet = new LinkedHashSet<URL>(); try { ClassLoader loggerFactoryClassLoader = LoggerFactory.class.getClassLoader(); Enumeration<URL> 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; }
其中获取到的path为jar:file:/D:/software/Server/Tomcat/apache-tomcat-7.0.30/webapps/ETeam/WEB-INF/lib/log4j-slf4j-impl-2.2.jar!/org/slf4j/impl/StaticLoggerBinder.class,如果为多个,则会随机选取一个bind,但这里只有一个,因此只会选择一个。
总结,其实这里获取StaticLoggerBinder类的路径只是用来记录用,使用哪一个在编译时期就已经决定了(log4j-slf4j-impl-2.2.jar包起了桥接的作用)。这个时候就和log4j2关联上了。获得的loggerFactory就是
/** * Private constructor to prevent instantiation */ private StaticLoggerBinder() { loggerFactory = new Log4jLoggerFactory(); }
2、看Log4jLoggerFactory.java中的getLogger是继承的AbstractLoggerAdapter类中的
@Override public L getLogger(final String name) { final LoggerContext context = getContext(); //获取logger环境 final ConcurrentMap<String, L> loggers = getLoggersInContext(context); if (loggers.containsKey(name)) { return loggers.get(name); } loggers.putIfAbsent(name, newLogger(name, context)); return loggers.get(name); 根据名字获取logger }
看getContext()干了什么
@Override protected LoggerContext getContext() { final Class<?> anchor = ReflectionUtil.getCallerClass(FQCN, PACKAGE); 获取调用类 return anchor == null ? LogManager.getContext() : getContext(ReflectionUtil.getCallerClass(anchor)); }
最终调用到了LogManager.java中,彻底进入log4j2
/** * Gets the {@link LoggerContext} associated with the given caller class. * * @param callerClass the caller class * @return the LoggerContext for the calling class */ protected LoggerContext getContext(final Class<?> callerClass) { ClassLoader cl = null; if (callerClass != null) { cl = callerClass.getClassLoader(); } if (cl == null) { cl = LoaderUtil.getThreadContextClassLoader(); } return LogManager.getContext(cl, false); }
public static LoggerContext getContext(final ClassLoader loader, final boolean currentContext) { return factory.getContext(FQCN, loader, null, currentContext); }
Log4jContextFactory.java
/** * Loads the LoggerContext using the ContextSelector. * @param fqcn The fully qualified class name of the caller. * @param loader The ClassLoader to use or null. * @param currentContext If true returns the current Context, if false returns the Context appropriate * for the caller if a more appropriate Context can be determined. * @param externalContext An external context (such as a ServletContext) to be associated with the LoggerContext. * @return The LoggerContext. */ @Override public LoggerContext getContext(final String fqcn, final ClassLoader loader, final Object externalContext, final boolean currentContext) { final LoggerContext ctx = selector.getContext(fqcn, loader, currentContext); if (externalContext != null && ctx.getExternalContext() == null) { ctx.setExternalContext(externalContext); } if (ctx.getState() == LifeCycle.State.INITIALIZED) { ctx.start(); //初始获取,会解析配置文件log4j2.xml } return ctx; }
LoggerContext.java
@Override public void start() { LOGGER.debug("Starting LoggerContext[name={}, {}]...", getName(), this); if (configLock.tryLock()) { try { if (this.isInitialized() || this.isStopped()) { this.setStarting(); reconfigure(); //解析配置文件 if (this.config.isShutdownHookEnabled()) { setUpShutdownHook(); } this.setStarted(); } } finally { configLock.unlock(); } } LOGGER.debug("LoggerContext[name={}, {}] started OK.", getName(), this); }
/** * Reconfigure the context. */ public synchronized void reconfigure() { final ClassLoader cl = ClassLoader.class.isInstance(externalContext) ? (ClassLoader) externalContext : null; LOGGER.debug("Reconfiguration started for context[name={}] at {} ({}) with optional ClassLoader: {}", name, configLocation, this, cl); final Configuration instance = ConfigurationFactory.getInstance().getConfiguration(name, configLocation, cl); setConfiguration(instance); /* * instance.start(); Configuration old = setConfiguration(instance); * updateLoggers(); if (old != null) { old.stop(); } */ LOGGER.debug("Reconfiguration complete for context[name={}] at {} ({}) with optional ClassLoader: {}", name, configLocation, this, cl); }
直接看最终的
private Configuration getConfiguration(final boolean isTest, final String name) { final boolean named = name != null && name.length() > 0; final ClassLoader loader = LoaderUtil.getThreadContextClassLoader(); for (final ConfigurationFactory factory : factories) { String configName; final String prefix = isTest ? TEST_PREFIX : DEFAULT_PREFIX; final String [] types = factory.getSupportedTypes(); if (types == null) { continue; } for (final String suffix : types) { if (suffix.equals("*")) { continue; } configName = named ? prefix + name + suffix : prefix + suffix; final ConfigurationSource source = getInputFromResource(configName, loader); if (source != null) { return factory.getConfiguration(source); } } } return null; }
</pre><pre class="java" name="code" code_snippet_id="1933544" snippet_file_name="blog_20161017_19_6401194">/** * File name prefix for test configurations. */ protected static final String TEST_PREFIX = "log4j2-test";
/** * File name prefix for standard configurations. */ protected static final String DEFAULT_PREFIX = "log4j2";
slf4j和log4j2的结合就此出现,log输出也是用的log4j2的。
/** * The default configuration writes all output to the Console using the default logging level. You configure default * logging level by setting the system property "org.apache.logging.log4j.level" to a level name. If you do not * specify the property, Log4j uses the ERROR Level. Log Events will be printed using the basic formatting provided * by each Message. */public class DefaultConfiguration extends AbstractConfiguration { private static final long serialVersionUID = 1L; /** * The name of the default configuration. */ public static final String DEFAULT_NAME = "Default"; /** * The System Property used to specify the logging level. */ public static final String DEFAULT_LEVEL = "org.apache.logging.log4j.level"; /** * The default Pattern used for the default Layout. */ public static final String DEFAULT_PATTERN = "%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n"; /** * Constructor to create the default configuration. */ public DefaultConfiguration() { super(ConfigurationSource.NULL_SOURCE); setName(DEFAULT_NAME); final Layout<? extends Serializable> layout = PatternLayout.newBuilder() .withPattern(DEFAULT_PATTERN) .withConfiguration(this) .build(); final Appender appender = ConsoleAppender.createDefaultAppenderForLayout(layout); appender.start(); addAppender(appender); final LoggerConfig root = getRootLogger(); root.addAppender(appender, null, null); final String levelName = PropertiesUtil.getProperties().getStringProperty(DEFAULT_LEVEL); final Level level = levelName != null && Level.valueOf(levelName) != null ? Level.valueOf(levelName) : Level.ERROR; root.setLevel(level); } @Override protected void doConfigure() { }}
- slf4j绑定log4j2日志系统的过程(源码分析)
- 日志学习:SLF4J & Log4J2
- 日志---slf4j和log4j2
- slf4j +log4j2 日志 配置
- slf4j初始化绑定源码分析
- slf4j初始化绑定源码分析
- 使用Slf4j集成Log4j2构建项目日志系统的完美解决方案
- 使用Slf4j集成Log4j2构建项目日志系统的完美解决方案
- 使用Slf4j集成Log4j2构建项目日志系统的完美解决方案
- 使用Slf4j集成Log4j2构建项目日志系统的完美解决方案
- 使用Slf4j集成Log4j2构建项目日志系统解决方案
- 使用slf4j + Log4j2构建日志
- 项目中实际使用的日志配置log4j2 + slf4j
- java的日志系统-slf4j
- Log4j,Log4j2,logback,slf4j日志学习
- Log4j,Log4j2,logback,slf4j日志学习
- spring4 + log4j2+ slf4j的配置
- slf4j+log4j2的整合配置
- JS中的valueOf方法
- 技术博客网站推荐
- JavaScript中的forEach、$.each、map方法
- 对PC某串口接收的外部数据进行处理操作的简单C程序 VC++6.0
- Gazebo機器人仿真學習探索筆記(三)機器人模型
- slf4j绑定log4j2日志系统的过程(源码分析)
- 求出轮廓的周长进行筛选并绘制轮廓
- Netty通信
- 编写一个Java程序将当100,101,102,103,104,105个数以数组的形式写入到Dest.txt文件中,并以相反的顺序读出显示在屏幕上
- 指向非静态成员函数的3种方式
- windows下的网络配置
- 微信端开发问题收集
- 斐波那契数列的齐肯多夫定理
- phpStorm 添加php调试工具 Xdebug