Java Logback简易教程
本作品采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可。
一、前言
本文以一个简单的项目为例,一步步展示logback的同步和异步配置方法,并且配置的日志要求满足阿里巴巴Java开发手册-日志规约 ,因为对于线上服务,日志对于排查问题有至关重要的作用,规范的日志格式配合shell脚本可以快速定位问题。
最开始使用Java日志系统,最大的疑惑就是分不清楚log4j、slf4j、logback等日志库之间的关系,不过网上有不少文章介绍这部分相关知识,比如理解Java日志体系、Java日志框架那些事儿、混乱的 Java 日志体系,可以作为提前阅读。
二、配置同步日志
2.1 日志要求
首先项目的整体结构如下图所示:

一共有两个package,util和http,下面分别有两个类Util.java和Http.java,我们的日志要求是:
http目录下的文件产生的日志全部记录到:~/logs/${appname}/http.log,级别:INFOutil目录下的文件产生的日志全部记录到:~/logs/${appname}/util.log,级别:DEBUG其余日志文件均记录到:~/logs/${appname}/${appname}.log,级别:INFO- 要求所有的日志均至少保存15天
- 如果保存15天,内容太大可能造成磁盘风险,则最大保存10GB的日志。
2.2 添加POM
使用logback+slf4j的组合,需要依赖的pom如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<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.test</groupId>
<artifactId>logback-test</artifactId>
<version>1.0-SNAPSHOT</version>
<dependencies>
<dependency>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
<version>1.7.10</version>
</dependency>
<dependency>
<groupId>ch.qos.logback</groupId>
<artifactId>logback-classic</artifactId>
<version>1.2.3</version>
</dependency>
</dependencies>
</project>
2.3 添加logback.xml
logback查找配置的顺序如下所示:
- 在系统配置文件System Properties中寻找是否有logback.configurationFile对应的value
- 在classpath下寻找是否有logback.groovy(即logback支持groovy与xml两种配置方式)
- 在classpath下寻找是否有logback-test.xml
- 在classpath下寻找是否有logback.xml
在resources目录下添加logback.xml,目前内容为空,如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
</configuration>

2.4 配置appender
appender用来配置日志的文件名、日志的写入策略,滚动策略等,我们按照2.1的要求配置appender如下所示:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="APP_NAME" value="logbacktest" />
<property name="LOG_NAME" value="${user.home}/logs/${APP_NAME}/${APP_NAME}.log" />
<appender name="APP_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--指定日志文件名称-->
<file>${LOG_NAME}</file>
<encoder>
<!--指定日志内容格式-->
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
<charset>utf8</charset>
</encoder>
<rollingPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${LOG_NAME}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!--日志最大保存15天-->
<maxHistory>15</maxHistory>
<!--日志最大的文件大小-->
<maxFileSize>100MB</maxFileSize>
<!--日志最大保存10GB-->
<totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>
</appender>
</configuration>
property可以用来定义变量,我们定义APP_NAME为logbacktest,后面可以用${APP_NAME}来使用这个变量,其它的配置见注释。
2.5 配置root
上面我们定义了appender,定义了日志文件名,日志写入策略,但是现在还有一个问题就是:哪个路径下的日志写入上面定义的appender?是Main.java下的,还是Util.java下的呢?
当然,logback有配置专门去配置路径,这里我们先配置root,即:默认表示所有路径。我们在2.1中的要求是其余日志文件均记录到:~/logs/${appname}/${appname}.log,级别:INFO,我们目前可以通过配置root来满足这个需求,如下:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="APP_NAME" value="logbacktest" />
<property name="LOG_NAME" value="${user.home}/logs/${APP_NAME}/${APP_NAME}.log" />
<appender name="APP_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
...
</appender>
<root level="INFO">
<!--ref表示具体的appender name-->
<appender-ref ref="APP_LOG" />
</root>
</configuration>
这样配置之后,所有的日志都会以root的level,即INFO去使用APP_LOG这个appender打印。
2.6 打印日志
编辑Util.java如下所示,打印5个级别的日志:
package com.test.util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author bodong.ybd
* @date 2019/6/17
*/
public class Util {
private static final Logger log = LoggerFactory.getLogger(Util.class);
public static void loginfo() {
log.trace("trace");
log.debug("debug");
log.info("info");
log.warn("warn");
log.error("error");
}
}
编辑Main.java文件内容如下所示:
package com.test;
import com.test.util.Util;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* @author bodong.ybd
* @date 2019/6/17
*/
public class Main {
private static final Logger log = LoggerFactory.getLogger(Main.class);
private static void loginfo() {
log.trace("trace");
log.debug("debug");
log.info("info");
log.warn("warn");
log.error("error");
}
public static void main(String[] args) {
loginfo();
Util.loginfo();
}
}
运行Main.java,打印日志,效果如下:
➜ ~ ls ~/logs/logbacktest
logbacktest.log
➜ ~ cat ~/logs/logbacktest/logbacktest.log
2019-06-19 19:33:26 [main] INFO com.test.Main - info
2019-06-19 19:33:26 [main] WARN com.test.Main - warn
2019-06-19 19:33:26 [main] ERROR com.test.Main - error
2019-06-19 19:33:26 [main] INFO com.test.util.Util - info
2019-06-19 19:33:26 [main] WARN com.test.util.Util - warn
2019-06-19 19:33:26 [main] ERROR com.test.util.Util - error
可以看到产生了一个日志文件logbacktest.log,并且Main.java和Util.java的日志都打印在了这个文件中,日志级别是root配置的INFO,但是这样并不满足要求,因为2.1要求:util目录下的文件产生的日志全部记录到:~/logs/${appname}/util.log,级别:DEBUG,这个问题需要配置logger来解决。
2.7 配置logger
logger用来设置某个包或者类具体日志的打印级别,下面我们把Util.java配置拆出去。
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<property name="APP_NAME" value="logbacktest" />
<property name="LOG_NAME" value="${user.home}/logs/${APP_NAME}/${APP_NAME}.log" />
<property name="UTIL_NAME" value="${user.home}/logs/${APP_NAME}/util.log" />
<appender name="APP_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
...
</appender>
<appender name="UTIL_LOG" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!--指定日志文件名称-->
<file>${UTIL_NAME}</file>
<encoder>
<!--指定日志内容格式-->
<pattern>%d{yyyy-MM-dd HH:mm:ss} [%thread] %-5level %logger{36} - %msg%n</pattern>
<charset>utf8</charset>
</encoder>
<rollingPolicy
class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<fileNamePattern>${UTIL_NAME}.%d{yyyy-MM-dd}.%i.log</fileNamePattern>
<!--日志最大保存15天-->
<maxHistory>15</maxHistory>
<!--日志最大的文件大小-->
<maxFileSize>100MB</maxFileSize>
<!--日志最大保存10GB-->
<totalSizeCap>10GB</totalSizeCap>
</rollingPolicy>
</appender>
<!--com.test.util目录下的文件产生的日志全部记录到util.log-->
<!--默认的日志级别是DEBUG-->
<!--additivity=false表示如果能匹配到这条规则就不用往上继续查找到root节点去-->
<logger name="com.test.util" level="DEBUG" additivity="false" >
<appender-ref ref="UTIL_LOG"/>
</logger>
<root level="INFO">
<!--ref表示具体的appender-->
<appender-ref ref="APP_LOG" />
</root>
</configuration>
这样配置完成后就可以把Util.java和Main.java的日志分开了,效果如下所示:
➜ ~ ls ~/logs/logbacktest
logbacktest.log util.log
➜ ~ cat ~/logs/logbacktest/logbacktest.log
2019-06-19 19:42:51 [main] INFO com.test.Main - info
2019-06-19 19:42:51 [main] WARN com.test.Main - warn
2019-06-19 19:42:51 [main] ERROR com.test.Main - error
➜ ~ cat ~/logs/logbacktest/util.log
2019-06-19 19:42:51 [main] DEBUG com.test.util.Util - debug
2019-06-19 19:42:51 [main] INFO com.test.util.Util - info
2019-06-19 19:42:51 [main] WARN com.test.util.Util - warn
2019-06-19 19:42:51 [main] ERROR com.test.util.Util - error
好了,看到这里,详细Http.java相关的日志你肯定也会配置了,试试看。
三、配置异步日志
| 写入方式 | 优点 | 缺点 |
|---|---|---|
| 同步 | 一般使用O_SYNC标志打开文件,即每条日志会至少写入磁盘缓存,安全。 | 慢,每条都会刷盘。 |
| 异步 | 将内容写入到内存即可返回(不同异步库可能用不同数据结构),速度快。更多可参考:https://logging.apache.org/log4j/2.x/manual/async.html | 不安全,如果内存数据结构满了或者机器断电,可能造成数据丢失。 |
使用logback配置异步日志可以使用appender:ch.qos.logback.classic.AsyncAppender,将上面的Util.java的log配置为异步如下所示:
<appender name ="ASYNC_UTIL_LOG" class="ch.qos.logback.classic.AsyncAppender">
<discardingThreshold>0</discardingThreshold>
<queueSize>512</queueSize>
<neverBlock>true</neverBlock>
<appender-ref ref="UTIL_LOG"/>
</appender>
<logger name="com.test.util" level="DEBUG" additivity="false" >
<appender-ref ref="ASYNC_UTIL_LOG"/>
</logger>
discardingThreshold:默认情况下,当阻塞队列剩余20%的容量时,它将丢弃级别TRACE,DEBUG和INFO的事件,仅保留级别WARN和ERROR的事件。要保留所有事件,请将discardingThreshold设置为0。
queueSize:阻塞队列的最大容量。默认情况下,queueSize设置为256。
neverBlock:如果为false(默认值),appender将阻塞在添加队列的接口处。设置为true,appender将删除消息,不会阻止您的应用程序。
这几个参数更加详细的解释见: https://logback.qos.ch/manual/appenders.html
logback还有另一种异步日志配置方式,即使用disruptor,可参考这里配置。
四、总结
本文以一个例子说明了logback+slf4配置同步和异步日志的方法,使用异步日志除了可以提高程序性能之外,还可以防止部分磁盘IO Hang导致的问题,水平有限,如有不足,请指出。
[完]
Java Logback简易教程的更多相关文章
- LogBack简易教程
1.简介 LogBack是一个日志框架,它与Log4j可以说是同出一源,都出自Ceki Gülcü之手.(log4j的原型是早前由Ceki Gülcü贡献给Apache基金会的) 1.1 LogBac ...
- Spark-Mllib中各分类算法的java实现(简易教程)
一.简述 Spark是当下非常流行的数据分析框架,而其中的机器学习包Mllib也是其诸多亮点之一,相信很多人也像我那样想要快些上手spark.下面我将列出实现mllib分类的简明代码,代码中将简述训练 ...
- JavaScript简易教程(转)
原文:http://www.cnblogs.com/yanhaijing/p/3685304.html 这是我所知道的最完整最简洁的JavaScript基础教程. 这篇文章带你尽快走进JavaScri ...
- Ant 简易教程
转载:http://www.cnblogs.com/jingmoxukong/p/4433945.html Ant 简易教程 Apache Ant,是一个将软件编译.测试.部署等步骤联系在一起加以自动 ...
- Intellj IDEA 简易教程
Intellj IDEA 简易教程 目录 JDK 安装测试 IDEA 安装测试 调试 单元测试 重构 Git Android 其他 参考资料 Java开发IDE(Integrated Developm ...
- Android开发简易教程
Android开发简易教程 Android 开发因为涉及到代码编辑.UI 布局.打包等工序,有一款好用的IDE非常重要.Google 最早提供了基于 Eclipse 的 ADT 作为开发工具,后来在2 ...
- 移动开发之【微信小程序】的原理与权限问题以及相关的简易教程
这几天圈子里到处都在传播着这样一个东西,微信公众平台提供了一种新的开放能力,开发者可以快速开发一个小程序,取名曰:微信公众平台-小程序 据说取代移动开发安卓和苹果,那这个东东究竟是干吗用的?但很多人觉 ...
- JavaScript简易教程
这是我所知道的最完整最简洁的JavaScript基础教程. 这篇文章带你尽快走进JavaScript的世界——前提是你有一些编程经验的话.本文试图描述这门语言的最小子集.我给这个子集起名叫做“Java ...
- Android实战简易教程-第三十九枪(第三方短信验证平台Mob和验证码自己主动填入功能结合实例)
用户注冊或者找回password时通常会用到短信验证功能.这里我们使用第三方的短信平台进行验证实例. 我们用到第三方短信验证平台是Mob,地址为:http://mob.com/ 一.注冊用户.获取SD ...
随机推荐
- Dapr实战(三)状态管理
状态管理解决了什么 分布式应用程序中的状态可能很有挑战性. 例如: 应用程序可能需要不同类型的数据存储. 访问和更新数据可能需要不同的一致性级别. 多个用户可以同时更新数据,这需要解决冲突. 服务必须 ...
- Java基础系列(27)- 什么是方法
何谓方法 System.out.println();它是什么呢 # System:类 # out:对象 # println():方法 Java方法是语句的集合,它们在一起执行一个功能 方法是解决一类问 ...
- 使用 FIO 对 Kubernetes 持久卷进行 Benchmark:读/写(IOPS)、带宽(MB/s)和延迟
工具 Dbench https://github.com/leeliu/dbench 用法 编辑 dbench.yaml 文件中的 storageClassName 以匹配你自己的 Storage C ...
- 【C++ Primer Plus】编程练习答案——第3章
1 void ch3_1() { 2 using namespace std; 3 unsigned int factor = 12; 4 unsigned int inch, feet; 5 cou ...
- 测试rac数据文件建本地及处理
模拟用户zytuser的表空间ZYTUSER_TBS表空间添加数据文件到本地.--环境准备1.创建一个表空间--创建表空间create tablespace ZYTUSER_TBS datafile ...
- 11.4.2 LVS—NAT
Virtual Server via NAT(VS-NAT) 用地址翻译实现虚拟服务器。地址转换器有能被外界访问到的合法IP地址,它修改来自专有网络的流出包的地址。外界看起来包是来自地址转换器本身,, ...
- 详解package-lock.json的作用
目录 详解package-lock.json package-lock.json的作用 版本号的定义规则与前缀对安装的影响 改动package.json后依旧能改变项目依赖的版本 当前项目的真实版本号 ...
- 题解 CF555E Case of Computer Network
题目传送门 题目大意 给出一个\(n\)个点\(m\)条边的无向图,有\(q\)次有向点对\((s,t)\),问是否存在一种方法定向每条边使得每个点对可以\(s\to t\). \(n,m,q\le ...
- 在python中实现BASE64编码
什么是Base64编码 BASE64是用于传输8Bit字节的编码方式之一,是一种基于64个可打印字符来表示二进制数据的方法. 如下是转换表:The Base64 Alphabet Base64编码可以 ...
- 网络通信IO的演变过程(一)(一个门外汉的理解)
以前从来不懂IO的底层,只知道一个大概,就是输入输出的管道怼到一起,然后就可以传输数据了. 最近看了周志垒老师的公开课后,醍醐灌顶. 所以做一个简单的记录. 0 计算机组成原理相关 0.1. 计算机的 ...