diff --git a/pom.xml b/pom.xml index bdbba283d..2f66ca0b4 100644 --- a/pom.xml +++ b/pom.xml @@ -152,6 +152,11 @@ 5.7.1 pom + + net.logstash.logback + logstash-logback-encoder + 6.6 + diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/metrics/LogstashTcpSocketAppenderFactory.java b/service/src/main/java/org/whispersystems/textsecuregcm/metrics/LogstashTcpSocketAppenderFactory.java new file mode 100644 index 000000000..d56f65530 --- /dev/null +++ b/service/src/main/java/org/whispersystems/textsecuregcm/metrics/LogstashTcpSocketAppenderFactory.java @@ -0,0 +1,81 @@ +/* + * Copyright 2021 Signal Messenger, LLC + * SPDX-License-Identifier: AGPL-3.0-only + */ + +package org.whispersystems.textsecuregcm.metrics; + +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.PatternLayout; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.Appender; +import ch.qos.logback.core.encoder.LayoutWrappingEncoder; +import ch.qos.logback.core.net.ssl.SSLConfiguration; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; +import io.dropwizard.logging.AbstractAppenderFactory; +import io.dropwizard.logging.async.AsyncAppenderFactory; +import io.dropwizard.logging.filter.LevelFilterFactory; +import io.dropwizard.logging.layout.LayoutFactory; +import java.time.Duration; +import javax.validation.constraints.NotEmpty; +import net.logstash.logback.appender.LogstashTcpSocketAppender; +import net.logstash.logback.encoder.LogstashEncoder; + +@JsonTypeName("logstashtcpsocket") +public class LogstashTcpSocketAppenderFactory extends AbstractAppenderFactory { + + @NotEmpty + private String destination; + + private Duration keepAlive = Duration.ofSeconds(20); + + @NotEmpty + private String apiKey; + + @JsonProperty + public String getDestination() { + return destination; + } + + @JsonProperty + public Duration getKeepAlive() { + return keepAlive; + } + + @JsonProperty + public String getApiKey() { + return apiKey; + } + + @Override + public Appender build( + final LoggerContext context, + final String applicationName, + final LayoutFactory layoutFactory, + final LevelFilterFactory levelFilterFactory, + final AsyncAppenderFactory asyncAppenderFactory) { + + final SSLConfiguration sslConfiguration = new SSLConfiguration(); + final LogstashTcpSocketAppender appender = new LogstashTcpSocketAppender(); + appender.setName("logstashtcpsocket-appender"); + appender.setContext(context); + appender.setSsl(sslConfiguration); + appender.addDestination(destination); + appender.setKeepAliveDuration(new ch.qos.logback.core.util.Duration(keepAlive.toMillis())); + + final LogstashEncoder encoder = new LogstashEncoder(); + final LayoutWrappingEncoder prefix = new LayoutWrappingEncoder<>(); + final PatternLayout layout = new PatternLayout(); + layout.setPattern(String.format("%s ", apiKey)); + prefix.setLayout(layout); + encoder.setPrefix(prefix); + appender.setEncoder(encoder); + + appender.addFilter(levelFilterFactory.build(threshold)); + getFilterFactories().forEach(f -> appender.addFilter(f.build())); + appender.start(); + + return wrapAsync(appender, asyncAppenderFactory); + } +} diff --git a/service/src/main/resources/META-INF/services/io.dropwizard.logging.AppenderFactory b/service/src/main/resources/META-INF/services/io.dropwizard.logging.AppenderFactory index 48771995f..ebe62c359 100644 --- a/service/src/main/resources/META-INF/services/io.dropwizard.logging.AppenderFactory +++ b/service/src/main/resources/META-INF/services/io.dropwizard.logging.AppenderFactory @@ -1 +1 @@ -org.whispersystems.textsecuregcm.metrics.LoggingNetworkAppenderFactory +org.whispersystems.textsecuregcm.metrics.LogstashTcpSocketAppenderFactory