Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add metrics & micrometer support to spring-boot-autoconfigure #6270

Merged
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,10 @@ muzzle {
dependencies {
library("io.micrometer:micrometer-core:1.5.0")

implementation("io.opentelemetry:opentelemetry-micrometer1-shim")
implementation("io.opentelemetry:opentelemetry-micrometer1-shim") {
// just get the instrumentation, without the micrometer itself
mateuszrzeszutek marked this conversation as resolved.
Show resolved Hide resolved
exclude("io.micrometer", "micrometer-core")
}
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oops 🙈

}

tasks {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,27 @@ plugins {
group = "io.opentelemetry.instrumentation"

val versions: Map<String, String> by project
val springBootVersion = versions["org.springframework.boot"]

dependencies {
implementation(project(":instrumentation-api-annotation-support"))

implementation("org.springframework.boot:spring-boot-autoconfigure:${versions["org.springframework.boot"]}")
annotationProcessor("org.springframework.boot:spring-boot-autoconfigure-processor:${versions["org.springframework.boot"]}")
implementation("org.springframework.boot:spring-boot-autoconfigure:$springBootVersion")
annotationProcessor("org.springframework.boot:spring-boot-autoconfigure-processor:$springBootVersion")
implementation("javax.validation:validation-api:2.0.1.Final")

implementation(project(":instrumentation:spring:spring-web-3.1:library"))
implementation(project(":instrumentation:spring:spring-webmvc-3.1:library"))
implementation(project(":instrumentation:spring:spring-webflux-5.0:library"))
implementation("io.opentelemetry:opentelemetry-micrometer1-shim") {
// just get the instrumentation, without the micrometer itself
mateuszrzeszutek marked this conversation as resolved.
Show resolved Hide resolved
exclude("io.micrometer", "micrometer-core")
}

compileOnly("org.springframework.boot:spring-boot-starter-aop:${versions["org.springframework.boot"]}")
compileOnly("org.springframework.boot:spring-boot-starter-web:${versions["org.springframework.boot"]}")
compileOnly("org.springframework.boot:spring-boot-starter-webflux:${versions["org.springframework.boot"]}")
compileOnly("org.springframework.boot:spring-boot-starter-actuator:$springBootVersion")
compileOnly("org.springframework.boot:spring-boot-starter-aop:$springBootVersion")
compileOnly("org.springframework.boot:spring-boot-starter-web:$springBootVersion")
compileOnly("org.springframework.boot:spring-boot-starter-webflux:$springBootVersion")

compileOnly("io.opentelemetry:opentelemetry-sdk-extension-autoconfigure-spi")
compileOnly("io.opentelemetry:opentelemetry-extension-annotations")
Expand All @@ -32,10 +38,11 @@ dependencies {
compileOnly("io.opentelemetry:opentelemetry-exporter-otlp")
compileOnly("io.opentelemetry:opentelemetry-exporter-zipkin")

testImplementation("org.springframework.boot:spring-boot-starter-aop:${versions["org.springframework.boot"]}")
testImplementation("org.springframework.boot:spring-boot-starter-webflux:${versions["org.springframework.boot"]}")
testImplementation("org.springframework.boot:spring-boot-starter-web:${versions["org.springframework.boot"]}")
testImplementation("org.springframework.boot:spring-boot-starter-test:${versions["org.springframework.boot"]}") {
testImplementation("org.springframework.boot:spring-boot-starter-actuator:$springBootVersion")
testImplementation("org.springframework.boot:spring-boot-starter-aop:$springBootVersion")
testImplementation("org.springframework.boot:spring-boot-starter-webflux:$springBootVersion")
testImplementation("org.springframework.boot:spring-boot-starter-web:$springBootVersion")
testImplementation("org.springframework.boot:spring-boot-starter-test:$springBootVersion") {
exclude("org.junit.vintage", "junit-vintage-engine")
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.spring.autoconfigure;

import java.time.Duration;
import javax.annotation.Nullable;
import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "otel.metric.export")
public class MetricExportProperties {

@Nullable private Duration interval;

@Nullable
public Duration getInterval() {
return interval;
}

public void setInterval(@Nullable Duration interval) {
this.interval = interval;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@
import io.opentelemetry.sdk.OpenTelemetrySdk;
import io.opentelemetry.sdk.autoconfigure.spi.ConfigProperties;
import io.opentelemetry.sdk.autoconfigure.spi.ResourceProvider;
import io.opentelemetry.sdk.metrics.SdkMeterProvider;
import io.opentelemetry.sdk.metrics.SdkMeterProviderBuilder;
import io.opentelemetry.sdk.metrics.export.MetricExporter;
import io.opentelemetry.sdk.metrics.export.PeriodicMetricReader;
import io.opentelemetry.sdk.metrics.export.PeriodicMetricReaderBuilder;
import io.opentelemetry.sdk.resources.Resource;
import io.opentelemetry.sdk.trace.SdkTracerProvider;
import io.opentelemetry.sdk.trace.SdkTracerProviderBuilder;
Expand All @@ -36,7 +41,7 @@
* <p>Updates the sampler probability for the configured {@link TracerProvider}.
*/
@Configuration
@EnableConfigurationProperties(SamplerProperties.class)
@EnableConfigurationProperties({MetricExportProperties.class, SamplerProperties.class})
public class OpenTelemetryAutoConfiguration {

@Configuration
Expand All @@ -61,6 +66,32 @@ public SdkTracerProvider sdkTracerProvider(
.build();
}

@Bean
@ConditionalOnMissingBean
public SdkMeterProvider sdkMeterProvider(
MetricExportProperties properties,
ObjectProvider<List<MetricExporter>> metricExportersProvider,
Resource otelResource) {

SdkMeterProviderBuilder meterProviderBuilder = SdkMeterProvider.builder();

metricExportersProvider.getIfAvailable(Collections::emptyList).stream()
.map(metricExporter -> createPeriodicMetricReader(properties, metricExporter))
.forEach(meterProviderBuilder::registerMetricReader);

return meterProviderBuilder.setResource(otelResource).build();
}

private static PeriodicMetricReader createPeriodicMetricReader(
MetricExportProperties properties, MetricExporter metricExporter) {
PeriodicMetricReaderBuilder metricReaderBuilder =
PeriodicMetricReader.builder(metricExporter);
if (properties.getInterval() != null) {
metricReaderBuilder.setInterval(properties.getInterval());
}
return metricReaderBuilder.build();
}

@Bean
@ConditionalOnMissingBean
public Resource otelResource(
Expand All @@ -76,12 +107,15 @@ public Resource otelResource(

@Bean
public OpenTelemetry openTelemetry(
ObjectProvider<ContextPropagators> propagatorsProvider, SdkTracerProvider tracerProvider) {
ObjectProvider<ContextPropagators> propagatorsProvider,
SdkTracerProvider tracerProvider,
SdkMeterProvider meterProvider) {

ContextPropagators propagators = propagatorsProvider.getIfAvailable(ContextPropagators::noop);

return OpenTelemetrySdk.builder()
.setTracerProvider(tracerProvider)
.setMeterProvider(meterProvider)
.setPropagators(propagators)
.build();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.spring.autoconfigure.exporters.logging;

import org.springframework.boot.context.properties.ConfigurationProperties;

/** Configuration for {@link io.opentelemetry.exporter.logging.LoggingSpanExporter}. */
mateuszrzeszutek marked this conversation as resolved.
Show resolved Hide resolved
@ConfigurationProperties(prefix = "otel.exporter.logging")
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should we open sdk issue to add these as sdk autoconfigure properties?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really think so - the spring starter uses a bit different configuration scheme than the SDK (which I dislike, I think they should use exactly the same property names), and the exporters are enabled/disabled in a bit different way.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I fully support changing the sprint boot configuration to match sdk configuration if that's possible

public final class LoggingExporterProperties {

private boolean enabled = true;
private final SignalProperties traces = new SignalProperties();
private final SignalProperties metrics = new SignalProperties();

public boolean isEnabled() {
return enabled;
}

public void setEnabled(boolean enabled) {
this.enabled = enabled;
}

public SignalProperties getTraces() {
return traces;
}

public SignalProperties getMetrics() {
return metrics;
}

public static class SignalProperties {

private boolean enabled = true;

public boolean isEnabled() {
return enabled;
}

public void setEnabled(boolean enabled) {
this.enabled = enabled;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.spring.autoconfigure.exporters.logging;

import io.opentelemetry.exporter.logging.LoggingMetricExporter;
import io.opentelemetry.exporter.logging.LoggingSpanExporter;
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/** Configures {@link LoggingSpanExporter} bean for tracing. */
@Configuration
@EnableConfigurationProperties(LoggingExporterProperties.class)
@AutoConfigureBefore(OpenTelemetryAutoConfiguration.class)
@ConditionalOnProperty(
prefix = "otel.exporter.logging",
name = {"enabled", "metrics.enabled"},
matchIfMissing = true)
@ConditionalOnClass(LoggingMetricExporter.class)
public class LoggingMetricExporterAutoConfiguration {

@Bean
@ConditionalOnMissingBean
public LoggingMetricExporter otelLoggingMetricExporter() {
return LoggingMetricExporter.create();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@

/** Configures {@link LoggingSpanExporter} bean for tracing. */
@Configuration
@EnableConfigurationProperties(LoggingSpanExporterProperties.class)
@EnableConfigurationProperties(LoggingExporterProperties.class)
@AutoConfigureBefore(OpenTelemetryAutoConfiguration.class)
@ConditionalOnProperty(prefix = "otel.exporter.logging", name = "enabled", matchIfMissing = true)
@ConditionalOnProperty(
prefix = "otel.exporter.logging",
name = {"enabled", "traces.enabled"},
matchIfMissing = true)
@ConditionalOnClass(LoggingSpanExporter.class)
public class LoggingSpanExporterAutoConfiguration {

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,13 @@
* <p>Get max wait time for Collector to process Span Batches
*/
@ConfigurationProperties(prefix = "otel.exporter.otlp")
public final class OtlpGrpcSpanExporterProperties {
public final class OtlpExporterProperties {

private boolean enabled = true;
@Nullable private String endpoint;
@Nullable private Duration timeout;
private final SignalProperties traces = new SignalProperties();
private final SignalProperties metrics = new SignalProperties();

public boolean isEnabled() {
return enabled;
Expand All @@ -50,4 +52,45 @@ public Duration getTimeout() {
public void setTimeout(Duration timeout) {
this.timeout = timeout;
}

public SignalProperties getTraces() {
return traces;
}

public SignalProperties getMetrics() {
return metrics;
}

public static class SignalProperties {

private boolean enabled = true;
@Nullable private String endpoint;
@Nullable private Duration timeout;

public boolean isEnabled() {
return enabled;
}

public void setEnabled(boolean enabled) {
this.enabled = enabled;
}

@Nullable
public String getEndpoint() {
return endpoint;
}

public void setEndpoint(@Nullable String endpoint) {
this.endpoint = endpoint;
}

@Nullable
public Duration getTimeout() {
return timeout;
}

public void setTimeout(@Nullable Duration timeout) {
this.timeout = timeout;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright The OpenTelemetry Authors
* SPDX-License-Identifier: Apache-2.0
*/

package io.opentelemetry.instrumentation.spring.autoconfigure.exporters.otlp;

import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporter;
import io.opentelemetry.exporter.otlp.metrics.OtlpGrpcMetricExporterBuilder;
import io.opentelemetry.instrumentation.spring.autoconfigure.OpenTelemetryAutoConfiguration;
import java.time.Duration;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
@AutoConfigureBefore(OpenTelemetryAutoConfiguration.class)
@EnableConfigurationProperties(OtlpExporterProperties.class)
@ConditionalOnProperty(
prefix = "otel.exporter.otlp",
name = {"enabled", "metrics.enabled"},
matchIfMissing = true)
@ConditionalOnClass(OtlpGrpcMetricExporter.class)
public class OtlpMetricExporterAutoConfiguration {

@Bean
@ConditionalOnMissingBean
public OtlpGrpcMetricExporter otelOtlpGrpcMetricExporter(OtlpExporterProperties properties) {
OtlpGrpcMetricExporterBuilder builder = OtlpGrpcMetricExporter.builder();

String endpoint = properties.getMetrics().getEndpoint();
if (endpoint == null) {
endpoint = properties.getEndpoint();
}
if (endpoint != null) {
builder.setEndpoint(endpoint);
}

Duration timeout = properties.getMetrics().getTimeout();
if (timeout == null) {
timeout = properties.getTimeout();
}
if (timeout != null) {
builder.setTimeout(timeout);
}

return builder.build();
}
}
Loading