在Logback的官方文档中对Appender的标签定义如下。
Logback delegates the task of writing a logging event to components called appenders. 翻译过来就是?Logback 将写入日志记录事件的任务委托给称为追加器的组件?. LogBack提供了几种常见的Appender,可以开箱即用。
1.控制台输出器ConsoleAppender
ConsoleAppender是logback.core提供的基础Appender 至于是如何实现控制台输出的,且看它的源码
public class ConsoleAppender<E> extends OutputStreamAppender<E> {
protected ConsoleTarget target = ConsoleTarget.SystemOut;
protected boolean withJansi = false;
......
}
再次进入ConsoleTarget枚举类型的源码,这个枚举类设置的比较巧,即设置了变量,也设计了枚举值,并且在枚举值上调用了枚举类的构造函数
public enum ConsoleTarget {
SystemOut("System.out", new OutputStream() {
@Override
public void write(int b) throws IOException {
System.out.write(b);
}
@Override
public void write(byte b[]) throws IOException {
System.out.write(b);
}
@Override
public void write(byte b[], int off, int len) throws IOException {
System.out.write(b, off, len);
}
@Override
public void flush() throws IOException {
System.out.flush();
}
}),
SystemErr("System.err", new OutputStream() {
@Override
public void write(int b) throws IOException {
System.err.write(b);
}
@Override
public void write(byte b[]) throws IOException {
System.err.write(b);
}
@Override
public void write(byte b[], int off, int len) throws IOException {
System.err.write(b, off, len);
}
@Override
public void flush() throws IOException {
System.err.flush();
}
});
public static ConsoleTarget findByName(String name) {
for (ConsoleTarget target : ConsoleTarget.values()) {
if (target.name.equalsIgnoreCase(name)) {
return target;
}
}
return null;
}
private final String name;
private final OutputStream stream;
private ConsoleTarget(String name, OutputStream stream) {
this.name = name;
this.stream = stream;
}
public String getName() {
return name;
}
public OutputStream getStream() {
return stream;
}
}
当然,以上得都只是看他如何工作,和实际使用没什么关系。
如果需要使用到控制台Appender,就只要配置个Appender标签即可。
<appender name="STDOUT" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>
%date{yyyy-MM-dd hh:mm:ss}| %5level [%thread] - %msg%n
</pattern>
</encoder>
</appender>
(PS:这里演示一下设置withJansi属性,前提是安装了jansi包) 如果你在ConsoleAppender的appender标签中设置了属性withJansi为true,则你可以在pattern标签中设置颜色,
<pattern>
%date{yyyy-MM-dd hh:mm:ss}| %blue(%5level) [%thread] - %msg%n
</pattern>
查看图片效果。
2.FileAppender文件输出器
FileAppender也是logback.core中的基础Appender组件,它允许我们把日志记录到文件 FileAppender对文件的修改和输出都是线程安全的,这些可以从源码中看出来,
public void openFile(String file_name) throws IOException {
lock.lock();
try {
File file = new File(file_name);
if (FileUtil.isParentDirectoryCreationRequired(file)) {
boolean result = FileUtil.createMissingParentDirectories(file);
if (!result) {
addError("Failed to create parent directories for ["
+ file.getAbsolutePath() + "]");
}
}
ResilientFileOutputStream resilientFos = new ResilientFileOutputStream(
file, append);
resilientFos.setContext(context);
setOutputStream(resilientFos);
} finally {
lock.unlock();
}
}
同时文件的写入环节也加了锁
private void safeWrite(E event) throws IOException {
ResilientFileOutputStream resilientFOS = (ResilientFileOutputStream) getOutputStream();
FileChannel fileChannel = resilientFOS.getChannel();
if (fileChannel == null) {
return;
}
FileLock fileLock = null;
try {
fileLock = fileChannel.lock();
long position = fileChannel.position();
long size = fileChannel.size();
if (size != position) {
fileChannel.position(size);
}
super.writeOut(event);
} finally {
if (fileLock != null) {
fileLock.release();
}
}
}
@Override
protected void writeOut(E event) throws IOException {
if (prudent) {
safeWrite(event);
} else {
super.writeOut(event);
}
}
同样使用起来也很简单
<appender name="FILE" class="ch.qos.logback.core.FileAppender">
<file>{可以设置一个变量}</file>
<append>true</append>
<prudent>false</prudent>
<encoder>
<pattern>
%date{yyyy-MM-dd hh:mm:ss}|%5level [%thread] - %msg%n
</pattern>
</encoder>
</appender>
警告如下,但是还是能完成向文件输出日志的功能
16:05:04,387 |-WARN in ch.qos.logback.core.FileAppender[FILE] - This appender no longer admits a layout as a sub-component, set an encoder instead.
16:05:04,387 |-WARN in ch.qos.logback.core.FileAppender[FILE] - To ensure compatibility, wrapping your layout in LayoutWrappingEncoder.
16:05:04,387 |-WARN in ch.qos.logback.core.FileAppender[FILE] - See also http://logback.qos.ch/codes.html
16:05:04,387 |-INFO in ch.qos.logback.core.FileAppender[FILE] - File property is
3.RollingFileAppender滚动文件输出器
这个Appender继承了FileApppender,可以实现比FileAppender更加高级的行为。 体现在源码中为多出了两个属性可以设置。
public class RollingFileAppender<E> extends FileAppender<E> {
File currentlyActiveFile;
TriggeringPolicy<E> triggeringPolicy;
RollingPolicy rollingPolicy;
........
}
RollingPolicy滚动策略
在Logback也提供了现成的滚动策略,像
1.TimeBasedRollingPolicy时间基准滚动策略
在实际的工作中可能用的比较多的就是TimeBasedRollingPolicy了, TimeBasedRollingPolicy有一个必要属性和一些非必要属性。
属性名 | 类型 | 描述 |
---|
fileNamePattern(必要) | String | 滚动生成文件名的模式 | maxHistory | int | 最大保留周期 | totalSizeCap | int | 最大空间占用 | cleanHistoryOnStart | boolean | 是否在开始运行时清除历史纪录 |
这里需要再介绍一下fileNamePattern属性了,其他的属性基本见名知意。
这里的fileNamePattern为滚动生成文件名的模式, 它必须包含%d字符用来描述 且必须在其后追加一个日期格式用来生成文件名 同时该日期格式的精度也是日志生成的频率 这里看一个例子:
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logFile.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
<fileNamePattern>logFile.%d{yyyy-MM-dd_HH-mm-ss}.log</fileNamePattern>
<maxHistory>30</maxHistory>
<totalSizeCap>3GB</totalSizeCap>
</rollingPolicy>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
测试代码:
public class MyLogTest {
final static Logger logger = LoggerFactory.getLogger(MyLogTest.class);
public static void main(String[] args) throws InterruptedException {
logger.warn("DoBefore");
DoSomething();
logger.debug("helloshit");
logger.error("helloshit");
logger.trace("helloshit");
logger.info("helloshit");
logger.debug("helloshit");
Thread.sleep(2000);
logger.info("ThisTimeBasedRollingFile");
}
public static void DoSomething() {
System.out.println("This method will do something");
}
}
结果: 更详细的细节可以看 https://logback.qos.ch/manual/appenders.html
2.FixedWindowRollingPolicy固定窗口滚动策略
它有三个属性
属性名 | 类型 | 描述 |
---|
minIndex | 最小索引 | i的开始 | maxIndex | 最大索引 | i的结束 | fileNamePattern | String | 文件名模式 |
这个策略的使用非常的简单,只需要你指定一个%i, i会在miIndex到maxindex中自增,形成一串连续的日志文件
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logTest.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>logTest_%i.log</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>5</maxIndex>
</rollingPolicy>
<triggeringPolicy class="ch.qos.logback.core.rolling.SizeBasedTriggeringPolicy">
<maxFileSize>1KB</maxFileSize>
</triggeringPolicy>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
测试代码
public static void main(String[] args){
logger.debug("FixedWindowRollingPolicyTest");X99
}
刷新,查看生成的日志 logback成功的按大小生成了三份日志。
TriggeringPolicy触发滚动策略
在上一个例子中我使用了TriggeringPolicy的一个现成的实现 SizedTriggeringPolicy 这是按日志文件大小触发的触发滚动策略 它只有MaxFileSize一个属性,在其中你可以设置触发的阈值。 在logback中提供两种现成的TriggeringPolicy, SizeBasedTriggeringPolicy空间触发器 TimeBasedFileNamingAndTriggeringPolicy时间和文件名除雾器 在上面的TimeBasedRollingPolicy的例子中其实是使用的第二个触发器。 当然了,它也可以继承TriggeringPolicyBase进行自定义。
自定义TriggeringPolicy
public class MyTriggeringPolicy<E> extends TriggeringPolicyBase<E>{
private String KeyWord;
public String getKeyWord() {
return KeyWord;
}
public void setKeyWord(String keyWord) {
KeyWord = keyWord;
}
@Override
public boolean isTriggeringEvent(final File activeFile,final E event) {
if(event.toString().contains(KeyWord))
return true;
else
return false;
}
}
在XML中配置你的自定义的TriggeringPolicy即可
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<file>logTest.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>logTest_%i.log</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>5</maxIndex>
</rollingPolicy>
<triggeringPolicy class="TestPack.MyTriggeringPolicy">
<keyWord>ASSHOLE</keyWord>
</triggeringPolicy>
测试一下
public static void main(String[] args) throws InterruptedException {
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("ASSHOLE");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("ASSHOLE");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
logger.debug("FixedWindowRollingPolicyTest");
}
成功的按照设置的关键词滚动生成了日志文件
4.日志事件过滤器Filter标签
有些appender中也许为了节省空间,干脆不记录一些DEBUG一下的记录,你可以使用Filter过滤掉, 也可以像本文的第一篇的方法,直接设置高等级的logger的level属性 当然你也可以指定一些特殊的规则,这里展示一个简单的例子
public class MyFilter<E> extends Filter<E>{
private String FilterMark;
public String getFilterMark() {
return FilterMark;
}
public void setFilterMark(String filterMark) {
FilterMark = filterMark;
}
@Override
public FilterReply decide(E event) {
if(event.toString().contains(FilterMark))
return FilterReply.DENY;
else
return FilterReply.ACCEPT;
}
}
配置到XML中去(这里偷个懒,直接加到上一个配置文件中去)
<appender name="FILE" class="ch.qos.logback.core.rolling.RollingFileAppender">
<filter class="Filters.MyFilter">
<FilterMark>Sultan</FilterMark>
</filter>
<file>logTest.log</file>
<rollingPolicy class="ch.qos.logback.core.rolling.FixedWindowRollingPolicy">
<fileNamePattern>logTest_%i.log</fileNamePattern>
<minIndex>1</minIndex>
<maxIndex>5</maxIndex>
</rollingPolicy>
<triggeringPolicy class="TestPack.MyTriggeringPolicy">
<keyWord>ASSHOLE</keyWord>
</triggeringPolicy>
<encoder>
<pattern>%-4relative [%thread] %-5level %logger{35} - %msg%n</pattern>
</encoder>
</appender>
测试:
public static void main(String[] args) throws InterruptedException {
logger.debug("FilterTest");
logger.debug("FilterTest");
logger.debug("FilterTest");
logger.debug("Sultan");
logger.debug("FilterTest");
logger.debug("FilterTest");
logger.debug("FilterTest");
logger.debug("Sultan");
}
结果(日志文件中):
467 [main] DEBUG TestPack.MyLogTest - FilterTest
470 [main] DEBUG TestPack.MyLogTest - FilterTest
470 [main] DEBUG TestPack.MyLogTest - FilterTest
471 [main] DEBUG TestPack.MyLogTest - FilterTest
471 [main] DEBUG TestPack.MyLogTest - FilterTest
471 [main] DEBUG TestPack.MyLogTest - FilterTest
可见,我们设置的filter已经将我们的指定的日志事件过滤了。
好了这里都是些日常可能会用到的Appender,希望对读者有一些帮助,谢谢观看
|