Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
24 changes: 23 additions & 1 deletion components/context/src/main/java/datadog/context/Context.java
Original file line number Diff line number Diff line change
Expand Up @@ -114,7 +114,7 @@ static Context detachFrom(Object carrier) {
/**
* Creates a copy of this context with the given key-value set.
*
* <p>Existing value with the given key will be replaced, and mapping to a {@code null} value will
* <p>Existing value with the given key will be replaced. Mapping to a {@code null} value will
* remove the key-value from the context copy.
*
* @param <T> the type of the value.
Expand All @@ -124,6 +124,28 @@ static Context detachFrom(Object carrier) {
*/
<T> Context with(ContextKey<T> key, @Nullable T value);

/**
* Creates a copy of this context with the given pair of key-values.
*
* <p>Existing values with the given keys will be replaced. Mapping to a {@code null} value will
* remove the key-value from the context copy.
*
* @param <T> the type of the first value.
* @param <U> the type of the second value.
* @param firstKey the first key to store the first value.
* @param firstValue the first value to store.
* @param secondKey the second key to store the second value.
* @param secondValue the second value to store.
* @return a new context with the pair of key-values set.
*/
default <T, U> Context with(
ContextKey<T> firstKey,
@Nullable T firstValue,
ContextKey<U> secondKey,
@Nullable U secondValue) {
return with(firstKey, firstValue).with(secondKey, secondValue);
}

/**
* Creates a copy of this context with the implicit key is mapped to the value.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,41 @@ void testWith(Context context) {
assertDoesNotThrow(() -> context.with(null), "Null implicitly keyed value not throw exception");
}

@ParameterizedTest
@MethodSource("contextImplementations")
void testWithPair(Context context) {
// Test retrieving value
String stringValue = "value";
Context context1 = context.with(BOOLEAN_KEY, false, STRING_KEY, stringValue);
assertEquals(stringValue, context1.get(STRING_KEY));
assertEquals(false, context1.get(BOOLEAN_KEY));
// Test overriding value
String stringValue2 = "value2";
Context context2 = context1.with(STRING_KEY, stringValue2, BOOLEAN_KEY, true);
assertEquals(stringValue2, context2.get(STRING_KEY));
assertEquals(true, context2.get(BOOLEAN_KEY));
// Test clearing value
Context context3 = context2.with(BOOLEAN_KEY, null, STRING_KEY, null);
assertNull(context3.get(STRING_KEY));
assertNull(context3.get(BOOLEAN_KEY));
// Test null key handling
assertThrows(
NullPointerException.class,
() -> context.with(null, "test", STRING_KEY, "test"),
"Context forbids null keys");
assertThrows(
NullPointerException.class,
() -> context.with(STRING_KEY, "test", null, "test"),
"Context forbids null keys");
// Test null value handling
assertDoesNotThrow(
() -> context.with(BOOLEAN_KEY, null, STRING_KEY, "test"),
"Null value should not throw exception");
assertDoesNotThrow(
() -> context.with(STRING_KEY, "test", BOOLEAN_KEY, null),
"Null value should not throw exception");
}

@ParameterizedTest
@MethodSource("contextImplementations")
void testGet(Context original) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public final class Constants {
*/
public static final String[] BOOTSTRAP_PACKAGE_PREFIXES = {
"datadog.slf4j",
"datadog.context",
"datadog.appsec.api",
"datadog.trace.api",
"datadog.trace.bootstrap",
Expand Down
1 change: 1 addition & 0 deletions dd-java-agent/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ def sharedShadowJar = tasks.register('sharedShadowJar', ShadowJar) {
exclude(project(':dd-java-agent:agent-logging'))
exclude(project(':dd-trace-api'))
exclude(project(':internal-api'))
exclude(project(':components:context'))
exclude(project(':utils:time-utils'))
exclude(dependency('org.slf4j::'))
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ public class SpockRunner extends JUnitPlatform {
*/
public static final String[] BOOTSTRAP_PACKAGE_PREFIXES_COPY = {
"datadog.slf4j",
"datadog.context",
"datadog.appsec.api",
"datadog.trace.api",
"datadog.trace.bootstrap",
Expand Down
1 change: 1 addition & 0 deletions gradle/dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ final class CachedData {
exclude(project(':internal-api'))
exclude(project(':internal-api:internal-api-9'))
exclude(project(':communication'))
exclude(project(':components:context'))
exclude(project(':components:json'))
exclude(project(':remote-config:remote-config-api'))
exclude(project(':remote-config:remote-config-core'))
Expand Down
2 changes: 2 additions & 0 deletions internal-api/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,7 @@ excludedClassesCoverage += [
"datadog.trace.bootstrap.instrumentation.api.StatsPoint",
"datadog.trace.bootstrap.instrumentation.api.Schema",
"datadog.trace.bootstrap.instrumentation.api.ScopeSource",
"datadog.trace.bootstrap.instrumentation.api.InternalContextKeys",
"datadog.trace.bootstrap.instrumentation.api.InternalSpanTypes",
"datadog.trace.bootstrap.instrumentation.api.TagContext",
"datadog.trace.bootstrap.instrumentation.api.TagContext.HttpHeaders",
Expand Down Expand Up @@ -210,6 +211,7 @@ dependencies {
// references TraceScope and Continuation from public api
api project(':dd-trace-api')
api libs.slf4j
api project(':components:context')
api project(":utils:time-utils")

// has to be loaded by system classloader:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,19 @@
package datadog.trace.bootstrap.instrumentation.api;

import static datadog.trace.bootstrap.instrumentation.api.InternalContextKeys.SPAN_KEY;

import datadog.context.Context;
import datadog.context.ContextKey;
import datadog.context.ImplicitContextKeyed;
import datadog.trace.api.DDTraceId;
import datadog.trace.api.TraceConfig;
import datadog.trace.api.gateway.IGSpanInfo;
import datadog.trace.api.gateway.RequestContext;
import datadog.trace.api.interceptor.MutableSpan;
import javax.annotation.Nullable;

public interface AgentSpan extends MutableSpan, IGSpanInfo, WithAgentSpan {
public interface AgentSpan
extends MutableSpan, ImplicitContextKeyed, Context, IGSpanInfo, WithAgentSpan {

DDTraceId getTraceId();

Expand Down Expand Up @@ -145,4 +152,21 @@ public interface AgentSpan extends MutableSpan, IGSpanInfo, WithAgentSpan {
default AgentSpan asAgentSpan() {
return this;
}

@Override
default Context storeInto(Context context) {
return context.with(SPAN_KEY, this);
}

@Nullable
@Override
default <T> T get(ContextKey<T> key) {
// noinspection unchecked
return SPAN_KEY == key ? (T) this : Context.root().get(key);
}

@Override
default <T> Context with(ContextKey<T> key, @Nullable T value) {
return Context.root().with(SPAN_KEY, this, key, value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package datadog.trace.bootstrap.instrumentation.api;

import datadog.context.ContextKey;

final class InternalContextKeys {
static final ContextKey<AgentSpan> SPAN_KEY = ContextKey.named("dd-span-key");

private InternalContextKeys() {}
}
Loading