From 007dde8d45f8f88b67749f150438c1275381fc2a Mon Sep 17 00:00:00 2001 From: Jonathan Klabunde Tomer <125505367+jkt-signal@users.noreply.github.com> Date: Mon, 22 Sep 2025 11:09:40 -0700 Subject: [PATCH] add OTLP logging appender factory --- pom.xml | 14 +++++ service/pom.xml | 52 ++++++++++++++----- .../textsecuregcm/WhisperServerService.java | 1 + .../textsecuregcm/metrics/MetricsUtil.java | 37 +++++++++++++ .../metrics/OpenTelemetryAppenderFactory.java | 50 ++++++++++++++++++ .../workers/CommandDependencies.java | 3 ++ ....dropwizard.logging.common.AppenderFactory | 1 + 7 files changed, 145 insertions(+), 13 deletions(-) create mode 100644 service/src/main/java/org/whispersystems/textsecuregcm/metrics/OpenTelemetryAppenderFactory.java diff --git a/pom.xml b/pom.xml index c05ea2db4..26659788a 100644 --- a/pom.xml +++ b/pom.xml @@ -163,6 +163,20 @@ pom import + + io.opentelemetry + opentelemetry-bom + 1.54.0 + pom + import + + + io.opentelemetry.instrumentation + opentelemetry-instrumentation-bom + 2.20.0 + pom + import + io.projectreactor reactor-bom diff --git a/service/pom.xml b/service/pom.xml index 4f0f7889e..f9f27173c 100644 --- a/service/pom.xml +++ b/service/pom.xml @@ -35,7 +35,7 @@ app-store-server-library ${storekit.version} - + com.squareup.okio okio-jvm @@ -198,6 +198,28 @@ test + + io.opentelemetry + opentelemetry-sdk + + + io.opentelemetry + opentelemetry-exporter-otlp + + + io.opentelemetry.instrumentation + opentelemetry-logback-appender-1.0 + + 2.19.0-alpha + + + + io.opentelemetry.instrumentation + opentelemetry-instrumentation-api-incubator + + + + party.iroiro.luajava luajava @@ -258,16 +280,11 @@ - com.google.firebase firebase-admin ${firebase-admin.version} - - io.opentelemetry.semconv - opentelemetry-semconv - com.google.guava @@ -275,18 +292,15 @@ - - io.opentelemetry.semconv - opentelemetry-semconv - 1.37.0 - + com.google.cloud google-cloud-firestore + - io.opentelemetry.semconv - opentelemetry-semconv + io.opentelemetry.instrumentation + opentelemetry-instrumentation-api-incubator @@ -561,9 +575,21 @@ org.jetbrains annotations + + + com.squareup.okio + okio-jvm + + + + com.squareup.okio + okio-jvm + 3.15.0 + + diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java index 8799d50b7..f1a36998a 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/WhisperServerService.java @@ -385,6 +385,7 @@ public class WhisperServerService extends Application dynamicConfigurationManager) { diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/metrics/OpenTelemetryAppenderFactory.java b/service/src/main/java/org/whispersystems/textsecuregcm/metrics/OpenTelemetryAppenderFactory.java new file mode 100644 index 000000000..201a4923b --- /dev/null +++ b/service/src/main/java/org/whispersystems/textsecuregcm/metrics/OpenTelemetryAppenderFactory.java @@ -0,0 +1,50 @@ +/* + * Copyright 2025 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.spi.ILoggingEvent; +import ch.qos.logback.core.Appender; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.annotation.JsonTypeName; +import io.dropwizard.logging.common.AbstractAppenderFactory; +import io.dropwizard.logging.common.async.AsyncAppenderFactory; +import io.dropwizard.logging.common.filter.LevelFilterFactory; +import io.dropwizard.logging.common.layout.LayoutFactory; +import io.opentelemetry.instrumentation.logback.appender.v1_0.OpenTelemetryAppender; +import jakarta.validation.constraints.NotEmpty; + +@JsonTypeName("otlp") +public class OpenTelemetryAppenderFactory extends AbstractAppenderFactory { + + @JsonProperty + private String destination; + + @Override + public Appender build( + final LoggerContext context, + final String applicationName, + final LayoutFactory layoutFactory, + final LevelFilterFactory levelFilterFactory, + final AsyncAppenderFactory asyncAppenderFactory) { + + final OpenTelemetryAppender appender = new OpenTelemetryAppender(); + appender.setCaptureCodeAttributes(true); + appender.setCaptureLoggerContext(true); + + // The installation of an OpenTelemetry configuration happens in + // WhisperServerService (or CommandDependencies), in order to let us tie + // into Dropwizard's lifecycle management; this allows us to buffer any + // logs emitted between now and that happening. + appender.setNumLogsCapturedBeforeOtelInstall(1000); + + appender.addFilter(levelFilterFactory.build(threshold)); + getFilterFactories().forEach(f -> appender.addFilter(f.build())); + appender.start(); + + return wrapAsync(appender, asyncAppenderFactory); + } +} diff --git a/service/src/main/java/org/whispersystems/textsecuregcm/workers/CommandDependencies.java b/service/src/main/java/org/whispersystems/textsecuregcm/workers/CommandDependencies.java index b6e790183..e5386896e 100644 --- a/service/src/main/java/org/whispersystems/textsecuregcm/workers/CommandDependencies.java +++ b/service/src/main/java/org/whispersystems/textsecuregcm/workers/CommandDependencies.java @@ -45,6 +45,7 @@ import org.whispersystems.textsecuregcm.experiment.ExperimentEnrollmentManager; import org.whispersystems.textsecuregcm.experiment.PushNotificationExperimentSamples; import org.whispersystems.textsecuregcm.grpc.net.GrpcClientConnectionManager; import org.whispersystems.textsecuregcm.limits.RateLimiters; +import org.whispersystems.textsecuregcm.metrics.MetricsUtil; import org.whispersystems.textsecuregcm.metrics.MicrometerAwsSdkMetricPublisher; import org.whispersystems.textsecuregcm.push.APNSender; import org.whispersystems.textsecuregcm.push.FcmSender; @@ -127,6 +128,8 @@ record CommandDependencies( throws IOException, GeneralSecurityException, InvalidInputException { Clock clock = Clock.systemUTC(); + MetricsUtil.configureLogging(configuration, environment); + environment.getObjectMapper().configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); final AwsCredentialsProvider awsCredentialsProvider = configuration.getAwsCredentialsConfiguration().build(); diff --git a/service/src/main/resources/META-INF/services/io.dropwizard.logging.common.AppenderFactory b/service/src/main/resources/META-INF/services/io.dropwizard.logging.common.AppenderFactory index ebe62c359..a4d1da70b 100644 --- a/service/src/main/resources/META-INF/services/io.dropwizard.logging.common.AppenderFactory +++ b/service/src/main/resources/META-INF/services/io.dropwizard.logging.common.AppenderFactory @@ -1 +1,2 @@ org.whispersystems.textsecuregcm.metrics.LogstashTcpSocketAppenderFactory +org.whispersystems.textsecuregcm.metrics.OpenTelemetryAppenderFactory