diff --git a/README.md b/README.md index c7b9a837c8d..bb199eebd5f 100644 --- a/README.md +++ b/README.md @@ -95,11 +95,7 @@ exporter.export([(counter, label_values)]) exporter.shutdown() ``` -See the [API -documentation](https://open-telemetry.github.io/opentelemetry-python/) for more -detail, and the -[opentelemetry-example-app](./examples/opentelemetry-example-app/README.rst) -for a complete example. +See the [API documentation](https://open-telemetry.github.io/opentelemetry-python/) for more detail, and the [examples folder](./examples) for a more sample code. ## Contributing diff --git a/examples/README.md b/examples/README.md new file mode 100644 index 00000000000..7fcc3f7dc48 --- /dev/null +++ b/examples/README.md @@ -0,0 +1,11 @@ +# Examples +This folder contains various examples to demonstrate using OpenTelemetry. + +##### basic_tracer +This example shows how to use OpenTelemetry to instrument an application - e.g. a batch job. + +##### http +This example shows how to use [OpenTelemetryMiddleware](https://github.com/open-telemetry/opentelemetry-python/tree/master/ext/opentelemetry-ext-wsgi) and [requests](https://github.com/open-telemetry/opentelemetry-python/tree/master/ext/opentelemetry-ext-http-requests) integrations to instrument a client and a server. + +##### opentelemetry-example-app +This package is a complete example of an application instrumented with OpenTelemetry. \ No newline at end of file diff --git a/examples/basic_tracer/README.md b/examples/basic_tracer/README.md new file mode 100644 index 00000000000..4dc0e96bea6 --- /dev/null +++ b/examples/basic_tracer/README.md @@ -0,0 +1,62 @@ +# Overview + +This example shows how to use OpenTelemetry to instrument a Python application - e.g. a batch job. +It supports exporting spans either to the console or to [Jaeger](https://www.jaegertracing.io). + +## Installation + +```sh +$ pip install opentelemetry-api opentelemetry-sdk +``` + +Setup [Jaeger Tracing](https://www.jaegertracing.io/docs/latest/getting-started/#all-in-one) + +## Run the Application + +### Console + +* Run the sample + +```bash +$ # from this directory +$ python tracer.py +``` + +The output will be displayed at the console + +```bash +AsyncRuntimeContext({'current_span': Span(name="baz", context=SpanContext(trace_id=0xf906f80f64d57c71ea8da4dfbbd2ddf2, span_id=0x5611c1407e06e4d7, trace_state={}))}) +Span(name="baz", context=SpanContext(trace_id=0xf906f80f64d57c71ea8da4dfbbd2ddf2, span_id=0x5611c1407e06e4d7, trace_state={}), kind=SpanKind.INTERNAL, parent=Span(name="bar", context=SpanContext(trace_id=0xf906f80f64d57c71ea8da4dfbbd2ddf2, span_id=0x1b9db0e0cc1a3f60, trace_state={})), start_time=2019-11-07T21:26:45.934412Z, end_time=2019-11-07T21:26:45.934567Z) +Span(name="bar", context=SpanContext(trace_id=0xf906f80f64d57c71ea8da4dfbbd2ddf2, span_id=0x1b9db0e0cc1a3f60, trace_state={}), kind=SpanKind.INTERNAL, parent=Span(name="foo", context=SpanContext(trace_id=0xf906f80f64d57c71ea8da4dfbbd2ddf2, span_id=0x1d5d87441ec2f410, trace_state={})), start_time=2019-11-07T21:26:45.934396Z, end_time=2019-11-07T21:26:45.934576Z) +Span(name="foo", context=SpanContext(trace_id=0xf906f80f64d57c71ea8da4dfbbd2ddf2, span_id=0x1d5d87441ec2f410, trace_state={}), kind=SpanKind.INTERNAL, parent=None, start_time=2019-11-07T21:26:45.934369Z, end_time=2019-11-07T21:26:45.934580Z) +``` + + +### Jaeger + +* Run the sample + +```sh +$ pip install opentelemetry-ext-jaeger +$ # from this directory +$ EXPORTER=jaeger python tracer.py +``` + +#### Jaeger UI + +Open the Jaeger UI in your browser [http://localhost:16686](http://localhost:16686) + +

+Select `basic-service` under *Service Name* and click on *Find Traces*. + +Click on the trace to view its details. + +

+ +## Useful links +- For more information on OpenTelemetry, visit: +- For more information on tracing in Python, visit: + +## LICENSE + +Apache License 2.0 diff --git a/examples/basic_tracer/__init__.py b/examples/basic_tracer/__init__.py new file mode 100644 index 00000000000..88051cd8bb0 --- /dev/null +++ b/examples/basic_tracer/__init__.py @@ -0,0 +1,14 @@ +# pylint: disable=C0103 +# Copyright 2019, OpenTelemetry Authors +# +# Licensed 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. diff --git a/examples/basic_tracer/images/jaeger-ui-detail.png b/examples/basic_tracer/images/jaeger-ui-detail.png new file mode 100644 index 00000000000..63491e132b5 Binary files /dev/null and b/examples/basic_tracer/images/jaeger-ui-detail.png differ diff --git a/examples/basic_tracer/images/jaeger-ui.png b/examples/basic_tracer/images/jaeger-ui.png new file mode 100644 index 00000000000..08e6718eef1 Binary files /dev/null and b/examples/basic_tracer/images/jaeger-ui.png differ diff --git a/examples/basic_tracer/tests/__init__.py b/examples/basic_tracer/tests/__init__.py new file mode 100644 index 00000000000..d853a7bcf65 --- /dev/null +++ b/examples/basic_tracer/tests/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2019, OpenTelemetry Authors +# +# Licensed 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. diff --git a/examples/basic_tracer/tests/test_tracer.py b/examples/basic_tracer/tests/test_tracer.py new file mode 100644 index 00000000000..d5922d6086b --- /dev/null +++ b/examples/basic_tracer/tests/test_tracer.py @@ -0,0 +1,27 @@ +# Copyright 2019, OpenTelemetry Authors +# +# Licensed 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. +import os +import subprocess +import unittest + + +class TestBasicTracerExample(unittest.TestCase): + def test_basic_tracer(self): + dirpath = os.path.dirname(os.path.realpath(__file__)) + test_script = "{}/../tracer.py".format(dirpath) + output = subprocess.check_output(test_script).decode() + + self.assertIn('name="foo"', output) + self.assertIn('name="bar"', output) + self.assertIn('name="baz"', output) diff --git a/examples/basic_tracer/tracer.py b/examples/basic_tracer/tracer.py new file mode 100755 index 00000000000..c99141f5aac --- /dev/null +++ b/examples/basic_tracer/tracer.py @@ -0,0 +1,52 @@ +#!/usr/bin/env python3 +# +# Copyright 2019, OpenTelemetry Authors +# +# Licensed 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. + +import os + +from opentelemetry import trace +from opentelemetry.context import Context +from opentelemetry.sdk.trace import Tracer +from opentelemetry.sdk.trace.export import ( + BatchExportSpanProcessor, + ConsoleSpanExporter, +) + +if os.getenv("EXPORTER") == "jaeger": + from opentelemetry.ext.jaeger import JaegerSpanExporter + + exporter = JaegerSpanExporter( + service_name="basic-service", + agent_host_name="localhost", + agent_port=6831, + ) +else: + exporter = ConsoleSpanExporter() + +# The preferred tracer implementation must be set, as the opentelemetry-api +# defines the interface with a no-op implementation. +trace.set_preferred_tracer_implementation(lambda T: Tracer()) +tracer = trace.tracer() + +# SpanExporter receives the spans and send them to the target location. +span_processor = BatchExportSpanProcessor(exporter) + +tracer.add_span_processor(span_processor) +with tracer.start_as_current_span("foo"): + with tracer.start_as_current_span("bar"): + with tracer.start_as_current_span("baz"): + print(Context) + +span_processor.shutdown() diff --git a/examples/http/README.md b/examples/http/README.md new file mode 100644 index 00000000000..5e6f2d7efad --- /dev/null +++ b/examples/http/README.md @@ -0,0 +1,80 @@ +# Overview + +This example shows how to use [OpenTelemetryMiddleware](https://github.com/open-telemetry/opentelemetry-python/tree/master/ext/opentelemetry-ext-wsgi) and [requests](https://github.com/open-telemetry/opentelemetry-python/tree/master/ext/opentelemetry-ext-http-requests) integrations to instrument a client and a server in Python. +It supports exporting spans either to the console or to [Jaeger](https://www.jaegertracing.io). + +## Installation + +```sh +$ pip install opentelemetry-api opentelemetry-sdk opentelemetry-ext-wsgi opentelemetry-ext-http-requests +``` + +Setup [Jaeger Tracing](https://www.jaegertracing.io/docs/latest/getting-started/#all-in-one) + +## Run the Application + +### Console + +* Run the server + +```bash +$ # from this directory +$ python server.py +``` + +* Run the client from a different terminal + +```bash +$ # from this directory +$ python tracer_client.py +``` + +The output will be displayed at the console on the client side + +```bash +Span(name="/", context=SpanContext(trace_id=0x7c5c0d62031570f00fd106d968139300, span_id=0x3703fd889dcdeb2b, trace_state={}), kind=SpanKind.CLIENT, parent=None, start_time=2019-11-07T21:52:59.591634Z, end_time=2019-11-07T21:53:00.386014Z) +``` + +And on the server + +```bash +127.0.0.1 - - [07/Nov/2019 13:53:00] "GET / HTTP/1.1" 200 - +Span(name="/wiki/Rabbit", context=SpanContext(trace_id=0x7c5c0d62031570f00fd106d968139300, span_id=0x4bf0be462b91d6ef, trace_state={}), kind=SpanKind.CLIENT, parent=Span(name="parent", context=SpanContext(trace_id=0x7c5c0d62031570f00fd106d968139300, span_id=0x68338643ccb2d53b, trace_state={})), start_time=2019-11-07T21:52:59.601597Z, end_time=2019-11-07T21:53:00.380491Z) +Span(name="parent", context=SpanContext(trace_id=0x7c5c0d62031570f00fd106d968139300, span_id=0x68338643ccb2d53b, trace_state={}), kind=SpanKind.INTERNAL, parent=Span(name="/", context=SpanContext(trace_id=0x7c5c0d62031570f00fd106d968139300, span_id=0x36050ac596949bc1, trace_state={})), start_time=2019-11-07T21:52:59.601233Z, end_time=2019-11-07T21:53:00.384485Z) +Span(name="/", context=SpanContext(trace_id=0x7c5c0d62031570f00fd106d968139300, span_id=0x36050ac596949bc1, trace_state={}), kind=SpanKind.SERVER, parent=SpanContext(trace_id=0x7c5c0d62031570f00fd106d968139300, span_id=0x3703fd889dcdeb2b, trace_state={}), start_time=2019-11-07T21:52:59.600816Z, end_time=2019-11-07T21:53:00.385322Z) +``` + +### Jaeger + +* Run the server + +```sh +$ pip install opentelemetry-ext-jaeger +$ # from this directory +$ EXPORTER=jaeger python server.py +``` + +* Run the client from a different terminal + +```bash +$ EXPORTER=jaeger python tracer_client.py +``` + +#### Jaeger UI + +Open the Jaeger UI in your browser [http://localhost:16686](http://localhost:16686) + +

+Select `http-server` under *Service Name* and click on *Find Traces*. + +Click on the trace to view its details. + +

+ +## Useful links +- For more information on OpenTelemetry, visit: +- For more information on tracing in Python, visit: + +## LICENSE + +Apache License 2.0 diff --git a/examples/http/__init__.py b/examples/http/__init__.py new file mode 100644 index 00000000000..d853a7bcf65 --- /dev/null +++ b/examples/http/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2019, OpenTelemetry Authors +# +# Licensed 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. diff --git a/examples/http/images/jaeger-ui-detail.png b/examples/http/images/jaeger-ui-detail.png new file mode 100644 index 00000000000..50d1901de6c Binary files /dev/null and b/examples/http/images/jaeger-ui-detail.png differ diff --git a/examples/http/images/jaeger-ui.png b/examples/http/images/jaeger-ui.png new file mode 100644 index 00000000000..02faad1de1e Binary files /dev/null and b/examples/http/images/jaeger-ui.png differ diff --git a/examples/http/requirements.txt b/examples/http/requirements.txt new file mode 100644 index 00000000000..7e1060246fd --- /dev/null +++ b/examples/http/requirements.txt @@ -0,0 +1 @@ +flask diff --git a/examples/trace/server.py b/examples/http/server.py similarity index 75% rename from examples/trace/server.py rename to examples/http/server.py index 3632540e213..82cb070c272 100755 --- a/examples/trace/server.py +++ b/examples/http/server.py @@ -14,6 +14,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +import os + import flask import requests @@ -22,30 +24,41 @@ from opentelemetry.ext.wsgi import OpenTelemetryMiddleware from opentelemetry.sdk.trace import Tracer from opentelemetry.sdk.trace.export import ( + BatchExportSpanProcessor, ConsoleSpanExporter, - SimpleExportSpanProcessor, ) +if os.getenv("EXPORTER") == "jaeger": + from opentelemetry.ext.jaeger import JaegerSpanExporter + + exporter = JaegerSpanExporter( + service_name="http-server", + agent_host_name="localhost", + agent_port=6831, + ) +else: + exporter = ConsoleSpanExporter() + # The preferred tracer implementation must be set, as the opentelemetry-api # defines the interface with a no-op implementation. trace.set_preferred_tracer_implementation(lambda T: Tracer()) +tracer = trace.tracer() + +# SpanExporter receives the spans and send them to the target location. +span_processor = BatchExportSpanProcessor(exporter) +tracer.add_span_processor(span_processor) # Integrations are the glue that binds the OpenTelemetry API and the # frameworks and libraries that are used together, automatically creating # Spans and propagating context as appropriate. -http_requests.enable(trace.tracer()) - -# SpanExporter receives the spans and send them to the target location. -span_processor = SimpleExportSpanProcessor(ConsoleSpanExporter()) -trace.tracer().add_span_processor(span_processor) - +http_requests.enable(tracer) app = flask.Flask(__name__) app.wsgi_app = OpenTelemetryMiddleware(app.wsgi_app) @app.route("/") def hello(): - with trace.tracer().start_as_current_span("parent"): + with tracer.start_as_current_span("parent"): requests.get("https://www.wikipedia.org/wiki/Rabbit") return "hello" diff --git a/examples/http/tests/__init__.py b/examples/http/tests/__init__.py new file mode 100644 index 00000000000..d853a7bcf65 --- /dev/null +++ b/examples/http/tests/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2019, OpenTelemetry Authors +# +# Licensed 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. diff --git a/examples/http/tests/test_http.py b/examples/http/tests/test_http.py new file mode 100644 index 00000000000..7aa3f93c153 --- /dev/null +++ b/examples/http/tests/test_http.py @@ -0,0 +1,36 @@ +# Copyright 2019, OpenTelemetry Authors +# +# Licensed 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. +import os +import subprocess +import unittest +from time import sleep + + +class TestHttpExample(unittest.TestCase): + @classmethod + def setup_class(cls): + dirpath = os.path.dirname(os.path.realpath(__file__)) + server_script = "{}/../server.py".format(dirpath) + cls.server = subprocess.Popen([server_script]) + sleep(1) + + def test_http(self): + dirpath = os.path.dirname(os.path.realpath(__file__)) + test_script = "{}/../tracer_client.py".format(dirpath) + output = subprocess.check_output(test_script).decode() + self.assertIn('name="/"', output) + + @classmethod + def teardown_class(cls): + cls.server.terminate() diff --git a/examples/trace/client.py b/examples/http/tracer_client.py similarity index 79% rename from examples/trace/client.py rename to examples/http/tracer_client.py index 662cea8d969..671d1d71f91 100755 --- a/examples/trace/client.py +++ b/examples/http/tracer_client.py @@ -14,29 +14,41 @@ # See the License for the specific language governing permissions and # limitations under the License. +import os + import requests from opentelemetry import trace from opentelemetry.ext import http_requests from opentelemetry.sdk.trace import Tracer from opentelemetry.sdk.trace.export import ( + BatchExportSpanProcessor, ConsoleSpanExporter, - SimpleExportSpanProcessor, ) +if os.getenv("EXPORTER") == "jaeger": + from opentelemetry.ext.jaeger import JaegerSpanExporter + + exporter = JaegerSpanExporter( + service_name="http-client", + agent_host_name="localhost", + agent_port=6831, + ) +else: + exporter = ConsoleSpanExporter() + # The preferred tracer implementation must be set, as the opentelemetry-api # defines the interface with a no-op implementation. trace.set_preferred_tracer_implementation(lambda T: Tracer()) tracer = trace.tracer() +# SpanExporter receives the spans and send them to the target location. +span_processor = BatchExportSpanProcessor(exporter) +tracer.add_span_processor(span_processor) + # Integrations are the glue that binds the OpenTelemetry API and the # frameworks and libraries that are used together, automatically creating # Spans and propagating context as appropriate. http_requests.enable(tracer) - -# SpanExporter receives the spans and send them to the target location. -span_processor = SimpleExportSpanProcessor(ConsoleSpanExporter()) -tracer.add_span_processor(span_processor) - response = requests.get(url="http://127.0.0.1:5000/") span_processor.shutdown() diff --git a/tox.ini b/tox.ini index e0e076fe77d..82512ebd8aa 100644 --- a/tox.ini +++ b/tox.ini @@ -2,8 +2,8 @@ skipsdist = True skip_missing_interpreters = True envlist = - py3{4,5,6,7,8}-test-{api,sdk,example-app,ext-wsgi,ext-http-requests,ext-jaeger,ext-pymongo,opentracing-shim} - pypy3-test-{api,sdk,example-app,ext-wsgi,ext-http-requests,ext-jaeger,ext-pymongo,opentracing-shim} + py3{4,5,6,7,8}-test-{api,sdk,example-app,example-basic-tracer,example-http,ext-wsgi,ext-http-requests,ext-jaeger,ext-pymongo,opentracing-shim} + pypy3-test-{api,sdk,example-app,example-basic-tracer,example-http,ext-wsgi,ext-http-requests,ext-jaeger,ext-pymongo,opentracing-shim} py3{4,5,6,7,8}-coverage ; Coverage is temporarily disabled for pypy3 due to the pytest bug. @@ -35,6 +35,8 @@ changedir = test-ext-pymongo: ext/opentelemetry-ext-pymongo/tests test-ext-wsgi: ext/opentelemetry-ext-wsgi/tests test-example-app: examples/opentelemetry-example-app/tests + test-example-basic-tracer: examples/basic_tracer/tests + test-example-http: examples/http/tests test-opentracing-shim: ext/opentelemetry-ext-opentracing-shim/tests commands_pre = @@ -46,6 +48,14 @@ commands_pre = example-app: pip install {toxinidir}/ext/opentelemetry-ext-http-requests example-app: pip install {toxinidir}/ext/opentelemetry-ext-wsgi example-app: pip install {toxinidir}/examples/opentelemetry-example-app + example-basic-tracer: pip install -e {toxinidir}/opentelemetry-api + example-basic-tracer: pip install -e {toxinidir}/opentelemetry-sdk + example-http: pip install -e {toxinidir}/opentelemetry-api + example-http: pip install -e {toxinidir}/opentelemetry-sdk + example-http: pip install -e {toxinidir}/ext/opentelemetry-ext-http-requests + example-http: pip install -e {toxinidir}/ext/opentelemetry-ext-wsgi + example-http: pip install -r {toxinidir}/examples/http/requirements.txt + ext: pip install {toxinidir}/opentelemetry-api wsgi: pip install {toxinidir}/ext/opentelemetry-ext-wsgi pymongo: pip install {toxinidir}/ext/opentelemetry-ext-pymongo @@ -122,7 +132,9 @@ commands = ext/opentelemetry-ext-pymongo/tests/ \ ext/opentelemetry-ext-wsgi/tests/ \ examples/opentelemetry-example-app/src/opentelemetry_example_app/ \ - examples/opentelemetry-example-app/tests/ + examples/opentelemetry-example-app/tests/ \ + examples/basic_tracer/ \ + examples/http/ flake8 . isort --check-only --diff --recursive .