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

Added an option to have baggage through Observation API #688

Merged
merged 5 commits into from
May 20, 2024
Merged
Show file tree
Hide file tree
Changes from 2 commits
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
15 changes: 15 additions & 0 deletions docs/modules/ROOT/pages/api.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,21 @@ IMPORTANT: For Brave, remember to set up the `PropagationFactory` so that it con
include::{include-java}/tracing/TracingApiTests.java[tags=baggage_brave_setup,indent=0]
-----

=== Baggage with Micrometer Observation API

If you're using Micrometer Observation API, there's no notion of baggage. If you set up a `BaggageManager` to have the baggage fields configured, we will assume that when the Observation gets put in scope, whatever low and high cardinality keys are set on the Observation will be put in scope as baggage (assuming that their names match with the configuration on the `BaggageManager`). Below you can find example of such setup with OpenTelemetry `BaggageManager`.

[source,java,subs=+attributes]
-----
include::{include-bridges-java}/micrometer-tracing-bridge-otel/src/test/java/io/micrometer/tracing/otel/bridge/BaggageTests.java[tags=baggageManager,indent=0]

include::{include-bridges-java}/micrometer-tracing-bridge-otel/src/test/java/io/micrometer/tracing/otel/bridge/BaggageTests.java[tags=observationRegistrySetup,indent=0]

include::{include-bridges-java}/micrometer-tracing-bridge-otel/src/test/java/io/micrometer/tracing/otel/bridge/BaggageTests.java[tags=observation,indent=0]

include::{include-bridges-java}/micrometer-tracing-bridge-otel/src/test/java/io/micrometer/tracing/otel/bridge/BaggageTests.java[tags=observationScope,indent=0]
-----

== Aspect Oriented Programming

Micrometer Tracing contains `@NewSpan`, `@ContinueSpan`, and `@SpanTag` annotations that frameworks can use to create or customize spans for either specific types of methods such as those serving web request endpoints or, more generally, to all methods.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright 2022 the original author or authors.
* Copyright 2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -22,6 +22,7 @@
import io.micrometer.tracing.*;

import java.io.Closeable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
Expand All @@ -36,15 +37,25 @@ public class BraveBaggageManager implements Closeable, BaggageManager {

private final List<String> tagFields;

private final List<String> remoteFields;

@Nullable
private Tracer tracer;

/**
* Create an instance of {@link BraveBaggageManager}.
* @param tagFields fields of baggage keys that should become tags on a span
* @param remoteFields fields of baggage keys that should be propagated over the wire
*/
public BraveBaggageManager(List<String> tagFields) {
public BraveBaggageManager(List<String> tagFields, List<String> remoteFields) {
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't we introduce a new ctor?
Can any of these be null?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

We can't - type inference is the same. They can't be null, we have non null api by default. We can pass empty collections

this.tagFields = tagFields;
this.remoteFields = remoteFields(tagFields, remoteFields);
}

private static List<String> remoteFields(List<String> tagFields, List<String> remoteFields) {
List<String> combined = new ArrayList<>(tagFields);
combined.addAll(remoteFields);
Copy link
Member

Choose a reason for hiding this comment

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

Why are these combined? It looks like the other baggage managers don't combine them.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

So when you have tag fields it means that it's baggage that additionally will be tagged. Inc case of Brave we never had a constructor that accepts just remote fields and I can't introduce one cause we have a constructor for tag fields already. So it does make sense to combine them. Of course I should combine those for other baggage managers too - I'll look into that, thanks

Copy link
Contributor

Choose a reason for hiding this comment

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

Is it better to create a unique set of fields?
The values seem to be used in the handler for contains, so in the end, it doesn't need to be unique here, but just for clarity.

Copy link
Member

Choose a reason for hiding this comment

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

I think I'm still confused. The assumption seems to be that all tagFields are remote baggage. Is local baggage that should also be tagged not possible? In the test code, it looks like the baggage key to be tagged is local baggage:

.add(BaggagePropagationConfig.SingleBaggageField.local(BaggageField.create(TAG_KEY)))

So shouldn't that not be combined with remote fields?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

From the point of view of a baggage manager there is no concept of local fields. That is really brave specific and has to be configured via propagation configuration. I understand that this might be confusing. We can rename this from remoteFields to baggageFields and that way that will mean all baggage regardless of whether it's local or remote. WDYT?

Copy link
Member

Choose a reason for hiding this comment

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

That sounds good.

return combined;
}

/**
Expand All @@ -53,6 +64,16 @@ public BraveBaggageManager(List<String> tagFields) {
*/
public BraveBaggageManager() {
this.tagFields = Collections.emptyList();
this.remoteFields = Collections.emptyList();
}

/**
* Create an instance of {@link BraveBaggageManager}.
* @param tagFields fields of baggage keys that should become tags on a span
*/
public BraveBaggageManager(List<String> tagFields) {
this.tagFields = tagFields;
this.remoteFields = new ArrayList<>(tagFields);
}

@Override
Expand Down Expand Up @@ -136,4 +157,9 @@ void setTracer(Tracer tracer) {
this.tracer = tracer;
}

@Override
public List<String> getRemoteFields() {
return this.remoteFields;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
import brave.propagation.TraceContextOrSamplingFlags;
import io.micrometer.tracing.*;

import java.util.List;
import java.util.Map;

/**
Expand Down Expand Up @@ -156,6 +157,11 @@ public CurrentTraceContext currentTraceContext() {
return this.currentTraceContext;
}

@Override
public List<String> getRemoteFields() {
return this.braveBaggageManager.getRemoteFields();
}

}

class BraveSpanInScope implements Tracer.SpanInScope {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright 2022 the original author or authors.
* Copyright 2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand Down Expand Up @@ -27,13 +27,17 @@
import io.micrometer.common.util.internal.logging.InternalLogger;
import io.micrometer.common.util.internal.logging.InternalLoggerFactory;
import io.micrometer.context.ContextRegistry;
import io.micrometer.observation.Observation;
import io.micrometer.observation.Observation.Scope;
import io.micrometer.observation.ObservationRegistry;
import io.micrometer.observation.contextpropagation.ObservationThreadLocalAccessor;
import io.micrometer.tracing.*;
import io.micrometer.tracing.contextpropagation.ObservationAwareSpanThreadLocalAccessor;
import io.micrometer.tracing.handler.DefaultTracingObservationHandler;
import org.assertj.core.api.Assertions;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import reactor.core.observability.micrometer.Micrometer;
import reactor.core.publisher.Hooks;
Expand All @@ -42,6 +46,7 @@
import reactor.core.scheduler.Schedulers;

import java.time.Duration;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
Expand All @@ -61,6 +66,10 @@ class BaggageTests {

public static final String TAG_VALUE = "tagValue";

public static final String OBSERVATION_BAGGAGE_KEY = "observationKey";

public static final String OBSERVATION_BAGGAGE_VALUE = "observationValue";

TestSpanHandler spanHandler = new TestSpanHandler();

StrictCurrentTraceContext braveCurrentTraceContext = StrictCurrentTraceContext.create();
Expand All @@ -73,6 +82,7 @@ class BaggageTests {
.traceId128Bit(true)
.propagationFactory(BaggagePropagation.newFactoryBuilder(B3Propagation.FACTORY)
.add(BaggagePropagationConfig.SingleBaggageField.remote(BaggageField.create(KEY_1)))
.add(BaggagePropagationConfig.SingleBaggageField.remote(BaggageField.create(OBSERVATION_BAGGAGE_KEY)))
.add(BaggagePropagationConfig.SingleBaggageField.local(BaggageField.create(TAG_KEY)))
.build())
.sampler(Sampler.ALWAYS_SAMPLE)
Expand All @@ -84,10 +94,15 @@ class BaggageTests {
BravePropagator propagator = new BravePropagator(tracing);

Tracer tracer = new BraveTracer(this.braveTracer, this.bridgeContext,
new BraveBaggageManager(Collections.singletonList(TAG_KEY)));
new BraveBaggageManager(Collections.singletonList(TAG_KEY), Arrays.asList(KEY_1, OBSERVATION_BAGGAGE_KEY)));

ObservationRegistry observationRegistry = ObservationThreadLocalAccessor.getInstance().getObservationRegistry();

@BeforeEach
void setupHandler() {
observationRegistry.observationConfig().observationHandler(new DefaultTracingObservationHandler(tracer));
}

@AfterEach
void cleanup() {
tracing.close();
Expand Down Expand Up @@ -258,6 +273,30 @@ void baggageTagKey() {
then(mutableSpan.tags().get(TAG_KEY)).isEqualTo(TAG_VALUE);
}

@Test
void baggageWithObservationApiWithRemoteFields() {
Observation observation = Observation.start("foo", observationRegistry)
.lowCardinalityKeyValue(KEY_1, TAG_VALUE)
.highCardinalityKeyValue(OBSERVATION_BAGGAGE_KEY, OBSERVATION_BAGGAGE_VALUE);
then(tracer.getBaggage(KEY_1).get()).isNull();
then(tracer.getBaggage(OBSERVATION_BAGGAGE_KEY).get()).isNull();

try (Scope scope = observation.openScope()) {
then(tracer.getBaggage(KEY_1).get()).isEqualTo(TAG_VALUE);
then(tracer.getBaggage(OBSERVATION_BAGGAGE_KEY).get()).isEqualTo(OBSERVATION_BAGGAGE_VALUE);
}

then(tracer.currentSpan()).isNull();
then(tracer.getBaggage(KEY_1).get()).isNull();
then(tracer.getBaggage(OBSERVATION_BAGGAGE_KEY).get()).isNull();
Comment on lines +290 to +291
Copy link
Member

Choose a reason for hiding this comment

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

Can we do these check before opening the scope too?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Of course

observation.stop();

then(spanHandler.spans()).hasSize(1);
MutableSpan mutableSpan = spanHandler.spans().get(0);
then(mutableSpan.tags().get(KEY_1)).isEqualTo(TAG_VALUE);
then(mutableSpan.tags().get(OBSERVATION_BAGGAGE_KEY)).isEqualTo(OBSERVATION_BAGGAGE_VALUE);
}

@Test
void baggageTagKeyWithLegacyApi() {
ScopedSpan span = this.tracer.startScopedSpan("call1");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,6 @@
*/
package io.micrometer.tracing.otel.bridge;

import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Deque;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.function.BiConsumer;

import io.micrometer.common.lang.Nullable;
import io.micrometer.tracing.BaggageInScope;
import io.micrometer.tracing.BaggageManager;
Expand All @@ -38,6 +26,9 @@
import io.opentelemetry.api.baggage.BaggageEntryMetadata;
import io.opentelemetry.context.Context;

import java.util.*;
import java.util.function.BiConsumer;

import static java.util.Collections.unmodifiableCollection;
import static java.util.Collections.unmodifiableMap;
import static java.util.function.Function.identity;
Expand Down Expand Up @@ -71,10 +62,16 @@ public class OtelBaggageManager implements BaggageManager {
public OtelBaggageManager(CurrentTraceContext currentTraceContext, List<String> remoteFields,
List<String> tagFields) {
this.currentTraceContext = currentTraceContext;
this.remoteFields = remoteFields;
this.remoteFields = remoteFields(tagFields, remoteFields);
this.tagFields = tagFields;
}

private static List<String> remoteFields(List<String> tagFields, List<String> remoteFields) {
List<String> combined = new ArrayList<>(tagFields);
combined.addAll(remoteFields);
return combined;
}

@Override
public Map<String, String> getAllBaggage() {
return toMap(currentBaggage());
Expand Down Expand Up @@ -213,6 +210,11 @@ private String propagationString(boolean remoteField) {
return propagation;
}

@Override
public List<String> getRemoteFields() {
return this.remoteFields;
}

}

class CompositeBaggage implements io.opentelemetry.api.baggage.Baggage {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/**
* Copyright 2022 the original author or authors.
* Copyright 2024 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
Expand All @@ -19,6 +19,7 @@
import io.opentelemetry.context.Context;
import io.opentelemetry.context.Scope;

import java.util.List;
import java.util.Map;

/**
Expand Down Expand Up @@ -196,6 +197,11 @@ public BaggageInScope createBaggageInScope(TraceContext traceContext, String nam
return this.otelBaggageManager.createBaggageInScope(traceContext, name, value);
}

@Override
public List<String> getRemoteFields() {
return this.otelBaggageManager.getRemoteFields();
}

/**
* Publisher of events.
*/
Expand Down
Loading