Java 日志系统详解之Logback在GCloud上的最佳实践

摘要:本文根据官方文档和实验,总结一下使用logback在GCloud Standard Environment 下的最佳实践。本文不对具体配置做过多的解释,详细解释参照文档。本文主要以问题和需求驱动,讨论实践中的最佳做法以及为达到某些目的的最佳做法。

日志问题抽象

我们可以思考,我们对于日志系统的需求抽象出来都有哪些需求呢?

  1. 通过配置,在不更改代码的情况下,打印不同级别的日志
  2. 通过配置,可以切换日志的输出位置
  3. 通过配置,可以切换日志的格式
  4. 可以灵活的控制日志的输出,比如不同的类输出到不同的日志中,根据关键字触发不同的日志输出
  5. 可以对日志的输出进行分页存储在文件中,可以基于时间或其他约定

Logback 与 GCloud Standard Environment

在 GCloud 的 Standard Environment 中,官方是使用的 JUL, 但是通过实验尝试发现,logback 也可以正常使用 (目前使用 spring boot 测试没有问题)。但是在使用中需要注意几点:

  1. 文件存储日志无法使用,目前测试发现如果logback配置中有文件的 appender,那么会报错,由于文件系统为只读,无法创建日志文件。所以我们还是使用控制台appender,这个 appender 会自动把日志输出到 SkyDriver 的 Logging 系统,也同样方便产看和追踪。
  2. 网络 appender 不能保证正确输出,目前测试发现使用 LogEntries 有些日志可以输出,但是有时候无法正确打印。(目前使用filter 输出到 LogEntries 能够成功)

最佳实践

下面根据近期对 logback的学习以及在 GCloud Standard Environment 的实验总结一下最佳实践。

基础配置

级别

Product 环境下,root 日志设置级别为 INFO
Dev 环境下,root 日志设置级别为 DEBUG,或者是默认。

logger

一般情况不需要单独配置 logger,每个类都应该有自己的类名的 logger。也就是每个类都会使用 Factory 创建一个当前类名的logger,通常不需要其他配置。

有两种特殊配置的 logger:

  1. 特殊的类(class)或包(package)需要特殊的配置,那么为该类或包配置一个logger。对于logger的 additivity="false" 和引用的 appender 的配置需要小心,一定要避免在控制台重复输出。
  2. 特殊的logger,指定一些特殊的名字,然后在全局使用。尽可能少用。

appender

格式

可以统一定义变量,便于统一修改格式。

1
<property name="patternString" value="%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n%rEx"  />

几种常用的 appender

  1. Console, 必备
1
2
3
4
5
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${patternString}</pattern>
</encoder>
</appender>
  1. 网络,比如 LogEntries
1
2
3
4
5
6
7
8
9
10
<appender name="LE"
class="com.logentries.logback.LogentriesAppender">
<Debug>False</Debug>
<Token>XXXX</Token>
<Ssl>True</Ssl>
<facility>USER</facility>
<layout>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</layout>
</appender>

高级用法

不同类的日志输出不同级别

两种方法,

  1. 代码指定
  2. 配置指定

推荐第二种,通过增加一个 logger 的配置,然后配置级别。

指定带有某些特定字符串的日志输出

比如指定某些特定日志输出到 LogEntries,减少流量,同时也能够追踪重要日志。

  1. 定义一个 Filter,可以扩展 SwitchFilter 抽象类 (自用),只需要指定开关的字符串。包含字符串则打印,不包含则不打印。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
public abstract class SwitchFilter extends Filter<ILoggingEvent> {

abstract String getSwitchString();

@Override
public FilterReply decide(ILoggingEvent event) {
if (event.getMessage().contains(this.getSwitchString())) {
return FilterReply.ACCEPT;
} else {
return FilterReply.DENY;
}
}
}

public class LogEntriesFilter extends SwitchFilter {

public static String SWITCH_STR = "LogEntries";
@Override
String getSwitchString() {
return SWITCH_STR;
}
}
  1. 创建一个 对应的 appender, 比如 LogEntries 的 appender,加入该filter的配置。
1
2
3
4
5
6
7
8
9
10
11
<appender name="FILTERED_LE"
class="com.logentries.logback.LogentriesAppender">
<filter class="science.mengxin.java.gcloudspringdemo.utils.logback.LogEntriesFilter" />
<Debug>False</Debug>
<Token>XXX</Token>
<Ssl>True</Ssl>
<facility>USER</facility>
<layout>
<pattern>FILTERED_LE: %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</layout>
</appender>
  1. 指定某些日志打印到该 appender
1
logger.error("{} - normal log", LogEntriesFilter.SWITCH_STR)

其他参考

最佳日志实践(v2.0)

最佳日志实践(v2.0)

BestPractices/Logging Best Practices

BestPractices/Logging Best Practices

10 Tips for Proper Application Logging

10 Tips for Proper Application Logging