Skip to content

Commit bdb7932

Browse files
committed
initial migration to contextAPI
1 parent fee8c80 commit bdb7932

File tree

9 files changed

+532
-198
lines changed

9 files changed

+532
-198
lines changed

dd-trace-core/src/main/java/datadog/trace/core/CoreTracer.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import static datadog.trace.api.DDTags.DJM_ENABLED;
66
import static datadog.trace.api.DDTags.DSM_ENABLED;
77
import static datadog.trace.api.DDTags.PROFILING_CONTEXT_ENGINE;
8+
import static datadog.trace.bootstrap.instrumentation.api.AgentPropagation.BAGGAGE_CONCERN;
89
import static datadog.trace.bootstrap.instrumentation.api.AgentPropagation.TRACING_CONCERN;
910
import static datadog.trace.common.metrics.MetricsAggregatorFactory.createMetricsAggregator;
1011
import static datadog.trace.util.AgentThreadFactory.AGENT_THREAD_GROUP;
@@ -76,6 +77,7 @@
7677
import datadog.trace.common.writer.WriterFactory;
7778
import datadog.trace.common.writer.ddintake.DDIntakeTraceInterceptor;
7879
import datadog.trace.context.TraceScope;
80+
import datadog.trace.core.baggage.BaggagePropagator;
7981
import datadog.trace.core.datastreams.DataStreamContextInjector;
8082
import datadog.trace.core.datastreams.DataStreamsMonitoring;
8183
import datadog.trace.core.datastreams.DefaultDataStreamsMonitoring;
@@ -723,6 +725,7 @@ private CoreTracer(
723725
new CorePropagation(builtExtractor, injector, injectors, dataStreamContextInjector);
724726

725727
Propagators.register(TRACING_CONCERN, new TracingPropagator(injector, extractor));
728+
Propagators.register(BAGGAGE_CONCERN, new BaggagePropagator());
726729

727730
this.tagInterceptor =
728731
null == tagInterceptor ? new TagInterceptor(new RuleFlags(config)) : tagInterceptor;
Lines changed: 194 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,194 @@
1+
// package datadog.trace.core.baggage;
2+
//
3+
// import static datadog.trace.api.TracePropagationStyle.BAGGAGE;
4+
//
5+
// import datadog.trace.api.Config;
6+
// import datadog.trace.api.TraceConfig;
7+
// import datadog.trace.api.TracePropagationStyle;
8+
// import datadog.trace.bootstrap.instrumentation.api.AgentPropagation;
9+
// import datadog.trace.bootstrap.instrumentation.api.TagContext;
10+
// import datadog.trace.core.DDSpanContext;
11+
// import java.nio.charset.StandardCharsets;
12+
// import java.util.Arrays;
13+
// import java.util.Collections;
14+
// import java.util.HashMap;
15+
// import java.util.HashSet;
16+
// import java.util.Map;
17+
// import java.util.Set;
18+
// import java.util.function.Supplier;
19+
//
20+
// import datadog.trace.core.propagation.ContextInterpreter;
21+
// import datadog.trace.core.propagation.HttpCodec;
22+
// import datadog.trace.core.propagation.TagContextExtractor;
23+
// import org.slf4j.Logger;
24+
// import org.slf4j.LoggerFactory;
25+
//
26+
/// ** A codec designed for HTTP transport via headers using Datadog headers */
27+
// class BaggageHttpCodec {
28+
// private static final Logger log = LoggerFactory.getLogger(BaggageHttpCodec.class);
29+
//
30+
// static final String BAGGAGE_KEY = "baggage";
31+
// private static final int MAX_CHARACTER_SIZE = 4;
32+
//
33+
// private static final Set<Character> UNSAFE_CHARACTERS_KEY =
34+
// new HashSet<>(
35+
// Arrays.asList(
36+
// '\"', ',', ';', '\\', '(', ')', '/', ':', '<', '=', '>', '?', '@', '[', ']', '{',
37+
// '}'));
38+
// private static final Set<Character> UNSAFE_CHARACTERS_VALUE =
39+
// new HashSet<>(Arrays.asList('\"', ',', ';', '\\'));
40+
//
41+
// private BaggageHttpCodec() {
42+
// // This class should not be created. This also makes code coverage checks happy.
43+
// }
44+
//
45+
// public static HttpCodec.Injector newInjector(Map<String, String> invertedBaggageMapping) {
46+
// return new Injector(invertedBaggageMapping);
47+
// }
48+
//
49+
// private static class Injector implements HttpCodec.Injector {
50+
//
51+
// private final Map<String, String> invertedBaggageMapping;
52+
//
53+
// public Injector(Map<String, String> invertedBaggageMapping) {
54+
// assert invertedBaggageMapping != null;
55+
// this.invertedBaggageMapping = invertedBaggageMapping;
56+
// }
57+
//
58+
// private int encodeKey(String key, StringBuilder builder) {
59+
// return encode(key, builder, UNSAFE_CHARACTERS_KEY);
60+
// }
61+
//
62+
// private int encodeValue(String key, StringBuilder builder) {
63+
// return encode(key, builder, UNSAFE_CHARACTERS_VALUE);
64+
// }
65+
//
66+
// private int encode(String input, StringBuilder builder, Set<Character> UNSAFE_CHARACTERS) {
67+
// int size = 0;
68+
// for (int i = 0; i < input.length(); i++) {
69+
// char c = input.charAt(i);
70+
// if (UNSAFE_CHARACTERS.contains(c) || c > 126 || c <= 32) { // encode character
71+
// byte[] bytes =
72+
// Character.toString(c)
73+
// .getBytes(StandardCharsets.UTF_8); // not most efficient but what URLEncoder
74+
// does
75+
// for (byte b : bytes) {
76+
// builder.append(String.format("%%%02X", b & 0xFF));
77+
// size += 1;
78+
// }
79+
// } else {
80+
// builder.append(c);
81+
// size += 1;
82+
// }
83+
// }
84+
// return size;
85+
// }
86+
//
87+
// @Override
88+
// public <C> void inject(
89+
// final DDSpanContext context, final C carrier, final AgentPropagation.Setter<C> setter) {
90+
// Config config = Config.get();
91+
//
92+
// StringBuilder baggageText = new StringBuilder();
93+
// int processedBaggage = 0;
94+
// int currentBytes = 0;
95+
// int maxItems = config.getTraceBaggageMaxItems();
96+
// int maxBytes = config.getTraceBaggageMaxBytes();
97+
// for (final Map.Entry<String, String> entry : context.baggageItems()) {
98+
// if (processedBaggage >= maxItems) {
99+
// break;
100+
// }
101+
// int additionalCharacters = 1; // accounting for potential comma and colon
102+
// if (processedBaggage != 0) {
103+
// additionalCharacters = 2; // allocating space for comma
104+
// }
105+
//
106+
// int byteSize = 1; // default include size of '='
107+
// if (additionalCharacters == 2) {
108+
// baggageText.append(',');
109+
// byteSize += 1;
110+
// }
111+
//
112+
// byteSize += encodeKey(entry.getKey(), baggageText);
113+
// baggageText.append('=');
114+
// byteSize += encodeValue(entry.getValue(), baggageText);
115+
//
116+
// if (currentBytes + byteSize > maxBytes) {
117+
// baggageText.setLength(currentBytes);
118+
// break;
119+
// }
120+
// currentBytes += byteSize;
121+
// processedBaggage++;
122+
// }
123+
//
124+
// setter.set(carrier, BAGGAGE_KEY, baggageText.toString());
125+
// }
126+
// }
127+
//
128+
// public static HttpCodec.Extractor newExtractor(
129+
// Config config, Supplier<TraceConfig> traceConfigSupplier) {
130+
// return new TagContextExtractor(
131+
// traceConfigSupplier, () -> new BaggageContextInterpreter(config));
132+
// }
133+
//
134+
// private static class BaggageContextInterpreter extends ContextInterpreter {
135+
//
136+
// private BaggageContextInterpreter(Config config) {
137+
// super(config);
138+
// }
139+
//
140+
// @Override
141+
// public TracePropagationStyle style() {
142+
// return BAGGAGE;
143+
// }
144+
//
145+
// private Map<String, String> parseBaggageHeaders(String input) {
146+
// Map<String, String> baggage = new HashMap<>();
147+
// char keyValueSeparator = '=';
148+
// char pairSeparator = ',';
149+
// int start = 0;
150+
//
151+
// int pairSeparatorInd = input.indexOf(pairSeparator);
152+
// pairSeparatorInd = pairSeparatorInd == -1 ? input.length() : pairSeparatorInd;
153+
// int kvSeparatorInd = input.indexOf(keyValueSeparator);
154+
// while (kvSeparatorInd != -1) {
155+
// int end = pairSeparatorInd;
156+
// if (kvSeparatorInd > end) { // value is missing
157+
// return Collections.emptyMap();
158+
// }
159+
// String key = HttpCodec.decode(input.substring(start, kvSeparatorInd).trim());
160+
// String value = HttpCodec.decode(input.substring(kvSeparatorInd + 1, end).trim());
161+
// if (key.isEmpty() || value.isEmpty()) {
162+
// return Collections.emptyMap();
163+
// }
164+
// baggage.put(key, value);
165+
//
166+
// kvSeparatorInd = input.indexOf(keyValueSeparator, pairSeparatorInd + 1);
167+
// pairSeparatorInd = input.indexOf(pairSeparator, pairSeparatorInd + 1);
168+
// pairSeparatorInd = pairSeparatorInd == -1 ? input.length() : pairSeparatorInd;
169+
// start = end + 1;
170+
// }
171+
// return baggage;
172+
// }
173+
//
174+
// @Override
175+
// public boolean accept(String key, String value) {
176+
// if (null == key || key.isEmpty()) {
177+
// return true;
178+
// }
179+
// if (LOG_EXTRACT_HEADER_NAMES) {
180+
// log.debug("Header: {}", key);
181+
// }
182+
//
183+
// if (key.equalsIgnoreCase(BAGGAGE_KEY)) { // Only process tags that are relevant to baggage
184+
// baggage = parseBaggageHeaders(value);
185+
// }
186+
// return true;
187+
// }
188+
//
189+
// @Override
190+
// protected TagContext build() {
191+
// return super.build();
192+
// }
193+
// }
194+
// }
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
1+
package datadog.trace.core.baggage;
2+
3+
import datadog.context.Context;
4+
import datadog.context.propagation.CarrierSetter;
5+
import datadog.context.propagation.CarrierVisitor;
6+
import datadog.context.propagation.Propagator;
7+
import datadog.trace.api.Config;
8+
import datadog.trace.bootstrap.instrumentation.api.BaggageContext;
9+
import java.io.UnsupportedEncodingException;
10+
import java.net.URLDecoder;
11+
import java.nio.charset.StandardCharsets;
12+
import java.util.Arrays;
13+
import java.util.Collections;
14+
import java.util.HashMap;
15+
import java.util.HashSet;
16+
import java.util.Map;
17+
import java.util.Set;
18+
import java.util.function.BiConsumer;
19+
import javax.annotation.ParametersAreNonnullByDefault;
20+
21+
/** Propagator for tracing concern. */
22+
@ParametersAreNonnullByDefault
23+
public class BaggagePropagator implements Propagator {
24+
static final String BAGGAGE_KEY = "baggage";
25+
26+
private static final Set<Character> UNSAFE_CHARACTERS_KEY =
27+
new HashSet<>(
28+
Arrays.asList(
29+
'\"', ',', ';', '\\', '(', ')', '/', ':', '<', '=', '>', '?', '@', '[', ']', '{',
30+
'}'));
31+
private static final Set<Character> UNSAFE_CHARACTERS_VALUE =
32+
new HashSet<>(Arrays.asList('\"', ',', ';', '\\'));
33+
34+
public BaggagePropagator() {}
35+
36+
private int encodeKey(String key, StringBuilder builder) {
37+
return encode(key, builder, UNSAFE_CHARACTERS_KEY);
38+
}
39+
40+
private int encodeValue(String key, StringBuilder builder) {
41+
return encode(key, builder, UNSAFE_CHARACTERS_VALUE);
42+
}
43+
44+
private int encode(String input, StringBuilder builder, Set<Character> UNSAFE_CHARACTERS) {
45+
int size = 0;
46+
for (int i = 0; i < input.length(); i++) {
47+
char c = input.charAt(i);
48+
if (UNSAFE_CHARACTERS.contains(c) || c > 126 || c <= 32) { // encode character
49+
byte[] bytes =
50+
Character.toString(c)
51+
.getBytes(StandardCharsets.UTF_8); // not most efficient but what URLEncoder does
52+
for (byte b : bytes) {
53+
builder.append(String.format("%%%02X", b & 0xFF));
54+
size += 1;
55+
}
56+
} else {
57+
builder.append(c);
58+
size += 1;
59+
}
60+
}
61+
return size;
62+
}
63+
64+
@Override
65+
public <C> void inject(Context context, C carrier, CarrierSetter<C> setter) {
66+
Config config = Config.get();
67+
68+
StringBuilder baggageText = new StringBuilder();
69+
int processedBaggage = 0;
70+
int currentBytes = 0;
71+
int maxItems = config.getTraceBaggageMaxItems();
72+
int maxBytes = config.getTraceBaggageMaxBytes();
73+
BaggageContext baggageContext = BaggageContext.fromContext(context);
74+
for (final Map.Entry<String, String> entry : baggageContext.getBaggage().entrySet()) {
75+
if (processedBaggage >= maxItems) {
76+
break;
77+
}
78+
int additionalCharacters = 1; // accounting for potential comma and colon
79+
if (processedBaggage != 0) {
80+
additionalCharacters = 2; // allocating space for comma
81+
}
82+
83+
int byteSize = 1; // default include size of '='
84+
if (additionalCharacters == 2) {
85+
baggageText.append(',');
86+
byteSize += 1;
87+
}
88+
89+
byteSize += encodeKey(entry.getKey(), baggageText);
90+
baggageText.append('=');
91+
byteSize += encodeValue(entry.getValue(), baggageText);
92+
93+
if (currentBytes + byteSize > maxBytes) {
94+
baggageText.setLength(currentBytes);
95+
break;
96+
}
97+
currentBytes += byteSize;
98+
processedBaggage++;
99+
}
100+
101+
setter.set(carrier, BAGGAGE_KEY, baggageText.toString());
102+
}
103+
104+
@Override
105+
public <C> Context extract(Context context, C carrier, CarrierVisitor<C> visitor) {
106+
//noinspection ConstantValue
107+
if (context == null || carrier == null || visitor == null) {
108+
return context;
109+
}
110+
BaggageContextExtractor baggageContextExtractor = new BaggageContextExtractor();
111+
visitor.forEachKeyValue(
112+
carrier, baggageContextExtractor); // If the extraction fails, return the original context
113+
BaggageContext extractedContext = baggageContextExtractor.extractedContext;
114+
if (extractedContext == null) {
115+
return context;
116+
}
117+
return extractedContext.storeInto(context); // does this make sense?
118+
}
119+
120+
public static class BaggageContextExtractor implements BiConsumer<String, String> {
121+
private BaggageContext extractedContext;
122+
123+
BaggageContextExtractor() {}
124+
125+
/** URL decode value */
126+
private String decode(final String value) {
127+
String decoded = value;
128+
try {
129+
decoded = URLDecoder.decode(value, "UTF-8");
130+
} catch (final UnsupportedEncodingException | IllegalArgumentException e) {
131+
}
132+
return decoded;
133+
}
134+
135+
private Map<String, String> parseBaggageHeaders(String input) {
136+
Map<String, String> baggage = new HashMap<>();
137+
char keyValueSeparator = '=';
138+
char pairSeparator = ',';
139+
int start = 0;
140+
141+
int pairSeparatorInd = input.indexOf(pairSeparator);
142+
pairSeparatorInd = pairSeparatorInd == -1 ? input.length() : pairSeparatorInd;
143+
int kvSeparatorInd = input.indexOf(keyValueSeparator);
144+
while (kvSeparatorInd != -1) {
145+
int end = pairSeparatorInd;
146+
if (kvSeparatorInd > end) { // value is missing
147+
return Collections.emptyMap();
148+
}
149+
String key = decode(input.substring(start, kvSeparatorInd).trim());
150+
String value = decode(input.substring(kvSeparatorInd + 1, end).trim());
151+
if (key.isEmpty() || value.isEmpty()) {
152+
return Collections.emptyMap();
153+
}
154+
baggage.put(key, value);
155+
156+
kvSeparatorInd = input.indexOf(keyValueSeparator, pairSeparatorInd + 1);
157+
pairSeparatorInd = input.indexOf(pairSeparator, pairSeparatorInd + 1);
158+
pairSeparatorInd = pairSeparatorInd == -1 ? input.length() : pairSeparatorInd;
159+
start = end + 1;
160+
}
161+
return baggage;
162+
}
163+
164+
@Override
165+
public void accept(String key, String value) {
166+
if (null == key || key.isEmpty()) {
167+
return;
168+
}
169+
if (key.equalsIgnoreCase(BAGGAGE_KEY)) { // Only process tags that are relevant to baggage
170+
extractedContext =
171+
BaggageContext.create(
172+
parseBaggageHeaders(
173+
value)); // is this correct to assume that one instance will be created for each
174+
// propagator?
175+
}
176+
}
177+
}
178+
}

0 commit comments

Comments
 (0)