java common log_Java日志体系(commons-logging)

Java日志系统学习

作为一名Java开发者,日志是我们工作中经常接触到的一项技术。对于Web应用而言,日志的重要性不言而喻,是必不可少的一部分;日志提供了丰富的记录功能,例如程序运行时的错误信息,描述信息,状态信息和执行时间信息等。

在实际生产环境中,日志是查找问题的重要来源,良好的日志格式和记录可以帮助Developer快速定位到错误的根源,找到问题的原因;

尽管对于现在的应用程序来说,日志至关重要,但是在JDK最初的版本当中并不包含日志记录的API和实现,直到JDK1.4后才被加入;因此,开源社区在此期间提供了众多贡献,其中名声最大、运用最广泛的当log4j莫属,当然后续的logback、log4j2也在迅速的普及;下面,就让笔者来进行具体的介绍。

1 commons-logging

1.1 简介

b818d9d26d39?from=timeline

Apache Commons Logging,又名JakartaCommons Logging (JCL),它是Apache提供的一个通用的日志接口,它的出现避免了和具体的日志方案直接耦合;在日常开发中,developer可以选择第三方日志组件进行搭配使用,例如log4j、logback等;

说的直白些,commons-logging提供了操作日志的接口,而具体实现交给log4j、logback这样的开源日志框架来完成;这样的方式,实现了程序的解耦,对于底层日志框架的改变,并不会影响到上层的业务代码。

1.2 commons-logging结构

b818d9d26d39?from=timeline

Log:日志对象接口,封装了操作日志的方法,定义了日志操作的5个级别:trace < debug < info < warn < error

LogFactory:抽象类,日志工厂,获取日志类;

LogFactoryImpl:LogFactory的实现类,真正获取日志对象的地方;

Log4JLogger:对log4j的日志对象封装;

Jdk14Logger:对JDK1.4的日志对象封装;

Jdk13LumberjackLogger:对JDK1.3以及以前版本的日志对象封装;

SimpleLog:commons-logging自带日志对象;

1.3 使用

commons-logging的使用非常简单。首先,需要在pom.xml文件中添加依赖:

commons-logging

commons-logging

1.1.3

声明测试代码:

public class commons_loggingDemo {

Log log= LogFactory.getLog(commons_loggingDemo.class);

@Test

public void test() throws IOException {

log.debug("Debug info.");

log.info("Info info");

log.warn("Warn info");

log.error("Error info");

log.fatal("Fatal info");

}

}

接下来,在classpath下定义配置文件:commons-logging.properties:

#指定日志对象:

org.apache.commons.logging.Log = org.apache.commons.logging.impl.Jdk14Logger

#指定日志工厂:

org.apache.commons.logging.LogFactory = org.apache.commons.logging.impl.LogFactoryImpl

在我们的项目中,如果只单纯的依赖了commons-logging,那么默认使用的日志对象就是Jdk14Logger,默认使用的日志工厂就是LogFactoryImpl

1.4 源码分析

public abstract class LogFactory {

public static final String HASHTABLE_IMPLEMENTATION_PROPERTY="org.apache.commons.logging.LogFactory.HashtableImpl";

private static final String WEAK_HASHTABLE_CLASSNAME = "org.apache.commons.logging.impl.WeakHashtable";

public static final String FACTORY_PROPERTIES = "commons-logging.properties";

public static final String FACTORY_PROPERTY = "org.apache.commons.logging.LogFactory";

public static final String FACTORY_DEFAULT = "org.apache.commons.logging.impl.LogFactoryImpl";

//LogFactory静态代码块:

static {

//获取LogFactory类加载器:AppClassLoader

thisClassLoader = getClassLoader(LogFactory.class);

String classLoaderName;

try {

ClassLoader classLoader = thisClassLoader;

if (thisClassLoader == null) {

classLoaderName = "BOOTLOADER";

} else {

//获取classLoader的名称:sun.misc.Launcher$AppClassLoader@150838093

classLoaderName = objectId(classLoader);

}

} catch (SecurityException e) {

classLoaderName = "UNKNOWN";

}

diagnosticPrefix = "[LogFactory from " + classLoaderName + "] ";

diagnosticsStream = initDiagnostics();

logClassLoaderEnvironment(LogFactory.class);

//创建存放日志的工厂缓存对象:实际为org.apache.commons.logging.impl.WeakHashtable

factories = createFactoryStore();

if (isDiagnosticsEnabled()) {

logDiagnostic("BOOTSTRAP COMPLETED");

}

}

//获取日志对象:

public static Log getLog(Class clazz) throws LogConfigurationException {

//得到LogFactoryImpl日志工厂后,实例化具体的日志对象:

return getFactory().getInstance(clazz);

}

//获取日志工厂

public static LogFactory getFactory() throws LogConfigurationException {

//获取当前线程的classCloader:

ClassLoader contextClassLoader = getContextClassLoaderInternal();

if (contextClassLoader == null) {

.....

}

//从缓存中获取LogFactory:此缓存就是刚才在静态代码块中创建的WeakHashtable

LogFactory factory = getCachedFactory(contextClassLoader);

//如果存在就返回:

if (factory != null) {

return factory;

}

if (isDiagnosticsEnabled()) {

......

}

//读取classpath下的commons-logging.properties文件:

Properties props = getConfigurationFile(contextClassLoader, FACTORY_PROPERTIES);

ClassLoader baseClassLoader = contextClassLoader;

if (props != null) {

//如果Properties对象不为空,从中获取 TCCL_KEY 的值:

String useTCCLStr = props.getProperty(TCCL_KEY);

if (useTCCLStr != null) {

if (Boolean.valueOf(useTCCLStr).booleanValue() == false) {

baseClassLoader = thisClassLoader;

}

}

}

.....

try {

/从系统属性中获取 FACTORY_PROPERTY 的值:

String factoryClass = getSystemProperty(FACTORY_PROPERTY, null);

if (factoryClass != null) {

//如果该值不为空,则实例化日志工厂对象:

factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);

} else {

.....

}

} catch (SecurityException e) {

.....

}

if (factory == null) {

if (isDiagnosticsEnabled()) {

....

}

try {

//如果日志工厂对象还为null,则从 META-INF/services/org.apache.commons.logging.LogFactory 中获取:

final InputStream is = getResourceAsStream(contextClassLoader, SERVICE_ID);

if( is != null ) {

.....

}

} catch (Exception ex) {

......

}

}

if (factory == null) {

if (props != null) {

//如果此时日志工厂为null,并props有值,则获取 FACTORY_PROPERTY 为key的值:

String factoryClass = props.getProperty(FACTORY_PROPERTY);

if (factoryClass != null) {

if (isDiagnosticsEnabled()) {

logDiagnostic(

"[LOOKUP] Properties file specifies LogFactory subclass '" + factoryClass + "'");

}

//如果我们配置了,则实例化我们配置的日志工厂对象

factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);

} else {

.....

}

} else {

......

}

}

if (factory == null) {

if (isDiagnosticsEnabled()) {

.....

}

//如果此时日志工厂依旧为null,则实例化默认工厂:org.apache.commons.logging.impl.LogFactoryImpl

factory = newFactory(FACTORY_DEFAULT, thisClassLoader, contextClassLoader);

}

if (factory != null) {

// 将日志工厂添加到缓存当中:

cacheFactory(contextClassLoader, factory);

if (props != null) {

Enumeration names = props.propertyNames();

while (names.hasMoreElements()) {

String name = (String) names.nextElement();

String value = props.getProperty(name);

factory.setAttribute(name, value);

}

}

}

return factory;

}

//创建存放日志的工厂缓存对象:

private static final Hashtable createFactoryStore() {

Hashtable result = null;

String storeImplementationClass;

try {

//从系统属性中获取 HASHTABLE_IMPLEMENTATION_PROPERTY 为key的值:

storeImplementationClass = getSystemProperty(HASHTABLE_IMPLEMENTATION_PROPERTY, null);

} catch (SecurityException ex) {

storeImplementationClass = null;

}

//如果 storeImplementationClass 为null

if (storeImplementationClass == null) {

//将 WEAK_HASHTABLE_CLASSNAME 赋值给storeImplementationClass字符串

storeImplementationClass = WEAK_HASHTABLE_CLASSNAME;

}

try {

//反射实例化缓存对象:org.apache.commons.logging.impl.WeakHashtable

Class implementationClass = Class.forName(storeImplementationClass);

result = (Hashtable) implementationClass.newInstance();

} catch (Throwable t) {

.....

}

if (result == null) {

result = new Hashtable();

}

return result;

}

}

public class LogFactoryImpl extends LogFactory {

protected Hashtable instances = new Hashtable();

protected Constructor logConstructor = null;

public Log getInstance(Class clazz) throws LogConfigurationException {

return getInstance(clazz.getName());

}

public Log getInstance(String name) throws LogConfigurationException {

//从缓存中获取日志对象:

Log instance = (Log) instances.get(name);

if (instance == null) {

//创建日志对象:

instance = newInstance(name);

instances.put(name, instance);

}

return instance;

}

protected Log newInstance(String name) throws LogConfigurationException {

Log instance;

try {

//日志构造器对象为null:

if (logConstructor == null) {

instance = discoverLogImplementation(name);

}else {

Object params[] = { name };

instance = (Log) logConstructor.newInstance(params);

}

if (logMethod != null) {

Object params[] = { this };

logMethod.invoke(instance, params);

}

return instance;

} catch (LogConfigurationException lce) {

....

}

}

//发现日志实现类:

private Log discoverLogImplementation(String logCategory) throws LogConfigurationException {

if (isDiagnosticsEnabled()) {

logDiagnostic("Discovering a Log implementation...");

}

initConfiguration();

Log result = null;

//从classpath中的commons-logging.properties文件中获取“org.apache.commons.logging.Log”的value:

String specifiedLogClassName = findUserSpecifiedLogClassName();

//如果配置文件中存在,那么就进行日志对象实例化:

if (specifiedLogClassName != null) {

if (isDiagnosticsEnabled()) {

logDiagnostic("Attempting to load user-specified log class '" + specifiedLogClassName + "'...");

}

//配置的日志对象必须存在,否则报错:

result = createLogFromClass(specifiedLogClassName, logCategory, true);

if (result == null) {

.......

}

return result;

}

if (isDiagnosticsEnabled()) {

....

}

/*

*如果配置文件中不存在“org.apache.commons.logging.Log”的value:

*那么还有遍历classesToDiscover字符串数组,进行实例化:

*此数组中包括:log4j、Jdk14Logger、Jdk13LumberjackLogger、SimpleLog

*/

for(int i=0; i

result = createLogFromClass(classesToDiscover[i], logCategory, true);

}

//如果此时日志对象还未null,为报错;

if (result == null) {

throw new LogConfigurationException("No suitable Log implementation");

}

//返回日志对象:

return result;

}

}

以上就是commons-logging获取日志对象的全过程,具体文字总结如下:

获取当前线程的classLoader,根据classLoader从缓存中获取LogFactroy,使用的缓存是WeakHashTable对象;如果缓存中存在,则返回,没有则进入下面流程;

读取classpath下的commons-logging.properties文件,判断其中是否设置了use_tccl属性,如果不为空则判断,该属性的值是否为false,若为false,则将baseClassLoader替换为当前类的classLoader;

接着,继续获取LogFactory对象,此步骤分为4中方式:

(1)在系统属性中查找“org.apache.commons.logging.LogFactory”属性的值,根据值生成LogFactory对象;

(2)通过资源“META-INF/services/org.apache.commons.logging.LogFactory”文件,获取的值生成LogFactory对象;

(3)通过配置文件commons-logging.properties,获取以“org.apache.commons.logging.LogFactory”为key的值,根据值生成logFactory;

(4)如果以上均不成功,则创建系统默认的日志工厂:org.apache.commons.logging.impl.LogFactoryImpl

成功获取日志工厂后,根据类名获取日志对象;

主要逻辑在discoverLogImplementation方法中:

(1)检查commons-logging.properties文件中是否存在“org.apache.commons.logging.Log”属性,若存在则创建具体的日志对象;若不存在,进行下面逻辑;

(2)遍历classesToDiscover数组,该数组存有日志具体实现类的全限定类名:org.apache.commons.logging.impl.Log4JLogger、org.apache.commons.logging.impl.Jdk14Logger、org.apache.commons.logging.impl.Jdk13LumberjackLogger、org.apache.commons.logging.impl.SimpleLog;

(3)根据数组中存着的全限定类名,按照顺序依次加载Class文件,进行实例化操作,最后返回Log实例,默认为Jdk14Logger;

其中,获取日志工厂的过程,诟病最多。究其原因,主要是commons-logging在获取日志工厂的过程中使用了classLoader来寻找日志工厂实现,进而导致了其他组件,如若使用自己的classload,则不能获取具体的日志工厂对象,则导致启动失败,这样就是我们常说的--动态查找机制。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Warning: No configuration directory set! Use --conf <dir> to override. Info: Including Hadoop libraries found via (/opt/hadoop-3.1.2/bin/hadoop) for HDFS access Info: Including HBASE libraries found via (/opt/hbase-2.2.6/bin/hbase) for HBASE access 错误: 找不到或无法加载主类 org.apache.flume.tools.GetJavaProperty Info: Including Hive libraries found via (/opt/hive-3.1.2) for Hive access + exec /opt/jdk1.8.0_351/bin/java -Xmx20m -cp '/opt/flume-1.9.0/lib/*:/opt/hadoop-3.1.2/etc/hadoop:/opt/hadoop-3.1.2/share/hadoop/common/lib/*:/opt/hadoop-3.1.2/share/hadoop/common/*:/opt/hadoop-3.1.2/share/hadoop/hdfs:/opt/hadoop-3.1.2/share/hadoop/hdfs/lib/*:/opt/hadoop-3.1.2/share/hadoop/hdfs/*:/opt/hadoop-3.1.2/share/hadoop/mapreduce/lib/*:/opt/hadoop-3.1.2/share/hadoop/mapreduce/*:/opt/hadoop-3.1.2/share/hadoop/yarn:/opt/hadoop-3.1.2/share/hadoop/yarn/lib/*:/opt/hadoop-3.1.2/share/hadoop/yarn/*:/opt/hbase-2.2.6/conf:/opt/jdk1.8.0_351//lib/tools.jar:/opt/hbase-2.2.6:/opt/hbase-2.2.6/lib/shaded-clients/hbase-shaded-client-byo-hadoop-2.2.6.jar:/opt/hbase-2.2.6/lib/client-facing-thirdparty/audience-annotations-0.5.0.jar:/opt/hbase-2.2.6/lib/client-facing-thirdparty/commons-logging-1.2.jar:/opt/hbase-2.2.6/lib/client-facing-thirdparty/findbugs-annotations-1.3.9-1.jar:/opt/hbase-2.2.6/lib/client-facing-thirdparty/htrace-core4-4.2.0-incubating.jar:/opt/hbase-2.2.6/lib/client-facing-thirdparty/log4j-1.2.17.jar:/opt/hbase-2.2.6/lib/client-facing-thirdparty/slf4j-api-1.7.25.jar:/opt/hadoop-3.1.2/etc/hadoop:/opt/hadoop-3.1.2/share/hadoop/common/lib/*:/opt/hadoop-3.1.2/share/hadoop/common/*:/opt/hadoop-3.1.2/share/hadoop/hdfs:/opt/hadoop-3.1.2/share/hadoop/hdfs/lib/*:/opt/hadoop-3.1.2/share/hadoop/hdfs/*:/opt/hadoop-3.1.2/share/hadoop/mapreduce/lib/*:/opt/hadoop-3.1.2/share/hadoop/mapreduce/*:/opt/hadoop-3.1.2/share/hadoop/yarn:/opt/hadoop-3.1.2/share/hadoop/yarn/lib/*:/opt/hadoop-3.1.2/share/hadoop/yarn/*:/opt/hadoop-3.1.2/etc/hadoop:/opt/hbase-2.2.6/conf:/opt/hive-3.1.2/lib/*' -Djava.library.path=:/opt/hadoop-3.1.2/lib/native org.apache.flume.node.Application --name a1 --conf/opt/flume-1.9.0/conf --conf-file/opt/flume-1.9.0/conf/dhfsspool.conf-Dflume.root.logger=DEBUG,consol SLF4J: Class path contains multiple SLF4J bindings. SLF4J: Found binding in [jar:file:/opt/flume-1.9.0/lib/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: Found binding in [jar:file:/opt/hadoop-3.1.2/share/hadoop/common/lib/slf4j-log4j12-1.7.25.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: Found binding in [jar:file:/opt/hive-3.1.2/lib/log4j-slf4j-impl-2.10.0.jar!/org/slf4j/impl/StaticLoggerBinder.class] SLF4J: See http://www.slf4j.org/codes.html#multiple_bindings for an explanation. SLF4J: Actual binding is of type [org.slf4j.impl.Log4jLoggerFactory] 2023-06-08 17:26:46,403 ERROR node.Application: A fatal error occurred while running. Exception follows. org.apache.commons.cli.UnrecognizedOptionException: Unrecognized option: --conf/opt/flume-1.9.0/conf at org.apache.commons.cli.Parser.processOption(Parser.java:363) at org.apache.commons.cli.Parser.parse(Parser.java:199) at org.apache.commons.cli.Parser.parse(Parser.java:85) at org.apache.flume.node.Application.main(Application.java:287)
最新发布
06-09

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值