|
1 | 1 | package datadog.opentelemetry.shim.context; |
2 | 2 |
|
3 | 3 | import datadog.opentelemetry.shim.trace.OtelSpan; |
4 | | -import datadog.trace.bootstrap.instrumentation.api.AgentScope; |
5 | 4 | import datadog.trace.bootstrap.instrumentation.api.AgentSpan; |
6 | | -import datadog.trace.bootstrap.instrumentation.api.AgentTracer; |
7 | 5 | import datadog.trace.bootstrap.instrumentation.api.AttachableWrapper; |
8 | | -import io.opentelemetry.api.trace.Span; |
9 | 6 | import io.opentelemetry.context.Context; |
10 | 7 | import io.opentelemetry.context.ContextKey; |
11 | 8 | import io.opentelemetry.context.Scope; |
12 | | -import java.util.Arrays; |
| 9 | +import java.util.Map; |
| 10 | +import java.util.concurrent.ConcurrentHashMap; |
13 | 11 | import javax.annotation.Nullable; |
14 | 12 | import javax.annotation.ParametersAreNonnullByDefault; |
15 | 13 |
|
| 14 | +@SuppressWarnings({"rawtypes", "unchecked"}) |
16 | 15 | @ParametersAreNonnullByDefault |
17 | 16 | public class OtelContext implements Context { |
18 | | - private static final Object[] NO_ENTRIES = {}; |
19 | 17 |
|
20 | 18 | /** Overridden root context. */ |
21 | | - public static final Context ROOT = new OtelContext(OtelSpan.invalid(), OtelSpan.invalid()); |
| 19 | + public static final Context ROOT = new OtelContext(datadog.context.Context.root()); |
22 | 20 |
|
23 | 21 | private static final String OTEL_CONTEXT_SPAN_KEY = "opentelemetry-trace-span-key"; |
24 | 22 | private static final String OTEL_CONTEXT_ROOT_SPAN_KEY = "opentelemetry-traces-local-root-span"; |
25 | 23 |
|
26 | | - /** Keep track of propagated context that has not been captured on the scope stack. */ |
27 | | - private static final ThreadLocal<OtelContext> lastPropagated = new ThreadLocal<>(); |
| 24 | + /** Records the keys needed to access the delegate context, mapped by key name. */ |
| 25 | + private static final Map<ContextKey<?>, datadog.context.ContextKey<?>> DELEGATE_KEYS = |
| 26 | + new ConcurrentHashMap<>(); |
28 | 27 |
|
29 | | - private final Span currentSpan; |
30 | | - private final Span rootSpan; |
| 28 | + private final datadog.context.Context delegate; |
31 | 29 |
|
32 | | - private final Object[] entries; |
| 30 | + public OtelContext(datadog.context.Context delegate) { |
| 31 | + this.delegate = delegate; |
| 32 | + } |
33 | 33 |
|
34 | | - public OtelContext(Span currentSpan, Span rootSpan) { |
35 | | - this(currentSpan, rootSpan, NO_ENTRIES); |
| 34 | + public static Context current() { |
| 35 | + return new OtelContext(datadog.context.Context.current()); |
36 | 36 | } |
37 | 37 |
|
38 | | - public OtelContext(Span currentSpan, Span rootSpan, Object[] entries) { |
39 | | - this.currentSpan = currentSpan; |
40 | | - this.rootSpan = rootSpan; |
41 | | - this.entries = entries; |
| 38 | + @Override |
| 39 | + public Scope makeCurrent() { |
| 40 | + return new OtelScope(Context.super.makeCurrent(), delegate.attach()); |
42 | 41 | } |
43 | 42 |
|
44 | 43 | @Nullable |
45 | 44 | @Override |
46 | | - @SuppressWarnings("unchecked") |
47 | 45 | public <V> V get(ContextKey<V> key) { |
48 | 46 | if (OTEL_CONTEXT_SPAN_KEY.equals(key.toString())) { |
49 | | - return (V) this.currentSpan; |
| 47 | + AgentSpan span = AgentSpan.fromContext(delegate); |
| 48 | + if (span != null) { |
| 49 | + return (V) toOtelSpan(span); |
| 50 | + } |
| 51 | + // fall-through and check for non-datadog span data |
50 | 52 | } else if (OTEL_CONTEXT_ROOT_SPAN_KEY.equals(key.toString())) { |
51 | | - return (V) this.rootSpan; |
52 | | - } |
53 | | - for (int i = 0; i < this.entries.length; i += 2) { |
54 | | - if (this.entries[i] == key) { |
55 | | - return (V) this.entries[i + 1]; |
| 53 | + AgentSpan span = AgentSpan.fromContext(delegate); |
| 54 | + if (span != null) { |
| 55 | + return (V) toOtelSpan(span.getLocalRootSpan()); |
56 | 56 | } |
| 57 | + // fall-through and check for non-datadog span data |
57 | 58 | } |
58 | | - return null; |
| 59 | + return (V) delegate.get(delegateKey(key)); |
59 | 60 | } |
60 | 61 |
|
61 | 62 | @Override |
62 | 63 | public <V> Context with(ContextKey<V> key, V value) { |
63 | 64 | if (OTEL_CONTEXT_SPAN_KEY.equals(key.toString())) { |
64 | | - return new OtelContext((Span) value, this.rootSpan, this.entries); |
65 | | - } else if (OTEL_CONTEXT_ROOT_SPAN_KEY.equals(key.toString())) { |
66 | | - return new OtelContext(this.currentSpan, (Span) value, this.entries); |
67 | | - } |
68 | | - Object[] newEntries = null; |
69 | | - int oldEntriesLength = this.entries.length; |
70 | | - for (int i = 0; i < oldEntriesLength; i += 2) { |
71 | | - if (this.entries[i] == key) { |
72 | | - if (this.entries[i + 1] == value) { |
73 | | - return this; |
74 | | - } |
75 | | - newEntries = this.entries.clone(); |
76 | | - newEntries[i + 1] = value; |
77 | | - break; |
| 65 | + if (value instanceof OtelSpan) { |
| 66 | + AgentSpan span = ((OtelSpan) value).asAgentSpan(); |
| 67 | + return new OtelContext(delegate.with(span)); |
78 | 68 | } |
| 69 | + // fall-through and store as non-datadog span data |
79 | 70 | } |
80 | | - if (null == newEntries) { |
81 | | - newEntries = Arrays.copyOf(this.entries, oldEntriesLength + 2); |
82 | | - newEntries[oldEntriesLength] = key; |
83 | | - newEntries[oldEntriesLength + 1] = value; |
84 | | - } |
85 | | - return new OtelContext(this.currentSpan, this.rootSpan, newEntries); |
| 71 | + return new OtelContext(delegate.with(delegateKey(key), value)); |
86 | 72 | } |
87 | 73 |
|
88 | 74 | @Override |
89 | | - public Scope makeCurrent() { |
90 | | - final Scope scope = Context.super.makeCurrent(); |
91 | | - if (this.currentSpan instanceof OtelSpan) { |
92 | | - // only keep propagated context until next span activation |
93 | | - lastPropagated.remove(); |
94 | | - AgentScope agentScope = ((OtelSpan) this.currentSpan).activate(); |
95 | | - return new OtelScope(scope, agentScope, this.entries); |
96 | | - } else { |
97 | | - // propagated context not on the scope stack, capture it here |
98 | | - lastPropagated.set(this); |
99 | | - return new Scope() { |
100 | | - @Override |
101 | | - public void close() { |
102 | | - lastPropagated.remove(); |
103 | | - scope.close(); |
104 | | - } |
105 | | - }; |
106 | | - } |
107 | | - } |
108 | | - |
109 | | - public static Context current() { |
110 | | - // Check for propagated context not on the scope stack |
111 | | - Context context = lastPropagated.get(); |
112 | | - if (null != context) { |
113 | | - return context; |
114 | | - } |
115 | | - // Check empty context |
116 | | - AgentScope agentCurrentScope = AgentTracer.activeScope(); |
117 | | - if (null == agentCurrentScope) { |
118 | | - return OtelContext.ROOT; |
119 | | - } |
120 | | - // Get OTel current span |
121 | | - Span otelCurrentSpan = null; |
122 | | - AgentSpan agentCurrentSpan = agentCurrentScope.span(); |
123 | | - if (agentCurrentSpan instanceof AttachableWrapper) { |
124 | | - Object wrapper = ((AttachableWrapper) agentCurrentSpan).getWrapper(); |
125 | | - if (wrapper instanceof OtelSpan) { |
126 | | - otelCurrentSpan = (OtelSpan) wrapper; |
127 | | - } |
| 75 | + public boolean equals(Object o) { |
| 76 | + if (o == null || getClass() != o.getClass()) { |
| 77 | + return false; |
128 | 78 | } |
129 | | - if (otelCurrentSpan == null) { |
130 | | - otelCurrentSpan = new OtelSpan(agentCurrentSpan); |
131 | | - } |
132 | | - // Get OTel root span |
133 | | - Span otelRootSpan = null; |
134 | | - AgentSpan agentRootSpan = agentCurrentSpan.getLocalRootSpan(); |
135 | | - if (agentRootSpan instanceof AttachableWrapper) { |
136 | | - Object wrapper = ((AttachableWrapper) agentRootSpan).getWrapper(); |
137 | | - if (wrapper instanceof OtelSpan) { |
138 | | - otelRootSpan = (OtelSpan) wrapper; |
139 | | - } |
140 | | - } |
141 | | - if (otelRootSpan == null) { |
142 | | - otelRootSpan = new OtelSpan(agentRootSpan); |
143 | | - } |
144 | | - // Get OTel custom context entries |
145 | | - Object[] contextEntries = NO_ENTRIES; |
146 | | - if (agentCurrentScope instanceof AttachableWrapper) { |
147 | | - Object wrapper = ((AttachableWrapper) agentCurrentScope).getWrapper(); |
148 | | - if (wrapper instanceof OtelScope) { |
149 | | - contextEntries = ((OtelScope) wrapper).contextEntries(); |
150 | | - } |
151 | | - } |
152 | | - return new OtelContext(otelCurrentSpan, otelRootSpan, contextEntries); |
| 79 | + return delegate.equals(((OtelContext) o).delegate); |
153 | 80 | } |
154 | 81 |
|
155 | | - /** Last propagated context not on the scope stack; {@code null} if there's no such context. */ |
156 | | - @Nullable |
157 | | - public static Context lastPropagated() { |
158 | | - return lastPropagated.get(); |
| 82 | + @Override |
| 83 | + public int hashCode() { |
| 84 | + return delegate.hashCode(); |
159 | 85 | } |
160 | 86 |
|
161 | 87 | @Override |
162 | 88 | public String toString() { |
163 | | - return "OtelContext{" |
164 | | - + "currentSpan=" |
165 | | - + this.currentSpan.getSpanContext() |
166 | | - + ", rootSpan=" |
167 | | - + this.rootSpan.getSpanContext() |
168 | | - + '}'; |
| 89 | + return "OtelContext{" + "delegate=" + delegate + '}'; |
| 90 | + } |
| 91 | + |
| 92 | + private static datadog.context.ContextKey delegateKey(ContextKey key) { |
| 93 | + return DELEGATE_KEYS.computeIfAbsent(key, OtelContext::mapByKeyName); |
| 94 | + } |
| 95 | + |
| 96 | + private static datadog.context.ContextKey mapByKeyName(ContextKey key) { |
| 97 | + return datadog.context.ContextKey.named(key.toString()); |
| 98 | + } |
| 99 | + |
| 100 | + private static OtelSpan toOtelSpan(AgentSpan span) { |
| 101 | + if (span instanceof AttachableWrapper) { |
| 102 | + Object wrapper = ((AttachableWrapper) span).getWrapper(); |
| 103 | + if (wrapper instanceof OtelSpan) { |
| 104 | + return (OtelSpan) wrapper; |
| 105 | + } |
| 106 | + } |
| 107 | + return new OtelSpan(span); |
169 | 108 | } |
170 | 109 | } |
0 commit comments