前言

前面两章节我们介绍了一些日志框架的常见配置及使用实践。一般上,在开发过程中,像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终端

logback自带Appender

和log4j2一样,自带的都差不多了。

名称

描述

ConsoleAppender

将日志输出到控制台

FileAppender

将日志输出到文件

RollingFileAppender

滚动文件生成,按条件生成不同文件,配合TriggeringPolicy使用

SocketAppender

输出日志到远程实例中,明文传输

SSLSocketAppender

输出日志到远程实例中,密文传输

SMTPAppender

将日志输出到邮件

DBAppender

日志事件插入数据库中,需要提前创建表

SyslogAppender

是一个SocketAppender,将日志输出到远程系统日志

SiftingAppender

可基于任何给定的实时属性分开(或者筛选)日志,如基于用户会话分开日志事件

AmqpAppender

将日志输出到MQ服务中

自定义Appender

自定义Appender时,可以按实现的功能,适当的继承(log4j2的appender类基本上被设置成了final无法继承)或者参考一些已有的功能,当然了也可以直接继承其基类接口的。以下就简单的示例下,没有实现特定的功能,⊙﹏⊙‖∣

log4j2自定义Appender

按官网的扩展说明,我们来简单实现一个appender。

0.编写自定义appender类,继承AbstractAppender抽象实现类:

MyLog4j2Appender.java

/**

* 自定义log4j2输出源,简单的输出到控制台

* @author oKong

*

*/

//这里的 MyLog4j2 对应就是 xml中,

/**

*

*

*

*

*

*

*/

@Plugin(name = "MyLog4j2", category = "Core", elementType = "appender", printObject = true)

public class MyLog4j2Appender extends AbstractAppender {

String printString;

/**

*构造函数 可自定义参数 这里直接传入一个常量并输出

*

*/

protected MyLog4j2Appender(String name, Filter filter, Layout extends Serializable> layout,String printString) {

super(name, filter, layout);

this.printString = printString;

}

@Override

public void append(LogEvent event) {

if (event != null && event.getMessage() != null) {

// 此处自定义实现输出

// 获取输出值:event.getMessage().toString()

// System.out.print(event.getMessage().toString());

// 格式化输出

System.out.print(printString + ":" + getLayout().toSerializable(event));

}

}

/** 接收配置文件中的参数

*

* @PluginAttribute 字面意思都知道,是xml节点的attribute值,如 这里的name 就是 attribute

* @PluginElement:表示xml子节点的元素,

* 如

*

*

*

* 其中,PatternLayout就是 的 Layout,其实就是{@link Layout}的实现类。

*/

@PluginFactory

public static MyLog4j2Appender createAppender(

@PluginAttribute("name") String name,

@PluginElement("Filter") final Filter filter,

@PluginElement("Layout") Layout extends Serializable> layout,

@PluginAttribute("printString") String printString) {

if (name == null) {

LOGGER.error("no name defined in conf.");

return null;

}

//默认使用 PatternLayout

if (layout == null) {

layout = PatternLayout.createDefaultLayout();

}

return new MyLog4j2Appender(name, filter, layout, printString);

}

@Override

public void start() {

System.out.println("log4j2-start方法被调用");

super.start();

}

@Override

public void stop() {

System.out.println("log4j2-stop方法被调用");

super.stop();

}

}

简单说明下,相关注意点:

@Plugin注解:这个注解,是为了在之后配置log4j2-spring.xml时,指定的Appender Tag。

构造函数:除了使用父类的以外,也可以增加一些自己的配置。

重写append()方法:这里面需要实现具体的逻辑,日志的去向。

createAppender()方法:主要是接收log4j2-spring.xml中的配置项。

1.使用自定义的appender。

log4j2-spring.xml

这里需要注意,需要在configuration中,加入属性packages为自定类所在包名cn.lqdev.learning才会被扫描生效,不知道是否还有其他方法。

2.启动后,就可以看见相关输出了。

...部分省略...

一枚趔趄的猿(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

@Getter

@Setter

public class MyLogbackAppender extends UnsynchronizedAppenderBase{

Layout layout;

//自定义配置

String printString;

@Override

public void start(){

//这里可以做些初始化判断 比如layout不能为null ,

if(layout == null) {

addWarn("Layout was not defined");

}

//或者写入数据库 或者redis时 初始化连接等等

super.start();

}

@Override

public void stop()

{

//释放相关资源,如数据库连接,redis线程池等等

System.out.println("logback-stop方法被调用");

if(!isStarted()) {

return;

}

super.stop();

}

@Override

public void append(ILoggingEvent event) {

if (event == null || !isStarted()){

return;

}

// 此处自定义实现输出

// 获取输出值:event.getFormattedMessage()

// System.out.print(event.getFormattedMessage());

// 格式化输出

System.out.print(printString + ":" + layout.doLayout(event));

}

}

也简单说明下,相关注意点:

start方法:初始时调用。故在编写如数据库入库,连接缓存或者mq时,可以在这个方法里面进行初始化操作。

stop:当停止时,调用。可做些资源释放操作。

1.使用自定义appender:

logback-spring.xml

class="cn.lqdev.learning.springboot.chapter25.config.MyLogbackAppender">

INFO

class="ch.qos.logback.classic.PatternLayout">

%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50} - %msg%n

一枚趔趄的猿(logback)

2.应用启动,查看控制台输出,效果是一样的:

...部分省略...

一枚趔趄的猿(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类即可,具体用法如下:

Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {

@Override

public void run() {

// 执行资源释放操作

}

}));

而在SpringBoot中,只需要配置logging.register-shutdown-hook为true即可。

logging.register-shutdown-hook=true

对于logback而言,也可以在logback-spring.xml中配置:

也是可以的。再或者在启动类手动注册这个DelayingShutdownHook也是可以的

这里有个坑,log4j2而言,配置失效了。谷歌了一圈也没有发现解决方法,网上的方案试了一遍都是不行。。很尴尬。要是使用log4j2的话,可以取巧下,在start()方法里面,注册钩子之后调用stop方法。希望有知道的大神分享下如何解决!

参考资料

最后

目前互联网上很多大佬都有SpringBoot系列教程,如有雷同,请多多包涵了。原创不易,码字不易,还希望大家多多支持。若文中有所错误之处,还望提出,谢谢。

老生常谈

jpa日志 logback_SpringBoot | 第二十五章:日志管理之自定义Appender相关推荐

  1. 25 linux ndk 头文件_正点原子Linux第二十五章RTC实时时钟实验

    1)资料下载:点击资料即可下载 2)对正点原子Linux感兴趣的同学可以加群讨论:935446741 3)关注正点原子公众号,获取最新资料更新 第二十五章RTC实时时钟实验 实时时钟是很常用的一个外设 ...

  2. 【正点原子MP157连载】第二十五章 pinctrl和gpio子系统实验-摘自【正点原子】STM32MP1嵌入式Linux驱动开发指南V1.7

    1)实验平台:正点原子STM32MP157开发板 2)购买链接:https://item.taobao.com/item.htm?&id=629270721801 3)全套实验源码+手册+视频 ...

  3. 第二十五章补充内容 3 assert()宏

    //第二十五章补充内容 3 assert()宏 //有的编译器还提供了assert()宏,这个宏在许多书中被翻译为断言,它的作用是当assert()的参数为真时,返回真,假如参数值为假,那么它将执行某 ...

  4. 鸟哥的Linux私房菜(基础篇)- 第二十五章、 Linux 备份策略

    第二十五章. Linux备份策略 最近升级日期:2009/09/18 万一不幸你的 Linux 被黑客入侵了.或是你的 Linux 系统由於硬件关系 (不论是天灾还是人祸) 而挂掉了!这个时候,请问如 ...

  5. 第二十五章补充内容 5 不能为0的变量

    // 第二十五章补充内容 5 不能为0的变量 /*#define DEBUG #include <iostream> #include <string> using names ...

  6. 第二十五章:重新吃上饭的李恪

    第二十五章:重新吃上饭的李恪 可能李丽质永远都不会忘记了自己饿肚子饿了很久以来的第一顿饭吧. 吃的非常的香,吃完这顿饭以后,李丽质开始也参与到了洗皮衣的过程中,不过由于他个子比较小,所以只能做一些简单 ...

  7. 现实迷途 第二十五章 重见故人

    第二十五章 重见故人 注:原创作品,请尊重原作者,未经同意,请勿转载,否则追究责任. 两天后,狗子又变得生龙活虎了,他决定要和江北不醉无归. "你想斋喝,还是荤喝?"电话中狗子问江 ...

  8. 游戏设计的艺术:一本透镜的书——第二十五章 好的游戏是通过游戏测试做出来的

    这是一本游戏设计方面的好书 转自天之虹的博客:http://blog.sina.com.cn/jackiechueng 感谢天之虹的无私奉献 Word版可到本人的资源中下载 第二十五章好的游戏是通过游 ...

  9. 系统集成项目管理工程师(软考中级)—— 第二十五章 法律法规(补充) 笔记分享

    前言 最近在复习软考高级,比较少更新.. 现在分享一些笔记给大家,希望能够帮助大家并顺利通过软考. 幕布地址:(补充)第二十五章 法律法规 - 幕布 概述 法规法规部分涉及招投标法.招投标法实施条例. ...

最新文章

  1. 使用大batch优化深度学习:训练BERT仅需76分钟 | ICLR 2020
  2. 用T-SQL得到数据库的可视化结构
  3. 一文入门 Kafka
  4. linux手动同步文件命令,Linux文件同步命令rsync详解
  5. PostgreSQL 中的递归查询 与oracle 的比较
  6. Go 实现 soundex 算法
  7. Java并发包源码学习之AQS框架(三)LockSupport和interrupt
  8. 多年前写的一个ASP.NET网站管理系统,到现在有些公司在用
  9. 【渝粤教育】国家开放大学2018年春季 0551-22T素描(二) 参考试题
  10. 阶段3 2.Spring_03.Spring的 IOC 和 DI_6 spring中bean的细节之三种创建Bean对象的方式
  11. BZOJ4060 : [Cerc2012]Word equations
  12. 用策略屏蔽135 139 445 3389端口+网络端口安全防护技 .
  13. android 产品上线流程图,产品上线工作流程(试行)20050302.doc
  14. MIT操作系统神课 - 6.828
  15. 【电机原理与拖动基础】Unit 2 直流电机的电力拖动系统
  16. Tableau可视化---Tableau简介
  17. 订单类 京东快递电子面单接口的使用开发
  18. Another exception was thrown: The PrimaryScrollController is currently attached to more than one Scr
  19. 活动回顾 |阿里云MVP Tech Show 第九期宁波站— 物联网一站式开发
  20. 笔记10.9:硬盘、计算机启动过程、缓冲缓存

热门文章

  1. ˋ◤林志玲代言真情年代北京烤鸭惊艳亮相化身鸭子骗称这辈子太美下辈子想是做一只丑小鸭省的人追(图)①ǒ
  2. 【DIY创意】自制3D折纸装饰
  3. 【原创】终结版 窗口前置 方案 最前面 Z 轴 窗体 最前面
  4. “理想男友职业”排行榜,高薪程序员输给了三大铁饭碗?原因现实
  5. 传博通正洽谈收购云服务提供商VMware,后者市值约400亿美元
  6. 3D和texture素材国外常用网站
  7. vcs与verdi的简单使用
  8. 写一首关于教育的诗,500字
  9. ARtillery报告:易用性是VR重要指标,非VR用户兴趣持续走低
  10. 机器视觉检测技术在螺丝螺母缺陷检测中的应用