diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_constants.py b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_constants.py index 8c0b9199f2a9..ad00666f0041 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_constants.py +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/azure/monitor/opentelemetry/exporter/_constants.py @@ -352,6 +352,7 @@ class _RP_Names(Enum): "opentelemetry-instrumentation-tornado", "opentelemetry-instrumentation-urllib", "opentelemetry.instrumentation.urllib3", + "opentelemetry.instrumentation.httpx", "opentelemetry.instrumentation.wsgi", ) diff --git a/sdk/monitor/azure-monitor-opentelemetry/CHANGELOG.md b/sdk/monitor/azure-monitor-opentelemetry/CHANGELOG.md index 67e21f475185..236ca51c0c0c 100644 --- a/sdk/monitor/azure-monitor-opentelemetry/CHANGELOG.md +++ b/sdk/monitor/azure-monitor-opentelemetry/CHANGELOG.md @@ -3,6 +3,8 @@ ## 1.8.10 (Unreleased) ### Features Added +- Add httpx instrumentation support + ([#47953](https://github.com/Azure/azure-sdk-for-python/pull/47953)) ### Breaking Changes diff --git a/sdk/monitor/azure-monitor-opentelemetry/README.md b/sdk/monitor/azure-monitor-opentelemetry/README.md index e74d0c656d79..ee2a93fed322 100644 --- a/sdk/monitor/azure-monitor-opentelemetry/README.md +++ b/sdk/monitor/azure-monitor-opentelemetry/README.md @@ -19,6 +19,7 @@ OpenTelemetry instrumentations allow automatic collection of requests sent from | [OpenTelemetry Django Instrumentation][ot_instrumentation_django] | [django][pypi_django] | [link][ot_instrumentation_django_version] | [OpenTelemetry FastApi Instrumentation][ot_instrumentation_fastapi] | [fastapi][pypi_fastapi] | [link][ot_instrumentation_fastapi_version] | [OpenTelemetry Flask Instrumentation][ot_instrumentation_flask] | [flask][pypi_flask] | [link][ot_instrumentation_flask_version] +| [OpenTelemetry Httpx Instrumentation][ot_instrumentation_httpx] | [httpx][pypi_httpx] | [link][ot_instrumentation_httpx_version] | [OpenTelemetry Psycopg2 Instrumentation][ot_instrumentation_psycopg2] | [psycopg2][pypi_psycopg2] | [link][ot_instrumentation_psycopg2_version] | [OpenTelemetry Requests Instrumentation][ot_instrumentation_requests] | [requests][pypi_requests] | [link][ot_instrumentation_requests_version] | [OpenTelemetry UrlLib Instrumentation][ot_instrumentation_urllib] | [urllib][pypi_urllib] | All @@ -243,6 +244,8 @@ contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additio [ot_instrumentation_fastapi_version]: https://github.com/open-telemetry/opentelemetry-python-contrib/blob/main/instrumentation/opentelemetry-instrumentation-fastapi/src/opentelemetry/instrumentation/fastapi/package.py#L16 [ot_instrumentation_flask]: https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-flask [ot_instrumentation_flask_version]: https://github.com/open-telemetry/opentelemetry-python-contrib/blob/main/instrumentation/opentelemetry-instrumentation-flask/src/opentelemetry/instrumentation/flask/package.py#L16 +[ot_instrumentation_httpx]: https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-httpx +[ot_instrumentation_httpx_version]: https://github.com/open-telemetry/opentelemetry-python-contrib/blob/main/instrumentation/opentelemetry-instrumentation-httpx/src/opentelemetry/instrumentation/httpx/package.py#L16 [ot_instrumentation_psycopg2]: https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-psycopg2 [ot_instrumentation_psycopg2_version]: https://github.com/open-telemetry/opentelemetry-python-contrib/blob/main/instrumentation/opentelemetry-instrumentation-psycopg2/src/opentelemetry/instrumentation/psycopg2/package.py#L16 [ot_instrumentation_requests]: https://github.com/open-telemetry/opentelemetry-python-contrib/tree/main/instrumentation/opentelemetry-instrumentation-requests @@ -260,6 +263,7 @@ contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additio [pypi_django]: https://pypi.org/project/Django/ [pypi_fastapi]: https://pypi.org/project/fastapi/ [pypi_flask]: https://pypi.org/project/Flask/ +[pypi_httpx]: https://pypi.org/project/httpx/ [pypi_psycopg2]: https://pypi.org/project/psycopg2/ [pypi_requests]: https://pypi.org/project/requests/ [pypi_urllib]: https://docs.python.org/3/library/urllib.html diff --git a/sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_constants.py b/sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_constants.py index 8e74c26db112..dde291d442dd 100644 --- a/sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_constants.py +++ b/sdk/monitor/azure-monitor-opentelemetry/azure/monitor/opentelemetry/_constants.py @@ -78,6 +78,7 @@ "django", "fastapi", "flask", + "httpx", "psycopg2", "requests", "urllib", diff --git a/sdk/monitor/azure-monitor-opentelemetry/dev_requirements.txt b/sdk/monitor/azure-monitor-opentelemetry/dev_requirements.txt index c2d665032413..a04a4e50aa62 100644 --- a/sdk/monitor/azure-monitor-opentelemetry/dev_requirements.txt +++ b/sdk/monitor/azure-monitor-opentelemetry/dev_requirements.txt @@ -6,4 +6,5 @@ flask psycopg2-binary requests urllib3 +httpx brotli diff --git a/sdk/monitor/azure-monitor-opentelemetry/samples/README.md b/sdk/monitor/azure-monitor-opentelemetry/samples/README.md index 0fcb0136ce6b..1b4f95fee6ef 100644 --- a/sdk/monitor/azure-monitor-opentelemetry/samples/README.md +++ b/sdk/monitor/azure-monitor-opentelemetry/samples/README.md @@ -33,6 +33,7 @@ For guidance on the samples README, visit the [sample guide](https://github.com/ |[tracing/db_psycopg2.py][db_psycopg2] | Instrument the PsycoPG2 library | |[tracing/http_fastapi.py][http_fastapi] | Instrument a FastAPI app | |[tracing/http_flask.py][http_flask] | Instrument a Flask app | +|[tracing/http_httpx.py][http_httpx] | Instrument the HTTPX library | |[tracing/http_requests.py][http_requests] | Instrument the Requests library | |[tracing/http_urllib.py][http_urllib] | Instrument the URLLib library | |[tracing/http_urllib3.py][http_urllib3] | Instrument the URLLib library | @@ -83,6 +84,7 @@ To learn more, see the [Azure Monitor OpenTelemetry Distro documentation][distro [db_psycopg2]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/tracing/db_psycopg2.py [http_fastapi]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/tracing/http_fastapi.py [http_flask]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/tracing/http_flask.py +[http_httpx]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/tracing/http_httpx.py [http_requests]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/tracing/http_requests.py [http_urllib]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/tracing/http_urllib.py [http_urllib3]: https://github.com/Azure/azure-sdk-for-python/tree/main/sdk/monitor/azure-monitor-opentelemetry/samples/tracing/http_urllib3.py diff --git a/sdk/monitor/azure-monitor-opentelemetry/samples/tracing/http_httpx.py b/sdk/monitor/azure-monitor-opentelemetry/samples/tracing/http_httpx.py new file mode 100644 index 000000000000..613c5e4aee3a --- /dev/null +++ b/sdk/monitor/azure-monitor-opentelemetry/samples/tracing/http_httpx.py @@ -0,0 +1,31 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License in the project root for +# license information. +# -------------------------------------------------------------------------- + +import logging + +import httpx +from opentelemetry import trace +from azure.monitor.opentelemetry import configure_azure_monitor + +logger = logging.getLogger(__name__) + +# Configure Azure monitor collection telemetry pipeline +configure_azure_monitor() + +client = httpx.Client() + +tracer = trace.get_tracer(__name__) +with tracer.start_as_current_span("Request parent span") as span: + try: + # Requests made using the httpx library will be automatically captured + response = client.get("https://www.example.org/") + logger.warning("Request sent") + except Exception as ex: # pylint: disable=broad-exception-caught + # If an exception occurs, this can be manually recorded on the parent span + span.set_attribute("status", "exception") + span.record_exception(ex) + +input() diff --git a/sdk/monitor/azure-monitor-opentelemetry/samples/tracing/instrumentation_options.py b/sdk/monitor/azure-monitor-opentelemetry/samples/tracing/instrumentation_options.py index 149601564518..db5cff77dc8d 100644 --- a/sdk/monitor/azure-monitor-opentelemetry/samples/tracing/instrumentation_options.py +++ b/sdk/monitor/azure-monitor-opentelemetry/samples/tracing/instrumentation_options.py @@ -17,5 +17,6 @@ "requests": {"enabled": True}, "urllib": {"enabled": False}, "urllib3": {"enabled": True}, + "httpx": {"enabled": True}, } ) diff --git a/sdk/monitor/azure-monitor-opentelemetry/setup.py b/sdk/monitor/azure-monitor-opentelemetry/setup.py index 857f4205add9..b0b88c3ff9c9 100644 --- a/sdk/monitor/azure-monitor-opentelemetry/setup.py +++ b/sdk/monitor/azure-monitor-opentelemetry/setup.py @@ -93,6 +93,7 @@ "opentelemetry-instrumentation-urllib>=0.64b0,<0.65.0", "opentelemetry-instrumentation-urllib3>=0.64b0,<0.65.0", "opentelemetry-instrumentation-logging>=0.64b0,<0.65.0", + "opentelemetry-instrumentation-httpx>=0.64b0,<0.65.0", "opentelemetry-resource-detector-azure<1.0.0,>=0.1.5", ], entry_points={ diff --git a/sdk/monitor/azure-monitor-opentelemetry/tests/instrumentation/test_httpx.py b/sdk/monitor/azure-monitor-opentelemetry/tests/instrumentation/test_httpx.py new file mode 100644 index 000000000000..cc74c0ea5d09 --- /dev/null +++ b/sdk/monitor/azure-monitor-opentelemetry/tests/instrumentation/test_httpx.py @@ -0,0 +1,20 @@ +# ------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. See License in the project root for +# license information. +# -------------------------------------------------------------------------- + +import unittest + +from opentelemetry.instrumentation.httpx import ( + HTTPXClientInstrumentor, +) + + +class TestHttpxInstrumentation(unittest.TestCase): + def test_instrument(self): + try: + HTTPXClientInstrumentor().instrument() + except Exception as ex: # pylint: disable=broad-except + print(ex) + self.fail(f"Unexpected exception raised when instrumenting {HTTPXClientInstrumentor.__name__}") diff --git a/sdk/monitor/azure-monitor-opentelemetry/tests/utils/test_configurations.py b/sdk/monitor/azure-monitor-opentelemetry/tests/utils/test_configurations.py index 81caeeda6faf..fab94569b51a 100644 --- a/sdk/monitor/azure-monitor-opentelemetry/tests/utils/test_configurations.py +++ b/sdk/monitor/azure-monitor-opentelemetry/tests/utils/test_configurations.py @@ -111,6 +111,7 @@ def test_get_configurations(self, resource_create_mock): "requests": {"enabled": True}, "urllib": {"enabled": True}, "urllib3": {"enabled": True}, + "httpx": {"enabled": True}, "previewlib1": {"enabled": False}, "previewlib2": {"enabled": False}, }, @@ -145,6 +146,7 @@ def test_get_configurations_defaults(self, resource_create_mock): "requests": {"enabled": True}, "urllib": {"enabled": True}, "urllib3": {"enabled": True}, + "httpx": {"enabled": True}, }, ) self.assertEqual(configurations["resource"].attributes, TEST_DEFAULT_RESOURCE.attributes) @@ -193,6 +195,7 @@ def test_get_configurations_env_vars(self, resource_create_mock): "requests": {"enabled": False}, "urllib": {"enabled": True}, "urllib3": {"enabled": True}, + "httpx": {"enabled": True}, }, ) self.assertEqual(configurations["resource"].attributes, TEST_DEFAULT_RESOURCE.attributes) @@ -276,6 +279,7 @@ def test_merge_instrumentation_options_conflict(self, resource_create_mock): "requests": {"enabled": True}, "urllib": {"enabled": True}, "urllib3": {"enabled": False}, + "httpx": {"enabled": True}, }, ) @@ -310,6 +314,7 @@ def test_merge_instrumentation_options_extra_args(self, resource_create_mock): "requests": {"enabled": True}, "urllib": {"enabled": True}, "urllib3": {"enabled": True}, + "httpx": {"enabled": True}, }, ) @@ -455,6 +460,7 @@ def test_get_configurations_env_vars_rate_limited(self, resource_create_mock): "requests": {"enabled": False}, "urllib": {"enabled": True}, "urllib3": {"enabled": True}, + "httpx": {"enabled": True}, }, ) self.assertEqual(configurations["resource"].attributes, TEST_DEFAULT_RESOURCE.attributes) @@ -482,6 +488,7 @@ def test_get_configurations_rate_limited_sampler_param(self, resource_create_moc "requests": {"enabled": True}, "urllib": {"enabled": True}, "urllib3": {"enabled": True}, + "httpx": {"enabled": True}, }, ) self.assertEqual(configurations["resource"].attributes, TEST_DEFAULT_RESOURCE.attributes) @@ -520,6 +527,7 @@ def test_get_configurations_env_vars_no_preference(self, resource_create_mock): "requests": {"enabled": False}, "urllib": {"enabled": True}, "urllib3": {"enabled": True}, + "httpx": {"enabled": True}, }, ) self.assertEqual(configurations["resource"].attributes, TEST_DEFAULT_RESOURCE.attributes) @@ -558,6 +566,7 @@ def test_get_configurations_env_vars_check_default(self, resource_create_mock): "requests": {"enabled": False}, "urllib": {"enabled": True}, "urllib3": {"enabled": True}, + "httpx": {"enabled": True}, }, ) self.assertEqual(configurations["resource"].attributes, TEST_DEFAULT_RESOURCE.attributes) @@ -597,6 +606,7 @@ def test_get_configurations_env_vars_fixed_percentage(self, resource_create_mock "requests": {"enabled": False}, "urllib": {"enabled": True}, "urllib3": {"enabled": True}, + "httpx": {"enabled": True}, }, ) self.assertEqual(configurations["resource"].attributes, TEST_DEFAULT_RESOURCE.attributes) @@ -635,6 +645,7 @@ def test_get_configurations_env_vars_always_on(self, resource_create_mock): "requests": {"enabled": False}, "urllib": {"enabled": True}, "urllib3": {"enabled": True}, + "httpx": {"enabled": True}, }, ) self.assertEqual(configurations["resource"].attributes, TEST_DEFAULT_RESOURCE.attributes) diff --git a/shared_requirements.txt b/shared_requirements.txt index 7be249c3bd61..36de241d8041 100644 --- a/shared_requirements.txt +++ b/shared_requirements.txt @@ -57,6 +57,7 @@ opentelemetry-instrumentation-psycopg2 opentelemetry-instrumentation-requests opentelemetry-instrumentation-urllib opentelemetry-instrumentation-urllib3 +opentelemetry-instrumentation-httpx opentelemetry-resource-detector-azure azure-nspkg azure-ai-nspkg