Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add httpx tracing extension #1098

Merged
merged 1 commit into from
Jan 13, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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.
118 changes: 118 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,118 @@
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
44 changes: 44 additions & 0 deletions contrib/opencensus-ext-httpx/setup.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# 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 :: 3",
"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