javalog4jlogbacklogstash-logback-encoder

Customizing log level display in with logback and logstash-logback encoder


I am using logstash-logback-encoder to print logs in json format.

My logback.xml looks like below:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="net.logstash.logback.encoder.LogstashEncoder">
            <includeContext>false</includeContext>
            <fieldNames>
                <timestamp>timestamp</timestamp>
                <version>[ignore]</version>
                <levelValue>[ignore]</levelValue>
            </fieldNames>
        </encoder>
    </appender>
    <appender name="stash"
        class="ch.qos.logback.core.rolling.RollingFileAppender">        
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>/tmp/SolrUpdater.%d{yyyy-MM-dd}.log
            </fileNamePattern>
            <maxHistory>7</maxHistory>
        </rollingPolicy>
        <encoder class="net.logstash.logback.encoder.LogstashEncoder">
            <includeContext>false</includeContext>
            <fieldNames>
                <timestamp>timestamp</timestamp>
                <version>[ignore]</version>
                <levelValue>[ignore]</levelValue>
            </fieldNames>
        </encoder>
    </appender>
    <root level="error">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="stash" />
    </root>
</configuration>

Can someone let me know how can I display all level in lowercase like error, rather than the default ERROR and WARN as warning?

UPDATE - I created a CustomLogLevelJsonProvider as suggested by Zakhar:

package com.jabong.discovery.importer.solrUpdater.log;

public class CustomLogLevelJsonProvider extends LogLevelJsonProvider {
    final static String DEBUG = "debug";
    final static String ERROR = "error";
    final static String INFO = "info";
    final static String WARNING = "warning";

    @Override
    public void writeTo(JsonGenerator generator, ILoggingEvent event)
        throws IOException {
    JsonWritingUtils.writeStringField(generator, getFieldName(),
        getCustomLogLevel(event));
    }

    private String getCustomLogLevel(ILoggingEvent event) {
    if (event.getLevel() == Level.ALL) {
        return Level.ALL.toString();
    }
    if (event.getLevel() == Level.DEBUG) {
        return DEBUG;
    }
    if (event.getLevel() == Level.ERROR) {
        return ERROR;
    }
    if (event.getLevel() == Level.INFO) {
        return INFO;
    }
    if (event.getLevel() == Level.WARN) {
        return WARNING;
    }
    return "";
    }
}

Updated logback.xml:

<?xml version="1.0" encoding="UTF-8"?>
<configuration>
    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
         <encoder class="com.jabong.discovery.importer.solrUpdater.log.CustomEncoder">
            <includeContext>false</includeContext>
            <fieldNames>
                <timestamp>timestamp</timestamp>
                <version>[ignore]</version>
                <levelValue>[ignore]</levelValue>
            </fieldNames>
        </encoder>
    </appender>
    <appender name="FILE"
        class="ch.qos.logback.core.rolling.RollingFileAppender">        
        <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">
            <fileNamePattern>/tmp/SolrUpdater.%d{yyyy-MM-dd}.log
            </fileNamePattern>
            <maxHistory>7</maxHistory>
        </rollingPolicy>
        <encoder class="net.logstash.logback.encoder.LogstashEncoder">
            <includeContext>false</includeContext>
            <fieldNames>
                <timestamp>timestamp</timestamp>
                <version>[ignore]</version>
                <levelValue>[ignore]</levelValue>
            </fieldNames>
        </encoder>
    </appender>
    <root level="error">
        <appender-ref ref="CONSOLE" />
        <appender-ref ref="FILE" />
    </root>
</configuration>

Now I am seeing two level:

{"timestamp":"2015-12-26T23:41:08.818+05:30","message":"Partial Updater Initialization Failed ","logger_name":"com.jabong.discovery.importer.solrUpdater.partialupdate.PartialUpdater","thread_name":"main","level":"ERROR","stack_trace":"java.lang.Exception: Failed to Connect to SolrIndex Type:solr, Core:discovery, Host:localhost, Port:8888\n\tat com.jabong.discovery.importer.solrUpdater.partialupdate.PartialUpdater.initIndexUpdater(PartialUpdater.java:83) [classes/:na]\n\tat com.jabong.discovery.importer.solrUpdater.partialupdate.PartialUpdater.initialise(PartialUpdater.java:36) [classes/:na]\n\tat com.jabong.discovery.importer.solrUpdater.partialupdate.PartialUpdater.<init>(PartialUpdater.java:30) [classes/:na]\n\tat Updater.initialise(Updater.java:50) [classes/:na]\n\tat Updater.main(Updater.java:70) [classes/:na]\n","level":"error"}

Solution

  • I do not know if this is somehow configurable via XML. But I just tried to override behavior you do not like. You will need to implement two new classes.

    1)

    public class CustomEncoder extends LogstashEncoder {
        public CustomEncoder() {
            LoggingEventJsonProviders providers = getFormatter().getProviders();
    
            // Remove provider that is responsible for log level appending
            removeDefaultProvider(providers);
    
            // Register our implementation
            providers.addLogLevel(new CustomLogLevelJsonProvider());
        }
    
        private void removeDefaultProvider(LoggingEventJsonProviders providers) {
            JsonProvider<ILoggingEvent> providerToDelete = null;
    
            for (JsonProvider<ILoggingEvent> provider : providers.getProviders()) {
                if (provider instanceof LogLevelJsonProvider) {
                    providerToDelete = provider;
                    break;
                }
            }
    
            providers.removeProvider(providerToDelete);
        }
    }
    

    2)

    public class CustomLogLevelJsonProvider extends LogLevelJsonProvider {
        @Override
        public void writeTo(JsonGenerator generator, ILoggingEvent event) throws IOException {
            JsonWritingUtils.writeStringField(
                    generator, getFieldName(), event.getLevel().toString().toLowerCase());
        }
    }
    

    3) Enable new Encoder in config.

    <appender name="CONSOLE" class="ch.qos.logback.core.ConsoleAppender">
        <encoder class="com.test.encoder.CustomEncoder">
            <includeContext>false</includeContext>
            <fieldNames>
                <timestamp>timestamp</timestamp>
                <version>[ignore]</version>
                <levelValue>[ignore]</levelValue>
            </fieldNames>
        </encoder>
    </appender>
    

    Update: You have updated your question. You will need some additional logic to transform WARN or warn to warning. As this will happen on each log message this will create overhead. Value that you want to change is hardcoded in class: ch.qos.logback.classic.Level