Skip to content

Commit

Permalink
Add httpx tracing ext
Browse files Browse the repository at this point in the history
  • Loading branch information
inirudebwoy committed Jan 11, 2023
1 parent 2e08df5 commit 10f3d4e
Show file tree
Hide file tree
Showing 13 changed files with 658 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.rst
Original file line number Diff line number Diff line change
Expand Up @@ -199,6 +199,7 @@ OpenCensus supports integration with popular web frameworks, client libraries an
- `Google Cloud Client Libraries`_
- `gRPC`_
- `httplib`_
- `httpx`_
- `logging`_
- `MySQL`_
- `PostgreSQL`_
Expand Down Expand Up @@ -244,6 +245,7 @@ Trace Exporter
.. _Google Cloud Client Libraries: https://github.com/census-instrumentation/opencensus-python/tree/master/contrib/opencensus-ext-google-cloud-clientlibs
.. _gRPC: https://github.com/census-instrumentation/opencensus-python/tree/master/contrib/opencensus-ext-grpc
.. _httplib: https://github.com/census-instrumentation/opencensus-python/tree/master/contrib/opencensus-ext-httplib
.. _httpx: https://github.com/census-instrumentation/opencensus-python/tree/master/contrib/opencensus-ext-httpx
.. _Jaeger: https://github.com/census-instrumentation/opencensus-python/tree/master/contrib/opencensus-ext-jaeger
.. _logging: https://github.com/census-instrumentation/opencensus-python/tree/master/contrib/opencensus-ext-logging
.. _MySQL: https://github.com/census-instrumentation/opencensus-python/tree/master/contrib/opencensus-ext-mysql
Expand Down
42 changes: 42 additions & 0 deletions contrib/opencensus-ext-httpx/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
OpenCensus httpx Integration
============================================================================

|pypi|

.. |pypi| image:: https://badge.fury.io/py/opencensus-ext-httpx.svg
:target: https://pypi.org/project/opencensus-ext-httpx/

OpenCensus can trace HTTP requests made with the `httpx package <https://www.python-httpx.org>`_. The request URL,
method, and status will be collected.

You can enable httpx integration by specifying ``'httpx'`` to ``trace_integrations``.

Only the hostname must be specified if only the hostname is specified in the URL request.


Installation
------------

::

pip install opencensus-ext-httpx

Usage
-----

.. code:: python
import httpx
from opencensus.trace import config_integration
from opencensus.trace.tracer import Tracer
if __name__ == '__main__':
config_integration.trace_integrations(['httpx'])
tracer = Tracer()
with tracer.span(name='parent'):
response = httpx.get(url='https://www.example.org')
References
----------

* `OpenCensus Project <https://opencensus.io/>`_
Empty file.
Empty file.
Empty file.
114 changes: 114 additions & 0 deletions contrib/opencensus-ext-httpx/opencensus/ext/httpx/trace.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
import logging

import httpx
import wrapt

from opencensus.trace import (
attributes_helper,
exceptions_status,
execution_context,
integrations,
)
from opencensus.trace import span as span_module
from opencensus.trace import utils

try:
from urllib.parse import urlparse
except ImportError:
from urlparse import urlparse


log = logging.getLogger(__name__)

MODULE_NAME = "httpx"

HTTP_HOST = attributes_helper.COMMON_ATTRIBUTES["HTTP_HOST"]
HTTP_METHOD = attributes_helper.COMMON_ATTRIBUTES["HTTP_METHOD"]
HTTP_PATH = attributes_helper.COMMON_ATTRIBUTES["HTTP_PATH"]
HTTP_ROUTE = attributes_helper.COMMON_ATTRIBUTES["HTTP_ROUTE"]
HTTP_STATUS_CODE = attributes_helper.COMMON_ATTRIBUTES["HTTP_STATUS_CODE"]
HTTP_URL = attributes_helper.COMMON_ATTRIBUTES["HTTP_URL"]


def trace_integration(tracer=None):
"""Wrap the requests library to trace it."""
log.info("Integrated module: {}".format(MODULE_NAME))

if tracer is not None:
# The execution_context tracer should never be None - if it has not
# been set it returns a no-op tracer. Most code in this library does
# not handle None being used in the execution context.
execution_context.set_opencensus_tracer(tracer)

wrapt.wrap_function_wrapper(MODULE_NAME, "Client.request", wrap_client_request)
# pylint: disable=protected-access
integrations.add_integration(integrations._Integrations.HTTPX)


def wrap_client_request(wrapped, instance, args, kwargs):
"""Wrap the session function to trace it."""
# Check if request was sent from an exporter. If so, do not wrap.
if execution_context.is_exporter():
return wrapped(*args, **kwargs)

method = kwargs.get("method") or args[0]
url = kwargs.get("url") or args[1]

excludelist_hostnames = execution_context.get_opencensus_attr(
"excludelist_hostnames"
)
parsed_url = urlparse(url)
if parsed_url.port is None:
dest_url = parsed_url.hostname
else:
dest_url = "{}:{}".format(parsed_url.hostname, parsed_url.port)
if utils.disable_tracing_hostname(dest_url, excludelist_hostnames):
return wrapped(*args, **kwargs)

path = parsed_url.path if parsed_url.path else "/"

_tracer = execution_context.get_opencensus_tracer()
_span = _tracer.start_span()

_span.name = "{}".format(path)
_span.span_kind = span_module.SpanKind.CLIENT

try:
tracer_headers = _tracer.propagator.to_headers(_tracer.span_context)
kwargs.setdefault("headers", {}).update(tracer_headers)
except Exception: # pragma: NO COVER
pass

# Add the component type to attributes
_tracer.add_attribute_to_current_span("component", "HTTP")

# Add the requests host to attributes
_tracer.add_attribute_to_current_span(HTTP_HOST, dest_url)

# Add the requests method to attributes
_tracer.add_attribute_to_current_span(HTTP_METHOD, method.upper())

# Add the requests path to attributes
_tracer.add_attribute_to_current_span(HTTP_PATH, path)

# Add the requests url to attributes
_tracer.add_attribute_to_current_span(HTTP_URL, url)

try:
result = wrapped(*args, **kwargs)
except httpx.TimeoutException:
_span.set_status(exceptions_status.TIMEOUT)
raise
except httpx.InvalidURL:
_span.set_status(exceptions_status.INVALID_URL)
raise
except Exception as e:
_span.set_status(exceptions_status.unknown(e))
raise
else:
# Add the status code to attributes
_tracer.add_attribute_to_current_span(HTTP_STATUS_CODE, result.status_code)
_span.set_status(utils.status_from_http_code(result.status_code))
return result
finally:
_tracer.end_span()
2 changes: 2 additions & 0 deletions contrib/opencensus-ext-httpx/setup.cfg
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
[bdist_wheel]
universal = 1
49 changes: 49 additions & 0 deletions contrib/opencensus-ext-httpx/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Copyright 2019, OpenCensus 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.

from setuptools import find_packages, setup
from version import __version__

setup(
name="opencensus-ext-httpx",
version=__version__, # noqa
author="Michał Klich",
author_email="[email protected]",
classifiers=[
"Intended Audience :: Developers",
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"License :: OSI Approved :: Apache Software License",
"Programming Language :: Python",
"Programming Language :: Python :: 2",
"Programming Language :: Python :: 2.7",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.4",
"Programming Language :: Python :: 3.5",
"Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
],
description="OpenCensus HTTPX Integration",
include_package_data=True,
long_description="",
install_requires=["opencensus >= 0.12.dev0, < 1.0.0", "httpx >= 0.22.0"],
extras_require={},
license="Apache-2.0",
packages=find_packages(exclude=("tests",)),
namespace_packages=[],
url="",
zip_safe=False,
)
Loading

0 comments on commit 10f3d4e

Please sign in to comment.