log4j2自定义Appender(输出到文件/RPC服务中)
1、背景
虽然log4j很强大,可以将日志输出到文件、DB、ES等。但是有时候确难免完全适合自己,此时我们就需要自定义Appender,使日志输出到指定的位置上。
本文,将通过两个例子说明自定义APPender,一个是将日志写入文件中,另一个是将日志发送到远程Thrift服务中。
本文代码详见:https://github.com/hawkingfoo/log-demo
2、自定义文件Appender
2.1 定义文件Appender
先上代码:
- @Plugin(name = "FileAppender", category = "Core", elementType = "appender", printObject = true)
- public class FileAppender extends AbstractAppender {
- private String fileName;
- /* 构造函数 */
- public FileAppender(String name, Filter filter, Layout<? extends Serializable> layout, boolean ignoreExceptions, String fileName) {
- super(name, filter, layout, ignoreExceptions);
- this.fileName = fileName;
- }
- @Override
- public void append(LogEvent event) {
- final byte[] bytes = getLayout().toByteArray(event);
- writerFile(bytes);
- }
- /* 接收配置文件中的参数 */
- @PluginFactory
- public static FileAppender createAppender(@PluginAttribute("name") String name,
- @PluginAttribute("fileName") String fileName,
- @PluginElement("Filter") final Filter filter,
- @PluginElement("Layout") Layout<? extends Serializable> layout,
- @PluginAttribute("ignoreExceptions") boolean ignoreExceptions) {
- if (name == null) {
- LOGGER.error("no name defined in conf.");
- return null;
- }
- if (layout == null) {
- layout = PatternLayout.createDefaultLayout();
- }
- // 创建文件
- if (!createFile(fileName)) {
- return null;
- }
- return new FileAppender(name, filter, layout, ignoreExceptions, fileName);
- }
- private static boolean createFile(String fileName) {
- Path filePath = Paths.get(fileName);
- try {
- // 每次都重新写文件,不追加
- if (Files.exists(filePath)) {
- Files.delete(filePath);
- }
- Files.createFile(filePath);
- } catch (IOException e) {
- LOGGER.error("create file exception", e);
- return false;
- }
- return true;
- }
- private void writerFile(byte[] log) {
- try {
- Files.write(Paths.get(fileName), log, StandardOpenOption.APPEND);
- } catch (IOException e) {
- LOGGER.error("write file exception", e);
- }
- }
- }
上面代码有几个需要注意的地方:
@Plugin..注解:这个注解,是为了在之后配置log4j2.xml时,指定的Appender Tag。- 构造函数:除了使用父类的以外,也可以增加一些自己的配置。
- 重写
append()方法:这里面需要实现具体的逻辑,日志的去向。 createAppender()方法:主要是接收log4j2.xml中的配置项。
2.2 添加log4j2.xml配置
- <?xml version="1.0" encoding="UTF-8"?>
- <configuration status="INFO" monitorInterval="30">
- <appenders>
- <!--这个输出控制台的配置-->
- <console name="Console" target="SYSTEM_OUT">
- <!--输出日志的格式-->
- <PatternLayout pattern="%highlight{[ %p ] [%-d{yyyy-MM-dd HH:mm:ss}] [%l] %m%n}"/>
- </console>
- <!-- 这个就是自定义的Appender -->
- <FileAppender name="File" fileName="log.log">
- <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] [%-5p] {%F:%L} - %m%n" />
- </FileAppender>
- </appenders>
- <loggers>
- <!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
- <logger name="org.springframework" level="INFO"></logger>
- <logger name="org.mybatis" level="INFO"></logger>
- <root level="all">
- <appender-ref ref="Console"/>
- <appender-ref ref="File"/>
- </root>
- </loggers>
- </configuration>
备注:
- 上面的log配置,一共配了2个输出。一个是终端输出,一个是采用自定义的FileAppender输出到文件中。
<FileAppender>标签要与自定义Appender中的类注解保持一致。
2.3 测试
- public class TestLogFile {
- private static final Logger logger = LogManager.getLogger(TestLogFile.class);
- public static void main(String[] args) {
- logger.info("1");
- logger.info("2");
- logger.info("3");
- }
- }
可以看到,日志一共输出了2份,一份到终端中,一份到log.log中(具体的文件路径可在log4j2.xml中配置)。
3、自定义Thrift Appender
上一节,主要是日志的文件输出。有时我们需要将日志发送给日志收集服务,常见的方法可以写一个日志收集Agent,收集日志;或者将日志输出方当成客户端直接发送到远程。
下文,通过自定义Appender的方式,将日志输出到远程的RPC服务中。
3.1 Thrift RPC服务
假设现在有一个Thrift RPC服务,实时接收日志消息。它的定义是下面的样子:
- namespace java thrift
- service LogServer {
- string getLogRes(1:string log);
- }
服务很简单,入参是log,返回值是String。
Thrift相关知识可以查看,Thrift RPC服务10分钟上手。
3.2 定义ThriftAppender
- @Plugin(name = "ThriftAppender", category = "Core", elementType = "appender", printObject = true)
- public class ThriftAppender extends AbstractAppender {
- private LogServer.Client client;
- private TTransport transport;
- /* 构造函数 */
- public ThriftAppender(String name, Filter filter, Layout<? extends Serializable> layout, boolean ignoreExceptions, String host) {
- super(name, filter, layout, ignoreExceptions);
- // 创建客户端
- createThriftClient(host);
- }
- @Override
- public void append(LogEvent event) {
- final byte[] bytes = getLayout().toByteArray(event);
- try {
- String response = client.getLogRes(new String(bytes));
- System.out.println(response);
- } catch (TException e) {
- e.printStackTrace();
- }
- }
- /* 接收配置文件中的参数 */
- @PluginFactory
- public static ThriftAppender createAppender(@PluginAttribute("name") String name,
- @PluginAttribute("host") String host,
- @PluginElement("Filter") final Filter filter,
- @PluginElement("Layout") Layout<? extends Serializable> layout,
- @PluginAttribute("ignoreExceptions") boolean ignoreExceptions) {
- if (name == null) {
- LOGGER.error("no name defined in conf.");
- return null;
- }
- if (layout == null) {
- layout = PatternLayout.createDefaultLayout();
- }
- return new ThriftAppender(name, filter, layout, ignoreExceptions, host);
- }
- @Override
- public void stop() {
- if (transport != null) {
- transport.close();
- }
- }
- private void createThriftClient(String host) {
- try {
- transport = new TFramedTransport(new TSocket(host, 9000));
- transport.open();
- TProtocol protocol = new TBinaryProtocol(transport);
- client = new LogServer.Client(protocol);
- LOGGER.info("create client success");
- } catch (Exception e) {
- LOGGER.error("create file exception", e);
- }
- }
- }
备注:
除了和文件Appender相同的外,这里需要注意两个地方。一个是Thrift Client的创建,另一个Thrift发送log。
具体的发送逻辑,在append()方法中实现。
3.3 添加log4j2.xml配置
- <?xml version="1.0" encoding="UTF-8"?>
- <configuration status="INFO" monitorInterval="30">
- <appenders>
- <!--这个输出控制台的配置-->
- <console name="Console" target="SYSTEM_OUT">
- <!--输出日志的格式-->
- <PatternLayout pattern="%highlight{[ %p ] [%-d{yyyy-MM-dd HH:mm:ss}] [%l] %m%n}"/>
- </console>
- <!-- 这个就是自定义的Appender -->
- <ThriftAppender name="Thrift" host="127.0.0.1">
- <PatternLayout pattern="%d{yyyy-MM-dd HH:mm:ss.SSS} [%t] [%-5p] {%F:%L} - %m%n" />
- </ThriftAppender>
- </appenders>
- <loggers>
- <!--过滤掉spring和mybatis的一些无用的DEBUG信息-->
- <logger name="org.springframework" level="INFO"></logger>
- <logger name="org.mybatis" level="INFO"></logger>
- <root level="all">
- <appender-ref ref="Console"/>
- <appender-ref ref="Thrift"/>
- </root>
- </loggers>
- </configuration>
这里同样是定义了两个输出路径,一个是终端,一个是Thrift服务。
3.4 测试
- public class TestThriftFile {
- private static final Logger logger = LogManager.getLogger(TestThriftFile.class);
- public static void main(String[] args) {
- logger.info("a");
- logger.info("b");
- logger.info("c");
- }
- }
可以看出,Server端成功接收到了log。
log4j2自定义Appender(输出到文件/RPC服务中)的更多相关文章
- Log4j/Log4j2自定义Appender来实现日志级别计数统计及监控
一.简述 本文主要讲如何基于Log4j2来实现自定义的Appender.一般用途是用于Log4j2自带的Appender不足以满足我们的需求,或者需要我们对日志进行拦截统计等操作时,需要我们自定义Ap ...
- [原创]java WEB学习笔记41:简单标签之带属性的自定义标签(输出指定文件,计算并输出两个数的最大值 demo)
本博客为原创:综合 尚硅谷(http://www.atguigu.com)的系统教程(深表感谢)和 网络上的现有资源(博客,文档,图书等),资源的出处我会标明 本博客的目的:①总结自己的学习过程,相当 ...
- SpringBoot | 第二十五章:日志管理之自定义Appender
前言 前面两章节我们介绍了一些日志框架的常见配置及使用实践.一般上,在开发过程中,像log4j2.logback日志框架都提供了很多Appender,基本上可以满足大部分的业务需求了.但在一些特殊需求 ...
- 软件性能测试分析与调优实践之路-JMeter对RPC服务的性能压测分析与调优-手稿节选
一.JMeter 如何通过自定义Sample来压测RPC服务 RPC(Remote Procedure Call)俗称远程过程调用,是常用的一种高效的服务调用方式,也是性能压测时经常遇到的一种服务调用 ...
- 【spring boot logback】日志使用自定义的logback-spring.xml文件后,application.properties中关于日志的相关配置还会起作用么
本篇 将针对[日志使用自定义的logback-spring.xml文件后,application.properties中关于日志的相关配置还会起作用么]这一个主题进行探索. 这个测试项目是根据[spr ...
- linux重定向总结:如何将shell命令的输出信息自动输出到文件中保存
在做批量实验室,例如跑批量MR的作业,我们会写好shell脚本,然后启动脚本,等所有作业执行完再去看结果,但是这些执行时的信息如何保存下来到文件中呢?下面这个命令可以完成这个任务. sh batchj ...
- Java自定义日志输出文件
Java自定义日志输出文件 日志的打印,在程序中是必不可少的,如果需要将不同的日志打印到不同的地方,则需要定义不同的Appender,然后定义每一个Appender的日志级别.打印形式和日志的输出路径 ...
- log4j日志输出到文件的配置
1.Maven的dependency 2.log4j.properties的配置 3.Junit的Test类 4.web.xml的配置(非必要) 5.spring的db.config的配置(非必要) ...
- 基于netty轻量的高性能分布式RPC服务框架forest<上篇>
工作几年,用过不不少RPC框架,也算是读过一些RPC源码.之前也撸过几次RPC框架,但是不断的被自己否定,最近终于又撸了一个,希望能够不断迭代出自己喜欢的样子. 顺便也记录一下撸RPC的过程,一来作为 ...
随机推荐
- dedecms首页入口的详细注释
今天闲来无事,就拿来dede首页的文件给大家详细解释一遍,以便于新手学习,注释过程非常非常非常的详细,里面解释到dede表前缀#@__代替的原理.解释到dede很多自定义函数的具体位置和具体作用等等疑 ...
- ng2tree在ios中无法触发click
问题描述: 从其他页面跳转回ng2tree的页面时,无法触发节点的click事件 解决方案: 1. 在node_modules中搜索onNodeSeclected方法,修改click为touchend ...
- mysql实现简单的增删改查,放入xmapp自带数据库中
1.mysql概念:SQL-Structured Query Language,是一种特殊的语言,专用于操作关系型数据库服务器中的数据,所有的SQL语句分为四类: (1)DDL(2)DQL(3)DML ...
- day 36 网络编程终结内容
今日概要: 1 gevent模块 协程:单线程下实现并发(并发指的是看起来同时运行,实现方式:切换+保存状态) 遇到IO切换到其他任务去执行,这种切换才能提高效率 gevent模块 1.切换+保存状态 ...
- rest模式get,post,put,delete简单讲解
1.请求方法为get时,会向数据库请求数据,类似于select语言,只能达到查询的效果,不会添加,修改,不会影响资源的内容,无副作用 2.请求方法为post时,该方法,用于向服务器添加数据,可以改变数 ...
- XamarinAndroid组件教程RecylerView适配器使用动画
XamarinAndroid组件教程RecylerView适配器使用动画 为RecylerView使用RecylerViewAnimators组件中提供的适配器动画,需要使用RecyclerView类 ...
- Codeforces.449D.Jzzhu and Numbers(容斥 高维前缀和)
题目链接 \(Description\) 给定\(n\)个正整数\(a_i\).求有多少个子序列\(a_{i_1},a_{i_2},...,a_{i_k}\),满足\(a_{i_1},a_{i_2}, ...
- ThinkPHP CI codeignitor 框架 apache 重写 url 隐藏index.php 服务器 报错:Object not found! 可能是.htaccess隐藏index.php
隐藏index.php可以去掉URL地址里面的入口文件index.php,但是需要额外配置WEB服务器的重写规则.以Apache为例,需要在入口文件的同级添加.htaccess文件(官方默认自带了该文 ...
- php 将两个数组进行相加 http://blog.csdn.net/lcstrive/article/details/38331487
刚刚在网上看到一个提问. 数组Array ( [0] => 1 [1] => 2 )和数组Array ( [0] => 5 [1] => 6 ) 怎么让他们想加得出: 数组Ar ...
- scrapy中间件
一.下载中间件 from scrapy import signals from scrapy.http import Response from scrapy.exceptions import Ig ...