前言
前面两章节我们介绍了一些日志框架的常见配置及使用实践。一般上,在开发过程中,像
log4j2
、logback
日志框架都提供了很多Appender
,基本上可以满足大部分的业务需求了。但在一些特殊需求或者需要将日志进行集中管理(集群部署时,日志是分拆到不同服务器上的,不可能去每一台服务器上去下载文件的,也不便于日志检索)时,就需要自定义Appender
,将日志集中输出或者其他一些特殊需求。所以本章节就来简单介绍下关于log4j2
和logback
的自定义Appender
知识。
一点知识
编写自定义
Appender
时,我们先来看看log4j2
和logback
自带了哪些Appender
,了解下是否可以满足我们的个性化需求,避免重复制造轮子。
log4j2自带Appender
先看一张官网提供的Appender
说明:
名称 | 描述 |
---|---|
AsyncAppender | 使用一个单独线程记录日志,实现异步处理日志事件。 |
CassandraAppender | 将日志信息输出到一个Apache的Cassandra数据库 |
ConsoleAppender | 将日志信息输出到控制台 |
FailoverAppender | 包含其他appenders,按顺序尝试,直至成功或结尾 |
FileAppender | 一个OutputStreamAppender,将日志输出到文件 |
FlumeAppender | 将日志输出到Apache Flume系统 |
JDBCAppender | 将日志通过JDBC输出到关系型数据库 |
JMS Appender | 将日志输出到JMS(Java Message Service) |
JPAAppender | 将日志输出到JPA框架 |
HttpAppender | 通过HTTP输出日志 |
KafkaAppender | 将日志输出到Apache Kafka |
MemoryMappedFileAppender | 将日志输出到一块文件关联的内存 |
OutputStreamAppender | 将日志输出到一个OutputStream |
RandomAccessFileAppender | 性能比FileAppender高20%~200%的文件输出Appender |
RewriteAppender | 允许对日志信息进行加工 |
RollingFileAppender | 按log文件最大长度限度生成新文件 |
RollingRandomAccessFA | 添加了缓存的RollingFileAppender |
RoutingAppender | 将日志事件分类,按条件分配给子appender |
SMTPAppender | 将日志输出到邮件 |
SocketAppender | 将日志输出到一个Socket |
SyslogAppender | 是一个SocketAppender,将日志输出到远程系统日志 |
ZeroMQ/JeroMQ Appender | 使用JeroMQ库将日志输出到ZeroMQ终端 |
基本上已经覆盖了百分之九十的业务场景了。相关的详细说明或者配置大家自行搜索或者查看官网说明。
官网地址:http://logging.apache.org/log4j/2.x/manual/appenders.html
logback自带Appender
和log4j2
一样,自带的都差不多了。
名称 | 描述 |
---|---|
ConsoleAppender | 将日志输出到控制台 |
FileAppender | 将日志输出到文件 |
RollingFileAppender | 滚动文件生成,按条件生成不同文件,配合TriggeringPolicy使用 |
SocketAppender | 输出日志到远程实例中,明文传输 |
SSLSocketAppender | 输出日志到远程实例中,密文传输 |
SMTPAppender | 将日志输出到邮件 |
DBAppender | 日志事件插入数据库中,需要提前创建表 |
SyslogAppender | 是一个SocketAppender,将日志输出到远程系统日志 |
SiftingAppender | 可基于任何给定的实时属性分开(或者筛选)日志,如基于用户会话分开日志事件 |
AmqpAppender | 将日志输出到MQ服务中 |
具体可查看:https://blog.csdn.net/tianyaleixiaowu/article/details/73327752 很详细!
或者查看官网:https://logback.qos.ch/manual/appenders.html
自定义Appender
自定义
Appender
时,可以按实现的功能,适当的继承(log4j2
的appender
类基本上被设置成了final
无法继承)或者参考一些已有的功能,当然了也可以直接继承其基类接口的。以下就简单的示例下,没有实现特定的功能,⊙﹏⊙‖∣
log4j2自定义Appender
按官网的扩展说明,我们来简单实现一个appender。
官网地址:http://logging.apache.org/log4j/2.x/manual/extending.html#Appenders
0.编写自定义appender类,继承AbstractAppender
抽象实现类:
MyLog4j2Appender.java
1 | /** |
简单说明下,相关注意点:
@Plugin
注解:这个注解,是为了在之后配置log4j2-spring.xml
时,指定的Appender Tag。- 构造函数:除了使用父类的以外,也可以增加一些自己的配置。
- 重写
append()
方法:这里面需要实现具体的逻辑,日志的去向。 createAppender()
方法:主要是接收log4j2-spring.xml
中的配置项。
1.使用自定义的appender。
log4j2-spring.xml
1 | <?xml version="1.0" encoding="UTF-8"?> |
这里需要注意,需要在configuration
中,加入属性packages
为自定类所在包名cn.lqdev.learning
才会被扫描生效,不知道是否还有其他方法。
2.启动后,就可以看见相关输出了。1
2
3
4
5...部分省略...
一枚趔趄的猿(log4j2):[14:47:43:751] [INFO] - org.apache.juli.logging.DirectJDKLog.log(DirectJDKLog.java:180) - Using a shared selector for servlet write/read
一枚趔趄的猿(log4j2):[14:47:43:761] [INFO] - org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer.start(TomcatEmbeddedServletContainer.java:216) - Tomcat started on port(s): 8080 (http)
一枚趔趄的猿(log4j2):[14:47:43:764] [INFO] - org.springframework.boot.StartupInfoLogger.logStarted(StartupInfoLogger.java:57) - Started Chapter25Application in 2.03 seconds (JVM running for 3.164)
一枚趔趄的猿(log4j2):[14:47:43:764] [INFO] - cn.lqdev.learning.springboot.chapter25.Chapter25Application.main(Chapter25Application.java:14) - Chapter25启动!
不知道如何整合log4j2
的,可以查看:《第二十三章:日志管理之整合篇》
logback自定义Appender
logback
的自定义,也是类似的,都是基于一个基类appender
来实现。本身logback
提供了AppenderBase
和UnsynchronizedAppenderBase
两个抽象类(同步和非同步),所以我们自定义时,只需要看实际业务继承其中的一个即可。先看下其类继承结构:
0.编写自定义appender
类。
MyLogbackAppender.java
1 |
|
也简单说明下,相关注意点:
start
方法:初始时调用。故在编写如数据库入库,连接缓存或者mq时,可以在这个方法里面进行初始化操作。stop
:当停止时,调用。可做些资源释放操作。
1.使用自定义appender:
logback-spring.xml
1 | <?xml version="1.0" encoding="UTF-8"?> |
2.应用启动,查看控制台输出,效果是一样的:1
2
3
4
5
6...部分省略...
一枚趔趄的猿(logback):2018-08-25 15:01:57.486 [main] INFO org.apache.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8080"]
一枚趔趄的猿(logback):2018-08-25 15:01:57.497 [main] INFO org.apache.tomcat.util.net.NioSelectorPool - Using a shared selector for servlet write/read
一枚趔趄的猿(logback):2018-08-25 15:01:57.520 [main] INFO o.s.b.c.e.tomcat.TomcatEmbeddedServletContainer - Tomcat started on port(s): 8080 (http)
一枚趔趄的猿(logback):2018-08-25 15:01:57.523 [main] INFO c.l.l.springboot.chapter25.Chapter25Application - Started Chapter25Application in 54.349 seconds (JVM running for 55.377)
一枚趔趄的猿(logback):2018-08-25 15:01:57.524 [main] INFO c.l.l.springboot.chapter25.Chapter25Application - Chapter25启动!
关于ShutdownHook
当你运行了以上的自定义
appender
后,停止应用时,你会发现定义的stop
方法并没有被执行。还需要配置一个ShutdownHook
系统钩子,使得在jvm
在退出时之前会调用。
一点知识
我们知道,在java
中,注册一个关闭钩子是很简单的,使用Runtime
类即可,具体用法如下:1
2
3
4
5
6
7
8Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
public void run() {
// 执行资源释放操作
}
}));
而在SpringBoot
中,只需要配置logging.register-shutdown-hook
为true
即可。1
logging.register-shutdown-hook=true
对于logback
而言,也可以在logback-spring.xml
中配置:1
<shutdownHook class="ch.qos.logback.core.hook.DelayingShutdownHook"/>
也是可以的。再或者在启动类手动注册这个DelayingShutdownHook
也是可以的
这里有个坑,log4j2
而言,配置失效了。谷歌了一圈也没有发现解决方法,网上的方案试了一遍都是不行。。很尴尬。要是使用log4j2
的话,可以取巧下,在start()
方法里面,注册钩子之后调用stop
方法。希望有知道的大神分享下如何解决!
参考资料
- https://blog.csdn.net/zhoucheng05_13/article/details/78494458
- http://logging.apache.org/log4j/2.x/manual/appenders.html
- http://logging.apache.org/log4j/2.x/manual/extending.html#Appenders
- https://logback.qos.ch/manual/appenders.html
- https://blog.csdn.net/hupoling/article/details/75353854
总结
本文主要是简单介绍了
log4j2
和logback
自定义appender
相关知识。实现起来是相对简单的,需要注意当涉及需要关闭释放相关资源时,需要确认下关闭前是否有被调用,不然可能造成连接未关闭等行为,避免不必要的问题。关于最后使用log4j2
关闭钩子未生效问题,由于现在都默认使用logback
了,这个问题就不深究了,还望有知道的同学分享下解决方案!谢谢!同时由于没有对两个框架有过多的深入了解,只能点到为止了,若文中有误,还望指出!
最后
目前互联网上很多大佬都有
SpringBoot
系列教程,如有雷同,请多多包涵了。原创不易,码字不易,还希望大家多多支持。若文中有所错误之处,还望提出,谢谢。
老生常谈
- 个人QQ:
499452441
- 微信公众号:
lqdevOps
个人博客:http://blog.lqdev.cn
完整示例:https://github.com/xie19900123/spring-boot-learning/tree/master/chapter-25