Skip to content

Commit eabee74

Browse files
committed
Revert "Drop support for opentracing (#2342)"
This reverts commit e4dff95.
1 parent b533c96 commit eabee74

12 files changed

Lines changed: 664 additions & 0 deletions

File tree

.ci/.matrix_framework.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ FRAMEWORK:
1212
- flask-3.0
1313
- jinja2-3
1414
- opentelemetry-newest
15+
- opentracing-newest
1516
- twisted-newest
1617
- celery-5-flask-2
1718
- celery-5-django-4

.ci/.matrix_framework_fips.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ FRAMEWORK:
66
- flask-3.0
77
- jinja2-3
88
- opentelemetry-newest
9+
- opentracing-newest
910
- twisted-newest
1011
- celery-5-flask-2
1112
- celery-5-django-5

.ci/.matrix_framework_full.yml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ FRAMEWORK:
3030
- celery-5-django-4
3131
- celery-5-django-5
3232
- opentelemetry-newest
33+
- opentracing-newest
34+
- opentracing-2.0
3335
- twisted-newest
3436
- twisted-18
3537
- twisted-17
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
# BSD 3-Clause License
2+
#
3+
# Copyright (c) 2019, Elasticsearch BV
4+
# All rights reserved.
5+
#
6+
# Redistribution and use in source and binary forms, with or without
7+
# modification, are permitted provided that the following conditions are met:
8+
#
9+
# * Redistributions of source code must retain the above copyright notice, this
10+
# list of conditions and the following disclaimer.
11+
#
12+
# * Redistributions in binary form must reproduce the above copyright notice,
13+
# this list of conditions and the following disclaimer in the documentation
14+
# and/or other materials provided with the distribution.
15+
#
16+
# * Neither the name of the copyright holder nor the names of its
17+
# contributors may be used to endorse or promote products derived from
18+
# this software without specific prior written permission.
19+
#
20+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30+
31+
32+
import warnings
33+
34+
from .span import OTSpan # noqa: F401
35+
from .tracer import Tracer # noqa: F401
36+
37+
warnings.warn(
38+
(
39+
"The OpenTracing bridge is deprecated and will be removed in the next major release. "
40+
"Please migrate to the OpenTelemetry bridge."
41+
),
42+
DeprecationWarning,
43+
)
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
# BSD 3-Clause License
2+
#
3+
# Copyright (c) 2019, Elasticsearch BV
4+
# All rights reserved.
5+
#
6+
# Redistribution and use in source and binary forms, with or without
7+
# modification, are permitted provided that the following conditions are met:
8+
#
9+
# * Redistributions of source code must retain the above copyright notice, this
10+
# list of conditions and the following disclaimer.
11+
#
12+
# * Redistributions in binary form must reproduce the above copyright notice,
13+
# this list of conditions and the following disclaimer in the documentation
14+
# and/or other materials provided with the distribution.
15+
#
16+
# * Neither the name of the copyright holder nor the names of its
17+
# contributors may be used to endorse or promote products derived from
18+
# this software without specific prior written permission.
19+
#
20+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30+
31+
from opentracing.span import Span as OTSpanBase
32+
from opentracing.span import SpanContext as OTSpanContextBase
33+
34+
from elasticapm import traces
35+
from elasticapm.utils import get_url_dict
36+
from elasticapm.utils.logging import get_logger
37+
38+
try:
39+
# opentracing-python 2.1+
40+
from opentracing import logs as ot_logs
41+
from opentracing import tags
42+
except ImportError:
43+
# opentracing-python <2.1
44+
from opentracing.ext import tags
45+
46+
ot_logs = None
47+
48+
49+
logger = get_logger("elasticapm.contrib.opentracing")
50+
51+
52+
class OTSpan(OTSpanBase):
53+
def __init__(self, tracer, context, elastic_apm_ref) -> None:
54+
super(OTSpan, self).__init__(tracer, context)
55+
self.elastic_apm_ref = elastic_apm_ref
56+
self.is_transaction = isinstance(elastic_apm_ref, traces.Transaction)
57+
self.is_dropped = isinstance(elastic_apm_ref, traces.DroppedSpan)
58+
if not context.span:
59+
context.span = self
60+
61+
def log_kv(self, key_values, timestamp=None):
62+
exc_type, exc_val, exc_tb = None, None, None
63+
if "python.exception.type" in key_values:
64+
exc_type = key_values["python.exception.type"]
65+
exc_val = key_values.get("python.exception.val")
66+
exc_tb = key_values.get("python.exception.tb")
67+
elif ot_logs and key_values.get(ot_logs.EVENT) == tags.ERROR:
68+
exc_type = key_values[ot_logs.ERROR_KIND]
69+
exc_val = key_values.get(ot_logs.ERROR_OBJECT)
70+
exc_tb = key_values.get(ot_logs.STACK)
71+
else:
72+
logger.debug("Can't handle non-exception type opentracing logs")
73+
if exc_type:
74+
agent = self.tracer._agent
75+
agent.capture_exception(exc_info=(exc_type, exc_val, exc_tb))
76+
return self
77+
78+
def set_operation_name(self, operation_name):
79+
self.elastic_apm_ref.name = operation_name
80+
return self
81+
82+
def set_tag(self, key, value):
83+
if self.is_transaction:
84+
if key == "type":
85+
self.elastic_apm_ref.transaction_type = value
86+
elif key == "result":
87+
self.elastic_apm_ref.result = value
88+
elif key == tags.HTTP_STATUS_CODE:
89+
self.elastic_apm_ref.result = "HTTP {}xx".format(str(value)[0])
90+
traces.set_context({"status_code": value}, "response")
91+
elif key == "user.id":
92+
traces.set_user_context(user_id=value)
93+
elif key == "user.username":
94+
traces.set_user_context(username=value)
95+
elif key == "user.email":
96+
traces.set_user_context(email=value)
97+
elif key == tags.HTTP_URL:
98+
traces.set_context({"url": get_url_dict(value)}, "request")
99+
elif key == tags.HTTP_METHOD:
100+
traces.set_context({"method": value}, "request")
101+
elif key == tags.COMPONENT:
102+
traces.set_context({"framework": {"name": value}}, "service")
103+
else:
104+
self.elastic_apm_ref.label(**{key: value})
105+
elif not self.is_dropped:
106+
if key.startswith("db."):
107+
span_context = self.elastic_apm_ref.context or {}
108+
if "db" not in span_context:
109+
span_context["db"] = {}
110+
if key == tags.DATABASE_STATEMENT:
111+
span_context["db"]["statement"] = value
112+
elif key == tags.DATABASE_USER:
113+
span_context["db"]["user"] = value
114+
elif key == tags.DATABASE_TYPE:
115+
span_context["db"]["type"] = value
116+
self.elastic_apm_ref.type = "db." + value
117+
else:
118+
self.elastic_apm_ref.label(**{key: value})
119+
self.elastic_apm_ref.context = span_context
120+
elif key == tags.SPAN_KIND:
121+
self.elastic_apm_ref.type = value
122+
else:
123+
self.elastic_apm_ref.label(**{key: value})
124+
return self
125+
126+
def finish(self, finish_time=None) -> None:
127+
if self.is_transaction:
128+
self.tracer._agent.end_transaction()
129+
elif not self.is_dropped:
130+
self.elastic_apm_ref.transaction.end_span()
131+
132+
133+
class OTSpanContext(OTSpanContextBase):
134+
def __init__(self, trace_parent, span=None) -> None:
135+
self.trace_parent = trace_parent
136+
self.span = span
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
# BSD 3-Clause License
2+
#
3+
# Copyright (c) 2019, Elasticsearch BV
4+
# All rights reserved.
5+
#
6+
# Redistribution and use in source and binary forms, with or without
7+
# modification, are permitted provided that the following conditions are met:
8+
#
9+
# * Redistributions of source code must retain the above copyright notice, this
10+
# list of conditions and the following disclaimer.
11+
#
12+
# * Redistributions in binary form must reproduce the above copyright notice,
13+
# this list of conditions and the following disclaimer in the documentation
14+
# and/or other materials provided with the distribution.
15+
#
16+
# * Neither the name of the copyright holder nor the names of its
17+
# contributors may be used to endorse or promote products derived from
18+
# this software without specific prior written permission.
19+
#
20+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30+
31+
import warnings
32+
33+
from opentracing import Format, InvalidCarrierException, SpanContextCorruptedException, UnsupportedFormatException
34+
from opentracing.scope_managers import ThreadLocalScopeManager
35+
from opentracing.tracer import ReferenceType
36+
from opentracing.tracer import Tracer as TracerBase
37+
38+
import elasticapm
39+
from elasticapm import get_client, instrument, traces
40+
from elasticapm.conf import constants
41+
from elasticapm.contrib.opentracing.span import OTSpan, OTSpanContext
42+
from elasticapm.utils import disttracing
43+
44+
45+
class Tracer(TracerBase):
46+
def __init__(self, client_instance=None, config=None, scope_manager=None) -> None:
47+
self._agent = client_instance or get_client() or elasticapm.Client(config=config)
48+
if scope_manager and not isinstance(scope_manager, ThreadLocalScopeManager):
49+
warnings.warn(
50+
"Currently, the Elastic APM opentracing bridge only supports the ThreadLocalScopeManager. "
51+
"Usage of other scope managers will lead to unpredictable results."
52+
)
53+
self._scope_manager = scope_manager or ThreadLocalScopeManager()
54+
if self._agent.config.instrument and self._agent.config.enabled:
55+
instrument()
56+
57+
def start_active_span(
58+
self,
59+
operation_name,
60+
child_of=None,
61+
references=None,
62+
tags=None,
63+
start_time=None,
64+
ignore_active_span=False,
65+
finish_on_close=True,
66+
):
67+
ot_span = self.start_span(
68+
operation_name,
69+
child_of=child_of,
70+
references=references,
71+
tags=tags,
72+
start_time=start_time,
73+
ignore_active_span=ignore_active_span,
74+
)
75+
scope = self._scope_manager.activate(ot_span, finish_on_close)
76+
return scope
77+
78+
def start_span(
79+
self, operation_name=None, child_of=None, references=None, tags=None, start_time=None, ignore_active_span=False
80+
):
81+
if isinstance(child_of, OTSpanContext):
82+
parent_context = child_of
83+
elif isinstance(child_of, OTSpan):
84+
parent_context = child_of.context
85+
elif references and references[0].type == ReferenceType.CHILD_OF:
86+
parent_context = references[0].referenced_context
87+
else:
88+
parent_context = None
89+
transaction = traces.execution_context.get_transaction()
90+
if not transaction:
91+
trace_parent = parent_context.trace_parent if parent_context else None
92+
transaction = self._agent.begin_transaction("custom", trace_parent=trace_parent)
93+
transaction.name = operation_name
94+
span_context = OTSpanContext(trace_parent=transaction.trace_parent)
95+
ot_span = OTSpan(self, span_context, transaction)
96+
else:
97+
# to allow setting an explicit parent span, we check if the parent_context is set
98+
# and if it is a span. In all other cases, the parent is found implicitly through the
99+
# execution context.
100+
parent_span_id = (
101+
parent_context.span.elastic_apm_ref.id
102+
if parent_context and parent_context.span and not parent_context.span.is_transaction
103+
else None
104+
)
105+
span = transaction._begin_span(operation_name, None, parent_span_id=parent_span_id)
106+
trace_parent = parent_context.trace_parent if parent_context else transaction.trace_parent
107+
span_context = OTSpanContext(trace_parent=trace_parent.copy_from(span_id=span.id))
108+
ot_span = OTSpan(self, span_context, span)
109+
if tags:
110+
for k, v in tags.items():
111+
ot_span.set_tag(k, v)
112+
return ot_span
113+
114+
def extract(self, format, carrier):
115+
if format in (Format.HTTP_HEADERS, Format.TEXT_MAP):
116+
trace_parent = disttracing.TraceParent.from_headers(carrier)
117+
if not trace_parent:
118+
raise SpanContextCorruptedException("could not extract span context from carrier")
119+
return OTSpanContext(trace_parent=trace_parent)
120+
raise UnsupportedFormatException
121+
122+
def inject(self, span_context, format, carrier):
123+
if format in (Format.HTTP_HEADERS, Format.TEXT_MAP):
124+
if not isinstance(carrier, dict):
125+
raise InvalidCarrierException("carrier for {} format should be dict-like".format(format))
126+
val = span_context.trace_parent.to_ascii()
127+
carrier[constants.TRACEPARENT_HEADER_NAME] = val
128+
if self._agent.config.use_elastic_traceparent_header:
129+
carrier[constants.TRACEPARENT_LEGACY_HEADER_NAME] = val
130+
return
131+
raise UnsupportedFormatException

setup.cfg

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,8 @@ tornado =
5555
tornado
5656
starlette =
5757
starlette
58+
opentracing =
59+
opentracing>=2.0.0
5860
sanic =
5961
sanic
6062
opentelemetry =
@@ -79,6 +81,7 @@ markers =
7981
gevent
8082
eventlet
8183
celery
84+
opentracing
8285
cassandra
8386
psycopg2
8487
mongodb
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# BSD 3-Clause License
2+
#
3+
# Copyright (c) 2019, Elasticsearch BV
4+
# All rights reserved.
5+
#
6+
# Redistribution and use in source and binary forms, with or without
7+
# modification, are permitted provided that the following conditions are met:
8+
#
9+
# * Redistributions of source code must retain the above copyright notice, this
10+
# list of conditions and the following disclaimer.
11+
#
12+
# * Redistributions in binary form must reproduce the above copyright notice,
13+
# this list of conditions and the following disclaimer in the documentation
14+
# and/or other materials provided with the distribution.
15+
#
16+
# * Neither the name of the copyright holder nor the names of its
17+
# contributors may be used to endorse or promote products derived from
18+
# this software without specific prior written permission.
19+
#
20+
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
21+
# AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22+
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
23+
# DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
24+
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25+
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
26+
# SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
27+
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
28+
# OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29+
# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

0 commit comments

Comments
 (0)