osiegmar / logback-gelf

Logback appender for sending GELF messages with zero additional dependencies.
GNU Lesser General Public License v2.1
218 stars 59 forks source link

Enable adding multiple static fields via environment variable and/or logback.properties #78

Closed kemsar closed 2 years ago

kemsar commented 2 years ago

Is your feature request related to a problem? Please describe. It would be nice to be able to declare multiple static fields in a comma-delimited list. I'm creating a base Docker image with a default Logback configuration for my coworkers to use for their projects. Currently, I've been able to be flexible enough using variables so that everything can be configured by either setting environment variables when the Docker image is started or by using a logback.properties file in their project. However, there's no easy way for them to customize the static fields they'd like to include in the GELF logging...only by overriding the entire GELF appender configuration.

Describe the solution you'd like Instead of declaring each <staticField> individually, I'd like to be able to add multiple fields in a single element:

...
    <staticFields>field1:my value,field2:second value,field_3:value</staticFields>
...

I'd then be able to declare a variable (either in the environment or in the logback.properties file) containing a comma-delimited list of name:value pairs...

GELF_STATIC_FIELDS=field1:my value,field2:second value,field_3:value

...and use the variable in my default configuration:

...
    <staticFields>${GELF_STATIC_FIELDS}</staticFields>
...

Describe alternatives you've considered I considered using logback.groovy, but the goal is to make it as easy and user-friendly as possible. I could also take each request and hard-code a new <staticField> everytime and rebuild the Docker image, but not every project wants/needs the same static fields.

osiegmar commented 2 years ago

Changing the semantics of staticFields as described would not only introduce a breaking change, it also seems pretty fragile as another separator (comma) must not be used in keys/values – especially if the fields are dynamic to some degree as you would pass them via environment variable.

Since version 4 you could easily register a custom GelfFieldMapper that adds additional fields as needed.

package mypackage;

import java.util.function.BiConsumer;

import ch.qos.logback.classic.spi.ILoggingEvent;
import de.siegmar.logbackgelf.GelfFieldMapper;

public class CustomFieldMapper implements GelfFieldMapper<String> {

    @Override
    public void mapField(ILoggingEvent event, BiConsumer<String, String> valueHandler) {
        valueHandler.accept("MY_KEY", "MY_VALUE");
        valueHandler.accept("MY_KEY2", "MY_VALUE2");
    }

}
<appender name="GELF" class="de.siegmar.logbackgelf.GelfUdpAppender">
    <graylogHost>localhost</graylogHost>
    <graylogPort>12201</graylogPort>
    <encoder class="de.siegmar.logbackgelf.GelfEncoder">
        <fieldMapper class="mypackage.CustomFieldMapper"/>
    </encoder>
</appender>

Probably this feature is currently somewhat undocumented. :-/

kemsar commented 2 years ago

Thanks for pointing me in that direction. I was able to satisfy my use-case with the following class:

package mypackage;

import ch.qos.logback.classic.spi.ILoggingEvent;
import de.siegmar.logbackgelf.GelfFieldMapper;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.function.BiConsumer;

public class GelfEnvVarsFieldMapper implements GelfFieldMapper<String> {

    private final Logger logger = LoggerFactory.getLogger(GelfEnvVarsFieldMapper.class);

    @Override
    public void mapField(ILoggingEvent iLoggingEvent, BiConsumer<String, String> biConsumer) {
        System.getenv().forEach((key,value) -> {
            if(key.toUpperCase().startsWith("GELF_")) {
                biConsumer.accept(key, value);
            }
        });
    }

}