Skip to content
This repository was archived by the owner on Sep 17, 2025. It is now read-only.

Commit 642df18

Browse files
authored
Add httpx tracing ext (#1098)
1 parent a54cd2e commit 642df18

File tree

13 files changed

+689
-2
lines changed

13 files changed

+689
-2
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
Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
OpenCensus httpx Integration
2+
============================================================================
3+
4+
|pypi|
5+
6+
.. |pypi| image:: https://badge.fury.io/py/opencensus-ext-httpx.svg
7+
:target: https://pypi.org/project/opencensus-ext-httpx/
8+
9+
OpenCensus can trace HTTP requests made with the `httpx package <https://www.python-httpx.org>`_. The request URL,
10+
method, and status will be collected.
11+
12+
You can enable httpx integration by specifying ``'httpx'`` to ``trace_integrations``.
13+
14+
Only the hostname must be specified if only the hostname is specified in the URL request.
15+
16+
17+
Installation
18+
------------
19+
20+
::
21+
22+
pip install opencensus-ext-httpx
23+
24+
Usage
25+
-----
26+
27+
.. code:: python
28+
29+
import httpx
30+
from opencensus.trace import config_integration
31+
from opencensus.trace.tracer import Tracer
32+
33+
if __name__ == '__main__':
34+
config_integration.trace_integrations(['httpx'])
35+
tracer = Tracer()
36+
with tracer.span(name='parent'):
37+
response = httpx.get(url='https://www.example.org')
38+
39+
References
40+
----------
41+
42+
* `OpenCensus Project <https://opencensus.io/>`_

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: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
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(
44+
MODULE_NAME, "Client.request", wrap_client_request
45+
)
46+
# pylint: disable=protected-access
47+
integrations.add_integration(integrations._Integrations.HTTPX)
48+
49+
50+
def wrap_client_request(wrapped, instance, args, kwargs):
51+
"""Wrap the session function to trace it."""
52+
# Check if request was sent from an exporter. If so, do not wrap.
53+
if execution_context.is_exporter():
54+
return wrapped(*args, **kwargs)
55+
56+
method = kwargs.get("method") or args[0]
57+
url = kwargs.get("url") or args[1]
58+
59+
excludelist_hostnames = execution_context.get_opencensus_attr(
60+
"excludelist_hostnames"
61+
)
62+
parsed_url = urlparse(url)
63+
if parsed_url.port is None:
64+
dest_url = parsed_url.hostname
65+
else:
66+
dest_url = "{}:{}".format(parsed_url.hostname, parsed_url.port)
67+
if utils.disable_tracing_hostname(dest_url, excludelist_hostnames):
68+
return wrapped(*args, **kwargs)
69+
70+
path = parsed_url.path if parsed_url.path else "/"
71+
72+
_tracer = execution_context.get_opencensus_tracer()
73+
_span = _tracer.start_span()
74+
75+
_span.name = "{}".format(path)
76+
_span.span_kind = span_module.SpanKind.CLIENT
77+
78+
try:
79+
tracer_headers = _tracer.propagator.to_headers(_tracer.span_context)
80+
kwargs.setdefault("headers", {}).update(tracer_headers)
81+
except Exception: # pragma: NO COVER
82+
pass
83+
84+
# Add the component type to attributes
85+
_tracer.add_attribute_to_current_span("component", "HTTP")
86+
87+
# Add the requests host to attributes
88+
_tracer.add_attribute_to_current_span(HTTP_HOST, dest_url)
89+
90+
# Add the requests method to attributes
91+
_tracer.add_attribute_to_current_span(HTTP_METHOD, method.upper())
92+
93+
# Add the requests path to attributes
94+
_tracer.add_attribute_to_current_span(HTTP_PATH, path)
95+
96+
# Add the requests url to attributes
97+
_tracer.add_attribute_to_current_span(HTTP_URL, url)
98+
99+
try:
100+
result = wrapped(*args, **kwargs)
101+
except httpx.TimeoutException:
102+
_span.set_status(exceptions_status.TIMEOUT)
103+
raise
104+
except httpx.InvalidURL:
105+
_span.set_status(exceptions_status.INVALID_URL)
106+
raise
107+
except Exception as e:
108+
_span.set_status(exceptions_status.unknown(e))
109+
raise
110+
else:
111+
# Add the status code to attributes
112+
_tracer.add_attribute_to_current_span(
113+
HTTP_STATUS_CODE, result.status_code
114+
)
115+
_span.set_status(utils.status_from_http_code(result.status_code))
116+
return result
117+
finally:
118+
_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: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
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+
from setuptools import find_packages, setup
15+
16+
from version import __version__
17+
18+
setup(
19+
name="opencensus-ext-httpx",
20+
version=__version__, # noqa
21+
author="Michał Klich",
22+
author_email="[email protected]",
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 :: 3",
30+
"Programming Language :: Python :: 3.7",
31+
"Programming Language :: Python :: 3.8",
32+
"Programming Language :: Python :: 3.9",
33+
],
34+
description="OpenCensus HTTPX Integration",
35+
include_package_data=True,
36+
long_description="",
37+
install_requires=["opencensus >= 0.12.dev0, < 1.0.0", "httpx >= 0.22.0"],
38+
extras_require={},
39+
license="Apache-2.0",
40+
packages=find_packages(exclude=("tests",)),
41+
namespace_packages=[],
42+
url="",
43+
zip_safe=False,
44+
)

0 commit comments

Comments
 (0)