Skip to content

Commit 271767a

Browse files
committed
Add httpx tracing ext
1 parent 5dd6f2f commit 271767a

File tree

12 files changed

+634
-0
lines changed

12 files changed

+634
-0
lines changed

README.rst

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,7 @@ OpenCensus supports integration with popular web frameworks, client libraries an
199199
- `Google Cloud Client Libraries`_
200200
- `gRPC`_
201201
- `httplib`_
202+
- `httpx`_
202203
- `logging`_
203204
- `MySQL`_
204205
- `PostgreSQL`_
@@ -244,6 +245,7 @@ Trace Exporter
244245
.. _Google Cloud Client Libraries: https://github.com/census-instrumentation/opencensus-python/tree/master/contrib/opencensus-ext-google-cloud-clientlibs
245246
.. _gRPC: https://github.com/census-instrumentation/opencensus-python/tree/master/contrib/opencensus-ext-grpc
246247
.. _httplib: https://github.com/census-instrumentation/opencensus-python/tree/master/contrib/opencensus-ext-httplib
248+
.. _httpx: https://github.com/census-instrumentation/opencensus-python/tree/master/contrib/opencensus-ext-httpx
247249
.. _Jaeger: https://github.com/census-instrumentation/opencensus-python/tree/master/contrib/opencensus-ext-jaeger
248250
.. _logging: https://github.com/census-instrumentation/opencensus-python/tree/master/contrib/opencensus-ext-logging
249251
.. _MySQL: https://github.com/census-instrumentation/opencensus-python/tree/master/contrib/opencensus-ext-mysql

contrib/opencensus-ext-httpx/opencensus/__init__.py

Whitespace-only changes.

contrib/opencensus-ext-httpx/opencensus/ext/__init__.py

Whitespace-only changes.

contrib/opencensus-ext-httpx/opencensus/ext/httpx/__init__.py

Whitespace-only changes.
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
import logging
2+
3+
import httpx
4+
import wrapt
5+
6+
from opencensus.trace import (
7+
attributes_helper,
8+
exceptions_status,
9+
execution_context,
10+
integrations,
11+
)
12+
from opencensus.trace import span as span_module
13+
from opencensus.trace import utils
14+
15+
try:
16+
from urllib.parse import urlparse
17+
except ImportError:
18+
from urlparse import urlparse
19+
20+
21+
log = logging.getLogger(__name__)
22+
23+
MODULE_NAME = "httpx"
24+
25+
HTTP_HOST = attributes_helper.COMMON_ATTRIBUTES["HTTP_HOST"]
26+
HTTP_METHOD = attributes_helper.COMMON_ATTRIBUTES["HTTP_METHOD"]
27+
HTTP_PATH = attributes_helper.COMMON_ATTRIBUTES["HTTP_PATH"]
28+
HTTP_ROUTE = attributes_helper.COMMON_ATTRIBUTES["HTTP_ROUTE"]
29+
HTTP_STATUS_CODE = attributes_helper.COMMON_ATTRIBUTES["HTTP_STATUS_CODE"]
30+
HTTP_URL = attributes_helper.COMMON_ATTRIBUTES["HTTP_URL"]
31+
32+
33+
def trace_integration(tracer=None):
34+
"""Wrap the requests library to trace it."""
35+
log.info("Integrated module: {}".format(MODULE_NAME))
36+
37+
if tracer is not None:
38+
# The execution_context tracer should never be None - if it has not
39+
# been set it returns a no-op tracer. Most code in this library does
40+
# not handle None being used in the execution context.
41+
execution_context.set_opencensus_tracer(tracer)
42+
43+
wrapt.wrap_function_wrapper(MODULE_NAME, "Client.request", wrap_client_request)
44+
# pylint: disable=protected-access
45+
integrations.add_integration(integrations._Integrations.HTTPX)
46+
47+
48+
def wrap_client_request(wrapped, instance, args, kwargs):
49+
"""Wrap the session function to trace it."""
50+
# Check if request was sent from an exporter. If so, do not wrap.
51+
if execution_context.is_exporter():
52+
return wrapped(*args, **kwargs)
53+
54+
method = kwargs.get("method") or args[0]
55+
url = kwargs.get("url") or args[1]
56+
57+
excludelist_hostnames = execution_context.get_opencensus_attr(
58+
"excludelist_hostnames"
59+
)
60+
parsed_url = urlparse(url)
61+
if parsed_url.port is None:
62+
dest_url = parsed_url.hostname
63+
else:
64+
dest_url = "{}:{}".format(parsed_url.hostname, parsed_url.port)
65+
if utils.disable_tracing_hostname(dest_url, excludelist_hostnames):
66+
return wrapped(*args, **kwargs)
67+
68+
path = parsed_url.path if parsed_url.path else "/"
69+
70+
_tracer = execution_context.get_opencensus_tracer()
71+
_span = _tracer.start_span()
72+
73+
_span.name = "{}".format(path)
74+
_span.span_kind = span_module.SpanKind.CLIENT
75+
76+
try:
77+
tracer_headers = _tracer.propagator.to_headers(_tracer.span_context)
78+
kwargs.setdefault("headers", {}).update(tracer_headers)
79+
except Exception: # pragma: NO COVER
80+
pass
81+
82+
# Add the component type to attributes
83+
_tracer.add_attribute_to_current_span("component", "HTTP")
84+
85+
# Add the requests host to attributes
86+
_tracer.add_attribute_to_current_span(HTTP_HOST, dest_url)
87+
88+
# Add the requests method to attributes
89+
_tracer.add_attribute_to_current_span(HTTP_METHOD, method.upper())
90+
91+
# Add the requests path to attributes
92+
_tracer.add_attribute_to_current_span(HTTP_PATH, path)
93+
94+
# Add the requests url to attributes
95+
_tracer.add_attribute_to_current_span(HTTP_URL, url)
96+
97+
try:
98+
result = wrapped(*args, **kwargs)
99+
except httpx.TimeoutException:
100+
_span.set_status(exceptions_status.TIMEOUT)
101+
raise
102+
except httpx.InvalidURL:
103+
_span.set_status(exceptions_status.INVALID_URL)
104+
raise
105+
except Exception as e:
106+
_span.set_status(exceptions_status.unknown(e))
107+
raise
108+
else:
109+
# Add the status code to attributes
110+
_tracer.add_attribute_to_current_span(HTTP_STATUS_CODE, result.status_code)
111+
_span.set_status(utils.status_from_http_code(result.status_code))
112+
return result
113+
finally:
114+
_tracer.end_span()
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
[bdist_wheel]
2+
universal = 1
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
# Copyright 2019, OpenCensus 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+
from setuptools import find_packages, setup
16+
from version import __version__
17+
18+
setup(
19+
name='opencensus-ext-httpx',
20+
version=__version__, # noqa
21+
author='Michał Klich',
22+
author_email='michal@klichx.dev',
23+
classifiers=[
24+
'Intended Audience :: Developers',
25+
'Development Status :: 3 - Alpha',
26+
'Intended Audience :: Developers',
27+
'License :: OSI Approved :: Apache Software License',
28+
'Programming Language :: Python',
29+
'Programming Language :: Python :: 2',
30+
'Programming Language :: Python :: 2.7',
31+
'Programming Language :: Python :: 3',
32+
'Programming Language :: Python :: 3.4',
33+
'Programming Language :: Python :: 3.5',
34+
'Programming Language :: Python :: 3.6',
35+
'Programming Language :: Python :: 3.7',
36+
'Programming Language :: Python :: 3.8',
37+
'Programming Language :: Python :: 3.9',
38+
],
39+
description='OpenCensus HTTPX Integration',
40+
include_package_data=True,
41+
long_description='',
42+
install_requires=[
43+
'opencensus >= 0.9.dev0, < 1.0.0',
44+
'httpx >= 0.22.0'
45+
],
46+
extras_require={},
47+
license='Apache-2.0',
48+
packages=find_packages(exclude=('tests',)),
49+
namespace_packages=[],
50+
url='',
51+
zip_safe=False,
52+
)

0 commit comments

Comments
 (0)