Java日志相关记录 (Jul jcl log4j log4j2 Logback SLF4J)

折月煮酒 提交于 2019-12-21 18:11:10

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

一、写在前面

在java 中实现记录日志的方式有很多种
1. 最初的方式,就是system.print.out ,err 这样直接在控制台打印消息,缺点太多了,这样与业务逻辑无关的代码会变得更多,不能按日志等及输出,以及那些不输出等。
2. JUL,java util logging在jdk的java.util.logging包中,也叫jkdLog或者jdk14log; 在JDK 1.4 版本之后,java提供了日志的API ,可以往文件中写日志了,最方便不需要第三方包,其实际使用人较少。
3. log4j , 最强大的记录日志的方式。 可以通过配置 .properties 或是 .xml 的文件, 配置日志的目的地,格式等等,基于老的jdk性能不高,该项目现在已经停止维护,但是用的人还是最多的。
4. Log4j2 该项目是log4j的升级版,性能较好,也吸收了logback等日志记录组件的优点。
5. commons-logging是一个日志接口,最综合和常见的日志记录方式, 经常是和log4j或者log4j2 结合起来使用。
6. Slf4j也是一个日志接口,最常见的是和logback一起使用。
7. 日志等级其实最常用的就是这四个等级(debug,info,warn,error)。

二、JUL(java util logger)

使用java 自带的logger最方便的作用提无需引入第三方包
直接上代码:

package com.xxx.test;
import java.io.IOException;
import java.util.Date;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;

public class TestLogJava {
    public static void main(String[] args) throws IOException{
        Logger log = Logger.getLogger("tesglog");
        log.setLevel(Level.ALL);
        FileHandler fileHandler = new FileHandler("testlog.log");
        fileHandler.setLevel(Level.ALL);
        fileHandler.setFormatter(new LogFormatter());
        log.addHandler(fileHandler);
        log.info("This is test java util log"); 
        log.warning("warning .........");
        log.log(Level.FINE, "fine...");
        log.finer("finer ....");
    }
}
 
class LogFormatter extends Formatter {
    @Override
    public String format(LogRecord record) {
        Date date = new Date();
        String sDate = date.toString();
        return "[" + sDate + "]" + "[" + record.getLevel() + "]"
                + record.getClass() + record.getMessage() + "\n";
    }
}



Java util logger使用的日志级别:SEVERE(最高值),WARNING,INFO,CONFIG,FINE,FINER,FINEST(最低值)以及OFF和ALL
先通地Logger工厂获得一个log实例,日志记当格是默认是xml 使用前最好先自己格式化自己所要的格式(上面的LogFormatter就是自定义格式)。log的Handler有许多可以使用常见是写到文件也就是FileHandler。
使用步骤:new 一个FileHandler 实例,设定fileHandler的等级和格式,并添加到log实例上,然后就是直接使用log.info()写入了。

三、JCL(Jakarta commons logger)

说的是java 通用日志,其实是apache出品的通用日志接口,是一个接口。
提供的是一个日志(Log)接口(interface),同时兼顾轻量级和不依赖于具体的日志实现工具。它提供给中间件/日志工具开发者一个简单的日志操作抽象,允许程序开发人员使用不同的具体日志实现工具。
JCL有两个基本的抽象类:Log(基本记录器)和LogFactory(负责创建Log实例)。当commons-logging.jar被加入到 CLASSPATH之后,它会合理地猜测你想用的日志工具,然后进行自我设置,用户根本不需要做任何设置。默认的LogFactory是按照下列的步骤去发现并决定那个日志工具将被使用的(按照顺序,寻找过程会在找到第一个工具时中止):
   1. 寻找当前factory中名叫org.apache.commons.logging.Log配置属性的值
   2. 寻找系统中属性中名叫org.apache.commons.logging.Log的值
   3. 如果应用程序的classpath中有log4j,则使用相关的包装(wrapper)类(Log4JLogger)
   4. 如果应用程序运行在jdk1.4的系统中,使用相关的包装类(Jdk14Logger)
   5. 使用简易日志包装类(SimpleLog)
org.apache.commons.logging.Log的具体实现有如下:
-org.apache.commons.logging.impl.Jdk14Logger 使用JDK1.4。
-org.apache.commons.logging.impl.Log4JLogger 使用Log4J。
-org.apache.commons.logging.impl.LogKitLogger 使用 avalon-Logkit。
-org.apache.commons.logging.impl.SimpleLog common-logging自带日志实现类。它实现了Log接口,把日志消息都输出到系统错误流System.err 中。 
-org.apache.commons.logging.impl.NoOpLog common-logging自带日志实现类。它实现了Log接口。 其输出日志的方法中不进行任何操作。

package com.xxx.test;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.*;
public class TestCommonLog {
	private static Log log = LogFactory.getLog(TestCommonLog.class);
	public static void main(String[] args) {
		log.debug("debug ......");
		log.info("info ......");
		log.warn("warn ......");
		log.error("error ......");
		log.fatal("fatal ......");
	}
}


四、log4j(log4j版本1)

Log4j是java开发用得最多最常见的日志组件,这个是组件,是有完整的实现。可以完整的独立使用。
直接上代码:

Log4j.properties

### \u8BBE\u7F6E###
log4j.rootLogger = debug,stdout,D,E

### \u8F93\u51FA\u4FE1\u606F\u5230\u63A7\u5236\u62AC ###
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

### \u8F93\u51FADEBUG \u7EA7\u522B\u4EE5\u4E0A\u7684\u65E5\u5FD7\u5230=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

### \u8F93\u51FAERROR \u7EA7\u522B\u4EE5\u4E0A\u7684\u65E5\u5FD7\u5230=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



Log4jTest.java

package com.xxx.test;
import org.apache.log4j.Logger;
public class Log4jTest {
	private static Logger logger = Logger.getLogger(Log4jTest.class);
	public static void main(String args[]) {
		// 记录debug级别的信息
		logger.debug("debug");
		// 记录info级别的信息
		logger.info("info");
		// 记录error级别的信息
		logger.error("error");
	}
}



五、log4j2使用

Log4j2是log4j的重构升级版,他改进了log4j性能和不足,吸取了logback优点
不说了直接上代码:

Log4j2.xml

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="warn">
	<Appenders>
		<Console name="Console" target="SYSTEM_OUT">
			<PatternLayout pattern="%m%n" />
		</Console>
	</Appenders>
	<Loggers>
		<Root level="DEBUG">
			<AppenderRef ref="Console" />
		</Root>
	</Loggers>
</Configuration>


Log4j2Test.java

package com.xxx.test;
import org.apache.logging.log4j.Level;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger; 
public class Log4j2Test {
	private static Logger logger = LogManager.getLogger(Log4j2Test.class);
	public static boolean hello() {
        logger.entry();   //trace级别的信息,单独列出来是希望你在某个方法或者程序逻辑开始的时候调用,和logger.trace("entry")基本一个意思
        logger.error("Did it again!");   //error级别的信息,参数就是你输出的信息
        logger.info("我是info信息");    //info级别的信息
        logger.debug("我是debug信息");
        logger.warn("我是warn信息");
        logger.fatal("我是fatal信息");
        logger.log(Level.DEBUG, "我是debug信息");   //这个就是制定Level类型的调用:谁闲着没事调用这个,也不一定哦!
        logger.exit();    //和entry()对应的结束方法,和logger.trace("exit");一个意思
        return false;
    }
	public static void main(String[] args) {
		hello();
	}
}

log4j2在没有任何配置的情况下文件时会自动使用默认的配置:

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="WARN">
    <Appenders>
        <Console name="Console" target="SYSTEM_OUT">
            <PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
        </Console>
    </Appenders>
    <Loggers>
        <Root level="error">
            <AppenderRef ref="Console" />
        </Root>
    </Loggers>
</Configuration>


该配置只有一个Appender:Console,目标是SYSTEM_OUT,即日志内容,都会打印在eclipse控制台上。Root Logger的级别是error,即:所有error及以上级别的日志才会记录。(注:日志级别顺序为 TRACE < DEBUG < INFO < WARN < ERROR < FATAL ),所以最终只有2日志会输出(error,fatal)


再来Log4j2的详细一点的配置

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="OFF">
	<Appenders>
		<Console name="Console" target="SYSTEM_OUT">
			<PatternLayout pattern="%d{HH:mm:ss.SSS} [%t] %-5level %logger{36} - %msg%n" />
		</Console>

		<File name="ERROR" fileName="logs/error.log">
			<ThresholdFilter level="error" onMatch="ACCEPT"
				onMismatch="DENY" />
			<PatternLayout
				pattern="%d{yyyy.MM.dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n" />
		</File>

		<RollingFile name="RollingFile" fileName="logs/app.log"
			filePattern="log/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">
			<PatternLayout
				pattern="%d{yyyy-MM-dd 'at' HH:mm:ss z} %-5level %class{36} %L %M - %msg%xEx%n" />
			<SizeBasedTriggeringPolicy size="50MB" />
		</RollingFile>

		<RollingFile name="ROLLING" fileName="d:/logs/howsun.log"
			filePattern="d:/logs/howsun_%d{yyyy-MM-dd}_%i.log">
			<PatternLayout pattern="%d %p %c{1.} [%t] %m%n" />
			<Policies>
				<TimeBasedTriggeringPolicy modulate="true"
					interval="24" />
				<SizeBasedTriggeringPolicy size="51200 KB" />
			</Policies>
			<DefaultRolloverStrategy max="20" />
		</RollingFile>

		<properties>
			<property name="filenameLog">logs/payPlatform.log</property>
		</properties>

		<!-- 定义后台文档日志记录 -->
		<RollingFile name="RollingFile" fileName="${filenameLog}"
			filePattern="logs/$${date:yyyy-MM}/app-%d{MM-dd-yyyy}-%i.log.gz">
			<PatternLayout>
				<Pattern>%d{yyyy-MM-dd HH:mm:ss} [%p] [%t] %c{1}.%M(%L) | %m%n
				</Pattern>
			</PatternLayout>
			<Policies>
				<!-- 定义log文件封存的周期 -->
				<TimeBasedTriggeringPolicy interval="1"
					modulate="true" />
				<SizeBasedTriggeringPolicy size="100 MB" />
			</Policies>
			<DefaultRolloverStrategy fileIndex="max" max="2" />
		</RollingFile>

	</Appenders>
	<Loggers>
		<Root level="DEBUG">
			<AppenderRef ref="Console" />
			<appender-ref ref="ROLLING" />
			<appenderRef ref="RollingFile" />
		</Root>
	</Loggers>
</Configuration>



六、SLF4J与Logback 

SLF4J全名Simple Logging Facade for Java
不是具体的日志解决方案,它只服务于各种各样的日志系统。按照官方的说法,SLF4J是一个用于日志系统的简单Facade,允许最终用户在部署其应用时使用其所希望的日志系统。
SLF4J同apache的commons-logger一样,都是供示一个简单的接口,不是一个完格的日志解决方案他一般需要和其它日志系统进行配合使用。
SLF4J创建项目在包中引入SLF4J的核心接口slf4j-api.jar包
上代码:

package com.xxx.test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogbackTest {
	private static Logger logger = LoggerFactory.getLogger(LogbackTest.class);
	public static void main(String[] args) {
		System.out.println("-----");
		logger.trace("trace");  
        logger.debug("debug");  
        logger.info("info");  
        logger.warn("warn");  
        logger.error("error");
	}
}



如果没有引入其它包,是打印不出日志的,


这里必须在再引入一个现实的包可以使用slf4j-simple.jar,这样slf4j-api会调用slf4j-simple.jarsimple现实(全部打印在console上),也可以使用slf4j-nop.jar就是无任何操作的实现。
同理,也可以在jdk1.4上使用slf4j-jdk14.jar包来使用jdk14logger的实现,也可使用slf4j-jcl.jarJakarta commons logger的实现,虽然他自身也是一个通用日志接口),同样可以使用slf4j-logj12.jar的实现了。只是在使用其它实现是需要加入其它相应日志系统的jar 包并同时做好相应日志系统的配置,例使用了slf4j-log4j12.jar则还需要加入log4j-1.2.7.jar 包,并且在写好相应的log4j.properties配置文件。

SLF4J这种机制还可以把原来用的不是slf4j这种日志接口的直接转换成slf4j。例原来使用的是commons-logging可以把原来的commons-logging.jar包删除,把jcl-over-slf4j.jar添加到项目即可。

提到slf4j就不得不提一下logback

Logback同也是由log4j主创人创立,slf4j也是他搞的。改写了log4j的核心提高了性能,意在替换log4j(指log4j-v1)。logback项目主要有三个部分core, classic, access, corelogback的核心,classicslf4japi的实现,而access则是配使servlet容器配合提供http访问记录。

使用同上,需要加入slf4j-api.jarlogback-core.jar,logback-classic.jar,logback-access.jar

Slf4jLogbackTest.java

package com.xxx.test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class LogbackTest {
	private static Logger logger = LoggerFactory.getLogger(LogbackTest.class);
	public static void main(String[] args) {	
	 logger.trace("======trace");  
        logger.debug("======debug");  
        logger.info("======info");  
        logger.warn("======warn");  
        logger.error("======error");
	}
}



Logback.xml

<?xml version="1.0" encoding="UTF-8" ?>
<configuration debug="true">
  <appender name="STDOUT"
    class="ch.qos.logback.core.ConsoleAppender">
    <!-- encoders are assigned by default the type
         ch.qos.logback.classic.encoder.PatternLayoutEncoder -->
    <encoder>
      <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
    </encoder>
  </appender>
  <root level="debug">
    <appender-ref ref="STDOUT" />
  </root>
</configuration>



这个是logback的配置文件,其实不配logback.xml也是可以的,程序会自动加载一个默认BasicConfigurator的配置,最 小化 配置 由 一个 关联 到 根 logger ConsoleAppender 组成。 输出用模式为%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36}- %msg%n 的 PatternLayoutEncoder 进行格式化,根 logger 默认级别是 DEBUG
说到这里也说一下logback读取配置文件规则:,先读取logback-test.xml,没有则查找logback.xml如果也没有就加载BasicConfigurator配置。

写在最后:

jdkLog就是JUL,在项目实际中用得较少,小的项目中都是直接使用log4j或者log4j2,或都使用commons-logging再配合log4j或者log4j2。也有许多项目直接使用slf4j+logback
一般建议是使用commons-logging+log4j或者slf4j+logback吧,如果项目的其它组件充许要不直接上log4j2也是可以的。


易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!