[ 
https://issues.apache.org/jira/browse/CAMEL-23349?page=com.atlassian.jira.plugin.system.issuetabpanels:comment-tabpanel&focusedCommentId=18078690#comment-18078690
 ] 

Bjorn Beskow commented on CAMEL-23349:
--------------------------------------

Here is a test that worked with [https://github.com/apache/camel/pull/22781], 
but stopped working [https://github.com/apache/camel/pull/22916]:

 

{code:language=java}

package org.apache.camel.opentelemetry2;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;

import io.opentelemetry.api.baggage.Baggage;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.api.trace.Tracer;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;
import java.io.IOException;
import java.util.Map;
import org.apache.camel.CamelContext;
import org.apache.camel.CamelContextAware;
import org.apache.camel.RoutesBuilder;
import org.apache.camel.builder.RouteBuilder;
import org.apache.camel.opentelemetry2.CamelOpenTelemetryExtension.OtelTrace;
import org.junit.jupiter.api.Test;

public class BaggageInjectionTestUsingOtelSDK extends 
OpenTelemetryTracerTestSupport {

  Tracer tracer = otelExtension.getOpenTelemetry().getTracer("spanInjection");

  @Override
  protected CamelContext createCamelContext() throws Exception {
    OpenTelemetryTracer tst = new OpenTelemetryTracer();
    tst.setTracer(tracer);
    
tst.setContextPropagators(otelExtension.getOpenTelemetry().getPropagators());
    tst.setTraceProcessors(false);
    CamelContext context = super.createCamelContext();
    CamelContextAware.trySetCamelContext(tst, context);
    tst.init(context);
    return context;
  }

  @Test
  void testRouteExternalBaggage() throws IOException {
    try (Scope rootScope = Context.root().makeCurrent();
        // Add a baggage the is expected to be propagated
        Scope baggageScope = Baggage.current().toBuilder().put("external.id", 
"9876").build().makeCurrent()) {
      Span span = tracer.spanBuilder("mySpan").startSpan();
      try (Scope scope = span.makeCurrent()) {
        template.sendBody("direct:start", "my-body");
        Map<String, OtelTrace> traces = otelExtension.getTraces();
        assertEquals(1, traces.size());
      } finally {
        span.end();
      }
    }
  }

  @Override
  protected RoutesBuilder createRouteBuilder() {
    return new RouteBuilder() {

      @Override
      public void configure() {
        from("direct:start")
            .process(exchange -> {
                // This is set from the external caller
                assertEquals("9876", 
Baggage.current().getEntryValue("external.id"));
                // This is not yet set
                assertNull(Baggage.current().getEntryValue("tenant.id"));
            })
            .process(exchange -> {
                Scope scope = Baggage.current().toBuilder()
                    .put("tenant.id", "1234")
                    .build()
                    .makeCurrent();
                exchange.setProperty("BaggageScope", scope);
            })
            .onCompletion()
            .process(exchange -> {
                Scope baggageScope = exchange.getProperty("BaggageScope", 
Scope.class);
                if (baggageScope != null) {
                    baggageScope.close();
                    exchange.removeProperty("BaggageScope");
                }
            })
            .end()
            .routeId("start")
            .log("A message")
            .process(exchange -> {
                assertEquals("9876", 
Baggage.current().getEntryValue("external.id"));
                assertEquals("1234", 
Baggage.current().getEntryValue("tenant.id"));
            })
            .to("log:info");
      }
    };
  }
}

{code}

 

If \{{TraceProcessorsOtelInterceptStrategy}} is disabled in 
\{{OpenTelemetryTracer}}, the test succeeds:

{code:language=java}

    @Override
    protected void initTracer() {

        ...


        // InterceptStrategy interceptStrategy = new 
TraceProcessorsOtelInterceptStrategy();
        // 
getCamelContext().getCamelContextExtension().addInterceptStrategy(interceptStrategy);
    }

{code}

[~squakez] Again, thanks a lot for working with me on this!

> Camel OpenTelemetry2 programmatic baggage management
> ----------------------------------------------------
>
>                 Key: CAMEL-23349
>                 URL: https://issues.apache.org/jira/browse/CAMEL-23349
>             Project: Camel
>          Issue Type: Improvement
>          Components: camel-opentelemetry
>    Affects Versions: 4.18.1, 4.19.0
>            Reporter: Bjorn Beskow
>            Assignee: Pasquale Congiusti
>            Priority: Major
>             Fix For: 4.20.0
>
>
> With Camel OpenTelemetry2 and OpenTelemetry spring boot starter, most aspects 
> of Span Customization such as adding attributes, events and creating nested 
> spans can be done using the OpenTelemetry apis. Programmatically adding OTEL 
> baggage is however an exception. The typical use case for OTEL baggage is to 
> allow for small pieces of contextual data that need to travel with a request 
> across distributed service boundaries, independent of any single span. In our 
> business context, it is in the form of a "business correlation id" that is 
> captured from a header or from the message payload as the very first 
> processor of a boundary component, and should be available to all downstream 
> components.
> The OTEL api itself cannot easily be used to add baggage items to a Camel 
> route, due to `Scope` handling: Baggage must be added to a baggage scope, and 
> is visible and propagated during the lifetime of the scope. The OTEL api 
> looks like this:
> // Create baggage with an entry
> Baggage baggage = Baggage.current().builder()
>     .put("user-id", "12345")
>     .build();
> // Attach baggage to current context
> Context contextWithBaggage = Context.current().with(baggage);
> // Make it current (scoped)
> try (var scope = contextWithBaggage.makeCurrent())
> {   // Your code here - baggage is now active }
> Doing this in a processor, the scope instance must be kept open as long as 
> needed, typically until the end of the exchange. In theory, the following 
> simple albeit brittle solution should work:
>  
> Baggage baggage = Baggage.current().toBuilder()
>   .put("user-id", "12345")
>   .build();
> Context ctxWithBaggage = Context.current().with(baggage);
> Scope scope = ctxWithBaggage.makeCurrent();
> exchange.getExchangeExtension().addOnCompletion(new SynchronizationAdapter() {
>   @Override public void onDone(Exchange exchange)
> {     scope.close();   }
> });
> In practice however, this doesn't work. A (final) baggageScope is already 
> (automatically) created at the start of a Camel route execution, stored on 
> the exchange as part of a wrapped instance of 
> org.apache.camel.opentelemetry2.OpenTelemetrySpanAdapter and properly closed 
> at the end of the route. It is this baggageScope that is passed to the OTEL 
> propagator, not the scope created programmatically as in the example above. 
> Hence the programmatically added baggage entry will not be propagated.
> To my understanding, the simplest way to programmatically add baggage items 
> would be to allow for mutating the baggage scope stored in the 
> org.apache.camel.opentelemetry2.OpenTelemetrySpanAdapter. The required pieces 
> are already present, but would need to be exposed in a public API (currently, 
> both context, baggage and baggage scope are protected).



--
This message was sent by Atlassian Jira
(v8.20.10#820010)

Reply via email to