Make it a habit to like it first

This article is participating in the Java Theme Month – Java Debug Notes EventActive link

In addition to optimizing business logic/code logic, it is also a good idea to make logging asynchronous when optimizing system response time

Log4j2 is unbeatable in asynchronous log performance due to the use of disruptor as an asynchronous queue

However, many service systems, especially core service systems, need to print detailed messages and processing parameters to track problems. However, if the message is formatted before logger.info (json/ XML, etc.), the performance of the log output will be greatly affected, because the log printing step is asynchronous, but the logger.info operation is synchronous. If the packet size is large, the formatting operation may consume time

How about making serialization asynchronous, too, and having logger.info output messages (objects) directly?

Log4j2 certainly supports this extension, but you just need to customize it

MessageFactory

Log4j2 provides a MessageFactory interface, which is used to create messages based on different message types. This interface allows you to format different message types

public class ExtendParameterizedMessageFactory extends AbstractMessageFactory {

    /** * matches the message of the object type. If output is logger.info(object), this method is called@param message
     * @return* /
    @Override
    public Message newMessage(Object message) {
        Function<Object, String> formattedFunction = MessageTypeMapping.match(message);
        // Create your own extended message type
        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 newParameterizedMessage(message, p0, p1, p2, p3, p4, p5, p6, p7, p8, p9); }}Copy the code

After MessageFactory is implemented, Through the environment variable specifies the specific MessageFactory implementation class can – Dlog4j2. MessageFactory = com. Making. Kongwur. Log4j2test. Log4j2. The extend. ExtendParameterizedMes sageFactory

Asynchronous formatting

Now that MessageFactory is implemented, how do you do asynchronous formatting?

Log4j2 provides a @ AsynchronouslyFormattable annotations, use the annotation to modify the Message Class, in the async logger, will be put before the actual printing the getFormat operation (asynchronous), rather than asynchronous before

  1. If the Message is annotated withAsynchronouslyFormattable, it can be passed to another thread as is.
  2. 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.

So you just need to create your own extension message type, use the @ AsynchronouslyFormattable modification

@AsynchronouslyFormattable
public class ExtendObjectParameterizedMessage implements Message {

    private final transient Object obj;
    private transient String objectString;
    private final transient Function<Object,String> formattedFunction;

    public ExtendObjectParameterizedMessage(final Object obj,Function<Object,String> formattedFunction) {
        this.obj = obj;
        this.formattedFunction = formattedFunction;
    }

    @Override
    public String getFormattedMessage(a) {
        if (objectString == null) {
            objectString = formattedFunction.apply(obj);
        }
        return objectString;
    }

    @Override
    public String getFormat(a) {
        return getFormattedMessage();
    }

    @Override
    public Object[] getParameters() {
        return new Object[] {obj};
    }

    @Override
    public Throwable getThrowable(a) {
        return null;
    }

    @Override
    public int hashCode(a) {
        returnobj ! =null ? obj.hashCode() : 0;
    }

    @Override
    public String toString(a) {
        returngetFormattedMessage(); }}Copy the code

Dynamic type matching

In actual projects, multiple objects or packets may be formatted asynchronously. Therefore, you are advised to dynamically match packet types and customize different formatting rules

Here we can also write a simple type-formatted map:

public class MessageTypeMapping {  
    private static final Map<Class, Function<Object,String>> MAPPING = new HashMap<>();  
  
 static {  
        // Add a mapping rule of the corresponding type
  MAPPING.put(BizObj.class,t -> {  
            try {  
                Thread.sleep(10);  
  } catch (InterruptedException e) {  
                e.printStackTrace();  
  }  
            return t.toString();  
  });  
  }  
  
    public static Function<Object,String> match(Object message){  
        if(message == null) {return null;  
  }  
        Class messageClass = message.getClass();  
 returnMAPPING.get(messageClass); }}Copy the code

use

When actually called, you can’t print through an API like SLf4J because SLF4J doesn’t provide a way to print objects directly

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)
Copy the code

But we can print it directly from the log4j2 API:

org.apache.logging.log4j.Logger logger = LogManager.getLogger(getClass());
/ / print object will invoke the defined above ExtendObjectParameterizedMessage here, to realize "asynchronous format"
logger.info(new BizObj());
Copy the code

reference

  • Logging.apache.org/log4j/2.x/m…

Original is not easy, prohibit unauthorized reprint. Like/like/follow my post if it helps you ❤❤❤❤❤❤