java 日志门面_Java门面日志commons-logging

1 简介

Apache Commons Logging,又名JakartaCommons Logging (JCL),它是Apache提供的一个通用的日志接口,也被称为“门面日志”。“门面日志”利用了设计模式中的门面模式思想,对外提供一套通用的日志记录的 API,而不提供具体的日志输出服务,如果要实现日志输出,需要集成其他的日志框架,比如 Log4j、Logback、Log4j2 等。

这种门面模式的好处在于,记录日志的 API 和日志输出的服务分离开,代码里面只需要关注记录日志的 API,通过 SLF4J 指定的接口记录日志;而日志输出通过引入 JAR 包的方式即可指定其他的日志框架。当我们需要改变系统的日志输出服务时,不用修改代码,只需要改变引入日志输出框架 JAR 包。

JUL最后更新于2014年7月,现以停止更新。

54856f628449

image

2 包结构

54856f628449

image

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

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

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

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

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

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

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

3 日志加载

jcl可以通过在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

jcl如果没有指定日志实现框架则默认加载时按照顺序log4j>jul> simpleLog依次加载,如果查找到log4j包则使用log4j,如果没有依次类推。

4 JCL使用

引用JCL依赖包

commons-logging

commons-logging

1.2

测试代码

此时由于项目也没有放入其他日志框架依赖包,会按照顺序log4j>jul> simpleLog加载,因此此时会使用jdk自带jul实现日志打印。

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");

}

}

引用log4j依赖包

在不修改代码的前提下,引入log4j依赖包。在次调用测试代码,此时会使用log4j实现日志打印。

log4j

log4j

1.2.16

5 JCL原理分析

5.1 LogFactory 类初始化

protected static Hashtable factories = null;

private static PrintStream diagnosticsStream = null;

static {

/** 获取LogFactory类加载器:AppClassLoader设置到thisClassLoader **/

thisClassLoader = getClassLoader(LogFactory.class);

String classLoaderName;

try {

ClassLoader classLoader = thisClassLoader;

if (thisClassLoader == null) {

classLoaderName = "BOOTLOADER";

} else {

classLoaderName = objectId(classLoader);

}

} catch (SecurityException e) {

classLoaderName = "UNKNOWN";

}

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

/** 初始化diagnosticsStream,本质实现为PrintStream负责打印输出**/

diagnosticsStream = initDiagnostics();

/** 获取LogFactory.class的类加载器,并通过diagnosticsStream打印 **/

logClassLoaderEnvironment(LogFactory.class);

/** 实例化存放日志的工厂缓存对象:默认实现为org.apache.commons.logging.impl.WeakHashtable **/

factories = createFactoryStore();

if (isDiagnosticsEnabled()) {

logDiagnostic("BOOTSTRAP COMPLETED");

}

}

实例化存放日志的工厂缓存对象

private static final Hashtable createFactoryStore() {

Hashtable result = null;

String storeImplementationClass;

/** 从系统属性中获取工厂缓存的实现类**/

try {

storeImplementationClass = getSystemProperty(HASHTABLE_IMPLEMENTATION_PROPERTY, null);

} catch (SecurityException ex) {

storeImplementationClass = null;

}

/** 从WEAK_HASHTABLE_CLASSNAME获取工厂缓存的默认实现类**/

if (storeImplementationClass == null) {

storeImplementationClass = WEAK_HASHTABLE_CLASSNAME;

}

/** 实例化工厂缓存实现类对象 **/

try {

Class implementationClass = Class.forName(storeImplementationClass);

result = (Hashtable) implementationClass.newInstance();

} catch (Throwable t) {

handleThrowable(t); // may re-throw t

if (!WEAK_HASHTABLE_CLASSNAME.equals(storeImplementationClass)) {

if (isDiagnosticsEnabled()) {

logDiagnostic("[ERROR] LogFactory: Load of custom hashtable failed");

} else {

System.err.println("[ERROR] LogFactory: Load of custom hashtable failed");

}

}

}

if (result == null) {

result = new Hashtable();

}

/** 返回 结果**/

return result;

}

5.2 从LogFactory获取Log对象

/**

* 获取日志对象

*/

public static Log getLog(Class clazz) throws LogConfigurationException {

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

return getFactory().getInstance(clazz);

}

/**

* 获取日志对象

*/

public static Log getLog(String name) throws LogConfigurationException {

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

return getFactory().getInstance(name);

}

5.3 获取并实例化LogFactory

public static LogFactory getFactory() throws LogConfigurationException {

/** 获取上下文类加载器 **/

ClassLoader contextClassLoader = getContextClassLoaderInternal();

if (contextClassLoader == null) {

if (isDiagnosticsEnabled()) {

logDiagnostic("Context classloader is null.");

}

}

/** 从缓存factories中获取LogFactory **/

LogFactory factory = getCachedFactory(contextClassLoader);

if (factory != null) {

return factory;

}

if (isDiagnosticsEnabled()) {

logDiagnostic(

"[LOOKUP] LogFactory implementation requested for the first time for context classloader " +

objectId(contextClassLoader));

logHierarchy("[LOOKUP] ", contextClassLoader);

}

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

Properties props = getConfigurationFile(contextClassLoader, FACTORY_PROPERTIES);

/**

* 读取commons-logging.properties配置文件中use_tccl属性值

* 如果属性值存在且为flase,设置baseClassLoader指向thisClassLoader,

* 如果不存在use_tccl属性值或属性值为true,baseClassLoader指向上下文类加载器

* **/

ClassLoader baseClassLoader = contextClassLoader;

if (props != null) {

String useTCCLStr = props.getProperty(TCCL_KEY);

if (useTCCLStr != null) {

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

baseClassLoader = thisClassLoader;

}

}

}

if (isDiagnosticsEnabled()) {

logDiagnostic("[LOOKUP] Looking for system property [" + FACTORY_PROPERTY +

"] to define the LogFactory subclass to use...");

}

/**

* 读取commons-logging.properties配置文件中org.apache.commons.logging.LogFactory属性值,作为LogFactory实现类,同时实例化LogFactory对象

**/

try {

String factoryClass = getSystemProperty(FACTORY_PROPERTY, null);

if (factoryClass != null) {

if (isDiagnosticsEnabled()) {

logDiagnostic("[LOOKUP] Creating an instance of LogFactory class '" + factoryClass +

"' as specified by system property " + FACTORY_PROPERTY);

}

factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);

} else {

if (isDiagnosticsEnabled()) {

logDiagnostic("[LOOKUP] No system property [" + FACTORY_PROPERTY + "] defined.");

}

}

} catch (SecurityException e) {

if (isDiagnosticsEnabled()) {

logDiagnostic("[LOOKUP] A security exception occurred while trying to create an" +

" instance of the custom factory class" + ": [" + trim(e.getMessage()) +

"]. Trying alternative implementations...");

}

} catch (RuntimeException e) {

if (isDiagnosticsEnabled()) {

logDiagnostic("[LOOKUP] An exception occurred while trying to create an" +

" instance of the custom factory class" + ": [" +

trim(e.getMessage()) +

"] as specified by a system property.");

}

throw e;

}

/**

* 读取META-INF/services/org.apache.commons.logging.LogFactory配置文件中数据,作为LogFactory实现类,同时实例化LogFactory对象

**/

if (factory == null) {

if (isDiagnosticsEnabled()) {

logDiagnostic("[LOOKUP] Looking for a resource file of name [" + SERVICE_ID +

"] to define the LogFactory subclass to use...");

}

try {

final InputStream is = getResourceAsStream(contextClassLoader, SERVICE_ID);

if( is != null ) {

BufferedReader rd;

try {

rd = new BufferedReader(new InputStreamReader(is, "UTF-8"));

} catch (java.io.UnsupportedEncodingException e) {

rd = new BufferedReader(new InputStreamReader(is));

}

String factoryClassName = rd.readLine();

rd.close();

if (factoryClassName != null && ! "".equals(factoryClassName)) {

if (isDiagnosticsEnabled()) {

logDiagnostic("[LOOKUP] Creating an instance of LogFactory class " +

factoryClassName +

" as specified by file '" + SERVICE_ID +

"' which was present in the path of the context classloader.");

}

factory = newFactory(factoryClassName, baseClassLoader, contextClassLoader );

}

} else {

if (isDiagnosticsEnabled()) {

logDiagnostic("[LOOKUP] No resource file with name '" + SERVICE_ID + "' found.");

}

}

} catch (Exception ex) {

if (isDiagnosticsEnabled()) {

logDiagnostic(

"[LOOKUP] A security exception occurred while trying to create an" +

" instance of the custom factory class" +

": [" + trim(ex.getMessage()) +

"]. Trying alternative implementations...");

}

}

}

/** 在次读取commons-logging.properties配置文件中org.apache.commons.logging.LogFactory属性值,作为LogFactory实现类,同时实例化LogFactory对象 **/

if (factory == null) {

if (props != null) {

if (isDiagnosticsEnabled()) {

logDiagnostic(

"[LOOKUP] Looking in properties file for entry with key '" + FACTORY_PROPERTY +

"' to define the LogFactory subclass to use...");

}

String factoryClass = props.getProperty(FACTORY_PROPERTY);

if (factoryClass != null) {

if (isDiagnosticsEnabled()) {

logDiagnostic(

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

}

factory = newFactory(factoryClass, baseClassLoader, contextClassLoader);

// TODO: think about whether we need to handle exceptions from newFactory

} else {

if (isDiagnosticsEnabled()) {

logDiagnostic("[LOOKUP] Properties file has no entry specifying LogFactory subclass.");

}

}

} else {

if (isDiagnosticsEnabled()) {

logDiagnostic("[LOOKUP] No properties file available to determine" + " LogFactory subclass from..");

}

}

}

/** 如果上诉操作没有获取factory实现对象,则使用org.apache.commons.logging.impl.LogFactoryImpl作为默认日志工厂实现类,并实例化工厂对象 **/

if (factory == null) {

if (isDiagnosticsEnabled()) {

logDiagnostic(

"[LOOKUP] Loading the default LogFactory implementation '" + FACTORY_DEFAULT +

"' via the same classloader that loaded this LogFactory" +

" class (ie not looking in the context classloader).");

}

factory = newFactory(FACTORY_DEFAULT, thisClassLoader, contextClassLoader);

}

/** 将LogFactory添加到缓存factories当中: **/

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;

}

5.4 从日志工厂获取日志对象:

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 Constructor logConstructor = null;

/**

* The signature of the Constructor to be used.

*/

protected Class logConstructorSignature[] = { String.class };

/**

* 实例化日志对象反相依赖日志工厂的方法setLogFactory

*/

protected Method logMethod = null;

protected Log newInstance(String name) throws LogConfigurationException {

Log instance;

try {

/** 如果构造器不存在,调用discoverLogImplementation尝试通过类名称实例化日志对象 **/

if (logConstructor == null) {

instance = discoverLogImplementation(name);

}

/** 如果构造器存在,使用构造器实例化日志对象 **/

else {

Object params[] = { name };

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

}

/** 如果实例化日志对象,存在setLogFactory方法,则调用和构日志工厂方法反相关联 **/

if (logMethod != null) {

Object params[] = { this };

logMethod.invoke(instance, params);

}

return instance;

} catch (LogConfigurationException lce) {

throw lce;

} catch (InvocationTargetException e) {

Throwable c = e.getTargetException();

throw new LogConfigurationException(c == null ? e : c);

} catch (Throwable t) {

handleThrowable(t);

throw new LogConfigurationException(t);

}

}

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) {

StringBuffer messageBuffer = new StringBuffer("User-specified log class '");

messageBuffer.append(specifiedLogClassName);

messageBuffer.append("' cannot be found or is not useable.");

// Mistyping or misspelling names is a common fault.

// Construct a good error message, if we can

informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_LOG4J_LOGGER);

informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_JDK14_LOGGER);

informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_LUMBERJACK_LOGGER);

informUponSimilarName(messageBuffer, specifiedLogClassName, LOGGING_IMPL_SIMPLE_LOGGER);

throw new LogConfigurationException(messageBuffer.toString());

}

return result;

}

if (isDiagnosticsEnabled()) {

logDiagnostic(

"No user-specified Log implementation; performing discovery" +

" using the standard supported logging implementations...");

}

/**

*如果配置文件中不存在“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;

6 缺陷

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

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值