java 日志框架_java的各种日志框架

本文是作者原创,版权归作者所有.若要转载,请注明出处.文章中若有错误和疏漏之处,还请各位大佬不吝指出,谢谢大家.

java日志框架有很多,这篇文章我们来整理一下各大主流的日志框架,

包括log4j   logback  jul(java.util.logging)  jcl(commons-logging)  slf4j(simple log facade for java)等常用框架

目前java日志的使用有两种形式:日志接口和日志实现

1.目前日志接口,常用的有两种,jcl(commons logging)和slf4j(simple log facade for java)。

2.日志实现目前有这几类,log4j、jul、logback、log4j2。

我们先从log4j开始

首先,引入maven依赖

log4j

log4j

1.2.17

然后是配置log4j.properties文件

### 设置###

log4j.rootLogger = debug,stdout,D,E

### 输出信息到控制抬 ###

log4j.appender.stdout = org.apache.log4j.ConsoleAppender

log4j.appender.stdout.Target = System.out

log4j.appender.stdout.layout = org.apache.log4j.PatternLayout

log4j.appender.stdout.layout.ConversionPattern = [%-5p] %d{yyyy-MM-dd HH:mm:ss,SSS} method:%l%n%m%n

### 输出DEBUG 级别以上的日志到=E://logs/error.log ###

log4j.appender.D = org.apache.log4j.DailyRollingFileAppender

log4j.appender.D.File = E://logs/log.log

log4j.appender.D.Append = true

log4j.appender.D.Threshold = DEBUG

log4j.appender.D.layout = org.apache.log4j.PatternLayout

log4j.appender.D.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n

### 输出ERROR 级别以上的日志到=E://logs/error.log ###

log4j.appender.E = org.apache.log4j.DailyRollingFileAppender

log4j.appender.E.File =E://logs/error.log

log4j.appender.E.Append = true

log4j.appender.E.Threshold = ERROR

log4j.appender.E.layout = org.apache.log4j.PatternLayout

log4j.appender.E.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n

最后是代码

importorg.apache.log4j.Logger;public classLog4j {public static voidmain(String[] args) {

Logger log4j= Logger.getLogger("log4j");//记录debug级别的信息

log4j.debug("This is debug message.");//记录info级别的信息

log4j.info("This is info message.");//记录error级别的信息

log4j.error("This is error message.");

}

}

最后是日志信息

14363e24f4085bfe9359723d68d21888.png

好,到这里,log4j就差不多了

,接下来我们看下 jul(java.util.logging) java官方日志jul,位于java.util.logging包下,不用引入依赖,但功能有限,不太常用看下demo

importjava.util.logging.Logger;public classJUL {public static voidmain(String[] args) {//获取logger实例,相同名的只能生成一个实例

Logger logger = Logger.getLogger("javaLog");//日志输出简写形式,有不同的级别

logger.warning("warning log");

logger.info("info log");

}

}

看下控制台输出结果,可以看出和log4j相比,日志的时间和颜色明显不同

d9894e20cb9b8ddf528add8b28e3ca0d.png

好,到这里,jul就差不多了

我们再看jcl(commons logging)是如何整合log4j和jul的

首先还是加入maven依赖,这里我们先加入log4j的依赖

log4j

log4j

1.2.17

commons-logging

commons-logging

1.2

看下demo

importorg.apache.commons.logging.Log;importorg.apache.commons.logging.LogFactory;/*** Created by admin on 2019/10/14.*/

public classJCL {public static voidmain(String[] args) {

Log log= LogFactory.getLog("JCL");

log.error("Hello World");

}

}

看下输出结果

43d3fc548b894cd63534975fd5908496.png

我们看到,这个好像是log4j的实现,我们把log4j的依赖注释,重新运行一次,再看下结果

e7aed1925e244fee5376a4dbf892981d.png

我们发现这次用的好像是jul打印的日志了,下面我们看下源码,看下jcl的底层是如何实现这种切换的

这里也只贴关键代码,看下getInstance方法

public Log getInstance(String name) throwsLogConfigurationException {

Log instance=(Log) instances.get(name);if (instance == null) {

instance=newInstance(name);

instances.put(name, instance);

}returninstance;

}

继续看newInstance方法

protected Log newInstance(String name) throwsLogConfigurationException {

Log instance;try{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);

}returninstance;

}catch(LogConfigurationException lce) {//this type of exception means there was a problem in discovery//and we've already output diagnostics about the issue, etc.;//just pass it on

throwlce;

}catch(InvocationTargetException e) {//A problem occurred invoking the Constructor or Method//previously discovered

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

}catch(Throwable t) {

handleThrowable(t);//may re-throw t//A problem occurred invoking the Constructor or Method//previously discovered

throw newLogConfigurationException(t);

}

}

看下上面我注释的代码

privateLog discoverLogImplementation(String logCategory)throwsLogConfigurationException {if(isDiagnosticsEnabled()) {

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

}

initConfiguration();

Log result= null;//See if the user specified the Log implementation to use

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 newLogConfigurationException(messageBuffer.toString());

}returnresult;

}if(isDiagnosticsEnabled()) {

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

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

}for(int i=0; i

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

}if (result == null) {throw newLogConfigurationException

("No suitable Log implementation");

}returnresult;

}

看下我注释的地方,classesToDiscover对象

private static final String[] classesToDiscover ={

LOGGING_IMPL_LOG4J_LOGGER,"org.apache.commons.logging.impl.Jdk14Logger","org.apache.commons.logging.impl.Jdk13LumberjackLogger","org.apache.commons.logging.impl.SimpleLog"}

这里的Jdk14Logger其实就是jul,我们发现classesToDiscover对象是一个数组,它包含了几个日志框架的的全限定路径

回到刚刚的代码,我看可以看到,jcl就是遍历这个数组,按顺序取值,这里有log4j,就优先log4j,没有的话,就是要jul框架

我们再看下下面一行代码,点进去,看看怎么实现的

privateLog createLogFromClass(String logAdapterClassName,

String logCategory,booleanaffectState)throwsLogConfigurationException {if(isDiagnosticsEnabled()) {

logDiagnostic("Attempting to instantiate '" + logAdapterClassName + "'");

}

Object[] params={ logCategory };

Log logAdapter= null;

Constructor constructor= null;

Class logAdapterClass= null;

ClassLoader currentCL=getBaseClassLoader();for(;;) {//Loop through the classloader hierarchy trying to find//a viable classloader.

logDiagnostic("Trying to load '" + logAdapterClassName + "' from classloader " +objectId(currentCL));try{if(isDiagnosticsEnabled()) {//Show the location of the first occurrence of the .class file//in the classpath. This is the location that ClassLoader.loadClass//will load the class from -- unless the classloader is doing//something weird.

URL url;

String resourceName= logAdapterClassName.replace('.', '/') + ".class";if (currentCL != null) {

url=currentCL.getResource(resourceName );

}else{

url= ClassLoader.getSystemResource(resourceName + ".class");

}if (url == null) {

logDiagnostic("Class '" + logAdapterClassName + "' [" + resourceName + "] cannot be found.");

}else{

logDiagnostic("Class '" + logAdapterClassName + "' was found at '" + url + "'");

}

}

Class c;try{

c= Class.forName(logAdapterClassName, true, currentCL);

}catch(ClassNotFoundException originalClassNotFoundException) {//The current classloader was unable to find the log adapter//in this or any ancestor classloader. There's no point in//trying higher up in the hierarchy in this case..

String msg =originalClassNotFoundException.getMessage();

logDiagnostic("The log adapter '" + logAdapterClassName + "' is not available via classloader " +objectId(currentCL)+ ": " +msg.trim());try{//Try the class classloader.//This may work in cases where the TCCL//does not contain the code executed or JCL.//This behaviour indicates that the application//classloading strategy is not consistent with the//Java 1.2 classloading guidelines but JCL can//and so should handle this case.

c =Class.forName(logAdapterClassName);//这里通过反射获取日志实现类的class对象

}catch(ClassNotFoundException secondaryClassNotFoundException) {//no point continuing: this adapter isn't available

msg =secondaryClassNotFoundException.getMessage();

logDiagnostic("The log adapter '" + logAdapterClassName +

"' is not available via the LogFactoryImpl class classloader: " +msg.trim());break;

}

}

constructor=c.getConstructor(logConstructorSignature);

Object o=constructor.newInstance(params);//这里实例化了日志实现类对象

if (o instanceof Log) {//这里判断是否是log的实现类

logAdapterClass = c;

logAdapter = (Log) o;//这里将对象传给logAdapter

break;

}

// Oops, we have a potential problem here. An adapter class

// has been found and its underlying lib is present too, but

// there are multiple Log interface classes available making it

// impossible to cast to the type the caller wanted. We

// certainly can't use this logger, but we need to know whether

// to keep on discovering or terminate now.

//

// The handleFlawedHierarchy method will throw

// LogConfigurationException if it regards this problem as

// fatal, and just return if not.

handleFlawedHierarchy(currentCL, c);

} catch (NoClassDefFoundError e) {

// We were able to load the adapter but it had references to

// other classes that could not be found. This simply means that

// the underlying logger library is not present in this or any

// ancestor classloader. There's no point in trying higher up

// in the hierarchy in this case..

String msg = e.getMessage();

logDiagnostic("The log adapter '" + logAdapterClassName +

"' is missing dependencies when loaded via classloader " + objectId(currentCL) +

": " + msg.trim());

break;

} catch (ExceptionInInitializerError e) {

// A static initializer block or the initializer code associated

// with a static variable on the log adapter class has thrown

// an exception.

//

// We treat this as meaning the adapter's underlying logging

// library could not be found.

String msg = e.getMessage();

logDiagnostic("The log adapter '" + logAdapterClassName +

"' is unable to initialize itself when loaded via classloader " + objectId(currentCL) +

": " + msg.trim());

break;

} catch (LogConfigurationException e) {

// call to handleFlawedHierarchy above must have thrown

// a LogConfigurationException, so just throw it on

throw e;

} catch (Throwable t) {

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

// handleFlawedDiscovery will determine whether this is a fatal

// problem or not. If it is fatal, then a LogConfigurationException

// will be thrown.

handleFlawedDiscovery(logAdapterClassName, currentCL, t);

}

if (currentCL == null) {

break;

}

// try the parent classloader

// currentCL = currentCL.getParent();

currentCL = getParentClassLoader(currentCL);

}

if (logAdapterClass != null && affectState) {

// We've succeeded, so set instance fields

this.logClassName = logAdapterClassName;

this.logConstructor = constructor;

// Identify the setLogFactory method (if there is one)

try {

this.logMethod = logAdapterClass.getMethod("setLogFactory", logMethodSignature);

logDiagnostic("Found method setLogFactory(LogFactory) in '" + logAdapterClassName + "'");

} catch (Throwable t) {

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

this.logMethod = null;

logDiagnostic("[INFO] '" + logAdapterClassName + "' from classloader " + objectId(currentCL) +

" does not declare optional method " + "setLogFactory(LogFactory)");

}

logDiagnostic("Log adapter '" + logAdapterClassName + "' from classloader " +

objectId(logAdapterClass.getClassLoader()) + " has been selected for use.");

}

return logAdapter;//返回此对象

}

我们可以看到底层是用了反射获取的对象,截个图

6fef3bef9a33d4f6a40a7c72bf6714c5.png

我们可以看到,这里是jcl包里的log4j.

好了,到这里jcl就差不多了,我们从源码可以看出jcl是如何切换日志框架的,

接下来学习下目前流行的slf4j,它支持所有主流的日志实现框架,非常强大,推荐使用

老规矩,添加依赖

org.slf4j

slf4j-api

1.7.25

看下demo

importorg.slf4j.LoggerFactory;importorg.slf4j.Logger;/*** Created by admin on 2019/10/14.*/

public classSlf4j {public static voidmain(String[] args) {

Logger logger= LoggerFactory.getLogger("slf4j");

logger.error("Hello World");

}

}

看下控制台

527734faa8565da1c8a752b663781056.png

哎,好像没打印,这里可以看出,只引入slf4j是不打印日志的,我们可以看下官网http://www.slf4j.org/

Simple Logging Facade for Java (SLF4J) 它是简单日志门面,不自己实现

好,我们再引入log4j的绑定器依赖

org.slf4j

slf4j-log4j12

1.7.25

,再运行一次,看下结果

82528d8afcb52e46a70bfb700a446985.png

成了,如何我们要切换日志呢,注释掉log4j,试试jul吧

org.slf4j

slf4j-jdk14

1.7.25

再看下控制台

d8a41c845f24766b035080f493ab8051.png

成功了,这就是推荐使用slf4j的原因,切换日志框架非常方便

注意,绑定器有且只能有一个

最后,看下logback的使用吧

添加依赖

ch.qos.logback

logback-core

1.1.11

ch.qos.logback

logback-classic

1.1.11

logback,xml

logbackStudy

%date %level %logger : %msg %n

logbackstudy_${nowDate}.log

logbackStudy_%d{yyyyMMdd}.log.gz

30

1G

%date %level %logger : %msg %n

demo实例

importorg.slf4j.LoggerFactory;importorg.slf4j.Logger;/*** Created by admin on 2019/10/14.*/

public classSlf4j {public static voidmain(String[] args) {

Logger logger= LoggerFactory.getLogger("slf4j");

logger.error("Hello World");

}

}

控制台

93c185d0a00f1689bd61822072b94580.png

输出的文件

ad8670ca19c52b1f0b495f06e0a53d5f.png

总结:目前来说日志框架推荐使用slf4j日志接口+一个你熟悉的日志实现,这样可以直接切换日志的依赖,不用改已有的代码

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值