-
Notifications
You must be signed in to change notification settings - Fork 320
/
OTelBridgeContext.java
170 lines (145 loc) · 5.62 KB
/
OTelBridgeContext.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/*
* Licensed to Elasticsearch B.V. under one or more contributor
* license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright
* ownership. Elasticsearch B.V. licenses this file to you under
* the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package co.elastic.apm.agent.opentelemetry.tracing;
import co.elastic.apm.agent.impl.ElasticApmTracer;
import co.elastic.apm.agent.impl.baggage.Baggage;
import co.elastic.apm.agent.impl.transaction.AbstractSpan;
import co.elastic.apm.agent.impl.transaction.ElasticContext;
import co.elastic.apm.agent.opentelemetry.baggage.OtelBaggage;
import io.opentelemetry.api.trace.Span;
import io.opentelemetry.context.Context;
import io.opentelemetry.context.ContextKey;
import io.opentelemetry.context.Scope;
import javax.annotation.Nullable;
import java.util.Objects;
/**
* Bridge implementation of OpenTelemetry {@link Context} that allows to provide compatibility with {@link ElasticContext}.
*/
public class OTelBridgeContext extends ElasticContext<OTelBridgeContext> implements Context, Scope {
/**
* Original root context as returned by {@link Context#root()} before instrumentation.
*/
@Nullable
private static volatile Context originalRootContext;
/**
* Bridged root context that will be returned by {@link Context#root()} after instrumentation
*/
@Nullable
private static volatile OTelBridgeContext root;
/**
* OTel context used for key/value storage
*/
private final Context otelContext;
private OTelBridgeContext(ElasticApmTracer tracer, Context otelContext) {
super(tracer);
this.otelContext = otelContext;
}
/**
* Captures the original root context and sets-up the bridged root if required
*
* @param tracer tracer
* @param originalRoot original OTel root context
* @return bridged context
*/
public static OTelBridgeContext bridgeRootContext(ElasticApmTracer tracer, Context originalRoot) {
if (root != null) {
return root;
}
synchronized (OTelBridgeContext.class) {
if (root == null) {
originalRootContext = originalRoot;
root = new OTelBridgeContext(tracer, originalRoot);
}
}
return root;
}
/**
* Bridges an active elastic span to an active OTel span context
*
* @param tracer tracer
* @param currentContext elastic (currently active) context
* @return bridged context with span as active
*/
public static OTelBridgeContext wrapElasticActiveSpan(ElasticApmTracer tracer, ElasticContext<?> currentContext) {
if (root == null) {
// Ensure that root context is being accessed at least once to capture the original root
// OTel 1.0 directly calls ArrayBasedContext.root() which is not publicly accessible, later versions delegate
// to ContextStorage.root() which we can't call from here either.
Context.root();
}
Objects.requireNonNull(originalRootContext, "OTel original context must be set through bridgeRootContext first");
Context result = originalRootContext;
if (currentContext.getSpan() != null) {
result = result.with(new OTelSpan(currentContext.getSpan()));
}
if (!currentContext.getBaggage().isEmpty()) {
result = result.with(OtelBaggage.fromElasticBaggage(currentContext.getBaggage()));
}
return new OTelBridgeContext(tracer, result);
}
@Nullable
@Override
public AbstractSpan<?> getSpan() {
// get otel span from context
Span span = Span.fromContext(otelContext);
if (span instanceof OTelSpan) {
return ((OTelSpan) span).getInternalSpan();
}
return null;
}
@Override
public Baggage getBaggage() {
io.opentelemetry.api.baggage.Baggage otelBaggage = io.opentelemetry.api.baggage.Baggage.fromContext(otelContext);
if (otelBaggage == null || otelBaggage.isEmpty()) {
return Baggage.EMPTY;
}
return OtelBaggage.toElasticBaggage(otelBaggage);
}
// OTel context implementation
@Nullable
@Override
public <V> V get(ContextKey<V> key) {
return otelContext.get(key);
}
@Override
public <V> Context with(ContextKey<V> key, V value) {
return new OTelBridgeContext(tracer, otelContext.with(key, value));
}
// OTel scope implementation
@Override
public void close() {
deactivate();
}
@Override
public String toString() {
return "OTelBridgeContext[" + otelContext + "]";
}
@Override
public boolean isEmpty() {
return this == root; //we only know that the root context is empty, other contexts could have any kind of keys
}
@Override
public void incrementReferences() {
//No need for reference counting: the contained span is always kept alive by the wrapping OTelSpan
}
@Override
public void decrementReferences() {
//No need for reference counting: the contained span is always kept alive by the wrapping OTelSpan
}
}