From 4b60ace162f3ad69dbbe0d9c22c4cce66ad54245 Mon Sep 17 00:00:00 2001 From: Matt Richardson Date: Thu, 28 Nov 2024 06:42:06 +1100 Subject: [PATCH] Move around where we configure the metrics --- .../endpoints/IOTELEndpointHandler.java | 3 +- .../custom/CustomOTELEndpointHandler.java | 3 +- .../HoneycombOTELEndpointHandler.java | 10 ++-- .../zipkin/ZipKinOTELEndpointHandler.java | 3 +- .../HelperPerBuildOTELHelperFactory.java | 11 +++-- .../server/helpers/OTELHelperImpl.java | 39 --------------- .../server/helpers/OTELMetrics.java | 49 +++++++++++++++++++ .../opentelemetry/server/OTELHelperTest.java | 2 +- .../server/TeamCityBuildListenerTest.java | 2 +- 9 files changed, 70 insertions(+), 52 deletions(-) create mode 100644 server/src/main/java/com/octopus/teamcity/opentelemetry/server/helpers/OTELMetrics.java diff --git a/server/src/main/java/com/octopus/teamcity/opentelemetry/server/endpoints/IOTELEndpointHandler.java b/server/src/main/java/com/octopus/teamcity/opentelemetry/server/endpoints/IOTELEndpointHandler.java index d8eef1c..f9d08f4 100644 --- a/server/src/main/java/com/octopus/teamcity/opentelemetry/server/endpoints/IOTELEndpointHandler.java +++ b/server/src/main/java/com/octopus/teamcity/opentelemetry/server/endpoints/IOTELEndpointHandler.java @@ -1,6 +1,7 @@ package com.octopus.teamcity.opentelemetry.server.endpoints; import com.octopus.teamcity.opentelemetry.server.SetProjectConfigurationSettingsRequest; +import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.sdk.metrics.export.MetricExporter; import io.opentelemetry.sdk.trace.SpanProcessor; import jetbrains.buildServer.serverSide.SBuild; @@ -13,7 +14,7 @@ public interface IOTELEndpointHandler { ModelAndView getBuildOverviewModelAndView(SBuild build, Map params, String traceId); - SpanProcessor buildSpanProcessor(String endpoint, Map params); + SpanProcessor buildSpanProcessor(String endpoint, Map params, MeterProvider meterProvider); @Nullable MetricExporter buildMetricsExporter(String endpoint, Map params); diff --git a/server/src/main/java/com/octopus/teamcity/opentelemetry/server/endpoints/custom/CustomOTELEndpointHandler.java b/server/src/main/java/com/octopus/teamcity/opentelemetry/server/endpoints/custom/CustomOTELEndpointHandler.java index ca52990..a6cc80e 100644 --- a/server/src/main/java/com/octopus/teamcity/opentelemetry/server/endpoints/custom/CustomOTELEndpointHandler.java +++ b/server/src/main/java/com/octopus/teamcity/opentelemetry/server/endpoints/custom/CustomOTELEndpointHandler.java @@ -2,6 +2,7 @@ import com.octopus.teamcity.opentelemetry.server.*; import com.octopus.teamcity.opentelemetry.server.endpoints.IOTELEndpointHandler; +import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporterBuilder; import io.opentelemetry.sdk.metrics.export.MetricExporter; @@ -36,7 +37,7 @@ public ModelAndView getBuildOverviewModelAndView(SBuild build, Map params) { + public SpanProcessor buildSpanProcessor(String endpoint, Map params, MeterProvider meterProvider) { Map headers = new HashMap<>(); params.forEach((k, v) -> { if (k.startsWith(PROPERTY_KEY_HEADERS)) { diff --git a/server/src/main/java/com/octopus/teamcity/opentelemetry/server/endpoints/honeycomb/HoneycombOTELEndpointHandler.java b/server/src/main/java/com/octopus/teamcity/opentelemetry/server/endpoints/honeycomb/HoneycombOTELEndpointHandler.java index 8cf4302..a843433 100644 --- a/server/src/main/java/com/octopus/teamcity/opentelemetry/server/endpoints/honeycomb/HoneycombOTELEndpointHandler.java +++ b/server/src/main/java/com/octopus/teamcity/opentelemetry/server/endpoints/honeycomb/HoneycombOTELEndpointHandler.java @@ -2,7 +2,6 @@ import com.octopus.teamcity.opentelemetry.server.*; import com.octopus.teamcity.opentelemetry.server.endpoints.IOTELEndpointHandler; -import io.opentelemetry.api.GlobalOpenTelemetry; import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter; import io.opentelemetry.exporter.otlp.trace.OtlpGrpcSpanExporter; @@ -56,12 +55,12 @@ public ModelAndView getBuildOverviewModelAndView(SBuild build, Map params) { + public SpanProcessor buildSpanProcessor(String endpoint, Map params, MeterProvider meterProvider) { Map headers = new HashMap<>(); //todo: add a setting to say "use classic" or "use environments" headers.put("x-honeycomb-dataset", params.get(PROPERTY_KEY_HONEYCOMB_DATASET)); headers.put("x-honeycomb-team", EncryptUtil.unscramble(params.get(PROPERTY_KEY_HONEYCOMB_APIKEY))); - return buildGrpcSpanProcessor(headers, endpoint); + return buildGrpcSpanProcessor(headers, endpoint, meterProvider); } @Override @@ -77,15 +76,16 @@ public MetricExporter buildMetricsExporter(String endpoint, Map return null; } - private SpanProcessor buildGrpcSpanProcessor(Map headers, String exporterEndpoint) { + private SpanProcessor buildGrpcSpanProcessor(Map headers, String exporterEndpoint, MeterProvider meterProvider) { var spanExporterBuilder = OtlpGrpcSpanExporter.builder(); headers.forEach(spanExporterBuilder::addHeader); var spanExporter = spanExporterBuilder .setEndpoint(exporterEndpoint) + .setMeterProvider(meterProvider) .build(); return BatchSpanProcessor.builder(spanExporter) - .setMeterProvider(GlobalOpenTelemetry.getMeterProvider()) + .setMeterProvider(meterProvider) .build(); } diff --git a/server/src/main/java/com/octopus/teamcity/opentelemetry/server/endpoints/zipkin/ZipKinOTELEndpointHandler.java b/server/src/main/java/com/octopus/teamcity/opentelemetry/server/endpoints/zipkin/ZipKinOTELEndpointHandler.java index 1dc5c33..f60dd4b 100644 --- a/server/src/main/java/com/octopus/teamcity/opentelemetry/server/endpoints/zipkin/ZipKinOTELEndpointHandler.java +++ b/server/src/main/java/com/octopus/teamcity/opentelemetry/server/endpoints/zipkin/ZipKinOTELEndpointHandler.java @@ -2,6 +2,7 @@ import com.octopus.teamcity.opentelemetry.server.SetProjectConfigurationSettingsRequest; import com.octopus.teamcity.opentelemetry.server.endpoints.IOTELEndpointHandler; +import io.opentelemetry.api.metrics.MeterProvider; import io.opentelemetry.exporter.zipkin.ZipkinSpanExporter; import io.opentelemetry.sdk.metrics.export.MetricExporter; import io.opentelemetry.sdk.trace.SpanProcessor; @@ -36,7 +37,7 @@ public ModelAndView getBuildOverviewModelAndView(SBuild build, Map params) { + public SpanProcessor buildSpanProcessor(String endpoint, Map params, MeterProvider meterProvider) { return buildZipkinSpanProcessor(endpoint); } diff --git a/server/src/main/java/com/octopus/teamcity/opentelemetry/server/helpers/HelperPerBuildOTELHelperFactory.java b/server/src/main/java/com/octopus/teamcity/opentelemetry/server/helpers/HelperPerBuildOTELHelperFactory.java index 8d58409..5a81fe7 100644 --- a/server/src/main/java/com/octopus/teamcity/opentelemetry/server/helpers/HelperPerBuildOTELHelperFactory.java +++ b/server/src/main/java/com/octopus/teamcity/opentelemetry/server/helpers/HelperPerBuildOTELHelperFactory.java @@ -1,7 +1,10 @@ package com.octopus.teamcity.opentelemetry.server.helpers; +import com.octopus.teamcity.opentelemetry.common.PluginConstants; import com.octopus.teamcity.opentelemetry.server.endpoints.OTELEndpointFactory; -import io.opentelemetry.sdk.trace.SpanProcessor; +import io.opentelemetry.api.common.Attributes; +import io.opentelemetry.sdk.resources.Resource; +import io.opentelemetry.semconv.resource.attributes.ResourceAttributes; import jetbrains.buildServer.serverSide.BuildPromotion; import jetbrains.buildServer.serverSide.ProjectManager; import org.apache.log4j.Logger; @@ -45,11 +48,13 @@ public OTELHelper getOTELHelper(BuildPromotion build) { var endpoint = params.get(PROPERTY_KEY_ENDPOINT); var otelHandler = otelEndpointFactory.getOTELEndpointHandler(params.get(PROPERTY_KEY_SERVICE)); - var spanProcessor = otelHandler.buildSpanProcessor(endpoint, params); var metricsExporter = otelHandler.buildMetricsExporter(endpoint, params); + var serviceNameResource = Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, PluginConstants.SERVICE_NAME)); + var meterProvider = OTELMetrics.getOTELMeterProvider(metricsExporter, serviceNameResource); + var spanProcessor = otelHandler.buildSpanProcessor(endpoint, params, meterProvider); long startTime = System.nanoTime(); - var otelHelper = new OTELHelperImpl(spanProcessor, metricsExporter, String.valueOf(buildId)); + var otelHelper = new OTELHelperImpl(spanProcessor, String.valueOf(buildId)); long endTime = System.nanoTime(); long duration = (endTime - startTime); diff --git a/server/src/main/java/com/octopus/teamcity/opentelemetry/server/helpers/OTELHelperImpl.java b/server/src/main/java/com/octopus/teamcity/opentelemetry/server/helpers/OTELHelperImpl.java index 67b75d7..2181189 100644 --- a/server/src/main/java/com/octopus/teamcity/opentelemetry/server/helpers/OTELHelperImpl.java +++ b/server/src/main/java/com/octopus/teamcity/opentelemetry/server/helpers/OTELHelperImpl.java @@ -10,11 +10,7 @@ import io.opentelemetry.api.trace.propagation.W3CTraceContextPropagator; import io.opentelemetry.context.Context; import io.opentelemetry.context.propagation.ContextPropagators; -import io.opentelemetry.exporter.logging.LoggingMetricExporter; import io.opentelemetry.sdk.OpenTelemetrySdk; -import io.opentelemetry.sdk.metrics.SdkMeterProvider; -import io.opentelemetry.sdk.metrics.export.MetricExporter; -import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; import io.opentelemetry.sdk.resources.Resource; import io.opentelemetry.sdk.trace.SdkTracerProvider; import io.opentelemetry.sdk.trace.SpanProcessor; @@ -23,10 +19,8 @@ import org.jetbrains.annotations.NotNull; import javax.annotation.Nullable; -import java.time.Duration; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicBoolean; public class OTELHelperImpl implements OTELHelper { static Logger LOG = Logger.getLogger(OTELHelperImpl.class.getName()); @@ -35,17 +29,13 @@ public class OTELHelperImpl implements OTELHelper { private final ConcurrentHashMap spanMap; private final SdkTracerProvider sdkTracerProvider; private final String helperName; - private static final AtomicBoolean metricsConfigured = new AtomicBoolean(false); public OTELHelperImpl( @NotNull SpanProcessor spanProcessor, - @Nullable MetricExporter metricExporter, @NotNull String helperName) { this.helperName = helperName; var serviceNameResource = Resource.create(Attributes.of(ResourceAttributes.SERVICE_NAME, PluginConstants.SERVICE_NAME)); - configureMetricsExport(metricExporter, serviceNameResource); - this.sdkTracerProvider = SdkTracerProvider.builder() .setResource(Resource.getDefault().merge(serviceNameResource)) .addSpanProcessor(spanProcessor) @@ -58,35 +48,6 @@ public OTELHelperImpl( this.spanMap = new ConcurrentHashMap<>(); } - public static void configureMetricsExport(@Nullable MetricExporter metricExporter, Resource serviceNameResource) { - if (metricsConfigured.get()) return; - metricsConfigured.set(true); - - var loggingMetricExporter = LoggingMetricExporter.create(); - var consoleLogMetricReader = PeriodicMetricReader.builder(loggingMetricExporter) - .setInterval(Duration.ofSeconds(10)) - .build(); - var meterProviderBuilder = SdkMeterProvider.builder() - .setResource(Resource.getDefault().merge(serviceNameResource)) - .registerMetricReader(consoleLogMetricReader); - if (metricExporter != null) { - var providedMetricExporterBuilder = PeriodicMetricReader.builder(metricExporter) - .setInterval(Duration.ofSeconds(10)) - .build(); - meterProviderBuilder - .registerMetricReader(providedMetricExporterBuilder); - } - var sdkMeterProvider = meterProviderBuilder.build(); - var globalOpenTelemetry = OpenTelemetrySdk.builder() - .setMeterProvider(sdkMeterProvider) - .build(); - - GlobalOpenTelemetry.set(globalOpenTelemetry); - - // Shutdown hooks to close resources properly - Runtime.getRuntime().addShutdownHook(new Thread(sdkMeterProvider::close)); - } - @Override public boolean isReady() { return this.openTelemetry != null && this.tracer != null && this.spanMap != null; diff --git a/server/src/main/java/com/octopus/teamcity/opentelemetry/server/helpers/OTELMetrics.java b/server/src/main/java/com/octopus/teamcity/opentelemetry/server/helpers/OTELMetrics.java new file mode 100644 index 0000000..ee34cc1 --- /dev/null +++ b/server/src/main/java/com/octopus/teamcity/opentelemetry/server/helpers/OTELMetrics.java @@ -0,0 +1,49 @@ +package com.octopus.teamcity.opentelemetry.server.helpers; + +import io.opentelemetry.api.GlobalOpenTelemetry; +import io.opentelemetry.exporter.logging.LoggingMetricExporter; +import io.opentelemetry.sdk.OpenTelemetrySdk; +import io.opentelemetry.sdk.metrics.SdkMeterProvider; +import io.opentelemetry.sdk.metrics.export.MetricExporter; +import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader; +import io.opentelemetry.sdk.resources.Resource; + +import javax.annotation.Nullable; +import java.time.Duration; +import java.util.concurrent.atomic.AtomicBoolean; + +public class OTELMetrics { + + private static final AtomicBoolean metricsConfigured = new AtomicBoolean(false); + private static SdkMeterProvider sdkMeterProvider; + + public static SdkMeterProvider getOTELMeterProvider(@Nullable MetricExporter metricExporter, Resource serviceNameResource) { + if (metricsConfigured.get()) return sdkMeterProvider; + metricsConfigured.set(true); + + var loggingMetricExporter = LoggingMetricExporter.create(); + var consoleLogMetricReader = PeriodicMetricReader.builder(loggingMetricExporter) + .setInterval(Duration.ofSeconds(10)) + .build(); + var meterProviderBuilder = SdkMeterProvider.builder() + .setResource(Resource.getDefault().merge(serviceNameResource)) + .registerMetricReader(consoleLogMetricReader); + if (metricExporter != null) { + var providedMetricExporterBuilder = PeriodicMetricReader.builder(metricExporter) + .setInterval(Duration.ofSeconds(10)) + .build(); + meterProviderBuilder + .registerMetricReader(providedMetricExporterBuilder); + } + sdkMeterProvider = meterProviderBuilder.build(); + var globalOpenTelemetry = OpenTelemetrySdk.builder() + .setMeterProvider(sdkMeterProvider) + .build(); + + GlobalOpenTelemetry.set(globalOpenTelemetry); + + // Shutdown hooks to close resources properly + Runtime.getRuntime().addShutdownHook(new Thread(sdkMeterProvider::close)); + return sdkMeterProvider; + } +} diff --git a/server/src/test/java/com/octopus/teamcity/opentelemetry/server/OTELHelperTest.java b/server/src/test/java/com/octopus/teamcity/opentelemetry/server/OTELHelperTest.java index e56ea16..76d0bd4 100644 --- a/server/src/test/java/com/octopus/teamcity/opentelemetry/server/OTELHelperTest.java +++ b/server/src/test/java/com/octopus/teamcity/opentelemetry/server/OTELHelperTest.java @@ -33,7 +33,7 @@ class OTELHelperTest { @BeforeEach void setUp() { GlobalOpenTelemetry.resetForTest(); - this.otelHelper = new OTELHelperImpl(mock(SpanProcessor.class, RETURNS_DEEP_STUBS), null, "helperNamr"); + this.otelHelper = new OTELHelperImpl(mock(SpanProcessor.class, RETURNS_DEEP_STUBS), "helperNamr"); } diff --git a/server/src/test/java/com/octopus/teamcity/opentelemetry/server/TeamCityBuildListenerTest.java b/server/src/test/java/com/octopus/teamcity/opentelemetry/server/TeamCityBuildListenerTest.java index e83e81d..2566fdb 100644 --- a/server/src/test/java/com/octopus/teamcity/opentelemetry/server/TeamCityBuildListenerTest.java +++ b/server/src/test/java/com/octopus/teamcity/opentelemetry/server/TeamCityBuildListenerTest.java @@ -34,7 +34,7 @@ class TeamCityBuildListenerTest { @BeforeEach void setUp(@Mock EventDispatcher buildServerListenerEventDispatcher) { GlobalOpenTelemetry.resetForTest(); - this.otelHelper = new OTELHelperImpl(mock(SpanProcessor.class, RETURNS_DEEP_STUBS), null, "helper"); + this.otelHelper = new OTELHelperImpl(mock(SpanProcessor.class, RETURNS_DEEP_STUBS), "helper"); this.factory = mock(OTELHelperFactory.class, RETURNS_DEEP_STUBS); var buildStorageManager = mock(BuildStorageManager.class, RETURNS_DEEP_STUBS);