Set schema_url on all tracers and meters (#1977)

This commit is contained in:
Liudmila Molkova
2023-11-06 11:39:51 -08:00
committed by GitHub
parent eb6024ca31
commit 4f6618324d
49 changed files with 504 additions and 128 deletions

View File

@ -11,10 +11,13 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#1800](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1800))
### Added
- `opentelemetry-instrumentation-botocore` Include SNS topic ARN as a span attribute with name `messaging.destination.name` to uniquely identify the SNS topic
([#1995](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1995))
- `opentelemetry-instrumentation-system-metrics` Add support for collecting process metrics
([#1948](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1948))
- Added schema_url (`"https://opentelemetry.io/schemas/1.11.0"`) to all metrics and traces
([#1977](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/1977))
### Fixed

View File

@ -64,7 +64,10 @@ class AioPikaInstrumentor(BaseInstrumentor):
def _instrument(self, **kwargs):
tracer_provider = kwargs.get("tracer_provider", None)
tracer = trace.get_tracer(
_INSTRUMENTATION_MODULE_NAME, __version__, tracer_provider
_INSTRUMENTATION_MODULE_NAME,
__version__,
tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
self._instrument_queue(tracer)
self._instrument_exchange(tracer)

View File

@ -163,7 +163,12 @@ def create_trace_config(
# Explicitly specify the type for the `request_hook` and `response_hook` param and rtype to work
# around this issue.
tracer = get_tracer(__name__, __version__, tracer_provider)
tracer = get_tracer(
__name__,
__version__,
tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
def _end_trace(trace_config_ctx: types.SimpleNamespace):
context_api.detach(trace_config_ctx.token)

View File

@ -134,6 +134,21 @@ class TestAioHttpIntegration(TestBase):
self.memory_exporter.clear()
def test_schema_url(self):
with self.subTest(status_code=200):
host, port = self._http_request(
trace_config=aiohttp_client.create_trace_config(),
url="/test-path?query=param#foobar",
status_code=200,
)
span = self.memory_exporter.get_finished_spans()[0]
self.assertEqual(
span.instrumentation_info.schema_url,
"https://opentelemetry.io/schemas/1.11.0",
)
self.memory_exporter.clear()
def test_not_recording(self):
mock_tracer = mock.Mock()
mock_span = mock.Mock()

View File

@ -495,9 +495,19 @@ class OpenTelemetryMiddleware:
meter=None,
):
self.app = guarantee_single_callable(app)
self.tracer = trace.get_tracer(__name__, __version__, tracer_provider)
self.tracer = trace.get_tracer(
__name__,
__version__,
tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
self.meter = (
get_meter(__name__, __version__, meter_provider)
get_meter(
__name__,
__version__,
meter_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
if meter is None
else meter
)

View File

@ -107,7 +107,12 @@ class AsyncPGInstrumentor(BaseInstrumentor):
def _instrument(self, **kwargs):
tracer_provider = kwargs.get("tracer_provider")
self._tracer = trace.get_tracer(__name__, __version__, tracer_provider)
self._tracer = trace.get_tracer(
__name__,
__version__,
tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
for method in [
"Connection.execute",

View File

@ -321,7 +321,12 @@ def _instrument(
except (IndexError, KeyError, TypeError):
span_kind = SpanKind.SERVER
tracer = get_tracer(__name__, __version__, tracer_provider)
tracer = get_tracer(
__name__,
__version__,
tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
with tracer.start_as_current_span(
name=orig_handler_name,

View File

@ -91,7 +91,10 @@ class BotoInstrumentor(BaseInstrumentor):
# pylint: disable=attribute-defined-outside-init
self._tracer = get_tracer(
__name__, __version__, kwargs.get("tracer_provider")
__name__,
__version__,
kwargs.get("tracer_provider"),
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
wrap_function_wrapper(

View File

@ -422,7 +422,10 @@ class Boto3SQSInstrumentor(BaseInstrumentor):
"tracer_provider"
)
self._tracer: Tracer = trace.get_tracer(
__name__, __version__, self._tracer_provider
__name__,
__version__,
self._tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
self._wrap_client_creation()

View File

@ -127,7 +127,10 @@ class BotocoreInstrumentor(BaseInstrumentor):
def _instrument(self, **kwargs):
# pylint: disable=attribute-defined-outside-init
self._tracer = get_tracer(
__name__, __version__, kwargs.get("tracer_provider")
__name__,
__version__,
kwargs.get("tracer_provider"),
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
self.request_hook = kwargs.get("request_hook")

View File

@ -55,7 +55,12 @@ def _instrument(tracer_provider, include_db_statement=False):
Wraps cassandra.cluster.Session.execute_async().
"""
tracer = trace.get_tracer(__name__, __version__, tracer_provider)
tracer = trace.get_tracer(
__name__,
__version__,
tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
name = "Cassandra"
def _traced_execute_async(func, instance, args, kwargs):
@ -65,7 +70,10 @@ def _instrument(tracer_provider, include_db_statement=False):
if span.is_recording():
span.set_attribute(SpanAttributes.DB_NAME, instance.keyspace)
span.set_attribute(SpanAttributes.DB_SYSTEM, "cassandra")
span.set_attribute(SpanAttributes.NET_PEER_NAME, instance.cluster.contact_points)
span.set_attribute(
SpanAttributes.NET_PEER_NAME,
instance.cluster.contact_points,
)
if include_db_statement:
query = args[0]
@ -74,7 +82,9 @@ def _instrument(tracer_provider, include_db_statement=False):
response = func(*args, **kwargs)
return response
wrap_function_wrapper("cassandra.cluster", "Session.execute_async", _traced_execute_async)
wrap_function_wrapper(
"cassandra.cluster", "Session.execute_async", _traced_execute_async
)
class CassandraInstrumentor(BaseInstrumentor):

View File

@ -46,15 +46,25 @@ class TestCassandraIntegration(TestBase):
def test_instrument_uninstrument(self):
instrumentation = CassandraInstrumentor()
instrumentation.instrument()
self.assertTrue(isinstance(cassandra.cluster.Session.execute_async, BoundFunctionWrapper))
self.assertTrue(
isinstance(
cassandra.cluster.Session.execute_async, BoundFunctionWrapper
)
)
instrumentation.uninstrument()
self.assertFalse(isinstance(cassandra.cluster.Session.execute_async, BoundFunctionWrapper))
self.assertFalse(
isinstance(
cassandra.cluster.Session.execute_async, BoundFunctionWrapper
)
)
@mock.patch("cassandra.cluster.Cluster.connect")
@mock.patch("cassandra.cluster.Session.__init__")
@mock.patch("cassandra.cluster.Session._create_response_future")
def test_instrumentor(self, mock_create_response_future, mock_session_init, mock_connect):
def test_instrumentor(
self, mock_create_response_future, mock_session_init, mock_connect
):
mock_create_response_future.return_value = mock.Mock()
mock_session_init.return_value = None
mock_connect.return_value = cassandra.cluster.Session()
@ -85,7 +95,9 @@ class TestCassandraIntegration(TestBase):
@mock.patch("cassandra.cluster.Cluster.connect")
@mock.patch("cassandra.cluster.Session.__init__")
@mock.patch("cassandra.cluster.Session._create_response_future")
def test_custom_tracer_provider(self, mock_create_response_future, mock_session_init, mock_connect):
def test_custom_tracer_provider(
self, mock_create_response_future, mock_session_init, mock_connect
):
mock_create_response_future.return_value = mock.Mock()
mock_session_init.return_value = None
mock_connect.return_value = cassandra.cluster.Session()
@ -107,7 +119,9 @@ class TestCassandraIntegration(TestBase):
@mock.patch("cassandra.cluster.Cluster.connect")
@mock.patch("cassandra.cluster.Session.__init__")
@mock.patch("cassandra.cluster.Session._create_response_future")
def test_instrument_connection_no_op_tracer_provider(self, mock_create_response_future, mock_session_init, mock_connect):
def test_instrument_connection_no_op_tracer_provider(
self, mock_create_response_future, mock_session_init, mock_connect
):
mock_create_response_future.return_value = mock.Mock()
mock_session_init.return_value = None
mock_connect.return_value = cassandra.cluster.Session()

View File

@ -126,10 +126,20 @@ class CeleryInstrumentor(BaseInstrumentor):
tracer_provider = kwargs.get("tracer_provider")
# pylint: disable=attribute-defined-outside-init
self._tracer = trace.get_tracer(__name__, __version__, tracer_provider)
self._tracer = trace.get_tracer(
__name__,
__version__,
tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
meter_provider = kwargs.get("meter_provider")
meter = get_meter(__name__, __version__, meter_provider)
meter = get_meter(
__name__,
__version__,
meter_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
self.create_celery_metrics(meter)

View File

@ -229,7 +229,10 @@ class ConfluentKafkaInstrumentor(BaseInstrumentor):
producer: Producer, tracer_provider=None
) -> ProxiedProducer:
tracer = trace.get_tracer(
__name__, __version__, tracer_provider=tracer_provider
__name__,
__version__,
tracer_provider=tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
manual_producer = ProxiedProducer(producer, tracer)
@ -241,7 +244,10 @@ class ConfluentKafkaInstrumentor(BaseInstrumentor):
consumer: Consumer, tracer_provider=None
) -> ProxiedConsumer:
tracer = trace.get_tracer(
__name__, __version__, tracer_provider=tracer_provider
__name__,
__version__,
tracer_provider=tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
manual_consumer = ProxiedConsumer(consumer, tracer)
@ -272,7 +278,10 @@ class ConfluentKafkaInstrumentor(BaseInstrumentor):
tracer_provider = kwargs.get("tracer_provider")
tracer = trace.get_tracer(
__name__, __version__, tracer_provider=tracer_provider
__name__,
__version__,
tracer_provider=tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
self._tracer = tracer

View File

@ -264,6 +264,7 @@ class DatabaseApiIntegration:
self._name,
instrumenting_library_version=self._version,
tracer_provider=tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
self.capture_parameters = capture_parameters
self.enable_commenter = enable_commenter

View File

@ -300,8 +300,14 @@ class DjangoInstrumentor(BaseInstrumentor):
__name__,
__version__,
tracer_provider=tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
meter = get_meter(
__name__,
__version__,
meter_provider=meter_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
meter = get_meter(__name__, __version__, meter_provider=meter_provider)
_DjangoMiddleware._tracer = tracer
_DjangoMiddleware._meter = meter
_DjangoMiddleware._excluded_urls = (

View File

@ -140,7 +140,12 @@ class ElasticsearchInstrumentor(BaseInstrumentor):
Instruments Elasticsearch module
"""
tracer_provider = kwargs.get("tracer_provider")
tracer = get_tracer(__name__, __version__, tracer_provider)
tracer = get_tracer(
__name__,
__version__,
tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
request_hook = kwargs.get("request_hook")
response_hook = kwargs.get("response_hook")
if es_transport_split:

View File

@ -254,9 +254,17 @@ class _InstrumentedFalconAPI(getattr(falcon, _instrument_app)):
self._middlewares_list = [self._middlewares_list]
self._otel_tracer = trace.get_tracer(
__name__, __version__, tracer_provider
__name__,
__version__,
tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
self._otel_meter = get_meter(
__name__,
__version__,
meter_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
self._otel_meter = get_meter(__name__, __version__, meter_provider)
self.duration_histogram = self._otel_meter.create_histogram(
name=MetricInstruments.HTTP_SERVER_DURATION,
unit="ms",

View File

@ -222,7 +222,12 @@ class FastAPIInstrumentor(BaseInstrumentor):
excluded_urls = _excluded_urls_from_env
else:
excluded_urls = parse_excluded_urls(excluded_urls)
meter = get_meter(__name__, __version__, meter_provider)
meter = get_meter(
__name__,
__version__,
meter_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
app.add_middleware(
OpenTelemetryMiddleware,
@ -295,7 +300,10 @@ class _InstrumentedFastAPI(fastapi.FastAPI):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
meter = get_meter(
__name__, __version__, _InstrumentedFastAPI._meter_provider
__name__,
__version__,
_InstrumentedFastAPI._meter_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
self.add_middleware(
OpenTelemetryMiddleware,

View File

@ -495,7 +495,10 @@ class _InstrumentedFlask(flask.Flask):
self._is_instrumented_by_opentelemetry = True
meter = get_meter(
__name__, __version__, _InstrumentedFlask._meter_provider
__name__,
__version__,
_InstrumentedFlask._meter_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
duration_histogram = meter.create_histogram(
name=MetricInstruments.HTTP_SERVER_DURATION,
@ -517,7 +520,10 @@ class _InstrumentedFlask(flask.Flask):
)
tracer = trace.get_tracer(
__name__, __version__, _InstrumentedFlask._tracer_provider
__name__,
__version__,
_InstrumentedFlask._tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
_before_request = _wrapped_before_request(
@ -594,7 +600,12 @@ class FlaskInstrumentor(BaseInstrumentor):
if excluded_urls is not None
else _excluded_urls_from_env
)
meter = get_meter(__name__, __version__, meter_provider)
meter = get_meter(
__name__,
__version__,
meter_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
duration_histogram = meter.create_histogram(
name=MetricInstruments.HTTP_SERVER_DURATION,
unit="ms",
@ -615,7 +626,12 @@ class FlaskInstrumentor(BaseInstrumentor):
excluded_urls=excluded_urls,
)
tracer = trace.get_tracer(__name__, __version__, tracer_provider)
tracer = trace.get_tracer(
__name__,
__version__,
tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
_before_request = _wrapped_before_request(
request_hook,

View File

@ -328,7 +328,9 @@ class TestProgrammatic(InstrumentationTest, WsgiTestBase):
if isinstance(point, NumberDataPoint):
self.assertEqual(point.value, 0)
def _assert_basic_metric(self, expected_duration_attributes, expected_requests_count_attributes):
def _assert_basic_metric(
self, expected_duration_attributes, expected_requests_count_attributes
):
metrics_list = self.memory_metrics_reader.get_metrics_data()
for resource_metric in metrics_list.resource_metrics:
for scope_metrics in resource_metric.scope_metrics:

View File

@ -576,7 +576,12 @@ def client_interceptor(
"""
from . import _client
tracer = trace.get_tracer(__name__, __version__, tracer_provider)
tracer = trace.get_tracer(
__name__,
__version__,
tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
return _client.OpenTelemetryClientInterceptor(
tracer,
@ -601,7 +606,12 @@ def server_interceptor(tracer_provider=None, filter_=None):
"""
from . import _server
tracer = trace.get_tracer(__name__, __version__, tracer_provider)
tracer = trace.get_tracer(
__name__,
__version__,
tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
return _server.OpenTelemetryServerInterceptor(tracer, filter_=filter_)
@ -619,7 +629,12 @@ def aio_client_interceptors(
"""
from . import _aio_client
tracer = trace.get_tracer(__name__, __version__, tracer_provider)
tracer = trace.get_tracer(
__name__,
__version__,
tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
return [
_aio_client.UnaryUnaryAioClientInterceptor(
@ -660,7 +675,12 @@ def aio_server_interceptor(tracer_provider=None, filter_=None):
"""
from . import _aio_server
tracer = trace.get_tracer(__name__, __version__, tracer_provider)
tracer = trace.get_tracer(
__name__,
__version__,
tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
return _aio_server.OpenTelemetryAioServerInterceptor(
tracer, filter_=filter_

View File

@ -290,6 +290,7 @@ class SyncOpenTelemetryTransport(httpx.BaseTransport):
__name__,
instrumenting_library_version=__version__,
tracer_provider=tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
self._request_hook = request_hook
self._response_hook = response_hook
@ -384,6 +385,7 @@ class AsyncOpenTelemetryTransport(httpx.AsyncBaseTransport):
__name__,
instrumenting_library_version=__version__,
tracer_provider=tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
self._request_hook = request_hook
self._response_hook = response_hook

View File

@ -130,7 +130,12 @@ class Jinja2Instrumentor(BaseInstrumentor):
def _instrument(self, **kwargs):
tracer_provider = kwargs.get("tracer_provider")
tracer = get_tracer(__name__, __version__, tracer_provider)
tracer = get_tracer(
__name__,
__version__,
tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
_wrap(jinja2, "environment.Template.render", _wrap_render(tracer))
_wrap(jinja2, "environment.Template.generate", _wrap_render(tracer))

View File

@ -102,7 +102,10 @@ class KafkaInstrumentor(BaseInstrumentor):
consume_hook = kwargs.get("consume_hook")
tracer = trace.get_tracer(
__name__, __version__, tracer_provider=tracer_provider
__name__,
__version__,
tracer_provider=tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
wrap_function_wrapper(

View File

@ -122,7 +122,12 @@ class PikaInstrumentor(BaseInstrumentor): # type: ignore
"Attempting to instrument Pika channel while already instrumented!"
)
return
tracer = trace.get_tracer(__name__, __version__, tracer_provider)
tracer = trace.get_tracer(
__name__,
__version__,
tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
PikaInstrumentor._instrument_blocking_channel_consumers(
channel, tracer, consume_hook
)

View File

@ -182,7 +182,12 @@ class PymemcacheInstrumentor(BaseInstrumentor):
def _instrument(self, **kwargs):
tracer_provider = kwargs.get("tracer_provider")
tracer = get_tracer(__name__, __version__, tracer_provider)
tracer = get_tracer(
__name__,
__version__,
tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
for cmd in COMMANDS:
_wrap(

View File

@ -248,7 +248,12 @@ class PymongoInstrumentor(BaseInstrumentor):
capture_statement = kwargs.get("capture_statement")
# Create and register a CommandTracer only the first time
if self._commandtracer_instance is None:
tracer = get_tracer(__name__, __version__, tracer_provider)
tracer = get_tracer(
__name__,
__version__,
tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
self._commandtracer_instance = CommandTracer(
tracer,

View File

@ -84,7 +84,11 @@ def _before_traversal(event):
return
start_time = request_environ.get(_ENVIRON_STARTTIME_KEY)
tracer = trace.get_tracer(__name__, __version__)
tracer = trace.get_tracer(
__name__,
__version__,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
if request.matched_route:
span_name = request.matched_route.pattern
@ -128,7 +132,11 @@ def trace_tween_factory(handler, registry):
# pylint: disable=too-many-statements
settings = registry.settings
enabled = asbool(settings.get(SETTING_TRACE_ENABLED, True))
meter = get_meter(__name__, __version__)
meter = get_meter(
__name__,
__version__,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
duration_histogram = meter.create_histogram(
name=MetricInstruments.HTTP_SERVER_DURATION,
unit="ms",

View File

@ -333,7 +333,10 @@ class RedisInstrumentor(BaseInstrumentor):
"""
tracer_provider = kwargs.get("tracer_provider")
tracer = trace.get_tracer(
__name__, __version__, tracer_provider=tracer_provider
__name__,
__version__,
tracer_provider=tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
_instrument(
tracer,

View File

@ -176,7 +176,12 @@ class RemouladeInstrumentor(BaseInstrumentor):
tracer_provider = kwargs.get("tracer_provider")
# pylint: disable=attribute-defined-outside-init
self._tracer = trace.get_tracer(__name__, __version__, tracer_provider)
self._tracer = trace.get_tracer(
__name__,
__version__,
tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
instrumentation_middleware = _InstrumentationMiddleware(self._tracer)
broker.add_extra_default_middleware(instrumentation_middleware)

View File

@ -277,13 +277,19 @@ class RequestsInstrumentor(BaseInstrumentor):
list of regexes used to exclude URLs from tracking
"""
tracer_provider = kwargs.get("tracer_provider")
tracer = get_tracer(__name__, __version__, tracer_provider)
tracer = get_tracer(
__name__,
__version__,
tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
excluded_urls = kwargs.get("excluded_urls")
meter_provider = kwargs.get("meter_provider")
meter = get_meter(
__name__,
__version__,
meter_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
duration_histogram = meter.create_histogram(
name=MetricInstruments.HTTP_CLIENT_DURATION,

View File

@ -129,9 +129,11 @@ def implement_span_function(func: Callable, name: str, attributes: Attributes):
@wraps(func)
def wrapper(*args, **kwargs):
with get_tracer(__name__, __version__).start_as_current_span(
name=name
) as span:
with get_tracer(
__name__,
__version__,
schema_url="https://opentelemetry.io/schemas/1.11.0",
).start_as_current_span(name=name) as span:
if span.is_recording():
for key, val in attributes.items():
span.set_attribute(key, val)

View File

@ -142,10 +142,20 @@ class SQLAlchemyInstrumentor(BaseInstrumentor):
An instrumented engine if passed in as an argument or list of instrumented engines, None otherwise.
"""
tracer_provider = kwargs.get("tracer_provider")
tracer = get_tracer(__name__, __version__, tracer_provider)
tracer = get_tracer(
__name__,
__version__,
tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
meter_provider = kwargs.get("meter_provider")
meter = get_meter(__name__, __version__, meter_provider)
meter = get_meter(
__name__,
__version__,
meter_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
connections_usage = meter.create_up_down_counter(
name=MetricInstruments.DB_CLIENT_CONNECTIONS_USAGE,

View File

@ -207,7 +207,12 @@ class StarletteInstrumentor(BaseInstrumentor):
tracer_provider=None,
):
"""Instrument an uninstrumented Starlette application."""
meter = get_meter(__name__, __version__, meter_provider)
meter = get_meter(
__name__,
__version__,
meter_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
if not getattr(app, "is_instrumented_by_opentelemetry", False):
app.add_middleware(
OpenTelemetryMiddleware,
@ -273,7 +278,10 @@ class _InstrumentedStarlette(applications.Starlette):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
meter = get_meter(
__name__, __version__, _InstrumentedStarlette._meter_provider
__name__,
__version__,
_InstrumentedStarlette._meter_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
self.add_middleware(
OpenTelemetryMiddleware,

View File

@ -172,6 +172,7 @@ class SystemMetricsInstrumentor(BaseInstrumentor):
__name__,
__version__,
meter_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
if "system.cpu.time" in self._config:

View File

@ -236,10 +236,20 @@ class TornadoInstrumentor(BaseInstrumentor):
process lifetime.
"""
tracer_provider = kwargs.get("tracer_provider")
tracer = trace.get_tracer(__name__, __version__, tracer_provider)
tracer = trace.get_tracer(
__name__,
__version__,
tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
meter_provider = kwargs.get("meter_provider")
meter = get_meter(__name__, __version__, meter_provider)
meter = get_meter(
__name__,
__version__,
meter_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
client_histograms = _create_client_histograms(meter)
server_histograms = _create_server_histograms(meter)

View File

@ -95,7 +95,12 @@ class TortoiseORMInstrumentor(BaseInstrumentor):
"""
tracer_provider = kwargs.get("tracer_provider")
# pylint: disable=attribute-defined-outside-init
self._tracer = trace.get_tracer(__name__, __version__, tracer_provider)
self._tracer = trace.get_tracer(
__name__,
__version__,
tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
self.capture_parameters = kwargs.get("capture_parameters", False)
if TORTOISE_SQLITE_SUPPORT:
funcs = [

View File

@ -137,10 +137,20 @@ class URLLibInstrumentor(BaseInstrumentor):
list of regexes used to exclude URLs from tracking
"""
tracer_provider = kwargs.get("tracer_provider")
tracer = get_tracer(__name__, __version__, tracer_provider)
tracer = get_tracer(
__name__,
__version__,
tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
excluded_urls = kwargs.get("excluded_urls")
meter_provider = kwargs.get("meter_provider")
meter = get_meter(__name__, __version__, meter_provider)
meter = get_meter(
__name__,
__version__,
meter_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
histograms = _create_client_histograms(meter)

View File

@ -163,12 +163,22 @@ class URLLib3Instrumentor(BaseInstrumentor):
list of regexes used to exclude URLs from tracking
"""
tracer_provider = kwargs.get("tracer_provider")
tracer = get_tracer(__name__, __version__, tracer_provider)
tracer = get_tracer(
__name__,
__version__,
tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
excluded_urls = kwargs.get("excluded_urls")
meter_provider = kwargs.get("meter_provider")
meter = get_meter(__name__, __version__, meter_provider)
meter = get_meter(
__name__,
__version__,
meter_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
duration_histogram = meter.create_histogram(
name=MetricInstruments.HTTP_CLIENT_DURATION,

View File

@ -138,6 +138,17 @@ class TestURLLib3Instrumentor(TestBase):
self.assert_success_span(response, self.HTTPS_URL)
def test_schema_url(self):
pool = urllib3.HTTPSConnectionPool("mock")
response = pool.request("GET", "/status/200")
self.assertEqual(b"Hello!", response.data)
span = self.assert_span()
self.assertEqual(
span.instrumentation_info.schema_url,
"https://opentelemetry.io/schemas/1.11.0",
)
def test_basic_not_found(self):
url_404 = "http://mock/status/404"
httpretty.register_uri(httpretty.GET, url_404, status=404)

View File

@ -155,6 +155,20 @@ class TestURLLib3InstrumentorMetric(HttpTestBase, TestBase):
],
)
def test_schema_url(self):
self.pool.request("POST", self.HTTP_URL, body="foobar")
resource_metrics = (
self.memory_metrics_reader.get_metrics_data().resource_metrics
)
for metrics in resource_metrics:
for scope_metrics in metrics.scope_metrics:
self.assertEqual(
scope_metrics.scope.schema_url,
"https://opentelemetry.io/schemas/1.11.0",
)
def test_bytes_request_body_size_metrics(self):
self.pool.request("POST", self.HTTP_URL, body=b"foobar")

View File

@ -302,7 +302,9 @@ def collect_request_attributes(environ):
"""
result = {
SpanAttributes.HTTP_METHOD: sanitize_method(environ.get("REQUEST_METHOD")),
SpanAttributes.HTTP_METHOD: sanitize_method(
environ.get("REQUEST_METHOD")
),
SpanAttributes.HTTP_SERVER_NAME: environ.get("SERVER_NAME"),
SpanAttributes.HTTP_SCHEME: environ.get("wsgi.url_scheme"),
}
@ -490,8 +492,18 @@ class OpenTelemetryMiddleware:
meter_provider=None,
):
self.wsgi = wsgi
self.tracer = trace.get_tracer(__name__, __version__, tracer_provider)
self.meter = get_meter(__name__, __version__, meter_provider)
self.tracer = trace.get_tracer(
__name__,
__version__,
tracer_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
self.meter = get_meter(
__name__,
__version__,
meter_provider,
schema_url="https://opentelemetry.io/schemas/1.11.0",
)
self.duration_histogram = self.meter.create_histogram(
name=MetricInstruments.HTTP_SERVER_DURATION,
unit="ms",

View File

@ -286,10 +286,12 @@ class TestWsgiApplication(WsgiTestBase):
self.assertTrue(number_data_point_seen and histogram_data_point_seen)
def test_nonstandard_http_method(self):
self.environ["REQUEST_METHOD"]= "NONSTANDARD"
self.environ["REQUEST_METHOD"] = "NONSTANDARD"
app = otel_wsgi.OpenTelemetryMiddleware(simple_wsgi)
response = app(self.environ, self.start_response)
self.validate_response(response, span_name="UNKNOWN /", http_method="UNKNOWN")
self.validate_response(
response, span_name="UNKNOWN /", http_method="UNKNOWN"
)
@mock.patch.dict(
"os.environ",
@ -298,10 +300,12 @@ class TestWsgiApplication(WsgiTestBase):
},
)
def test_nonstandard_http_method_allowed(self):
self.environ["REQUEST_METHOD"]= "NONSTANDARD"
self.environ["REQUEST_METHOD"] = "NONSTANDARD"
app = otel_wsgi.OpenTelemetryMiddleware(simple_wsgi)
response = app(self.environ, self.start_response)
self.validate_response(response, span_name="NONSTANDARD /", http_method="NONSTANDARD")
self.validate_response(
response, span_name="NONSTANDARD /", http_method="NONSTANDARD"
)
def test_default_span_name_missing_path_info(self):
"""Test that default span_names with missing path info."""

View File

@ -47,17 +47,25 @@ EXPECTED_AZURE_AMS_ATTRIBUTES = [
ResourceAttributes.SERVICE_INSTANCE_ID,
]
class AzureVMResourceDetector(ResourceDetector):
# pylint: disable=no-self-use
def detect(self) -> "Resource":
attributes = {}
metadata_json = _AzureVMMetadataServiceRequestor().get_azure_vm_metadata()
metadata_json = (
_AzureVMMetadataServiceRequestor().get_azure_vm_metadata()
)
if not metadata_json:
return Resource(attributes)
for attribute_key in EXPECTED_AZURE_AMS_ATTRIBUTES:
attributes[attribute_key] = _AzureVMMetadataServiceRequestor().get_attribute_from_metadata(metadata_json, attribute_key)
attributes[
attribute_key
] = _AzureVMMetadataServiceRequestor().get_attribute_from_metadata(
metadata_json, attribute_key
)
return Resource(attributes)
class _AzureVMMetadataServiceRequestor:
def get_azure_vm_metadata(self):
request = Request(_AZURE_VM_METADATA_ENDPOINT)
@ -86,8 +94,10 @@ class _AzureVMMetadataServiceRequestor:
ams_value = metadata_json["location"]
elif attribute_key == ResourceAttributes.CLOUD_RESOURCE_ID:
ams_value = metadata_json["resourceId"]
elif attribute_key == ResourceAttributes.HOST_ID or \
attribute_key == ResourceAttributes.SERVICE_INSTANCE_ID:
elif (
attribute_key == ResourceAttributes.HOST_ID
or attribute_key == ResourceAttributes.SERVICE_INSTANCE_ID
):
ams_value = metadata_json["vmId"]
elif attribute_key == ResourceAttributes.HOST_NAME:
ams_value = metadata_json["name"]

View File

@ -28,8 +28,11 @@ TEST_WEBSITE_HOME_STAMPNAME = "TEST_WEBSITE_HOME_STAMPNAME"
TEST_WEBSITE_RESOURCE_GROUP = "TEST_WEBSITE_RESOURCE_GROUP"
TEST_WEBSITE_OWNER_NAME = "TEST_WEBSITE_OWNER_NAME"
class TestAzureAppServiceResourceDetector(unittest.TestCase):
@patch.dict("os.environ", {
@patch.dict(
"os.environ",
{
"WEBSITE_SITE_NAME": TEST_WEBSITE_SITE_NAME,
"REGION_NAME": TEST_REGION_NAME,
"WEBSITE_SLOT_NAME": TEST_WEBSITE_SLOT_NAME,
@ -38,7 +41,9 @@ class TestAzureAppServiceResourceDetector(unittest.TestCase):
"WEBSITE_HOME_STAMPNAME": TEST_WEBSITE_HOME_STAMPNAME,
"WEBSITE_RESOURCE_GROUP": TEST_WEBSITE_RESOURCE_GROUP,
"WEBSITE_OWNER_NAME": TEST_WEBSITE_OWNER_NAME,
}, clear=True)
},
clear=True,
)
def test_on_app_service(self):
resource = AzureAppServiceResourceDetector().detect()
attributes = resource.attributes
@ -46,16 +51,26 @@ class TestAzureAppServiceResourceDetector(unittest.TestCase):
self.assertEqual(attributes["cloud.provider"], "azure")
self.assertEqual(attributes["cloud.platform"], "azure_app_service")
self.assertEqual(attributes["cloud.resource_id"], \
f"/subscriptions/{TEST_WEBSITE_OWNER_NAME}/resourceGroups/{TEST_WEBSITE_RESOURCE_GROUP}/providers/Microsoft.Web/sites/{TEST_WEBSITE_SITE_NAME}")
self.assertEqual(
attributes["cloud.resource_id"],
f"/subscriptions/{TEST_WEBSITE_OWNER_NAME}/resourceGroups/{TEST_WEBSITE_RESOURCE_GROUP}/providers/Microsoft.Web/sites/{TEST_WEBSITE_SITE_NAME}",
)
self.assertEqual(attributes["cloud.region"], TEST_REGION_NAME)
self.assertEqual(attributes["deployment.environment"], TEST_WEBSITE_SLOT_NAME)
self.assertEqual(
attributes["deployment.environment"], TEST_WEBSITE_SLOT_NAME
)
self.assertEqual(attributes["host.id"], TEST_WEBSITE_HOSTNAME)
self.assertEqual(attributes["service.instance.id"], TEST_WEBSITE_INSTANCE_ID)
self.assertEqual(attributes["azure.app.service.stamp"], TEST_WEBSITE_HOME_STAMPNAME)
self.assertEqual(
attributes["service.instance.id"], TEST_WEBSITE_INSTANCE_ID
)
self.assertEqual(
attributes["azure.app.service.stamp"], TEST_WEBSITE_HOME_STAMPNAME
)
@patch.dict("os.environ", {
@patch.dict(
"os.environ",
{
"WEBSITE_SITE_NAME": TEST_WEBSITE_SITE_NAME,
"REGION_NAME": TEST_REGION_NAME,
"WEBSITE_SLOT_NAME": TEST_WEBSITE_SLOT_NAME,
@ -63,7 +78,9 @@ class TestAzureAppServiceResourceDetector(unittest.TestCase):
"WEBSITE_INSTANCE_ID": TEST_WEBSITE_INSTANCE_ID,
"WEBSITE_HOME_STAMPNAME": TEST_WEBSITE_HOME_STAMPNAME,
"WEBSITE_OWNER_NAME": TEST_WEBSITE_OWNER_NAME,
}, clear=True)
},
clear=True,
)
def test_on_app_service_no_resource_group(self):
resource = AzureAppServiceResourceDetector().detect()
attributes = resource.attributes
@ -74,12 +91,20 @@ class TestAzureAppServiceResourceDetector(unittest.TestCase):
self.assertTrue("cloud.resource_id" not in attributes)
self.assertEqual(attributes["cloud.region"], TEST_REGION_NAME)
self.assertEqual(attributes["deployment.environment"], TEST_WEBSITE_SLOT_NAME)
self.assertEqual(
attributes["deployment.environment"], TEST_WEBSITE_SLOT_NAME
)
self.assertEqual(attributes["host.id"], TEST_WEBSITE_HOSTNAME)
self.assertEqual(attributes["service.instance.id"], TEST_WEBSITE_INSTANCE_ID)
self.assertEqual(attributes["azure.app.service.stamp"], TEST_WEBSITE_HOME_STAMPNAME)
self.assertEqual(
attributes["service.instance.id"], TEST_WEBSITE_INSTANCE_ID
)
self.assertEqual(
attributes["azure.app.service.stamp"], TEST_WEBSITE_HOME_STAMPNAME
)
@patch.dict("os.environ", {
@patch.dict(
"os.environ",
{
"WEBSITE_SITE_NAME": TEST_WEBSITE_SITE_NAME,
"REGION_NAME": TEST_REGION_NAME,
"WEBSITE_SLOT_NAME": TEST_WEBSITE_SLOT_NAME,
@ -87,7 +112,9 @@ class TestAzureAppServiceResourceDetector(unittest.TestCase):
"WEBSITE_INSTANCE_ID": TEST_WEBSITE_INSTANCE_ID,
"WEBSITE_HOME_STAMPNAME": TEST_WEBSITE_HOME_STAMPNAME,
"WEBSITE_OWNER_NAME": TEST_WEBSITE_OWNER_NAME,
}, clear=True)
},
clear=True,
)
def test_on_app_service_no_owner(self):
resource = AzureAppServiceResourceDetector().detect()
attributes = resource.attributes
@ -98,19 +125,29 @@ class TestAzureAppServiceResourceDetector(unittest.TestCase):
self.assertTrue("cloud.resource_id" not in attributes)
self.assertEqual(attributes["cloud.region"], TEST_REGION_NAME)
self.assertEqual(attributes["deployment.environment"], TEST_WEBSITE_SLOT_NAME)
self.assertEqual(
attributes["deployment.environment"], TEST_WEBSITE_SLOT_NAME
)
self.assertEqual(attributes["host.id"], TEST_WEBSITE_HOSTNAME)
self.assertEqual(attributes["service.instance.id"], TEST_WEBSITE_INSTANCE_ID)
self.assertEqual(attributes["azure.app.service.stamp"], TEST_WEBSITE_HOME_STAMPNAME)
self.assertEqual(
attributes["service.instance.id"], TEST_WEBSITE_INSTANCE_ID
)
self.assertEqual(
attributes["azure.app.service.stamp"], TEST_WEBSITE_HOME_STAMPNAME
)
@patch.dict("os.environ", {
@patch.dict(
"os.environ",
{
"REGION_NAME": TEST_REGION_NAME,
"WEBSITE_SLOT_NAME": TEST_WEBSITE_SLOT_NAME,
"WEBSITE_HOSTNAME": TEST_WEBSITE_HOSTNAME,
"WEBSITE_INSTANCE_ID": TEST_WEBSITE_INSTANCE_ID,
"WEBSITE_HOME_STAMPNAME": TEST_WEBSITE_HOME_STAMPNAME,
"WEBSITE_OWNER_NAME": TEST_WEBSITE_OWNER_NAME,
}, clear=True)
},
clear=True,
)
def test_off_app_service(self):
resource = AzureAppServiceResourceDetector().detect()
self.assertEqual(resource.attributes, {})

View File

@ -175,7 +175,7 @@ LINUX_JSON = """
"zone": "1"
}
"""
WINDOWS_JSON ="""
WINDOWS_JSON = """
{
"additionalCapabilities": {
"hibernationEnabled": "false"
@ -370,7 +370,9 @@ class TestAzureVMResourceDetector(unittest.TestCase):
mock_open.read.return_value = LINUX_JSON
attributes = AzureVMResourceDetector().detect().attributes
for attribute_key in LINUX_ATTRIBUTES:
self.assertEqual(attributes[attribute_key], LINUX_ATTRIBUTES[attribute_key])
self.assertEqual(
attributes[attribute_key], LINUX_ATTRIBUTES[attribute_key]
)
@patch("opentelemetry.resource.detector.azure.vm.urlopen")
def test_windows(self, mock_urlopen):
@ -379,4 +381,6 @@ class TestAzureVMResourceDetector(unittest.TestCase):
mock_open.read.return_value = WINDOWS_JSON
attributes = AzureVMResourceDetector().detect().attributes
for attribute_key in WINDOWS_ATTRIBUTES:
self.assertEqual(attributes[attribute_key], WINDOWS_ATTRIBUTES[attribute_key])
self.assertEqual(
attributes[attribute_key], WINDOWS_ATTRIBUTES[attribute_key]
)

View File

@ -190,16 +190,32 @@ def normalise_response_header_name(header: str) -> str:
key = header.lower().replace("-", "_")
return f"http.response.header.{key}"
def sanitize_method(method: Optional[str]) -> Optional[str]:
if method is None:
return None
method = method.upper()
if (environ.get(OTEL_PYTHON_INSTRUMENTATION_HTTP_CAPTURE_ALL_METHODS) or
if (
environ.get(OTEL_PYTHON_INSTRUMENTATION_HTTP_CAPTURE_ALL_METHODS)
or
# Based on https://www.rfc-editor.org/rfc/rfc7231#section-4.1 and https://www.rfc-editor.org/rfc/rfc5789#section-2.
method in ["GET", "HEAD", "POST", "PUT", "DELETE", "CONNECT", "OPTIONS", "TRACE", "PATCH"]):
method
in [
"GET",
"HEAD",
"POST",
"PUT",
"DELETE",
"CONNECT",
"OPTIONS",
"TRACE",
"PATCH",
]
):
return method
return "UNKNOWN"
def get_custom_headers(env_var: str) -> List[str]:
custom_headers = environ.get(env_var, [])
if custom_headers:

View File

@ -20,6 +20,7 @@ from opentelemetry.util.http import (
sanitize_method,
)
class TestSanitizeMethod(unittest.TestCase):
def test_standard_method_uppercase(self):
method = sanitize_method("GET")