Limit message size with custom encoder using logback

4.8k Views Asked by At

I'm trying to limit the log message size, I succeed to do it with the error traceback (using ShortenedThrowableConverter) but I didn't manage to find a solution to the message itself.

I know one way is to use <pattern/> but since I'm implementing the encoder by myself.

I tried using layout but encountered with this error below:

no applicable action for [layout], current ElementPath  is [[configuration][appender][encoder][layout]]

Here is some of my code:

logback.xml

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="console-json" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="com.test.log.logback.JsonEncoder">
            <throwableConverter class="net.logstash.logback.stacktrace.ShortenedThrowableConverter">
                <maxLength>20</maxLength>
            </throwableConverter>
        </encoder>
    </appender>

    <root level="info">
        <appender-ref ref="console-json" />
    </root>

    <shutdownHook class="ch.qos.logback.core.hook.DelayingShutdownHook"/>

    <contextListener class="ch.qos.logback.classic.jul.LevelChangePropagator">
        <resetJUL>true</resetJUL>
    </contextListener>
</configuration>

encoder:

package com.test.log.logback;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.logstash.logback.stacktrace.ShortenedThrowableConverter;

import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;


public class JsonEncoder extends net.logstash.logback.encoder.LogstashEncoder {


    private String customFields;
    ShortenedThrowableConverter converter = new ShortenedThrowableConverter();
    public JsonEncoder() {
        converter.setMaxLength(10);
        setThrowableConverter(converter);
        setFieldNames(new FieldNames());
        setTimeZone("UTC");
        setFindAndRegisterJacksonModules(false);
    }   
    
}
2

There are 2 best solutions below

2
On BEST ANSWER

To limit the message field within the JSON output when using the net.logstash.logback.encoder.LogstashEncoder, you'll need to:

  1. Disable the default message field output
  2. Add a custom message field using the pattern provider

For example:

<encoder class="net.logstash.logback.encoder.LogstashEncoder">
    <!-- Disable the default message field -->
    <fieldNames>
        <message>[ignore]</message>
    </fieldNames>
    <!-- Add a new message field that truncates the message to 20 characters -->
    <provider class="net.logstash.logback.composite.loggingevent.LoggingEventPatternJsonProvider">
        <pattern>{"message":"%0.-20message"}</pattern>
    </provider>
</encoder>

or as a custom encoder:

public class JsonEncoder extends LogstashEncoder {
    @Override
    public void start() {
        // Disable the default message field
        getFieldNames().setMessage("[ignore]");

        // Add a new message field that truncates the message to 20 characters
        LoggingEventPatternJsonProvider patternProvider = new LoggingEventPatternJsonProvider();
        patternProvider.setContext(getContext());
        patternProvider.setPattern("{\"message\":\"%0.-20message\"}");
        addProvider(patternProvider);
        super.start();
    }
}

Alternatively, you can use net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder (which is the superclass of LogstashEncoder), and build up the JSON event exactly as you like with JSON providers. See here.

For example:

<encoder class="net.logstash.logback.encoder.LoggingEventCompositeJsonEncoder">
    <providers>
        <pattern>
            <pattern>
                {
                "timestamp": "%date{ISO8601}",
                "message":"%0.-20message"
                }
            </pattern>
        </pattern>
        <!--
            To add additional fields to the JSON output, either
            add the fields to the above pattern, or
            add additional JSON providers here.
         -->
    </providers>
</encoder>

or as a custom encoder:

public class JsonEncoder extends LoggingEventCompositeJsonEncoder {
    @Override
    public void start() {
        getProviders().addProvider(new LoggingEventFormattedTimestampJsonProvider());

        LoggingEventPatternJsonProvider patternProvider = new LoggingEventPatternJsonProvider();
        patternProvider.setContext(getContext());
        patternProvider.setPattern("{\"message\":\"%0.-20message\"}");
        getProviders().addProvider(patternProvider);

        /*
         * To add additional fields to the JSON output, either
         * add the fields to the above pattern, or
         * add additional JSON providers here.
         */
        super.start();
    }
}
0
On

My version:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <property resource="application.yml" />
    <contextName>Operator Platform</contextName>
    <springProfile name="!local">
        <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
            <encoder class="net.logstash.logback.encoder.LogstashEncoder">
                <fieldNames>
                    <message>[ignore]</message>
                </fieldNames>
                <provider class="net.logstash.logback.composite.loggingevent.LoggingEventPatternJsonProvider">
                    <pattern>{"message":"%0.-10500message"}</pattern>
                </provider>
                <throwableConverter class="net.logstash.logback.stacktrace.ShortenedThrowableConverter">
                    <maxDepthPerThrowable>30</maxDepthPerThrowable>
                    <maxLength>4096</maxLength>
                    <shortenedClassNameLength>40</shortenedClassNameLength>
                    <!-- generated class names -->
                    <exclude>\$\$FastClassByCGLIB\$\$</exclude>
                    <exclude>\$\$EnhancerBySpringCGLIB\$\$</exclude>
                    <exclude>^sun\.reflect\..*\.invoke</exclude>
                    <!-- JDK internals -->
                    <exclude>^com\.sun\.</exclude>
                    <exclude>^sun\.net\.</exclude>
                    <!-- dynamic invocation -->
                    <exclude>^net\.sf\.cglib\.proxy\.MethodProxy\.invoke</exclude>
                    <exclude>^org\.springframework\.cglib\.</exclude>
                    <exclude>^org\.springframework\.transaction\.</exclude>
                    <exclude>^org\.springframework\.validation\.</exclude>
                    <exclude>^org\.springframework\.app\.</exclude>
                    <exclude>^org\.springframework\.aop\.</exclude>
                    <exclude>^org\.springframework\.beans\.</exclude>
                    <exclude>^java\.lang\.reflect\.Method\.invoke</exclude>
                    <rootCauseFirst>true</rootCauseFirst>
                    <inlineHash>true</inlineHash>
                </throwableConverter>
                <providers>
                    <contextName>
                        <fieldName>app</fieldName>
                    </contextName>
                    <timestamp>
                        <fieldName>ts</fieldName>
                        <timeZone>UTC</timeZone>
                    </timestamp>
                    <loggerName>
                        <fieldName>logger</fieldName>
                    </loggerName>
                    <logLevel>
                        <fieldName>level</fieldName>
                    </logLevel>
                    <callerData>
                        <classFieldName>class</classFieldName>
                        <methodFieldName>method</methodFieldName>
                        <lineFieldName>line</lineFieldName>
                        <fileFieldName>file</fileFieldName>
                    </callerData>
                    <threadName>
                        <fieldName>thread</fieldName>
                    </threadName>
                    <mdc>
                        <fieldName>requestId</fieldName>
                    </mdc>
                    <arguments>
                        <includeNonStructuredArguments>false</includeNonStructuredArguments>
                    </arguments>
                    <stackTrace>
                        <fieldName>stack</fieldName>
                    </stackTrace>
                </providers>
            </encoder>
        </appender>
        <root level="INFO">
            <appender-ref ref="CONSOLE" />
        </root>
    </springProfile>
    <springProfile name="local">
        <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
            <encoder>
                <pattern>
                    %d{HH:mm:ss.SSS} | %5p | %logger{25} | %m%n
                </pattern>
                <charset>utf8</charset>
            </encoder>
        </appender>

        <root level="DEBUG">
            <appender-ref ref="CONSOLE"/>
        </root>
    </springProfile>
</configuration>