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

Distributed tracing support with Micrometer and OpenTelemetry #376

Closed
wants to merge 8 commits into from

Conversation

o-shevchenko
Copy link

Context: #362

import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.TraceId;

public class TracingClientInterceptor implements ClientInterceptor {
Copy link
Author

Choose a reason for hiding this comment

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

Should it be moved to grpc-client-spring-boot-starter?

Copy link
Collaborator

Choose a reason for hiding this comment

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

Yes

Copy link
Author

Choose a reason for hiding this comment

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

done

import org.springframework.stereotype.Component;

@Component
public class LogInterceptor implements ServerInterceptor {
Copy link
Author

Choose a reason for hiding this comment

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

There is an example but LogInterceptor was not a part of grpc-spring-boot-starter. I can add it in a separate PR if needed

Copy link
Collaborator

Choose a reason for hiding this comment

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

Why is this class needed ?

Copy link
Author

Choose a reason for hiding this comment

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

Just wanted to use it from the library. Not sure why it doesn't exist. Removed

@codecov
Copy link

codecov bot commented Sep 7, 2023

Codecov Report

Patch coverage has no change and project coverage change: -2.18% ⚠️

Comparison is base (11d1660) 87.53% compared to head (56e34d9) 85.35%.
Report is 1 commits behind head on master.

Additional details and impacted files
@@             Coverage Diff              @@
##             master     #376      +/-   ##
============================================
- Coverage     87.53%   85.35%   -2.18%     
  Complexity      327      327              
============================================
  Files            51       54       +3     
  Lines          1612     1653      +41     
  Branches         96       99       +3     
============================================
  Hits           1411     1411              
- Misses          159      200      +41     
  Partials         42       42              
Files Changed Coverage Δ
...ingboot/grpc/tracing/TracingClientInterceptor.java 0.00% <0.00%> (ø)
.../grpc/autoconfigure/tracing/OtelConfiguration.java 0.00% <0.00%> (ø)
...ingboot/grpc/tracing/TracingServerInterceptor.java 0.00% <0.00%> (ø)

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@o-shevchenko
Copy link
Author

@jvmlet Any thoughts?

@jvmlet
Copy link
Collaborator

jvmlet commented Sep 12, 2023

@jvmlet Any thoughts?

Sorry, busy days. Will review the PR soon


@Configuration
@EnableConfigurationProperties(OtlpProperties.class)
public class OtelConfiguration {
Copy link
Collaborator

Choose a reason for hiding this comment

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

this should be conditional on OpenTelemetrySdk class

Copy link
Author

Choose a reason for hiding this comment

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

makes sense

import org.springframework.stereotype.Component;

@Component
public class LogInterceptor implements ServerInterceptor {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why is this class needed ?

import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;

public class TracingServerInterceptor implements ServerInterceptor {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Copy link
Author

Choose a reason for hiding this comment

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

added @order(20)? I don't see you use @Order. Have you had in mind any other mechanisms?

Copy link
Collaborator

Choose a reason for hiding this comment

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

see this, we will need to move recovery and auth down 1

Copy link
Author

Choose a reason for hiding this comment

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

I've set HIGHEST_PRECEDENCE. Do I need to touch recovery and auth here? Please, let me know if you want to change order somehow

@@ -286,6 +286,8 @@ dependencies {
compileOnly "org.springframework.boot:spring-boot-starter-actuator"
compileOnly "org.springframework.boot:spring-boot-starter-validation"
compileOnly 'org.springframework.cloud:spring-cloud-starter-consul-discovery'
compileOnly 'io.opentelemetry:opentelemetry-exporter-otlp:1.29.0'
compileOnly 'io.micrometer:micrometer-tracing-bridge-otel:1.1.4'
Copy link
Collaborator

Choose a reason for hiding this comment

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

io.micrometer:micrometer-tracing-bridge-otel is it in-use ?

Copy link
Author

Choose a reason for hiding this comment

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

image Used in OtelConfiguration, removed from client

@kprasad99
Copy link

probably better to use grpc-instrumentation dependency and set context, the above changes doesn't seems to work, header here considered is traceId and not traceparent, propagators can be different. Following are the steps I followed.

  1. Add gradle dependency.
implementation "io.opentelemetry.instrumentation:opentelemetry-grpc-1.6:${otelInstrumentationVersion}"
  1. Add Grpc otel configuration class. the below @autoConfiguration is simpler one, however we might need to add additional conditionals to enable the tracer.
@Slf4j
@Configuration
@ConditionalOnEnabledTracing
public class GrpcOtelConfiguration {

   @Bean
    @GRpcGlobalInterceptor
    public ServerInterceptor otelGrpcInterceptor(OpenTelemetry telemetry) {
		log.info("Enabling otel tracer for grpc");
		return GrpcTelemetry.create(telemetry).newServerInterceptor();
	}
	
}

@pieter-stek
Copy link

Micrometer already supports gRPC instrumentation. So, using ObservationGrpcServerInterceptor and ObservationGrpcClientInterceptor from Micrometer is also an option

@o-shevchenko
Copy link
Author

o-shevchenko commented Feb 14, 2024

Micrometer already supports gRPC instrumentation. So, using ObservationGrpcServerInterceptor and ObservationGrpcClientInterceptor from Micrometer is also an option

@GRpcService(interceptors = [LogInterceptor::class, ObservationGrpcServerInterceptor::class])

That's what I tried from the beginning. But it leads to the error:

Caused by: java.lang.RuntimeException: Failed to start GRPC server
	at org.lognet.springboot.grpc.GRpcServerRunner.start(GRpcServerRunner.java:112)
	at org.springframework.context.support.DefaultLifecycleProcessor.doStart(DefaultLifecycleProcessor.java:284)
	... 18 common frames omitted
Caused by: org.springframework.beans.factory.BeanCreationException: Failed to create interceptor instance.
	at org.lognet.springboot.grpc.GRpcServerRunner.lambda$bindInterceptors$3(GRpcServerRunner.java:129)
	at java.base/java.util.stream.ReferencePipeline$3$1.accept(ReferencePipeline.java:197)
	at java.base/java.util.Spliterators$ArraySpliterator.forEachRemaining(Spliterators.java:992)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.StreamSpliterators$WrappingSpliterator.forEachRemaining(StreamSpliterators.java:310)
	at java.base/java.util.stream.Streams$ConcatSpliterator.forEachRemaining(Streams.java:735)
	at java.base/java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:509)
	at java.base/java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:499)
	at java.base/java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:921)
	at java.base/java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
	at java.base/java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:682)
	at org.lognet.springboot.grpc.GRpcServerRunner.bindInterceptors(GRpcServerRunner.java:140)
	at org.lognet.springboot.grpc.GRpcServerRunner.lambda$start$0(GRpcServerRunner.java:72)
	at java.base/java.util.HashMap.forEach(HashMap.java:1421)
	at org.lognet.springboot.grpc.GRpcServerRunner.start(GRpcServerRunner.java:66)
	... 19 common frames omitted
Caused by: java.lang.InstantiationException: io.micrometer.core.instrument.binder.grpc.ObservationGrpcServerInterceptor
	at java.base/java.lang.Class.newInstance(Class.java:639)
	at org.lognet.springboot.grpc.GRpcServerRunner.lambda$bindInterceptors$3(GRpcServerRunner.java:127)
	... 34 common frames omitted
Caused by: java.lang.NoSuchMethodException: io.micrometer.core.instrument.binder.grpc.ObservationGrpcServerInterceptor.<init>()
	at java.base/java.lang.Class.getConstructor0(Class.java:3585)
	at java.base/java.lang.Class.newInstance(Class.java:626)
	... 35 common frames omitted

@o-shevchenko
Copy link
Author

o-shevchenko commented Feb 14, 2024

The issue can be avoided if we create a bean manually:

@Bean
    fun serverInterceptor(observationRegistry: ObservationRegistry): ObservationGrpcServerInterceptor {
        return ObservationGrpcServerInterceptor(observationRegistry);
    }

But not sure how to pass traceId and spanId for headers now

@o-shevchenko
Copy link
Author

o-shevchenko commented Feb 14, 2024

ObservationGrpcServerInterceptor doesn't provide integration with SpanContext. It doesn't add headers into opentelemetry Context, so I can't log traceId and spanId

@pieter-stek
Copy link

ObservationGrpcServerInterceptor doesn't provide integration with SpanContext. It doesn't add headers into opentelemetry Context, so I can't log traceId and spanId

Spring Boot supports both OpenTelemetry and OpenZipkin Brave for tracing. Micrometer is used as an abstraction layer over both libraries. I think grpc-spring-boot-starter should also support both tracing libraries, so only Micrometer API's should be used and no OpenTelemetry API's.

@o-shevchenko
Copy link
Author

yes, but I don't see in micrometer ability to send traces to gRPC collector otlp.tracing.endpoint: http://collector:4317

@pieter-stek
Copy link

Correct, because that isn't the responsibility of Micrometer, but the responsibility of one of the tracer implementations that can be chosen: https://docs.spring.io/spring-boot/docs/current/reference/html/actuator.html#actuator.micrometer-tracing.tracer-implementations

@o-shevchenko
Copy link
Author

o-shevchenko commented Feb 14, 2024

Right, that's what I currently use. But I can't get traceId and spanId in logback
I used TracingServerInterceptor to put my headers into Context manually, so it's available in MDC.

logging.pattern.level: "trace_id=%mdc{trace_id} trace_id=%mdc{x-b3-traceid} trace_id=%X{trace_id} trace_id=%X{traceId:-} span_id=%mdc{spanId} %p"

Probably, it's because of webflux: spring-projects/spring-framework#29466

@o-shevchenko
Copy link
Author

No, I've tried everything. GrpcServerObservationContext is not propagated to GrpcServerObservationContext in GRpcService

@o-shevchenko o-shevchenko deleted the otel_tracing branch February 15, 2024 11:39
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants