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

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

[~squakez]:

Having read the tracing design proposal 
[https://github.com/apache/camel/blob/main/proposals/tracing.adoc] more 
thoroughly, 
I understand we move away from a ThreadLocal-based approach to Scope and 
BaggageScope (and instead continue to rely on the Otel span with corresponding 
baggage to be associated with the Exchange and updated when needed). I 
completely agree with that move. The fix in PR 
[https://github.com/apache/camel/pull/22916] is fully in line with that. The 
OTEL ThreadLocal-based programming model for Baggage clearly needs to be 
"adapted" to fit the exchange-centric/event-centric programming model in Camel.

Back to the original problem then:

The OTEL Baggage API mutates the baggage content via the Thread scope (even 
though the Baggage object itself is immutable, with a Builder pattern to add 
new entries by creating a new, extended instance):

{code:java}
Baggage baggage = Baggage.builder()
  .put("tenant.id", "1234")
  .build();
try (Scope scope = baggage.storeInContext(Context.current()).makeCurrent()){
  // baggage is now tied to the current thread, and hence propagated and 
accessible for e.g. logging
  System.out.println(Baggage.current().getEntryValue("tenant.id");
}
{code}

If the current span associated with the exchange should be the preferred 
carrier of the tracing information, isn't it reasonable that a Camel-idiomatic 
Baggage API can extend (i.e. replace) the Baggage object associated with the 
exchange? It could look something like

{code:java}
OpenTelemetryBaggageAdapter.builder(exchange)
  .put("tenant.id", "1234")
  .build();
// baggage is now tied to the current Otel span associated with the Exchange, 
and hence propagated and accessible for e.g. logging
System.out.println(BaggageAdapter.current(exchange).getEntryValue("tenant.id");
{code}

To implement such a OpenTelemetryBaggageAdapter, it must be possible to replace 
the Baggage instance associated with the exchange with the an extended baggage 
instance, i.e. in {{OpenTelemetrySpanAdapter}} the Baggage instance would be 
non-final and mutable via a setter:
{code:java}
    private Baggage baggage;
    ...
    protected void setBaggage(Baggage baggage) {
        this.baggage = baggage;
    }
{code}

With such a change, the OpenTelemetryBaggageAdapter can be implemented cleanly:

{code:java}
public class OpenTelemetryBaggageAdapter {

    static SpanStorageManagerExchange spanManager = new 
SpanStorageManagerExchange();

    private OpenTelemetryBaggageAdapter() {
    }

    public static Baggage current(Exchange exchange) {
        Span span = spanManager.peek(exchange);
        if (span instanceof OpenTelemetrySpanAdapter spanAdapter) {
            return spanAdapter.getBaggage();
        } else {
            throw new IllegalStateException("No current OTEL span available");
        }
    }

    public static Builder builder(Exchange exchange) {
        return new Builder(exchange);
    }

    public static final class Builder {

        private final Exchange exchange;
        private final Map<String, String> entries = new LinkedHashMap<>();

        private Builder(Exchange exchange) {
            this.exchange = exchange;
        }

        public Builder put(String key, String value) {
            entries.put(key, value);
            return this;
        }

        public Baggage build() {
            Span span = spanManager.peek(exchange);
            if (span instanceof OpenTelemetrySpanAdapter spanAdapter) {
                Baggage baggage = spanAdapter.getBaggage();
                var builder = baggage.toBuilder();
                entries.forEach(builder::put);
                baggage = builder.build();
                spanAdapter.setBaggage(baggage);
                return baggage;
            } else {
                throw new IllegalStateException("No current OTEL span 
available");
            }
        }
    }
}
{code}

This implementation has a fully consistent behaviour with other OTEL 
instrumentation libraries, as well as the Camel threading model (I have tested 
it with e.g. the threads() construct).

This is an OpenTelemetry-specific solution, but the Baggage concept is itself 
OpenTelemetry-specific. The tracing design suggests common concepts should be 
extracted into strong abstractions in camel-telemetry, but the implementation 
details be kept in the implementation-specific components (for very good 
reasons). But that doesn't have to imply a "least common denominator" for the 
implementation-specific components, IMHO.

Can I submit a pull request for further discussion?

> 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