-
Notifications
You must be signed in to change notification settings - Fork 227
Opentracing bridge #388
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Opentracing bridge #388
Changes from all commits
b6e5a27
a1e9582
847c991
b1b7400
d70d6e2
283e7db
d33852d
0b596f6
8a391bf
7f1bb1b
a1352ef
4ca8859
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,134 @@ | ||
ifdef::env-github[] | ||
NOTE: For the best reading experience, | ||
please view this documentation at https://www.elastic.co/guide/en/apm/agent/java[elastic.co] | ||
endif::[] | ||
|
||
[[opentracing-bridge]] | ||
== Elastic APM OpenTracing bridge | ||
|
||
The Elastic APM OpenTracing bridge allows to create Elastic APM `Transactions` and `Spans`, | ||
using the OpenTracing API. | ||
In other words, | ||
it translates the calls to the OpenTracing API to Elastic APM and thus allows for reusing existing instrumentation. | ||
|
||
The first span of a service will be converted to an Elastic APM | ||
{apm-overview-ref-v}/transactions.html[`Transaction`], | ||
subsequent spans are mapped to Elastic APM | ||
{apm-overview-ref-v}/transaction-spans.html[`Span`]. | ||
|
||
[float] | ||
[[opentracing-getting-started]] | ||
=== Getting started | ||
The first step in getting started with the OpenTracing API bridge is to install the `opentracing` library: | ||
|
||
[source,bash] | ||
---- | ||
pip install elastic-apm[opentracing] | ||
---- | ||
|
||
Or if you already have installed `elastic-apm` | ||
|
||
|
||
[source,bash] | ||
---- | ||
pip install opentracing>=2.0.0 | ||
---- | ||
|
||
|
||
[float] | ||
[[opentracing-init-tracer]] | ||
=== Initialize tracer | ||
|
||
[source,python] | ||
---- | ||
from elasticapm.contrib.opentracing import Tracer | ||
|
||
tracer = Tracer(); | ||
---- | ||
|
||
`Tracer` accepts the following optional arguments: | ||
|
||
* `client_instance`: an already instantiated Elastic APM client | ||
* `config`: a configuration dictionary, which will be used to instantiate a new Elastic APM client, | ||
e.g. `{"SERVER_URL": "https://example.org"}. See <<configuration, configuration>> for more information. | ||
* `scope_manager`: a scope manager instance. Defaults to `ThreadLocalScopeManager` (see | ||
|
||
|
||
[float] | ||
[[opentracing-elastic-apm-tags]] | ||
=== Elastic APM specific tags | ||
|
||
Elastic APM defines some tags which are not included in the OpenTracing API but are relevant in the context of Elastic APM. | ||
|
||
- `type` - sets the type of the transaction, | ||
for example `request`, `ext` or `db` | ||
- `user.id` - sets the user id, | ||
appears in the "User" tab in the transaction details in the Elastic APM UI | ||
- `user.email` - sets the user email, | ||
appears in the "User" tab in the transaction details in the Elastic APM UI | ||
- `user.username` - sets the user name, | ||
appears in the "User" tab in the transaction details in the Elastic APM UI | ||
- `result` - sets the result of the transaction. Overrides the default value of `success`. | ||
|
||
NOTE: these tags need to be set on the first span of an operation (e.g. an incoming request of your webapp). | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. users still can set any arbitrary tags on spans, right? only that they will not be mapped to know fields. Ill rephrase L61 to something like: "Some tags are well defined in Elastic APM. If you defined these tags in the first span of an operation, the will be automatically mapped to defined fields..." and remove this Note |
||
|
||
[float] | ||
[[opentracing-caveats]] | ||
=== Caveats | ||
Not all features of the OpenTracing API are supported. | ||
|
||
[float] | ||
[[opentracing-scope-managers]] | ||
==== Scope Managers | ||
Currently, only the `ThreadLocalScopeManager` is supported. | ||
Using other scope managers will lead to unpredictable and possibly app-breaking behaviour. | ||
|
||
[float] | ||
[[opentracing-instrumentation]] | ||
==== Instrumentation | ||
|
||
It is recommended to not use the built-in instrumentations of Elastic APM together with third-party OpenTracing instrumentations | ||
like https://pypi.org/project/opentracing_instrumentation/[opentracing_instrumentation] in the same service. | ||
If you would like to use such instrumentations, we recommend to disable the built-in instrumentation using the <<config-instrument,`instrument`>> config option. | ||
|
||
[float] | ||
[[opentracing-propagation]] | ||
==== Context propagation | ||
This bridge only supports the formats `Format.Builtin.TEXT_MAP` and `Format.Builtin.HTTP_HEADERS`. | ||
`Format.Builtin.BINARY` is currently not supported. | ||
|
||
[float] | ||
[[opentracing-references]] | ||
==== Span References | ||
Currently, this bridge only supports `child_of` references. | ||
Other references, | ||
like `follows_from` are not supported yet. | ||
|
||
[float] | ||
[[opentracing-baggage]] | ||
==== Baggage | ||
The `Span.set_baggage_item(key, value)` method is not supported. | ||
Baggage items are silently dropped. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. how this exactly happens? i was expecting to have There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We inherit the noop-method from the super class There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. could use a test, for some more explicitness 🙈 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. done in 4ca8859 (plus another few tests plus a fix for an issue I found thanks to those tests. "write more tests" is the lesson here I guess) |
||
|
||
[float] | ||
[[opentracing-logs]] | ||
==== Logs | ||
Only exception logging is supported. | ||
Logging an Exception on the OpenTracing span will create an Elastic APM | ||
{apm-overview-ref-v}/errors.html[`Error`]. | ||
Example: | ||
|
||
[source,python] | ||
---- | ||
with tracer.start_active_span("transaction") as tx_scope: | ||
try: | ||
raise ValueError("oops") | ||
except ValueError: | ||
exc_type, exc_val, exc_tb = sys.exc_info()[:3] | ||
tx_scope.span.log_kv({ | ||
"python.exception.type": exc_type, | ||
"python.exception.val": exc_val, | ||
"python.exception.tb": exc_tb | ||
}) | ||
---- | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
class BaseContext(object): | ||
def set_transaction(self, transaction): | ||
raise NotImplementedError | ||
|
||
def get_transaction(self, clear=False): | ||
raise NotImplementedError | ||
|
||
def set_span(self, span): | ||
raise NotImplementedError | ||
|
||
def get_span(self): | ||
raise NotImplementedError |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,31 +1,33 @@ | ||
from __future__ import absolute_import | ||
|
||
import contextvars | ||
from elasticapm.context.base import BaseContext | ||
|
||
elasticapm_transaction_var = contextvars.ContextVar("elasticapm_transaction_var") | ||
elasticapm_span_var = contextvars.ContextVar("elasticapm_span_var") | ||
|
||
class ContextVarsContext(BaseContext): | ||
elasticapm_transaction_var = contextvars.ContextVar("elasticapm_transaction_var") | ||
elasticapm_span_var = contextvars.ContextVar("elasticapm_span_var") | ||
|
||
def get_transaction(clear=False): | ||
try: | ||
transaction = elasticapm_transaction_var.get() | ||
if clear: | ||
set_transaction(None) | ||
return transaction | ||
except LookupError: | ||
return None | ||
def get_transaction(self, clear=False): | ||
try: | ||
transaction = self.elasticapm_transaction_var.get() | ||
if clear: | ||
self.set_transaction(None) | ||
return transaction | ||
except LookupError: | ||
return None | ||
|
||
def set_transaction(self, transaction): | ||
self.elasticapm_transaction_var.set(transaction) | ||
|
||
def set_transaction(transaction): | ||
elasticapm_transaction_var.set(transaction) | ||
def get_span(self): | ||
try: | ||
return self.elasticapm_span_var.get() | ||
except LookupError: | ||
return None | ||
|
||
def set_span(self, span): | ||
self.elasticapm_span_var.set(span) | ||
|
||
def get_span(): | ||
try: | ||
return elasticapm_span_var.get() | ||
except LookupError: | ||
return None | ||
|
||
|
||
def set_span(span): | ||
elasticapm_span_var.set(span) | ||
execution_context = ContextVarsContext() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,30 +1,33 @@ | ||
import threading | ||
|
||
thread_local = threading.local() | ||
thread_local.transaction = None | ||
elasticapm_span_var = None | ||
from elasticapm.context.base import BaseContext | ||
|
||
|
||
def get_transaction(clear=False): | ||
""" | ||
Get the transaction registered for the current thread. | ||
class ThreadLocalContext(BaseContext): | ||
thread_local = threading.local() | ||
thread_local.transaction = None | ||
thread_local.span = None | ||
|
||
:return: | ||
:rtype: Transaction | ||
""" | ||
transaction = getattr(thread_local, "transaction", None) | ||
if clear: | ||
thread_local.transaction = None | ||
return transaction | ||
def get_transaction(self, clear=False): | ||
""" | ||
Get the transaction registered for the current thread. | ||
|
||
:return: | ||
:rtype: Transaction | ||
""" | ||
transaction = getattr(self.thread_local, "transaction", None) | ||
if clear: | ||
self.thread_local.transaction = None | ||
return transaction | ||
|
||
def set_transaction(transaction): | ||
thread_local.transaction = transaction | ||
def set_transaction(self, transaction): | ||
self.thread_local.transaction = transaction | ||
|
||
def get_span(self): | ||
return getattr(self.thread_local, "span", None) | ||
|
||
def get_span(): | ||
return getattr(thread_local, "span", None) | ||
def set_span(self, span): | ||
self.thread_local.span = span | ||
|
||
|
||
def set_span(span): | ||
thread_local.span = span | ||
execution_context = ThreadLocalContext() |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
from .span import OTSpan # noqa: F401 | ||
from .tracer import Tracer # noqa: F401 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
first span of a service trace / operation?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Saying "trace" here might be misleading, as a trace represents the request through multiple services.
But I agree that it is not super clear what is meant here. I'd love if opentracing would define something like an entry span, but they do not.