Skip to content
Open
Show file tree
Hide file tree
Changes from 4 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,17 @@ 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):
if span.is_recording():
span.set_attribute(
ERROR_TYPE,
type(recordable_exc).__qualname__,
)
span.set_status(
Status(StatusCode.ERROR, str(recordable_exc))
)
Expand Down
Loading