在优化系统响应时间的时候,除了优化业务逻辑/代码逻辑之外,把日志改成异步也是一种不错的方案

Log4j2在异步日志的性能上已经无人能挡了,其异步效率高的主要原因是使用disruptor来做异步队列

但是很多业务系统,尤其是核心业务系统,需要打印详细的报文和处理参数来追踪问题;但是如果在logger.info之前就对报文进行格式化(转json/xml之类),又会非常影响日志输出的性能,因为就算日志打印这一步是异步的,但是logger.info这一操作是同步的;如果报文较大,格式化的这个操作可能会比较消耗时间

那么能不能让序列化这一步也变成异步呢,让logger.info直接输出报文(对象)?

log4j2当然是支持这种扩展的,只是需要自己定制一下

MessageFactory

log4j2提供了一个MessageFactory接口,该接口用于根据消息类型的不同来创建消息,通过该接口即可实现对不同消息类型的格式化

public class ExtendParameterizedMessageFactory extends AbstractMessageFactory {

/**

* 匹配对象类型消息,如果用logger.info(object)输出,则会调用本方法

* @param message

* @return

*/

@Override

public Message newMessage(Object message) {

Function formattedFunction = MessageTypeMapping.match(message);

//创建自己的扩展消息类型

if(formattedFunction != null){

return new ExtendObjectParameterizedMessage(message,formattedFunction);

}else{

return super.newMessage(message);

}

}

@Override

public Message newMessage(final String message, final Object... params) {

return new ParameterizedMessage(message, params);

}

@Override

public Message newMessage(final String message, final Object p0) {

return new ParameterizedMessage(message, p0);

}

@Override

public Message newMessage(final String message, final Object p0, final Object p1) {

return new ParameterizedMessage(message, p0, p1);

}

@Override

public Message newMessage(final String message, final Object p0, final Object p1, final Object p2) {

return new ParameterizedMessage(message, p0, p1, p2);

}

@Override

public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3) {

return new ParameterizedMessage(message, p0, p1, p2, p3);

}

@Override

public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4) {

return new ParameterizedMessage(message, p0, p1, p2, p3, p4);

}

@Override

public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5) {

return new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5);

}

@Override

public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,

final Object p6) {

return new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5, p6);

}

@Override

public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,

final Object p6, final Object p7) {

return new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5, p6, p7);

}

@Override

public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,

final Object p6, final Object p7, final Object p8) {

return new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5, p6, p7, p8);

}

@Override

public Message newMessage(final String message, final Object p0, final Object p1, final Object p2, final Object p3, final Object p4, final Object p5,

final Object p6, final Object p7, final Object p8, final Object p9) {

return new ParameterizedMessage(message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9);

}

}

MessageFactory实现后,通过环境变量指定具体的MessageFactory实现类即可-Dlog4j2.messageFactory=com.github.kongwur.log4j2test.log4j2.extend.ExtendParameterizedMessageFactory

异步格式化

上面已经实现了MessageFactory,那么怎么才能做到异步格式化呢?

log4j2提供了一个@AsynchronouslyFormattable注解,用该注解修饰的Message Class,在async logger时,会将getFormat的操作放实际打印之前(即异步之后),而不是异步之前

If the Message is annotated withAsynchronouslyFormattable, it can be passed to another thread as is.

Otherwise, asynchronous logging components in the Log4j implementation will callMessage.getFormattedMessage()before passing the Message object to another thread. This gives the Message implementation class a chance to create a formatted message String with the current value of the mutable object. The intention is that the Message implementation caches this formatted message and returns it on subsequent calls.

所以只需要创建自己的扩展消息类型,用@AsynchronouslyFormattable修饰即可

@AsynchronouslyFormattable

public class ExtendObjectParameterizedMessage implements Message {

private final transient Object obj;

private transient String objectString;

private final transient Function formattedFunction;

public ExtendObjectParameterizedMessage(final Object obj,Function formattedFunction) {

this.obj = obj;

this.formattedFunction = formattedFunction;

}

@Override

public String getFormattedMessage() {

if (objectString == null) {

objectString = formattedFunction.apply(obj);

}

return objectString;

}

@Override

public String getFormat() {

return getFormattedMessage();

}

@Override

public Object[] getParameters() {

return new Object[] {obj};

}

@Override

public Throwable getThrowable() {

return null;

}

@Override

public int hashCode() {

return obj != null ? obj.hashCode() : 0;

}

@Override

public String toString() {

return getFormattedMessage();

}

}

动态类型匹配

实际项目中,可能会对多种对象/报文进行异步格式化,所以最好可以动态匹配报文的类型,定制不同的格式化规则

这里还可以写一个简单类型格式化的映射:

public class MessageTypeMapping {

private static final Map> MAPPING = new HashMap<>();

static {

//添加对应类型的映射规则

MAPPING.put(BizObj.class,t -> {

try {

Thread.sleep(10);

} catch (InterruptedException e) {

e.printStackTrace();

}

return t.toString();

});

}

public static Function match(Object message){

if(message == null){

return null;

}

Class messageClass = message.getClass();

return MAPPING.get(messageClass);

}

}

使用

实际调用时,无法通过slf4j之类的api来打印,因为slf4j并没有提供直接打印对象的方法

org.slf4j.Logger#info(java.lang.String)

org.slf4j.Logger#info(java.lang.String, java.lang.Object)

org.slf4j.Logger#info(java.lang.String, java.lang.Object, java.lang.Object)

org.slf4j.Logger#info(java.lang.String, java.lang.Object...)

org.slf4j.Logger#info(java.lang.String, java.lang.Throwable)

org.slf4j.Logger#isInfoEnabled(org.slf4j.Marker)

org.slf4j.Logger#info(org.slf4j.Marker, java.lang.String)

org.slf4j.Logger#info(org.slf4j.Marker, java.lang.String, java.lang.Object)

org.slf4j.Logger#info(org.slf4j.Marker, java.lang.String, java.lang.Object, java.lang.Object)

org.slf4j.Logger#info(org.slf4j.Marker, java.lang.String, java.lang.Object...)

org.slf4j.Logger#info(org.slf4j.Marker, java.lang.String, java.lang.Throwable)

但是我们可以直接通过log4j2的api来打印:

org.apache.logging.log4j.Logger logger = LogManager.getLogger(getClass());

//这里打印对象会调用上面定义的ExtendObjectParameterizedMessage,实现“异步格式化”

logger.info(new BizObj());

参考

java log4j 异步_Log4j2异步日志之异步格式化相关推荐

  1. java log4j基本配置及日志级别配置详解,java基础面试笔试题

    我总结出了很多互联网公司的面试题及答案,并整理成了文档,以及各种学习的进阶学习资料,免费分享给大家. 扫描二维码或搜索下图红色VX号,加VX好友,拉你进[程序员面试学习交流群]免费领取.也欢迎各位一起 ...

  2. java epoll select_Java 非阻塞 IO 和异步 IO

    点击上方 Java后端,选择 设为星标 优质文章,及时送达 作者 | HongJie 链接 | javadoop.com/post/nio-and-aio 本文将介绍非阻塞 IO 和异步 IO,也就是 ...

  3. java ee打印功能_Java EE:异步构造和功能

    java ee打印功能 介绍 Java EE具有许多API和构造以支持异步执行. 从可伸缩性和性能的角度来看,这是至关重要的. 让我们假设2个模块相互交互. 当模块A (发送方)以同步方式向模块B ( ...

  4. java异步刷新集合,同步和异步集合的性能测试,异步集合性能测试,package cn.o

    同步和异步集合的性能测试,异步集合性能测试,package cn.opackage cn.outofmemory.snippets.core;import java.util.ArrayList;im ...

  5. java异步获取结果_java获取异步计算的结果

    java Future/Callable/Executors 如何获取一个异步线程的结果呢?使用普通的方法是无法获取异步线程返回结果的,比如继承Thread类.实现Runnable接口这两种方式创建的 ...

  6. Java网络编程------IO模型的同步/异步/阻塞/非阻塞(1)

    IO模型的同步/异步/阻塞/非阻塞 一.同步/异步/阻塞/非阻塞 1.同步和异步 2.阻塞和非阻塞 3.同步.异步和阻塞.非阻塞组合 二.IO 1.I/O 2.阻塞IO和非阻塞IO 3.同步IO和同步 ...

  7. java 异步调用接口_Java接口异步调用

    java接口调用从调用方式上可以分为3类:同步调用,异步调用,回调:同步调用基本不用说了,它是一种阻塞式的调用,就是A方法中直接调用方法B,从上往下依次执行.今天来说说异步调用. 什么是异步调用? 我 ...

  8. JAVA异步处理结束处理,CompletableFuture-java异步处理

    一. 概述 在开发中, 经常碰到一写场景就是, 一些耗时的操作有时与业务主干关联性不大, 为了提高业务主干的响应速度, 我们会把这些耗时的任务异步处理. 本文介绍的异步工具类是java8自带的工具类- ...

  9. java log4j logback jcl_知识总结-Java日志框架Log4j、Log4j2、logback、slf4j、简介

    功能简介 上一篇介绍了为什么打印日志.什么时候打印日志以及怎么打印日志.本篇介绍下在项目开发中常见的日志组件以及关系. 先看一张图 接口:将所有日志实现适配到了一起,用统一的接口调用. 实现:目前主流 ...

最新文章

  1. 第二阶段小组冲刺第五天总结
  2. python可视化界面工具_8个流行的 Python可视化工具包,你喜欢哪个?
  3. Linux学习笔记6月1日任务
  4. stream distinct去重_再来看看Java的新特性——Stream流
  5. 收好这份 Vue 升级图,假期偷偷上个钻
  6. tcp wireshark 过滤syn_使用 WireShark 分析 TCP/IP 三次握手 和 四次挥手
  7. Laravel框架登录功能实例
  8. Linux之使用MobaXterm远程连接Linux前提条件
  9. python 人脸检测_借助摄像头在Python中实现人脸检测
  10. UltraNumTextBox【实现所有数字输入的同时,可以控制当控件禁用时ForeColor】
  11. 上班族中流行以貌取人 汉王人脸通变普及
  12. 清华大学计算机学院教授简介,清华大学计算机科学与技术系导师教师师资介绍简介-王继龙...
  13. 路由器刷openwrt
  14. 电脑网络连接怎么设置
  15. 程序员未来职业规划分析
  16. spring boot参数校验 告别校验胶水代码
  17. 标签系统mysql设计_关于tag标签系统的实现
  18. 美女暴强的吃比萨过程 可做MBA案例
  19. 软件性能优化漫谈(一):软件性能测量与分析
  20. 无需注册码就可使用的PDF转换器

热门文章

  1. 07树莓派下的浏览器
  2. 关系数据库的基本概念和MySQL说明
  3. pat1069. The Black Hole of Numbers (20)
  4. vue服务端转html,普通vue-cli初始项目转为服务端渲染SSR
  5. mybatis mapper配置 bigint_Mybatis-Plus入门
  6. 用终端访问路由器设置端口开发_网络故障排查最全总结!ONU、机顶盒、路由器常见网络问题及处理方法...
  7. android 数据文件存储,实例详解Android文件存储数据方式
  8. 如果你需要品质背景素材,看过来
  9. 引导页设计灵感,高大上的设计案例
  10. 不会PS抠图?免抠(扣)PNG图片网就解决了