本作品采用知识共享署名-非商业性使用 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. springboot多数据源配合docker部署mysql主从实现读写分离

    本篇主要有两部分: 1.使用docker部署mysql主从 实现主从复制 2.springboot项目多数据源配置,实现读写分离 一.使用docker部署mysql主从 实现主从复制 此次使用的是wi ...

  2. Linux系列(40) - 自动同步时间chrony

    前言 Centos8开始取消了ntp同步时间,改为chrony同步 chrony工具安装 yum -y install chrony 修改配置文件 将配置文件中的同步服务器修改为国内的时间服务器(推荐 ...

  3. element-ui的Tree树组件使用技巧

    目录 1,前言 2,需求 3,解决思路 4,完整代码 5,总结 1,前言 最近这段时间在做一个新的模块,其中有一个三层的树结构,产品经理提出了一个很古怪的需求,整的我只能自己控制树的交互,写完之后,感 ...

  4. openTSDB-采集器批量部署-tcollector

        前提: 所需安装采集器服务器与部署服务器之间都已经配置无密码登录 1.安装服务器安装expect包 安装服务器与需要安装Tcollector服务器之间未配置无密登录需要此步. yum inst ...

  5. HTML 网页开发、CSS 基础语法——九.CSS概述

    1.产生背景 从HTML的答案盛开时,样式就以各种形式存在,最初的HTML只i包含很少的显示属性.随着HTML的成长为了满足页面设计者的要求,HTML添加了许多显示功能,随着功能的增加HTML页面变得 ...

  6. CF280C-Game on Tree【数学期望】

    正题 题目链接:https://www.luogu.com.cn/problem/CF280C 题目大意 \(n\)个点的一棵树,每次选择一个没有染色的点把它和它的子树染黑,求期望全部染黑的步数. 解 ...

  7. unittest基本原理及介绍

    unittest基本原理: unittest是python自带的测试框架,还有一个框架是:pytest,这里简单介绍下unittest模块的简单应用 unittest是python的标准测试库,相比于 ...

  8. 在windoes server2008部署kettle遇到的问题

    本机电脑是windows10,在部署ketle时一切顺利,但在windows server2008服务器上部署,各种报错,毕竟线上环境比较复杂-- 问题一:启动kettle的spoon.bat文件时, ...

  9. 前端规范之CSS规范(Stylelint)

    代码规范是软件开发领域经久不衰的话题,几乎所有工程师在开发过程中都会遇到或思考过这一问题.而随着前端应用的大型化和复杂化,越来越多的前端团队也开始重视代码规范.同样,前段时间,笔者所在的团队也开展了一 ...

  10. MySQL8 根据某属性查询字段排名由自定义变量到rank()的变动

    在mysql8 之前的版本,因为没有rank()方法的存在,所以在对字段进行排名时,使用的是自定义自变量的方法,比如: select id,name,@rank=@rank+1 as ranks fr ...