Skip to content
Open
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 CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
([#3967](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3967))
- `opentelemetry-instrumentation-redis`: add missing copyright header for opentelemetry-instrumentation-redis
([#3976](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3976))
- `opentelemetry-instrumentation-pyramid` Implement new semantic convention opt-in migration
([#3982](https://github.com/open-telemetry/opentelemetry-python-contrib/pull/3982))

### Fixed

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,13 @@
from pyramid.settings import aslist
from wrapt import wrap_function_wrapper as _wrap

from opentelemetry.instrumentation._semconv import (
_OpenTelemetrySemanticConventionStability,
_OpenTelemetryStabilitySignalType,
_StabilityMode,
)
from opentelemetry.instrumentation.instrumentor import BaseInstrumentor
from opentelemetry.instrumentation.pyramid import callbacks
from opentelemetry.instrumentation.pyramid.callbacks import (
SETTING_TRACE_ENABLED,
TWEEN_NAME,
Expand Down Expand Up @@ -248,10 +254,20 @@ def _instrument(self, **kwargs):
"""Integrate with Pyramid Python library.
https://docs.pylonsproject.org/projects/pyramid/en/latest/
"""
# Initialize semantic conventions opt-in mode
_OpenTelemetrySemanticConventionStability._initialize()
sem_conv_opt_in_mode = _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
_OpenTelemetryStabilitySignalType.HTTP,
)
# Set module-level opt-in mode in callbacks
callbacks._sem_conv_opt_in_mode = sem_conv_opt_in_mode

_wrap("pyramid.config", "Configurator.__init__", _traced_init)

def _uninstrument(self, **kwargs):
""" "Disable Pyramid instrumentation"""
# Reset module-level opt-in mode to default
callbacks._sem_conv_opt_in_mode = _StabilityMode.DEFAULT
unwrap(Configurator, "__init__")

@staticmethod
Expand All @@ -261,8 +277,18 @@ def instrument_config(config):
Args:
config: The Configurator to instrument.
"""
# Initialize semantic conventions opt-in mode
_OpenTelemetrySemanticConventionStability._initialize()
sem_conv_opt_in_mode = _OpenTelemetrySemanticConventionStability._get_opentelemetry_stability_opt_in_mode(
_OpenTelemetryStabilitySignalType.HTTP,
)
# Set module-level opt-in mode in callbacks
callbacks._sem_conv_opt_in_mode = sem_conv_opt_in_mode

config.include("opentelemetry.instrumentation.pyramid.callbacks")

@staticmethod
def uninstrument_config(config):
# Reset module-level opt-in mode to default
callbacks._sem_conv_opt_in_mode = _StabilityMode.DEFAULT
config.add_settings({SETTING_TRACE_ENABLED: False})
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,24 @@

import opentelemetry.instrumentation.wsgi as otel_wsgi
from opentelemetry import context, trace
from opentelemetry.instrumentation._semconv import (
HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
_get_schema_url,
_report_new,
_report_old,
_StabilityMode,
)
from opentelemetry.instrumentation.propagators import (
get_global_response_propagator,
)
from opentelemetry.instrumentation.pyramid.version import __version__
from opentelemetry.instrumentation.utils import _start_internal_or_server_span
from opentelemetry.metrics import get_meter
from opentelemetry.semconv.attributes.error_attributes import ERROR_TYPE
from opentelemetry.semconv.metrics import MetricInstruments
from opentelemetry.semconv.metrics.http_metrics import (
HTTP_SERVER_REQUEST_DURATION,
)
from opentelemetry.semconv.trace import SpanAttributes
from opentelemetry.trace.status import Status, StatusCode
from opentelemetry.util.http import get_excluded_urls
Expand All @@ -47,6 +58,7 @@


_excluded_urls = get_excluded_urls("PYRAMID")
_sem_conv_opt_in_mode = _StabilityMode.DEFAULT


def includeme(config):
Expand Down Expand Up @@ -88,7 +100,7 @@ def _before_traversal(event):
tracer = trace.get_tracer(
__name__,
__version__,
schema_url="https://opentelemetry.io/schemas/1.11.0",
schema_url=_get_schema_url(_sem_conv_opt_in_mode),
)

if request.matched_route:
Expand All @@ -105,7 +117,9 @@ def _before_traversal(event):
)

if span.is_recording():
attributes = otel_wsgi.collect_request_attributes(request_environ)
attributes = otel_wsgi.collect_request_attributes(
request_environ, _sem_conv_opt_in_mode
)
if request.matched_route:
attributes[SpanAttributes.HTTP_ROUTE] = (
request.matched_route.pattern
Expand Down Expand Up @@ -133,20 +147,45 @@ def trace_tween_factory(handler, registry):
# pylint: disable=too-many-statements
settings = registry.settings
enabled = asbool(settings.get(SETTING_TRACE_ENABLED, True))

# Create meters and histograms based on opt-in mode
duration_histogram_old = None
if _report_old(_sem_conv_opt_in_mode):
meter_old = get_meter(
__name__,
__version__,
schema_url=_get_schema_url(_StabilityMode.DEFAULT),
)
duration_histogram_old = meter_old.create_histogram(
name=MetricInstruments.HTTP_SERVER_DURATION,
unit="ms",
description="Measures the duration of inbound HTTP requests.",
)

duration_histogram_new = None
if _report_new(_sem_conv_opt_in_mode):
meter_new = get_meter(
__name__,
__version__,
schema_url=_get_schema_url(_StabilityMode.HTTP),
)
duration_histogram_new = meter_new.create_histogram(
name=HTTP_SERVER_REQUEST_DURATION,
unit="s",
description="Duration of HTTP server requests.",
explicit_bucket_boundaries_advisory=HTTP_DURATION_HISTOGRAM_BUCKETS_NEW,
)

# Use a single meter for active requests counter (attributes are compatible)
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",
description="Measures the duration of inbound HTTP requests.",
schema_url=_get_schema_url(_sem_conv_opt_in_mode),
)
active_requests_counter = meter.create_up_down_counter(
name=MetricInstruments.HTTP_SERVER_ACTIVE_REQUESTS,
unit="requests",
description="measures the number of concurrent HTTP requests that are currently in-flight",
unit="{request}",
description="Number of active HTTP server requests.",
)

if not enabled:
Expand All @@ -167,14 +206,20 @@ def trace_tween(request):
# short-circuit when we don't want to trace anything
return handler(request)

attributes = otel_wsgi.collect_request_attributes(request.environ)
attributes = otel_wsgi.collect_request_attributes(
request.environ, _sem_conv_opt_in_mode
)

request.environ[_ENVIRON_ENABLED_KEY] = True
request.environ[_ENVIRON_STARTTIME_KEY] = time_ns()
active_requests_count_attrs = (
otel_wsgi._parse_active_request_count_attrs(attributes)
otel_wsgi._parse_active_request_count_attrs(
attributes, _sem_conv_opt_in_mode
)
)
duration_attrs = otel_wsgi._parse_duration_attrs(
attributes, _sem_conv_opt_in_mode
)
duration_attrs = otel_wsgi._parse_duration_attrs(attributes)

start = default_timer()
active_requests_counter.add(1, active_requests_count_attrs)
Expand All @@ -200,16 +245,34 @@ def trace_tween(request):
# should infer a internal server error and raise
status = "500 InternalServerError"
recordable_exc = exc
if _report_new(_sem_conv_opt_in_mode):
attributes[ERROR_TYPE] = type(exc).__qualname__
raise
finally:
duration = max(round((default_timer() - start) * 1000), 0)
duration_s = default_timer() - start
status = getattr(response, "status", status)
status_code = otel_wsgi._parse_status_code(status)
if status_code is not None:
duration_attrs[SpanAttributes.HTTP_STATUS_CODE] = (
otel_wsgi._parse_status_code(status)
duration_attrs[SpanAttributes.HTTP_STATUS_CODE] = status_code

# Record metrics for old semconv (milliseconds)
if duration_histogram_old:
duration_attrs_old = otel_wsgi._parse_duration_attrs(
duration_attrs, _StabilityMode.DEFAULT
)
duration_histogram.record(duration, duration_attrs)
duration_histogram_old.record(
max(round(duration_s * 1000), 0), duration_attrs_old
)

# Record metrics for new semconv (seconds)
if duration_histogram_new:
duration_attrs_new = otel_wsgi._parse_duration_attrs(
duration_attrs, _StabilityMode.HTTP
)
duration_histogram_new.record(
max(duration_s, 0), duration_attrs_new
)

active_requests_counter.add(-1, active_requests_count_attrs)
span = request.environ.get(_ENVIRON_SPAN_KEY)
enabled = request.environ.get(_ENVIRON_ENABLED_KEY)
Expand All @@ -225,9 +288,19 @@ def trace_tween(request):
span,
status,
getattr(response, "headerlist", None),
duration_attrs,
_sem_conv_opt_in_mode,
)

if recordable_exc is not None:
if (
_report_new(_sem_conv_opt_in_mode)
and span.is_recording()
):
span.set_attribute(
ERROR_TYPE,
type(recordable_exc).__qualname__,
)
span.set_status(
Status(StatusCode.ERROR, str(recordable_exc))
)
Expand Down
Loading