自定义拓展日志

平常的使用过程中我们经常需要使用日志打印,有些信息比如线程信息、时间戳、日志级别等都可以用一些默认规则很容易的使用。但是如果是一些自定义的信息可能就需要拓展一下了。

Logback

简单使用

1
2
3
4
5
6
7
8
9
10
11
12
13
package chapters.introduction;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class HelloWorld1 {

public static void main(String[] args) {
Logger logger = LoggerFactory.getLogger("chapters.introduction.HelloWorld1");
logger.debug("Hello world.");

}
}

20:49:07.962 [main] DEBUG chapters.introduction.HelloWorld1 - Hello world.

配置

1
2
3
4
5
6
7
8
9
10
11
12
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<!-- encoders are assigned the type
ch.qos.logback.classic.encoder.PatternLayoutEncoder by default -->
<encoder>
<pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
</appender>
<root level="debug">
<appender-ref ref="STDOUT" />
</root>
</configuration>
1
2
3
4
5
6
7
8
9
10
11
12
package chapters.configuration;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class Foo {
static final Logger logger = LoggerFactory.getLogger(Foo.class);

public void doIt() {
logger.debug("Did it again!");
}
}

16:06:09.031 [main] INFO chapters.configuration.MyApp1 - Entering application.
16:06:09.046 [main] DEBUG chapters.configuration.Foo - Did it again!
16:06:09.046 [main] INFO chapters.configuration.MyApp1 - Exiting application.

通过配置规则*%d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n*来输出响应信息

logback主要由三块组成: Logger, Appender and Layout

Appender负责把日志事件的任务写入

1
2
3
4
5
public interface Appender<E> extends LifeCycle, ContextAware, FilterAttachable {
public String getName();
public void setName(String name);
void doAppend(E event);
}

encoder将事件转换为一个字节数组

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package ch.qos.logback.core.encoder;

public interface Encoder<E> extends ContextAware, LifeCycle {

/**
* This method is called when the owning appender starts or whenever output
* needs to be directed to a new OutputStream, for instance as a result of a
* rollover.
*/
void init(OutputStream os) throws IOException;

/**
* Encode and write an event to the appropriate {@link OutputStream}.
* Implementations are free to defer writing out of the encoded event and
* instead write in batches.
*/
void doEncode(E event) throws IOException;


/**
* This method is called prior to the closing of the underling
* {@link OutputStream}. Implementations MUST not close the underlying
* {@link OutputStream} which is the responsibility of the owning appender.
*/
void close() throws IOException;
}

Layout负责将传入的事件转换为一个字符串

1
2
3
4
5
6
7
8
9
public interface Layout<E> extends ContextAware, LifeCycle {

String doLayout(E event);
String getFileHeader();
String getPresentationHeader();
String getFileFooter();
String getPresentationFooter();
String getContentType();
}

完全自定义layout

LayoutBase

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<configuration>
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="chapters.layouts.MySampleLayout2">
<prefix>MyPrefix</prefix>
<printThreadName>false</printThreadName>
</layout>
</encoder>
</appender>

<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package chapters.layouts;

import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.core.LayoutBase;

public class MySampleLayout2 extends LayoutBase<ILoggingEvent> {

String prefix = null;
boolean printThreadName = true;

public void setPrefix(String prefix) {
this.prefix = prefix;
}

public void setPrintThreadName(boolean printThreadName) {
this.printThreadName = printThreadName;
}

public String doLayout(ILoggingEvent event) {
StringBuffer sbuf = new StringBuffer(128);
if (prefix != null) {
sbuf.append(prefix + ": ");
}
sbuf.append(event.getTimeStamp() - event.getLoggerContextVO().getBirthTime());
sbuf.append(" ");
sbuf.append(event.getLevel());
if (printThreadName) {
sbuf.append(" [");
sbuf.append(event.getThreadName());
sbuf.append("] ");
} else {
sbuf.append(" ");
}
sbuf.append(event.getLoggerName());
sbuf.append(" - ");
sbuf.append(event.getFormattedMessage());
sbuf.append(LINE_SEP);
return sbuf.toString();
}
}

PatternLayout

Logback classic附带一个叫做PatternLayout灵活的布局。所有Layout,PatternLayout负责日志事件并返回一个字符串。这个字符串可以通过调整PatternLayout定制的转换模式。

你可以在转换模式里面插入任意文字,格式都是以百分号开头,括号括起来的。比如[%thread]

创建一个自定义转换说明符
  1. 继承ClassicConverter
1
2
3
4
5
6
7
8
9
10
public class MySampleConverter extends ClassicConverter {

long start = System.nanoTime();

@Override
public String convert(ILoggingEvent event) {
long nowInNanos = System.nanoTime();
return Long.toString(nowInNanos-start);
}
}
  1. 声明转换文件
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<configuration>

<conversionRule conversionWord="nanos"
converterClass="chapters.layouts.MySampleConverter" />

<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>%-6nanos [%thread] - %msg%n</pattern>
</encoder>
</appender>

<root level="DEBUG">
<appender-ref ref="STDOUT" />
</root>
</configuration>

4868695 [main] DEBUG - Everything’s going well
5758748 [main] ERROR - maybe not quite…

另外一种方式

  1. 继承PatternLayout,默认转换说明符号多设置一条
1
2
3
4
5
public class TraceIdPatternLogbackLayout extends PatternLayout {
static {
defaultConverterMap.put("traceId", LogbackPatternConverter.class.getName());
}
}
  1. 继承ClassicConverter
1
2
3
4
5
6
public class LogbackPatternConverter extends ClassicConverter {
@Override
public String convert(ILoggingEvent iLoggingEvent) {
return Strings.isNullOrEmpty(TraceUtil.getTraceId()) ? "N/A" : TraceUtil.getTraceId();
}
}
  1. 配置自定义layout配置文件
1
2
3
4
5
6
7
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder class="ch.qos.logback.core.encoder.LayoutWrappingEncoder">
<layout class="com.absurd.logback.TraceIdPatternLogbackLayout">
<pattern>[%d{MM-dd HH:mm:ss.SSS}] [%traceId] [%thread] %-5level %logger[%M] - %msg%n</pattern>
</layout>
</encoder>
</appender>

Log4j

简单使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import com.foo.Bar;

import org.apache.log4j.Logger;
import org.apache.log4j.BasicConfigurator;

public class MyApp {

// Define a static logger variable so that it references the
// Logger instance named "MyApp".
static Logger logger = Logger.getLogger(MyApp.class);

public static void main(String[] args) {

// 打印到控制台
BasicConfigurator.configure();

logger.info("Entering application.");
Bar bar = new Bar();
bar.doIt();
logger.info("Exiting application.");
}
}

0 [main] INFO MyApp - Entering application.
36 [main] DEBUG com.foo.Bar - Did it again!
51 [main] INFO MyApp - Exiting application.

手动设置配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import com.foo.Bar;

import org.apache.log4j.Logger;
import org.apache.log4j.PropertyConfigurator;

public class MyApp {

static Logger logger = Logger.getLogger(MyApp.class.getName());

public static void main(String[] args) {


// BasicConfigurator replaced with PropertyConfigurator.
PropertyConfigurator.configure(args[0]);

logger.info("Entering application.");
Bar bar = new Bar();
bar.doIt();
logger.info("Exiting application.");
}
}

屏蔽日志

1
2
3
4
5
6
7
8
9
log4j.rootLogger=DEBUG, A1
log4j.appender.A1=org.apache.log4j.ConsoleAppender
log4j.appender.A1.layout=org.apache.log4j.PatternLayout

# Print the date in ISO 8601 format
log4j.appender.A1.layout.ConversionPattern=%d [%t] %-5p %c - %m%n

# Print only messages of level WARN or above in the package com.foo.
log4j.logger.com.foo=WARN

2000-09-07 14:07:41,508 [main] INFO MyApp - Entering application.
2000-09-07 14:07:41,529 [main] INFO MyApp - Exiting application.

多个appender

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
log4j.rootLogger=debug, stdout, R

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=org.apache.log4j.PatternLayout

# Pattern to output the caller's file name and line number.
log4j.appender.stdout.layout.ConversionPattern=%5p [%t] (%F:%L) - %m%n

log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=example.log

log4j.appender.R.MaxFileSize=100KB
# Keep one backup file
log4j.appender.R.MaxBackupIndex=1

log4j.appender.R.layout=org.apache.log4j.PatternLayout
log4j.appender.R.layout.ConversionPattern=%p %t %c - %m%n

INFO [main] (MyApp2.java:12) - Entering application.
DEBUG [main] (Bar.java:8) - Doing it again!
INFO [main] (MyApp2.java:15) - Exiting application.

自定义

log4j-1.x

1
2
3
4
5
6
public class TraceIdPatternConverter  extends PatternConverter {
@Override
protected String convert(LoggingEvent loggingEvent) {
return Strings.isNullOrEmpty(TraceUtil.getTraceId()) ? "N/A" : TraceUtil.getTraceId();
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class TraceIdPatternParser extends PatternParser {

public TraceIdPatternParser(String pattern) {
super(pattern);
}

@Override
protected void finalizeConverter(char c) {
if ('T' == c) {
addConverter(new TraceIdPatternConverter());
} else {
super.finalizeConverter(c);
}
}
}
1
2
3
4
5
6
public class TraceIdPatternLayout extends PatternLayout {
@Override
protected PatternParser createPatternParser(String pattern) {
return new TraceIdPatternParser(pattern);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
log4j.rootLogger=debug, stdout, R

log4j.appender.stdout=org.apache.log4j.ConsoleAppender
log4j.appender.stdout.layout=com.absurd.log4j.TraceIdPatternLayout

# Pattern to output the caller's file name and line number.
log4j.appender.stdout.layout.ConversionPattern=%5p [%T] [%t] (%F:%L) - %m%n

log4j.appender.R=org.apache.log4j.RollingFileAppender
log4j.appender.R.File=trace.log

log4j.appender.R.MaxFileSize=100KB
# Keep one backup file
log4j.appender.R.MaxBackupIndex=1

log4j.appender.R.layout=com.absurd.log4j.TraceIdPatternLayout
log4j.appender.R.layout.ConversionPattern=%p [%T] %t %c - %m%n

INFO [1524824013684_bOcv][main] (TraceTest.java:44) - Entering application.
INFO [1524824013684_bOcv][main] (TraceTest.java:45) - Exiting application.

log4j-2.x

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
@Plugin(name = "TraceIdConverter", category = "Converter")
@ConverterKeys({"traceId"})
public class TraceIdConverter extends LogEventPatternConverter {

protected TraceIdConverter(String name, String style) {
super(name, style);
}

public static TraceIdConverter newInstance(String[] options) {
return new TraceIdConverter("traceId", "traceId");
}

@Override
public void format(LogEvent event, StringBuilder toAppendTo) {
Log4j2OutputAppender.append(toAppendTo);
}
}
1
2
3
4
5
6
public class Log4j2OutputAppender {

public static void append(StringBuilder toAppendTo) {
toAppendTo.append(Strings.isNullOrEmpty(TraceUtil.getTraceId()) ? "N/A" : TraceUtil.getTraceId());
}
}
1
2
3
4
5
<Appenders>
<Console name="Console" target="SYSTEM_OUT">
<PatternLayout pattern="%d [%traceId] %-5p %c{1}:%L - %m%n"/>
</Console>
</Appenders>