本作品采用知识共享署名-非商业性使用 4.0 国际许可协议进行许可。

一、前言

本文以一个简单的项目为例,一步步展示logback的同步和异步配置方法,并且配置的日志要求满足阿里巴巴Java开发手册-日志规约 ,因为对于线上服务,日志对于排查问题有至关重要的作用,规范的日志格式配合shell脚本可以快速定位问题。

最开始使用Java日志系统,最大的疑惑就是分不清楚log4jslf4jlogback等日志库之间的关系,不过网上有不少文章介绍这部分相关知识,比如理解Java日志体系Java日志框架那些事儿混乱的 Java 日志体系,可以作为提前阅读。

二、配置同步日志

2.1 日志要求

首先项目的整体结构如下图所示:

一共有两个packageutilhttp,下面分别有两个类Util.javaHttp.java,我们的日志要求是:

  • http目录下的文件产生的日志全部记录到:~/logs/${appname}/http.log,级别:INFO
  • util目录下的文件产生的日志全部记录到:~/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_NAMElogbacktest,后面可以用${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.javaUtil.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.javaMain.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简易教程的更多相关文章

  1. LogBack简易教程

    1.简介 LogBack是一个日志框架,它与Log4j可以说是同出一源,都出自Ceki Gülcü之手.(log4j的原型是早前由Ceki Gülcü贡献给Apache基金会的) 1.1 LogBac ...

  2. Spark-Mllib中各分类算法的java实现(简易教程)

    一.简述 Spark是当下非常流行的数据分析框架,而其中的机器学习包Mllib也是其诸多亮点之一,相信很多人也像我那样想要快些上手spark.下面我将列出实现mllib分类的简明代码,代码中将简述训练 ...

  3. JavaScript简易教程(转)

    原文:http://www.cnblogs.com/yanhaijing/p/3685304.html 这是我所知道的最完整最简洁的JavaScript基础教程. 这篇文章带你尽快走进JavaScri ...

  4. Ant 简易教程

    转载:http://www.cnblogs.com/jingmoxukong/p/4433945.html Ant 简易教程 Apache Ant,是一个将软件编译.测试.部署等步骤联系在一起加以自动 ...

  5. Intellj IDEA 简易教程

    Intellj IDEA 简易教程 目录 JDK 安装测试 IDEA 安装测试 调试 单元测试 重构 Git Android 其他 参考资料 Java开发IDE(Integrated Developm ...

  6. Android开发简易教程

    Android开发简易教程 Android 开发因为涉及到代码编辑.UI 布局.打包等工序,有一款好用的IDE非常重要.Google 最早提供了基于 Eclipse 的 ADT 作为开发工具,后来在2 ...

  7. 移动开发之【微信小程序】的原理与权限问题以及相关的简易教程

    这几天圈子里到处都在传播着这样一个东西,微信公众平台提供了一种新的开放能力,开发者可以快速开发一个小程序,取名曰:微信公众平台-小程序 据说取代移动开发安卓和苹果,那这个东东究竟是干吗用的?但很多人觉 ...

  8. JavaScript简易教程

    这是我所知道的最完整最简洁的JavaScript基础教程. 这篇文章带你尽快走进JavaScript的世界——前提是你有一些编程经验的话.本文试图描述这门语言的最小子集.我给这个子集起名叫做“Java ...

  9. Android实战简易教程-第三十九枪(第三方短信验证平台Mob和验证码自己主动填入功能结合实例)

    用户注冊或者找回password时通常会用到短信验证功能.这里我们使用第三方的短信平台进行验证实例. 我们用到第三方短信验证平台是Mob,地址为:http://mob.com/ 一.注冊用户.获取SD ...

随机推荐

  1. golang 开发环境 配置 go语言 liteIDE

    Mac: * 下载go安装包 go语言中文网 ** 通过源码编译安装需要先安装go1.4 wget https://studygolang.com/dl/golang/go1.10.3.src.tar ...

  2. css定位,class属性之间有空格与无空格的区别

    中间有空格的情况 是选择到.class1类下的.class2类子节点,即.class2类的节点要是.class1类子节点 <style> .class1 { color: black; } ...

  3. 2021“MINIEYE杯”中国大学生算法设计超级联赛(7)部分题解

    前言 找大佬嫖到个号来划水打比赛了,有的题没写或者不是我写的就不放了. 目前只有:1004,1005,1007,1008,1011 正题 题目链接:https://acm.hdu.edu.cn/con ...

  4. GDOI 2021 退役记

    Day -n 时常想自己不学OI会怎样,经常畏惧自己其实没有心里想的那样有能力,去机房来麻痹自己 从 3.21 始加大频率刷题,复习以前都学会,而现在都被抛在脑后的算法 反正都要退役了,成绩也得鲜亮点 ...

  5. YbtOJ#493-最大分数【斜率优化dp,分治】

    正题 题目链接:http://www.ybtoj.com.cn/contest/117/problem/1 题目大意 \(n\)个数的一个序列,给其中的一些数打上标记. 一个标记方案的贡献为\(s_1 ...

  6. Jmeter压测学习4--XPath提取器

    没有遇到,直接转载:https://www.cnblogs.com/yoyoketang/p/11962553.html 前言 有些web项目是前后端不分离的,返回的内容不是那种纯进口返回json格式 ...

  7. idea使用gitee的小坑

    1. 账号配置 账号配置登陆时提示 *** is not a valid login name: Email support only. 翻译:只能支持邮箱登录 解决方法:在gitee网站上查看自己配 ...

  8. CSS 奇技淫巧 | 妙用 drop-shadow 实现线条光影效果

    本文将介绍一种利用 CSS 滤镜 filter 的 drop-shadow(),实现对 HTML 元素及 SVG 元素的部分添加阴影效果,以实现一种酷炫的光影效果,用于各种不同的场景之中.通过本文,你 ...

  9. ORACLE 坏块的模拟和查看

    坏块的模拟和查看使用bbed工具修改数据文件的块,然后使用dbv和rman工具查看坏块. 1.创建数据:根据dbv查看没有坏块Total Pages Marked Corrupt : 0create ...

  10. CEF使用过程问题合集

    CEF使用过程问题合集 1.Couldn't mmap icu data file 解决方案:检查程序执行目录下是否有icudtl.dat文件,如果没有请从cef的Resources文件夹中复制一份. ...