MusikPolice
MusikPolice

Reputation: 1749

Dropwizard 0.9.1: Use Application Configuration Values in Custom AppenderFactory Implementation

I'm building a web stack based on Dropwizard v0.9.1. All logs from the stack are sent to Loggly via a custom implementation of the AppenderFactory interface:

@JsonTypeName("loggly")
public class LogglyAppenderFactory extends AbstractAppenderFactory {
    @JsonProperty
    private String token;

    @JsonProperty
    private final Optional<String> tag = Optional.absent();

    @Override
    public Appender<ILoggingEvent> build(LoggerContext context, String applicationName, Layout<ILoggingEvent> layout) {
        ...
    }

    protected Layout<ILoggingEvent> buildLayout(LoggerContext context) {
        ...
    }
}

This class isn't registered with the Environment in my Application class. Instead, it seems to be autowired by Dropwizard based on the @JsonTypeName annotation. Despite this, the token and tag fields are populated by values that appear in my configuration yaml file:

logging:
    level: INFO
    appenders:
        # send logs to loggly
        - type: loggly
          threshold: INFO
          token: "my-secret-loggly-token"
          tag: "webservice-ci"

The thing is, these configuration values don't appear in my application's Configuration class, which means that I can't re-use them when building other Resources or HealthChecks.

Ideally, I'd like to manually register the LogglyAppenderFactory like this:

 public class WebServiceApplication extends Application<WebServiceAppConfiguration> {
    @Override
    public void run(WebServiceAppConfiguration configuration, Environment environment) throws Exception {
        final HttpClient httpClient = new HttpClientBuilder(environment).using(configuration.getHttpClientConfiguration()).build("httpClient");

        // to be clear, environment.logAppenders() doesn't exist, and this doesn't work
        final LogglyAppenderFactory logglyAppenderFactory = new LogglyAppenderFactoryBuilder(configuration).build();
        environment.logAppenders().register(logglyAppenderFactory)

        // when we make this HealthCheck, it would be cool to reference the same config values that the AppenderFactory used
        final LogglyHealthCheck logglyHealthCheck = new LogglyHealthCheck(httpClient, configuration);
        environment.healthChecks().register("Loggly", logglyHealthCheck);
    }
}

so that both the LogglyAppenderFactory and the LogglyHealthCheck can use the same configuration values that dictate how they talk to the external service.

I suspect that this might be possible if I introduce a Dependency Injection framework like Google Guice and use it to inject the application configuration into both objects, but on the other hand, since logback appenders are typically created very early in the application lifecycle, I don't know if Guice will be ready to go when the AppenderFactory is being created.

Does anybody know how to do this? If not, am I stuck with putting the token into my config file twice? Once in the logging.appenders section, and again in some other section that the LogglyHealthCheck can access?

Upvotes: 1

Views: 805

Answers (2)

MusikPolice
MusikPolice

Reputation: 1749

Turns out that the Configuration class exposes a LoggingFactory that contains the logging config. I added this helper method to my Configuration class that extracts the field that I care about from my custom AppenderFactory:

/**
 * Extracts the Loggly token from the logging.appenders section of the configuration file.
 * If a {@link LogglyAppenderFactory} is not specified as one of the log appenders, this method returns null.
 * @return the Loggly token or null if no token is specified
 */
public String getLogglyToken() {
    final DefaultLoggingFactory loggingFactory = (DefaultLoggingFactory) getLoggingFactory();
    for (final AppenderFactory appenderFactory : loggingFactory.getAppenders()) {
        if (appenderFactory instanceof LogglyAppenderFactory) {
            final LogglyAppenderFactory logglyAppenderFactory = (LogglyAppenderFactory) appenderFactory;
            return logglyAppenderFactory.getToken();
        }
    }
    return null;
}

It isn't pretty, but it works!

Upvotes: 1

jpennell
jpennell

Reputation: 651

I think you're looking for

configuration.getLoggingFactory();

This API gives you all the logging config from your config file.

eg.

configuration.getLoggingFactory().getLevel();

configuration.getLoggingFactory().getAppenders()

((ConsoleAppenderFactory)configuration.getLoggingFactory().getAppenders().get(0)).getLogFormat();

Not the prettiest API, but it should work

Upvotes: 2

Related Questions