Logback是由log4j创始人设计的又一个开源日志组件。logback当前分成三个模块:logback-core,logback-classic和logback-access。logback-core是其它两个模块的基础模块。logback-classic是log4j的一个改良版本。此外logback-classic完整实现SLF4J API使你可以很方便地更换成其它日志系统如log4j或JDK14 Logging。logback-access访问模块与Servlet容器集成提供通过Http来访问日志的功能。
依赖配置
logback的核心,
logback-core
包logback建议使用slf4j来管理日志,方便替换底层实现,要用slf4j,就在依赖中加入
logback-classic
包和slf4j-api
包。加入
junit
测试。
pom.xml:
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.fengyuan</groupId>
<artifactId>logback-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.12</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-core</artifactId>
<version>1.1.2</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.1.3</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>
</project>
配置文件
在项目classpath下加入logback.xml
:
<?xml version="1.0" encoding="UTF-8"?>
<configuration debug="true" scan="true" scanPeriod="30 seconds">
<!-- 输出到控制台 -->
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%level] - %m%n</pattern>
</encoder>
</appender>
<!-- error日志 -->
<!-- 按日期滚动日志 -->
<appender name="ERROR-OUT" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 日志存放位置 -->
<file>logs/error.log</file>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%class:%line] - %m%n</pattern>
</encoder>
<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>error.%d{yyyy-MM-dd}.log.zip</fileNamePattern>
<!-- 保存30天历史 -->
<maxHistory>30</maxHistory>
</rollingPolicy>
</appender>
<!-- info日志 -->
<!-- 按文件大小滚动日志 -->
<appender name="INFO-OUT"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/info.log</file>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%class:%line] - %m%n</pattern>
</encoder>
<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.FixedWindowRollingPolicy">
<fileNamePattern>info.%i.log</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>3</maxIndex>
</rollingPolicy>
<triggeringPolicy
class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>5MB</maxFileSize>
</triggeringPolicy>
</appender>
<!-- debug日志 -->
<!-- 按日期和大小滚动日志 -->
<appender name="DEBUG-OUT"
class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logs/debug.log</file>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%class:%line] - %m%n</pattern>
</encoder>
<filter class="ch.qos.logback.classic.filter.LevelFilter">
<level>DEBUG</level>
<onMatch>ACCEPT</onMatch>
<onMismatch>DENY</onMismatch>
</filter>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>debug-%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<timeBasedFileNamingAndTriggeringPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP">
<maxFileSize>100MB</maxFileSize>
</timeBasedFileNamingAndTriggeringPolicy>
</rollingPolicy>
</appender>
<!-- 配置好前面对应的appender -->
<root level="debug">
<appender-ref ref="STDOUT" />
<appender-ref ref="ERROR-OUT" />
<appender-ref ref="INFO-OUT" />
<appender-ref ref="DEBUG-OUT" />
</root>
</configuration>
包括日志文件的路径,保存的时间,已经滚动的策略都在配置文件中配置,具体看注释。配置文件是从棂枫的这篇博客里学习的,感谢。
测试
测试类:
package com.fengyuan.client;
import org.junit.Test;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Main {
private static Logger log = LoggerFactory.getLogger(Main.class);
@Test
public void testLogBack() {
log.debug("I am debug");
log.info("I am info");
log.error("I am error");
try {
throw new IllegalStateException("I am exception");
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
执行结果:
控制台输出:
10:35:19,739 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback.groovy]
10:35:19,739 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Could NOT find resource [logback-test.xml]
10:35:19,739 |-INFO in ch.qos.logback.classic.LoggerContext[default] - Found resource [logback.xml] at [file:/D:/scm-workspace/logback-demo/target/classes/logback.xml]
10:35:19,803 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - Setting ReconfigureOnChangeFilter scanning period to 30 seconds
10:35:19,803 |-INFO in ReconfigureOnChangeFilter{invocationCounter=0} - Will scan for changes in [[D:\scm-workspace\logback-demo\target\classes\logback.xml]] every 30 seconds.
10:35:19,803 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - Adding ReconfigureOnChangeFilter as a turbo filter
10:35:19,810 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.ConsoleAppender]
10:35:19,812 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [STDOUT]
10:35:19,826 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property
10:35:19,848 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.rolling.RollingFileAppender]
10:35:19,850 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [ERROR-OUT]
10:35:19,854 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property
10:35:19,866 |-INFO in c.q.l.core.rolling.TimeBasedRollingPolicy - Will use zip compression
10:35:19,868 |-INFO in c.q.l.core.rolling.TimeBasedRollingPolicy - Will use the pattern error.%d{yyyy-MM-dd}.log for the active file
10:35:19,875 |-INFO in c.q.l.core.rolling.DefaultTimeBasedFileNamingAndTriggeringPolicy - The date pattern is 'yyyy-MM-dd' from file name pattern 'error.%d{yyyy-MM-dd}.log.zip'.
10:35:19,875 |-INFO in c.q.l.core.rolling.DefaultTimeBasedFileNamingAndTriggeringPolicy - Roll-over at midnight.
10:35:19,877 |-INFO in c.q.l.core.rolling.DefaultTimeBasedFileNamingAndTriggeringPolicy - Setting initial period to Fri Aug 26 10:35:19 CST 2016
10:35:19,879 |-INFO in ch.qos.logback.core.rolling.RollingFileAppender[ERROR-OUT] - Active log file name: logs/error.log
10:35:19,879 |-INFO in ch.qos.logback.core.rolling.RollingFileAppender[ERROR-OUT] - File property is set to [logs/error.log]
10:35:19,880 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.rolling.RollingFileAppender]
10:35:19,880 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [INFO-OUT]
10:35:19,881 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property
10:35:19,884 |-INFO in ch.qos.logback.core.rolling.FixedWindowRollingPolicy@234bef66 - No compression will be used
10:35:19,887 |-INFO in ch.qos.logback.core.rolling.RollingFileAppender[INFO-OUT] - Active log file name: logs/info.log
10:35:19,887 |-INFO in ch.qos.logback.core.rolling.RollingFileAppender[INFO-OUT] - File property is set to [logs/info.log]
10:35:19,887 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.core.rolling.RollingFileAppender]
10:35:19,887 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [DEBUG-OUT]
10:35:19,888 |-INFO in ch.qos.logback.core.joran.action.NestedComplexPropertyIA - Assuming default type [ch.qos.logback.classic.encoder.PatternLayoutEncoder] for [encoder] property
10:35:19,893 |-INFO in c.q.l.core.rolling.TimeBasedRollingPolicy - No compression will be used
10:35:19,893 |-INFO in c.q.l.core.rolling.TimeBasedRollingPolicy - Will use the pattern debug-%d{yyyy-MM-dd}.%i.log for the active file
10:35:19,894 |-INFO in ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP@39fb3ab6 - The date pattern is 'yyyy-MM-dd' from file name pattern 'debug-%d{yyyy-MM-dd}.%i.log'.
10:35:19,894 |-INFO in ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP@39fb3ab6 - Roll-over at midnight.
10:35:19,894 |-INFO in ch.qos.logback.core.rolling.SizeAndTimeBasedFNATP@39fb3ab6 - Setting initial period to Fri Aug 26 10:35:19 CST 2016
10:35:19,895 |-INFO in ch.qos.logback.core.rolling.RollingFileAppender[DEBUG-OUT] - Active log file name: logs/debug.log
10:35:19,895 |-INFO in ch.qos.logback.core.rolling.RollingFileAppender[DEBUG-OUT] - File property is set to [logs/debug.log]
10:35:19,895 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - About to instantiate appender of type [ch.qos.logback.classic.db.DBAppender]
10:35:19,899 |-INFO in ch.qos.logback.core.joran.action.AppenderAction - Naming appender as [DB]
10:35:20,088 |-INFO in ch.qos.logback.core.db.DriverManagerConnectionSource@63753b6d - Driver name=MySQL Connector Java
10:35:20,088 |-INFO in ch.qos.logback.core.db.DriverManagerConnectionSource@63753b6d - Driver version=mysql-connector-java-5.1.38 ( Revision: fe541c166cec739c74cc727c5da96c1028b4834a )
10:35:20,088 |-INFO in ch.qos.logback.core.db.DriverManagerConnectionSource@63753b6d - supportsGetGeneratedKeys=true
10:35:20,090 |-INFO in ch.qos.logback.classic.joran.action.RootLoggerAction - Setting level of ROOT logger to DEBUG
10:35:20,091 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [STDOUT] to Logger[ROOT]
10:35:20,091 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [ERROR-OUT] to Logger[ROOT]
10:35:20,091 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [INFO-OUT] to Logger[ROOT]
10:35:20,091 |-INFO in ch.qos.logback.core.joran.action.AppenderRefAction - Attaching appender named [DEBUG-OUT] to Logger[ROOT]
10:35:20,091 |-INFO in ch.qos.logback.classic.joran.action.ConfigurationAction - End of configuration.
10:35:20,092 |-INFO in ch.qos.logback.classic.joran.JoranConfigurator@731a74c - Registering current configuration as safe fallback point
2016-08-26 10:35:20 [DEBUG] - I am debug
2016-08-26 10:35:20 [INFO] - I am info
2016-08-26 10:35:20 [ERROR] - I am error
2016-08-26 10:35:20 [ERROR] - I am exception
java.lang.IllegalStateException: I am exception
at com.fengyuan.client.Main.testLogBack(Main.java:19) ~[classes/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_65]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_65]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_65]
at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_65]
at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50) [junit-4.12.jar:4.12]
at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12) [junit-4.12.jar:4.12]
at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47) [junit-4.12.jar:4.12]
at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17) [junit-4.12.jar:4.12]
at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325) [junit-4.12.jar:4.12]
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:78) [junit-4.12.jar:4.12]
at org.junit.runners.BlockJUnit4ClassRunner.runChild(BlockJUnit4ClassRunner.java:57) [junit-4.12.jar:4.12]
at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290) [junit-4.12.jar:4.12]
at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71) [junit-4.12.jar:4.12]
at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288) [junit-4.12.jar:4.12]
at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58) [junit-4.12.jar:4.12]
at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268) [junit-4.12.jar:4.12]
at org.junit.runners.ParentRunner.run(ParentRunner.java:363) [junit-4.12.jar:4.12]
at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86) [.cp/:na]
at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) [.cp/:na]
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459) [.cp/:na]
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:675) [.cp/:na]
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) [.cp/:na]
at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192) [.cp/:na]
在项目路径下多了一个logs文件夹,里面有3个文件,每个文件记录对应的日志:
Lombok
可以借助Lombok来简化代码的编写。
有了Lombok,只需要在要写日志的类中加入@Slf4j等注解,不用再写private static Logger log = LoggerFactory.getLogger(Main.class);
使用Lombok后的测试类:
package com.fengyuan.client;
import org.junit.Test;
import lombok.extern.slf4j.Slf4j;
@Slf4j
public class Main {
@Test
public void testLogBack() {
log.debug("I am debug");
log.info("I am info");
log.error("I am error");
try {
throw new IllegalStateException("I am exception");
} catch (Exception e) {
log.error(e.getMessage(), e);
}
}
}
日志输出到数据库
建表
首先在数据库中建三张表,用来存储日志记录:
BEGIN;
DROP TABLE IF EXISTS logging_event_property;
DROP TABLE IF EXISTS logging_event_exception;
DROP TABLE IF EXISTS logging_event;
COMMIT;
BEGIN;
CREATE TABLE logging_event(
timestmp BIGINT NOT NULL,
formatted_message TEXT NOT NULL,
logger_name VARCHAR(254) NOT NULL,
level_string VARCHAR(254) NOT NULL,
thread_name VARCHAR(254),
reference_flag SMALLINT,
arg0 VARCHAR(254),
arg1 VARCHAR(254),
arg2 VARCHAR(254),
arg3 VARCHAR(254),
caller_filename VARCHAR(254) NOT NULL,
caller_class VARCHAR(254) NOT NULL,
caller_method VARCHAR(254) NOT NULL,
caller_line CHAR(4) NOT NULL,
event_id BIGINT NOT NULL AUTO_INCREMENT PRIMARY KEY
);
COMMIT;
BEGIN;
CREATE TABLE logging_event_property(
event_id BIGINT NOT NULL,
mapped_key VARCHAR(254) NOT NULL,
mapped_value TEXT,
PRIMARY KEY(event_id, mapped_key),
FOREIGN KEY (event_id) REFERENCES logging_event(event_id)
);
COMMIT;
BEGIN;
CREATE TABLE logging_event_exception (
event_id BIGINT NOT NULL,
i SMALLINT NOT NULL,
trace_line VARCHAR(254) NOT NULL,
PRIMARY KEY(event_id, i),
FOREIGN KEY (event_id) REFERENCES logging_event(event_id)
);
COMMIT;
配置
在logback.xml
中加入输出到数据库的appender,配置好数据源:
<!-- 输出到数据库 -->
<appender name="DB" class="ch.qos.logback.classic.db.DBAppender">
<connectionSource
class="ch.qos.logback.core.db.DriverManagerConnectionSource">
<driverClass>com.mysql.jdbc.Driver</driverClass>
<url>jdbc:mysql://localhost:3306/test</url>
<user>root</user>
<password>123456</password>
</connectionSource>
</appender>
加入引用
<appender-ref ref="DB" />
加入数据库驱动依赖
这一步很多人容易忘记,配置好相应的配置之后,要记得加入mysql驱动的依赖:
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.38</version>
</dependency>
然后,日志就会输出到数据表中了。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。