Skip to content

Commit 694b26c

Browse files
authored
Merge branch 'master' into record-exceptions
2 parents 5c04297 + 9be899e commit 694b26c

File tree

34 files changed

+737
-146
lines changed

34 files changed

+737
-146
lines changed

docs/examples/basic_meter/http.py

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Copyright The OpenTelemetry Authors
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
#
15+
"""
16+
This module shows how you can enable collection and exporting of http metrics
17+
related to instrumentations.
18+
"""
19+
import requests
20+
21+
from opentelemetry import metrics
22+
from opentelemetry.instrumentation.requests import RequestsInstrumentor
23+
from opentelemetry.sdk.metrics import MeterProvider
24+
from opentelemetry.sdk.metrics.export import ConsoleMetricsExporter
25+
26+
# Sets the global MeterProvider instance
27+
metrics.set_meter_provider(MeterProvider())
28+
29+
# Exporter to export metrics to the console
30+
exporter = ConsoleMetricsExporter()
31+
32+
# Instrument the requests library
33+
RequestsInstrumentor().instrument()
34+
35+
# Indicate to start collecting and exporting requests related metrics
36+
metrics.get_meter_provider().start_pipeline(
37+
RequestsInstrumentor().meter, exporter, 5
38+
)
39+
40+
response = requests.get("http://example.com")
41+
42+
input("...\n")

docs/instrumentation/instrumentation.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,3 +13,4 @@ Submodules
1313
:maxdepth: 1
1414

1515
instrumentor
16+
metric

docs/instrumentation/metric.rst

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
opentelemetry.instrumentation.metric package
2+
============================================
3+
4+
.. automodule:: opentelemetry.instrumentation.metric
5+
:members:
6+
:undoc-members:
7+
:show-inheritance:

exporter/opentelemetry-exporter-zipkin/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
- Zipkin exporter now accepts a ``max_tag_value_length`` attribute to customize the
66
maximum allowed size a tag value can have. ([#1151](https://github.com/open-telemetry/opentelemetry-python/pull/1151))
7+
- Fixed OTLP events to Zipkin annotations translation. ([#1161](https://github.com/open-telemetry/opentelemetry-python/pull/1161))
78

89
## Version 0.13b0
910

exporter/opentelemetry-exporter-zipkin/src/opentelemetry/exporter/zipkin/__init__.py

Lines changed: 17 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -237,20 +237,25 @@ def _extract_tags_from_span(self, span: Span):
237237
tags.update(self._extract_tags_from_dict(span.resource.attributes))
238238
return tags
239239

240-
def _extract_annotations_from_events(
241-
self, events
242-
): # pylint: disable=R0201
243-
return (
244-
[
240+
def _extract_annotations_from_events(self, events):
241+
if not events:
242+
return None
243+
244+
annotations = []
245+
for event in events:
246+
attrs = {}
247+
for key, value in event.attributes.items():
248+
if isinstance(value, str):
249+
value = value[: self.max_tag_value_length]
250+
attrs[key] = value
251+
252+
annotations.append(
245253
{
246-
"timestamp": _nsec_to_usec_round(e.timestamp),
247-
"value": e.name,
254+
"timestamp": _nsec_to_usec_round(event.timestamp),
255+
"value": json.dumps({event.name: attrs}),
248256
}
249-
for e in events
250-
]
251-
if events
252-
else None
253-
)
257+
)
258+
return annotations
254259

255260

256261
def _nsec_to_usec_round(nsec):

exporter/opentelemetry-exporter-zipkin/tests/test_zipkin_exporter.py

Lines changed: 25 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,8 @@ def test_constructor_explicit(self):
9898
self.assertEqual(exporter.ipv6, ipv6)
9999
self.assertEqual(exporter.url, url)
100100

101-
# pylint: disable=too-many-locals
101+
# pylint: disable=too-many-locals,too-many-statements
102102
def test_export(self):
103-
104103
span_names = ("test1", "test2", "test3", "test4")
105104
trace_id = 0x6E0C63257DE34C926F9EFCD03927272E
106105
span_id = 0x34BF92DEEFC58C92
@@ -204,7 +203,7 @@ def test_export(self):
204203
local_endpoint = {"serviceName": service_name, "port": 9411}
205204

206205
exporter = ZipkinSpanExporter(service_name)
207-
expected = [
206+
expected_spans = [
208207
{
209208
"traceId": format(trace_id, "x"),
210209
"id": format(span_id, "x"),
@@ -220,14 +219,20 @@ def test_export(self):
220219
"otel.status_code": "2",
221220
"otel.status_description": "Example description",
222221
},
222+
"debug": True,
223+
"parentId": format(parent_id, "x"),
223224
"annotations": [
224225
{
225226
"timestamp": event_timestamp // 10 ** 3,
226-
"value": "event0",
227+
"value": {
228+
"event0": {
229+
"annotation_bool": True,
230+
"annotation_string": "annotation_test",
231+
"key_float": 0.3,
232+
}
233+
},
227234
}
228235
],
229-
"debug": True,
230-
"parentId": format(parent_id, "x"),
231236
},
232237
{
233238
"traceId": format(trace_id, "x"),
@@ -281,11 +286,21 @@ def test_export(self):
281286
status = exporter.export(otel_spans)
282287
self.assertEqual(SpanExportResult.SUCCESS, status)
283288

284-
mock_post.assert_called_with(
285-
url="http://localhost:9411/api/v2/spans",
286-
data=json.dumps(expected),
287-
headers={"Content-Type": "application/json"},
289+
# pylint: disable=unsubscriptable-object
290+
kwargs = mock_post.call_args[1]
291+
292+
self.assertEqual(kwargs["url"], "http://localhost:9411/api/v2/spans")
293+
actual_spans = sorted(
294+
json.loads(kwargs["data"]), key=lambda span: span["timestamp"]
288295
)
296+
for expected, actual in zip(expected_spans, actual_spans):
297+
expected_annotations = expected.pop("annotations", None)
298+
actual_annotations = actual.pop("annotations", None)
299+
if actual_annotations:
300+
for annotation in actual_annotations:
301+
annotation["value"] = json.loads(annotation["value"])
302+
self.assertEqual(expected, actual)
303+
self.assertEqual(expected_annotations, actual_annotations)
289304

290305
# pylint: disable=too-many-locals
291306
def test_zero_padding(self):

instrumentation/opentelemetry-instrumentation-celery/CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,9 @@
22

33
## Unreleased
44

5+
- Span operation names now include the task type. ([#1135](https://github.com/open-telemetry/opentelemetry-python/pull/1135))
6+
- Added automatic context propagation. ([#1135](https://github.com/open-telemetry/opentelemetry-python/pull/1135))
7+
58
## Version 0.12b0
69

710
Released 2020-08-14

instrumentation/opentelemetry-instrumentation-celery/README.rst

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,11 +29,20 @@ Usage
2929

3030
.. code-block:: python
3131
32+
from opentelemetry import trace
33+
from opentelemetry.sdk.trace import TracerProvider
34+
from opentelemetry.sdk.trace.export import BatchExportSpanProcessor
3235
from opentelemetry.instrumentation.celery import CeleryInstrumentor
3336
34-
CeleryInstrumentor().instrument()
35-
3637
from celery import Celery
38+
from celery.signals import worker_process_init
39+
40+
@worker_process_init.connect(weak=False)
41+
def init_celery_tracing(*args, **kwargs):
42+
trace.set_tracer_provider(TracerProvider())
43+
span_processor = BatchExportSpanProcessor(ConsoleSpanExporter())
44+
trace.get_tracer_provider().add_span_processor(span_processor)
45+
CeleryInstrumentor().instrument()
3746
3847
app = Celery("tasks", broker="amqp://localhost")
3948
@@ -43,6 +52,15 @@ Usage
4352
4453
add.delay(42, 50)
4554
55+
56+
Setting up tracing
57+
--------------------
58+
59+
When tracing a celery worker process, tracing and instrumention both must be initialized after the celery worker
60+
process is initialized. This is required for any tracing components that might use threading to work correctly
61+
such as the BatchExportSpanProcessor. Celery provides a signal called ``worker_process_init`` that can be used to
62+
accomplish this as shown in the example above.
63+
4664
References
4765
----------
4866
* `OpenTelemetry Celery Instrumentation <https://opentelemetry-python.readthedocs.io/en/latest/ext/celery/celery.html>`_

instrumentation/opentelemetry-instrumentation-celery/setup.cfg

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ install_requires =
4646
[options.extras_require]
4747
test =
4848
pytest
49+
celery ~= 4.0
4950
opentelemetry-test == 0.14.dev0
5051

5152
[options.packages.find]

instrumentation/opentelemetry-instrumentation-celery/src/opentelemetry/instrumentation/celery/__init__.py

Lines changed: 37 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,11 +30,20 @@
3030
3131
.. code:: python
3232
33+
from opentelemetry import trace
34+
from opentelemetry.sdk.trace import TracerProvider
35+
from opentelemetry.sdk.trace.export import BatchExportSpanProcessor
3336
from opentelemetry.instrumentation.celery import CeleryInstrumentor
3437
35-
CeleryInstrumentor().instrument()
36-
3738
from celery import Celery
39+
from celery.signals import worker_process_init
40+
41+
@worker_process_init.connect(weak=False)
42+
def init_celery_tracing(*args, **kwargs):
43+
trace.set_tracer_provider(TracerProvider())
44+
span_processor = BatchExportSpanProcessor(ConsoleSpanExporter())
45+
trace.get_tracer_provider().add_span_processor(span_processor)
46+
CeleryInstrumentor().instrument()
3847
3948
app = Celery("tasks", broker="amqp://localhost")
4049
@@ -50,13 +59,15 @@ def add(x, y):
5059

5160
import logging
5261
import signal
62+
from collections.abc import Iterable
5363

5464
from celery import signals # pylint: disable=no-name-in-module
5565

56-
from opentelemetry import trace
66+
from opentelemetry import propagators, trace
5767
from opentelemetry.instrumentation.celery import utils
5868
from opentelemetry.instrumentation.celery.version import __version__
5969
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
70+
from opentelemetry.trace.propagation import get_current_span
6071
from opentelemetry.trace.status import Status, StatusCanonicalCode
6172

6273
logger = logging.getLogger(__name__)
@@ -106,9 +117,16 @@ def _trace_prerun(self, *args, **kwargs):
106117
if task is None or task_id is None:
107118
return
108119

120+
request = task.request
121+
tracectx = propagators.extract(carrier_extractor, request) or {}
122+
parent = get_current_span(tracectx)
123+
109124
logger.debug("prerun signal start task_id=%s", task_id)
110125

111-
span = self._tracer.start_span(task.name, kind=trace.SpanKind.CONSUMER)
126+
operation_name = "{0}/{1}".format(_TASK_RUN, task.name)
127+
span = self._tracer.start_span(
128+
operation_name, parent=parent, kind=trace.SpanKind.CONSUMER
129+
)
112130

113131
activation = self._tracer.use_span(span, end_on_exit=True)
114132
activation.__enter__()
@@ -146,7 +164,10 @@ def _trace_before_publish(self, *args, **kwargs):
146164
if task is None or task_id is None:
147165
return
148166

149-
span = self._tracer.start_span(task.name, kind=trace.SpanKind.PRODUCER)
167+
operation_name = "{0}/{1}".format(_TASK_APPLY_ASYNC, task.name)
168+
span = self._tracer.start_span(
169+
operation_name, kind=trace.SpanKind.PRODUCER
170+
)
150171

151172
# apply some attributes here because most of the data is not available
152173
span.set_attribute(_TASK_TAG_KEY, _TASK_APPLY_ASYNC)
@@ -158,6 +179,10 @@ def _trace_before_publish(self, *args, **kwargs):
158179
activation.__enter__()
159180
utils.attach_span(task, task_id, (span, activation), is_publish=True)
160181

182+
headers = kwargs.get("headers")
183+
if headers:
184+
propagators.inject(type(headers).__setitem__, headers)
185+
161186
@staticmethod
162187
def _trace_after_publish(*args, **kwargs):
163188
task = utils.retrieve_task_from_sender(kwargs)
@@ -221,3 +246,10 @@ def _trace_retry(*args, **kwargs):
221246
# Use `str(reason)` instead of `reason.message` in case we get
222247
# something that isn't an `Exception`
223248
span.set_attribute(_TASK_RETRY_REASON_KEY, str(reason))
249+
250+
251+
def carrier_extractor(carrier, key):
252+
value = getattr(carrier, key, [])
253+
if isinstance(value, str) or not isinstance(value, Iterable):
254+
value = (value,)
255+
return value

0 commit comments

Comments
 (0)