一、日志的作用和目的
日志用于记录系统的操作事件的文件集合,可分为事件日志和消息日志,对于我们处理历史数据、诊断问题的追踪以及理解系统的活动等有着重要的作用。
二、日志分类
日志门面: JCL 、slf4j
日志实现:JUL、logback、log4j、log4j2
2.1 JUL
全称 java util logging 是java的原生日志框架。
<!--**JUL 日志包的引用**-->
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.11</version>
</dependency>
JavaJULTest.class
public class JavaJULTest {
@Test
public void testJUL() {
// 1.获取日志记录器对象
Logger logger = Logger.getLogger("JavaJULTest");
// 2.日志记录输出
logger.info("hello jul");
// 3.通用方法进行日志记录
logger.log(Level.INFO, "=====hello jul");
// 4.通过占位符,传输变量
String name = "test";
Integer age = 12;
logger.log(Level.INFO, "hello {0}{1}", new Object[]{name, age});
}
}
日志的级别:
SEVERE(最高值)
WARNING
INFO
CONFIG
FINE
FINER
FINEST(最低值)
还有一个级别 OFF,可用来关闭日志记录,使用级别 ALL 启用所有消息的日志记录。
logger默认的级别是INFO,比INFO更低的日志将不显示。
使用自定义的日志配置文件:logging.properties
# 输出到文件和控制台
handlers= java.util.logging.FileHandler,java.util.logging.ConsoleHandler
# root日志输出级别 level
.level= INFO
# 控制台输出级别和格式
java.util.logging.ConsoleHandler.level = FINER
java.util.logging.ConsoleHandler.formatter = java.util.logging.SimpleFormatter
# 文件输出级别和地址
java.util.logging.FileHandler.level=CONFIG
java.util.logging.FileHandler.pattern = C:\\Users\\\13965\\TestProperties.txt
#限制文件的大小(50000字节)
java.util.logging.FileHandler.limit = 50000
#过滤,总共保存1个文件,接着覆盖
java.util.logging.FileHandler.count = 1
#XMLFormatter是以xml样式输出,SimpleFormatter是以普通样式输出
java.util.logging.FileHandler.formatter = java.util.logging.SimpleFormatter
#指定是否应该将 FileHandler 追加到任何现有文件上(false会覆盖,但默认为false)。
java.util.logging.FileHandler.append=true
自定义类加载器:LogManager.class
public class LogManager {
// 初始化LogManager
static {
ClassLoader cl = LogManager.class.getClassLoader();
InputStream inputStream = null;
if (cl != null) {
inputStream = cl.getResourceAsStream("logging.properties");
} else {
inputStream = ClassLoader
.getSystemResourceAsStream("logging.properties");
}
java.util.logging.LogManager logManager = java.util.logging.LogManager
.getLogManager();
try {
// 重新初始化日志属性并重新读取日志配置。
logManager.readConfiguration(inputStream);
} catch (SecurityException e) {
System.err.println(e);
} catch (IOException e) {
System.err.println(e);
}
}
/**
* 获取日志对象
* @param clazz
* @return
*/
public static Logger getLogger(Class clazz) {
Logger logger = Logger
.getLogger(clazz.getName());
return logger;
}
}
测试方法:
// 加载自定义的配置类
@Test
public void testLogiProperties() {
// 读取配置文件
Logger logger = LogManager.getLogger(JavaJULTest.class);
logger.info("sssssssssssssssssssssssssssssss");
}
2.2 Log4j
是apache下的一款开源日志框架,可以控制日志信息输出的位置,格式,级别,更灵活的控制日志的输出过程。
pom引入依赖
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.17</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.13-beta-1</version>
</dependency>
Log4jTest.class
public class Log4jTest {
@Test
public void testLog4j() {
// 初始化配置信息
BasicConfigurator.configure();
// 获取日志记录器对象
Logger logger = Logger.getLogger(Log4jTest.class);
// 日志记录输出
logger.info("demo------------------");
}
}
log4j的日志级别(由高到低):
- fatal: 重大错误,这种级别可以直接停止程序了。
- error: 错误信息。用的也比较多。
- warn: 有些信息不是错误信息,但是也要给程序员的一些提示。
- info: 输出感兴趣的或者重要的信息,这个用的最多。
- debug: 调试,一般就只用这个作为最低级别,trace压根不用。
- trace: 是追踪。
自定义配置文件 log4j.properties
# 指定 RootLogger 顶级父元素默认配置信息,输出的地方包括,控制台+文件
log4j.rootLogger =trace,console,fileAppender,dailyRollingFile
# 配置输出到控制台
log4j.appender.console = org.apache.log4j.ConsoleAppender
# 配置输出格式
log4j.appender.console.layout= org.apache.log4j.PatternLayout
# 指定消息内容的格式
log4j.appender.console.layout.ConversionPattern =%p %r %c %t %F %l %d{yyyy-MM-dd HH:mm:ss} %m%n
# %m 输出代码中指定的日志信息
# %p 输出优先级,及debug、info等
# %n 换行符
# %r 输出自应用启动到输出该log信息耗费的毫秒数
# %c 输出打印语句所属的类的全名
# %t 输出产生该日志的线程全名
# %d 输出服务器当前时间,可指定格式,如 %d{yyyy年MM月dd日 HH:mm:ss}
# %l 输出日志时间发生的位置,包括类名、线程、及在代码中的行数
# %F 输出日志消息产生时所在的文件名称
# %L 输出代码中的行号
# %% 输出一个 “%” 字符
### 配置输出到文件 ###
log4j.appender.fileAppender = org.apache.log4j.FileAppender
log4j.appender.fileAppender.layout = org.apache.log4j.PatternLayout
log4j.appender.fileAppender.layout.ConversionPattern = %-d{yyyy-MM-dd HH:mm:ss} [ %t:%r ] - [ %p ] %m%n
log4j.appender.fileAppender.File = C:/Users/13965/Log4jTestProperties.log
log4j.appender.fileAppender.encoding=UTF-8
#文件的拆分规则
log4j.appender.dailyRollingFile = org.apache.log4j.DailyRollingFileAppender
2.3 JCL
全称为 Jakarta Commons Loggin 是apache 提供的一个通用日志api。它为所有的java日志实现一个统一的接口,自身也提供一个日志实现,但是功能非常的薄弱。他有两个基本的抽象类: Log(基本记录器) 和 LogFactory(负责创建Log实例)
pom依赖:
<!--jcl-->
<dependency>
<groupId>commons-logging</groupId>
<artifactId>commons-logging</artifactId>
<version>1.2</version>
</dependency>
JCLTestDemo.class
public class JCLTestDemo {
@Test
public void testJCLMethod() {
Log log = LogFactory.getLog(JCLTestDemo.class);
log.info("=============jcl==========");
}
}
3 日志门面
简单日志门面(Simple Logging Facade For Java) SLF4J主要是为了给Java日志访问提供一套标准、规范的API框架,其主要意义在于提供接口,具体的实现可以交由其他日志框架,例如log4j和logback等,当然 slf4j 中内置了日志功能较为简单的实现,但一般用的比较少,都是结合第三方日志框架进行使用比如 slf4j+logback / log4j2。他提供了两大功能:日志框架的绑定 和 日志框架的桥接
日志框架的出现历史顺序:
log4j >> JUL >> JCL(日志门面) >> slf4j(日志门面) >> logback >> log4j2
3.1 slf4j
pom.xml 依赖
<!-- slf4j 日志门面-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
</dependency>
<!--slf4j 内置的简单实现-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-simple</artifactId>
<version>1.7.21</version>
</dependency>
Slf4jDemo.class
public class Slf4jDemo {
public static final Logger logger = LoggerFactory.getLogger(Slf4jDemo.class);
@Test
public void slf4jMethod() {
logger.error("error...........");
logger.warn("warn...........");
logger.info("info...........");
logger.debug("debug...........");
logger.trace("trace...........");
// 占位符
String name = "demo+++";
Integer age = 12;
logger.info("===test:{}{}", name, age);
try {
int num = 1 / 0;
} catch (Exception e) {
logger.info("存在异常:", e);
}
}
}
上面我们用的是他内置的 simple 简单框架,下面我们来绑定第三方日志框架。
注意:如果同时绑定内置的简单日志框架和第三方的日志框架,slf4j默认会使用 内置的简单框架;
绑定 logback 日志框架:
<dependency>
<groupId>net.logstash.logback</groupId>
<artifactId>logstash-logback-encoder</artifactId>
<version>5.3</version>
</dependency>
另外,在绑定 log4j 和 JUL框架时要加适配器。
使用 slf4j 的日志绑定流程:
slf4j 日志开关,通过引入该开关,可以关闭slf4j对日志框架的绑定,也就是不使用日志框架。
<!--slf4j 日志开关-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-nop</artifactId>
<version>1.7.25</version>
</dependency>
3.2 logback 日志框架
logback是由log4j创始人设计的另一个开源日志框架,性能要优于log4j。
主要包含三个模块:
logback-core:其他两个模块的基础模块
logback-classic:是log4j的一个改良版本,同时它完整实现了 slf4j API
logback-access:访问模块与Servlet 容器集成提供通过 http 来访问日志的功能。
使用 slf4j + logback 实现
logback 的配置:
以此会读取以下类型的配置文件:
logback.groovy
logback-test.xml
logback.xml
这里配置的是:logback.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!-- 最好使用绝对路径 -->
<property name="LOG_HOME" value="C:/files/open_platform_log"/>
<property name="log.pattern" value="%d{yyyy-MM-dd HH:mm:ss.SSS} %5level [%10thread] [%X{request-id}] %40.40logger{40} [%10method,%line] : %msg%n"/>
<!--控制台日志输出-->
<appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
<encoder charset="UTF-8">
<pattern>${log.pattern}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<appender name="SYS_INFO" class="ch.qos.logback.core.rolling.RollingFileAppender">
<append>true</append>
<!--过滤器,只打INFO级别的日志-->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>INFO</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/info/open-info-%d{yyyy-MM-dd}.log</fileNamePattern>
<!--保留最近365日的日志,最大日志保存10GB,如果超过则删除旧日志-->
<maxHistory>365</maxHistory>
<totalSizeCap>5GB</totalSizeCap>
</rollingPolicy>
<encoder charset="UTF-8">
<pattern>${log.pattern}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<appender name="SYS_ERROR" class="ch.qos.logback.core.rolling.RollingFileAppender">
<append>true</append>
<!--过滤器,只打ERROR级别的日志-->
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>ERROR</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>${LOG_HOME}/error/open-error-%d{yyyy-MM-dd}.log</fileNamePattern>
<maxHistory>12</maxHistory>
</rollingPolicy>
<encoder charset="UTF-8">
<pattern>${log.pattern}</pattern>
<charset>UTF-8</charset>
</encoder>
</appender>
<!--info和error分开打印-->
<root level="INFO">
<appender-ref ref="CONSOLE"/>
<appender-ref ref="SYS_INFO"/>
<appender-ref ref="SYS_ERROR"/>
<appender-ref ref="LOGSTASH"/>
</root>
</configuration>
3.3 log4j2 日志框架
log4j2 是对Log4j的升级版参考了logback的一些设计理念,并修复了一些问题,因此有一定的提升:
- 异常处理:在logback中,Appender中的异常不会被应用感知到,但是在log4j2中,提供了一些异常处理机制。
- 性能提升:log4j2 相对于 logback 和 log4j 都具有很明显的性能提升。
- 自动重载配置:参考了 logback 的设计,提供了自动刷新参数配置,最实用的就是我们在生成上可以动态的修改日志的级别而不需要重启应用。
- 无垃圾机制:log4j2在大部分情况下,都可以使用其设计的一套无垃圾机制,避免频繁的日志收集导致的 jvm gc。
log4j2 + slf4j 使用:
pom的jar导入:
<!-- slf4j 日志门面-->
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.26</version>
</dependency>
<!--log4j2 日志实现-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-core</artifactId>
<version>2.4.1</version>
</dependency>
<!--适配器log4j2-->
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-slf4j-impl</artifactId>
<version>2.4.1</version>
</dependency>
配置文件 log4j2.xml
<?xml version="1.0" encoding="UTF-8"?>
<!-- status 日志框架本身的输出日志级别 monitotInterval 不低于5秒-->
<Configuration status="debug" monitotInterval="5">
<!--集中配置属性进行管理-->
<properties>
<property name="LOG_HOME">/logs</property>
<property name="ERROR_LOG_FILE_NAME">error</property>
</properties>
<!--日志处理-->
<appenders>
<!--控制台输出-->
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="-%d %-5p (%F:%L) - %m%n" />
</Console>
<RollingRandomAccessFile name="ErrorLog"
fileName="logs/${ERROR_LOG_FILE_NAME}.log"
filePattern="logs/${ERROR_LOG_FILE_NAME}.log.%d{yyyy-MM-dd}.gz">
<PatternLayout
pattern="%d %-5p (%F:%L) - %m%n"/>
<Policies>
<SizeBasedTriggeringPolicy size="1KB"/>
</Policies>
</RollingRandomAccessFile>
</appenders>
<loggers>
<root level="info">
<appender-ref ref="ErrorLog"></appender-ref>
</root>
</loggers>
</Configuration>
Slf4j2TestDemo.class
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Slf4j2TestDemo {
private static final Logger logger = LoggerFactory.getLogger(Slf4j2TestDemo.class);
@Test
public void slf4j2Method() {
// 日志记录输出
logger.info("demo------------------");
logger.debug("debug------------------");
logger.error("error------------------");
}
}
log4j2 异步日志
Log4j 2中记录日志的方式有同步日志和异步日志两种方式,其中异步日志又可分为使用AsyncAppender和使用AsyncLogger(最优的选择)两种方式。
【同步日志】:当输出日志时,必须等待日志输出语句执行完毕后,才能执行后面的业务逻辑语句。
【异步日志】:日志输出语句与业务逻辑语句并不是在同一个线程中运行,而是有专门的线程用于进行日志输出操作,处理业务逻辑的主线程不用等待即可执行后续业务逻辑。
log4j2 使用异步日志,要引用jar包:
<!--log4j2 异步日志-->
<dependency>
<groupId>com.lmax</groupId>
<artifactId>disruptor</artifactId>
<version>3.3.4</version>
</dependency>
全局异步:所有的日志都异步记录,在配置文件上不用做任何的改动,只需要添加一个 log4j2.component.properties 配置:
Log4jContextSelector=org.apache.logging.log4j.core.async.AsyncLoggerContextSelector
混合异步:可以在应用中同时使用同步日志和异步日志,使得日志的配置方式更加的灵活。
(1)全局日志:
在 log4j2.xml 中添加:
(2) 混合异步:要先把全局异步的配置给关闭,然后在 log4j2.xml 中添加配置:
注意:
1、如果使用异步日志,AsyncAppender、AsyncLogger 和全局日志,不要同时使用,否则性能可能会降低。
2、设置 includeLocation=false,打印位置信息会急剧降低异步日志的性能,比同步日志还要慢。
三、springboot 中使用 sfl4j +log4j2