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

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-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>

然后是配置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

最后是代码

import org.apache.log4j.Logger;

public class Log4j {

    public static void main(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.");
} }

最后是日志信息

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

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

import java.util.logging.Logger;

public class JUL {

    public static void main(String[] args) {
//获取logger实例,相同名的只能生成一个实例
Logger logger = Logger.getLogger("javaLog");
//日志输出简写形式,有不同的级别
logger.warning("warning log");
logger.info("info log"); } }

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

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

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

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

  <!--log4j-->
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency> <!--jcl-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>

看下demo

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory; /**
* Created by admin on 2019/10/14.
*/
public class JCL { public static void main(String[] args) {
Log log = LogFactory.getLog("JCL");
log.error("Hello World");
} }

看下输出结果

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

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

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

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;
}

继续看newInstance方法

protected Log newInstance(String name) throws LogConfigurationException {
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);
} return instance; } 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
throw lce; } 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 new LogConfigurationException(t);
}
}

看下上面我注释的代码

private Log discoverLogImplementation(String logCategory)
throws LogConfigurationException {
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 new LogConfigurationException(messageBuffer.toString());
} return result;
} if (isDiagnosticsEnabled()) {
logDiagnostic(
"No user-specified Log implementation; performing discovery" +
" using the standard supported logging implementations...");
}
for(int i=0; i<classesToDiscover.length && result == null; ++i) {//这里就是关键代码
result = createLogFromClass(classesToDiscover[i], logCategory, true);
} if (result == null) {
throw new LogConfigurationException
("No suitable Log implementation");
} return result;
}

看下我注释的地方,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框架

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

private Log createLogFromClass(String logAdapterClassName,
String logCategory,
boolean affectState)
throws LogConfigurationException { 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 <code>setLogFactory</code> 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;//返回此对象
}

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

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

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

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

老规矩,添加依赖

<!--slf4j-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.25</version>
</dependency>

看下demo

import org.slf4j.LoggerFactory;
import org.slf4j.Logger; /**
* Created by admin on 2019/10/14.
*/
public class Slf4j { public static void main(String[] args) {
Logger logger= LoggerFactory.getLogger("slf4j");
logger.error("Hello World");
} }

看下控制台

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

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

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

<!--log4j的绑定器-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>

,再运行一次,看下结果

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

<!--log4j的绑定器-->
<!--<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-log4j12</artifactId>
<version>1.7.25</version>
</dependency>--> <!--jul的绑定器-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-jdk14</artifactId>
<version>1.7.25</version>
</dependency>

再看下控制台

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

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

最后,看下logback的使用吧

添加依赖

<!-- logback -->
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.11</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.11</version>
</dependency>

logback,xml

<?xml version="1.0" encoding="UTF-8"?>
<!--
scan:当此属性设置为true时,配置文件如果发生改变,将会被重新加载,默认值为true。
scanPeriod:设置监测配置文件是否有修改的时间间隔,如果没有给出时间单位,默认单位是毫秒当scan为true时,此属性生效。默认的时间间隔为1分钟。
debug:当此属性设置为true时,将打印出logback内部日志信息,实时查看logback运行状态。默认值为false。
-->
<configuration debug="false" scan="true" scanperiod="1800 seconds"> <!--当前logger上下文名称-->
<contextName>logbackStudy</contextName> <!--当前日期-->
<timestamp key="nowDate" datePattern="yyyyMMdd" /> <!--输出到控制台-->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%date %level %logger : %msg %n</pattern>
</encoder>
</appender> <!--输出到文件-->
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>logbackstudy_${nowDate}.log</file> <!--日志滚动的策略,按时间归档,实现了RollingPolicy和TriggeringPolicy接口,RollingPolicy指历史文件归档策略,TriggeringPolicy指历史文件归档时机-->
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logbackStudy_%d{yyyyMMdd}.log.gz</fileNamePattern>
<!--最多保存30天历史-->
<maxHistory>30</maxHistory>
<!--日志文件合起来最大1G,超出后会删除旧日志-->
<totalSizeCap>1G</totalSizeCap>
</rollingPolicy>
<encoder>
<!--日志模板-->
<pattern>%date %level %logger : %msg %n</pattern>
</encoder>
</appender> <!--控制指定包或类的日志输出(包括等级和目的地), additivity表示日志信息是否向上传递,false为不传递(即不重复打印)-->
<logger name="com.dragon.study.log.Slf4jAndLogbackMainTwo" level="warn" additivity="false">
<!--可多个appender-->
<appender-ref ref="STDOUT" />
</logger> <root level="info">
<appender-ref ref="STDOUT" />
<appender-ref ref="FILE" />
</root>
</configuration>

demo实例

import org.slf4j.LoggerFactory;
import org.slf4j.Logger; /**
* Created by admin on 2019/10/14.
*/
public class Slf4j { public static void main(String[] args) {
Logger logger= LoggerFactory.getLogger("slf4j");
logger.error("Hello World");
} }

控制台

输出的文件

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

java的各种日志框架的更多相关文章

  1. Java中的日志框架

    日志框架的介绍和使用 常见的日志框架:JUL(Java.util.logging),JCL(jakarta commons logging),SLF4J,jboss-logging,Log4j,Log ...

  2. Java常用的日志框架

    1.Java常用日志框架对比 https://www.jianshu.com/p/bbbdcb30bba8 2.Log4j,Log4j2,Logback日志框架性能对比 https://bbs.hua ...

  3. java自带日志框架打印info以下级别日志

    本文为CSDN博主「LanTingShuXu」的原创文章,原文链接:https://blog.csdn.net/LanTingShuXu/article/details/80528558 java.u ...

  4. Java〜slf4日志框架的使用

    slf4日志可以支持注解的方式开启它,然后在使用时直接使用占位符,而不需要手动拼接字符串,这点在性能上也做到了最好. 一 build.gradle依赖项 compileOnly('org.projec ...

  5. Java日志框架那些事儿

    文章首发于[博客园-陈树义],点击跳转到原文Java日志框架那些事儿. 在项目开发过程中,我们可以通过 debug 查找问题.而在线上环境我们查找问题只能通过打印日志的方式查找问题.因此对于一个项目而 ...

  6. Java日志框架(一)

    在项目开发过程中,我们可以通过 debug 查找问题.而在线上环境我们查找问题只能通过打印日志的方式查找问题.因此对于一个项目而言,日志记录是一个非常重要的问题.因此,如何选择一个合适的日志记录框架也 ...

  7. 带你掌握Java各种日志框架

    一:日志基本概念及框架 1:什么是日志 Java程序员在开发项目时都是依赖Eclipse/IDEA等集成开发工具的Debug调试功能来跟踪解决Bug,但项目打包部署发布到了测试环境和生产环境怎么办?难 ...

  8. 日志框架 log4j2 全解析

    概述 logging翻译为日志记录 那问题是什么是日志? 日志实际上是日记的一种,用于记录某个时间点发生了什么事情,比如大学老师的教学日志,工作日志等 为什么要记录日志? 在实际生活中记录日志主要为了 ...

  9. JUL 日志框架

    1.JUL 简介 JUL 全称 Java Util Logging,位于java.util.logging.Logger 包.它是 java 原生的日志框架,使用时无需另外引用第三方的类库,相对其他的 ...

随机推荐

  1. PTA 1140 1141 1142 1143

    1140 Look-and-say Sequence 思路:模拟 #include<bits/stdc++.h> using namespace std; typedef long lon ...

  2. 第八次作业-非确定的自动机NFA确定化为DFA

    NFA 确定化为 DFA 子集法: f(q,a)={q1,q2,…,qn},状态集的子集 将{q1,q2,…,qn}看做一个状态A,去记录NFA读入输入符号之后可能达到的所有状态的集合. 步骤: 1. ...

  3. 人人学IoT 助学思维导图

    原来学IoT记录的学习笔记,学完之后,对考试和工作都有些帮助,特分享给大家 笔记分享链接 https://share.mindmanager.com/#publish/s6TqusKeSG6aflXL ...

  4. Python不再为字符集编码发愁,使用chardet轻松解决你的困扰。

    欢迎添加华为云小助手微信(微信号:HWCloud002 或 HWCloud003),输入关键字"加群",加入华为云线上技术讨论群:输入关键字"最新活动",获取华 ...

  5. Centos7使用Yum安装高版本的LNMP

    [摘要] 本文旨在介绍使用yum的方式安装一些高版本的NGINX.MySQL.PHP服务.当然如果觉得红帽给的就够用,就用红帽给的就行. 在红帽系列的Linux操作系统中,nginx/mysql/ph ...

  6. springboot整合thymleaf模板引擎

    thymeleaf作为springboot官方推荐使用的模板引擎,简单易上手,功能强大,thymeleaf的功能和jsp有许多相似之处,两者都属于服务器端渲染技术,但thymeleaf比jsp的功能更 ...

  7. syslog日志

    Syslog协议 系统日志(Syslog)协议是在一个IP网络中转发系统日志信息的标准,它是在美国加州大学伯克利软件分布研究中心(BSD)的TCP/IP系统实施中开发的,目前已成为工业标准协议,可用它 ...

  8. [AHOI2017初中组]guide

    题目描述 农场主John最近在网上买了一辆新车,在购买汽车配件时,John不小心点了两次"提交"按钮.导致汽车上安装了两套GPS系统,更糟糕的是John在使用GPS导航时,两套系统 ...

  9. Json schema 以及在python中的jsonschema

    目录 1. JSON Schema简介 2. JSON Schema关键字详解 2.1 $schema 2.2 title和description 2.3 type 3 type常见取值 3.1 当t ...

  10. ios 测试网络是否连接

    转自:http://blog.csdn.net/lwq421336220/article/details/16982857 - (BOOL) connectedToNetwork { //创建零地址, ...