diff --git a/src/instana/instrumentation/aio_pika.py b/src/instana/instrumentation/aio_pika.py index 6dbe5778..a332771f 100644 --- a/src/instana/instrumentation/aio_pika.py +++ b/src/instana/instrumentation/aio_pika.py @@ -1,29 +1,24 @@ # (c) Copyright IBM Corp. 2025 try: + from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Tuple, Type + import aio_pika # noqa: F401 import wrapt - from typing import ( - TYPE_CHECKING, - Dict, - Any, - Callable, - Tuple, - Type, - Optional, - ) + from opentelemetry.context import get_current from instana.log import logger from instana.propagators.format import Format - from instana.util.traceutils import get_tracer_tuple from instana.singletons import get_tracer + from instana.util.traceutils import get_tracer_tuple if TYPE_CHECKING: - from instana.span.span import InstanaSpan + from aio_pika.abc import AbstractMessage, ConsumerTag from aio_pika.exchange import Exchange - from aiormq.abc import ConfirmationFrameType - from aio_pika.abc import ConsumerTag, AbstractMessage from aio_pika.queue import Queue, QueueIterator + from aiormq.abc import ConfirmationFrameType + + from instana.span.span import InstanaSpan def _extract_span_attributes( span: "InstanaSpan", connection, sort: str, routing_key: str, exchange: str @@ -41,11 +36,11 @@ async def publish_with_instana( args: Tuple[object], kwargs: Dict[str, Any], ) -> Optional["ConfirmationFrameType"]: - tracer, parent_span, _ = get_tracer_tuple() + tracer, _, _ = get_tracer_tuple() if not tracer: return await wrapped(*args, **kwargs) - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() def _bind_args( message: Type["AbstractMessage"], @@ -57,9 +52,7 @@ def _bind_args( (message, routing_key, args, kwargs) = _bind_args(*args, **kwargs) - with tracer.start_as_current_span( - "rabbitmq", span_context=parent_context - ) as span: + with tracer.start_as_current_span("rabbitmq", context=parent_context) as span: connection = instance.channel._connection _extract_span_attributes( @@ -105,7 +98,7 @@ async def callback_wrapper( Format.HTTP_HEADERS, message.headers, disable_w3c_trace_context=True ) with tracer.start_as_current_span( - "rabbitmq", span_context=parent_context + "rabbitmq", context=parent_context ) as span: _extract_span_attributes( span, connection, "consume", message.routing_key, message.exchange diff --git a/src/instana/instrumentation/aioamqp.py b/src/instana/instrumentation/aioamqp.py index a54efca6..ad973f78 100644 --- a/src/instana/instrumentation/aioamqp.py +++ b/src/instana/instrumentation/aioamqp.py @@ -1,10 +1,11 @@ # (c) Copyright IBM Corp. 2025 try: - import aioamqp from typing import Any, Callable, Dict, Tuple + import aioamqp import wrapt + from opentelemetry.context import get_current from opentelemetry.trace.status import StatusCode from instana.log import logger @@ -21,9 +22,9 @@ async def basic_publish_with_instana( if not tracer: return await wrapped(*argv, **kwargs) - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() with tracer.start_as_current_span( - "aioamqp-publisher", span_context=parent_context + "aioamqp-publisher", context=parent_context ) as span: try: span.set_attribute("amqp.command", "publish") @@ -62,7 +63,7 @@ async def basic_consume_with_instana( return await wrapped(*argv, **kwargs) callback = argv[0] - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() @wrapt.decorator async def callback_wrapper( @@ -72,7 +73,7 @@ async def callback_wrapper( kwargs: Dict, ) -> object: with tracer.start_as_current_span( - "aioamqp-consumer", span_context=parent_context + "aioamqp-consumer", context=parent_context ) as span: try: span.set_status(StatusCode.OK) diff --git a/src/instana/instrumentation/aiohttp/client.py b/src/instana/instrumentation/aiohttp/client.py index f30adf52..3b4e833a 100644 --- a/src/instana/instrumentation/aiohttp/client.py +++ b/src/instana/instrumentation/aiohttp/client.py @@ -4,21 +4,23 @@ from types import SimpleNamespace from typing import TYPE_CHECKING, Any, Awaitable, Callable, Dict, Tuple -import wrapt +import wrapt from opentelemetry.semconv.trace import SpanAttributes from instana.log import logger from instana.propagators.format import Format from instana.singletons import agent from instana.util.secrets import strip_secrets_from_query -from instana.util.traceutils import get_tracer_tuple, extract_custom_headers +from instana.util.traceutils import extract_custom_headers, get_tracer_tuple try: import aiohttp + from opentelemetry.context import get_current if TYPE_CHECKING: from aiohttp.client import ClientSession + from instana.span.span import InstanaSpan async def stan_request_start( @@ -31,9 +33,9 @@ async def stan_request_start( trace_config_ctx.span_context = None return - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() - span = tracer.start_span("aiohttp-client", span_context=parent_context) + span = tracer.start_span("aiohttp-client", context=parent_context) extract_custom_headers(span, params.headers) diff --git a/src/instana/instrumentation/aiohttp/server.py b/src/instana/instrumentation/aiohttp/server.py index 1cb04b38..0930f163 100644 --- a/src/instana/instrumentation/aiohttp/server.py +++ b/src/instana/instrumentation/aiohttp/server.py @@ -32,7 +32,7 @@ async def stan_middleware( tracer = get_tracer() span_context = tracer.extract(Format.HTTP_HEADERS, request.headers) span: "InstanaSpan" = tracer.start_span( - "aiohttp-server", span_context=span_context + "aiohttp-server", context=span_context ) request["span"] = span diff --git a/src/instana/instrumentation/asgi.py b/src/instana/instrumentation/asgi.py index a2df2cce..e7e5e207 100644 --- a/src/instana/instrumentation/asgi.py +++ b/src/instana/instrumentation/asgi.py @@ -74,7 +74,7 @@ async def __call__( if isinstance(request_headers, list): request_context = tracer.extract(Format.BINARY, request_headers) - with tracer.start_as_current_span("asgi", span_context=request_context) as span: + with tracer.start_as_current_span("asgi", context=request_context) as span: self._collect_kvs(scope, span) if "headers" in scope: extract_custom_headers(span, scope["headers"]) diff --git a/src/instana/instrumentation/aws/boto3.py b/src/instana/instrumentation/aws/boto3.py index e29dc2ca..2da62ca2 100644 --- a/src/instana/instrumentation/aws/boto3.py +++ b/src/instana/instrumentation/aws/boto3.py @@ -4,6 +4,7 @@ try: from typing import TYPE_CHECKING, Any, Callable, Dict, Sequence, Tuple, Type + from opentelemetry.context import get_current from opentelemetry.semconv.trace import SpanAttributes from instana.instrumentation.aws.dynamodb import create_dynamodb_span @@ -23,10 +24,7 @@ from instana.log import logger from instana.propagators.format import Format from instana.singletons import get_tracer - from instana.util.traceutils import ( - extract_custom_headers, - get_tracer_tuple, - ) + from instana.util.traceutils import extract_custom_headers, get_tracer_tuple def lambda_inject_context( tracer: "InstanaTracer", @@ -74,16 +72,14 @@ def make_api_call_with_instana( if not tracer: return wrapped(*args, **kwargs) - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() if instance.meta.service_model.service_name == "dynamodb": create_dynamodb_span(wrapped, instance, args, kwargs, parent_context) elif instance.meta.service_model.service_name == "s3": create_s3_span(wrapped, instance, args, kwargs, parent_context) else: - with tracer.start_as_current_span( - "boto3", span_context=parent_context - ) as span: + with tracer.start_as_current_span("boto3", context=parent_context) as span: operation = args[0] payload = args[1] diff --git a/src/instana/instrumentation/aws/dynamodb.py b/src/instana/instrumentation/aws/dynamodb.py index bb1e15d2..722ce83c 100644 --- a/src/instana/instrumentation/aws/dynamodb.py +++ b/src/instana/instrumentation/aws/dynamodb.py @@ -19,7 +19,7 @@ def create_dynamodb_span( parent_context: SpanContext, ) -> None: tracer = get_tracer() - with tracer.start_as_current_span("dynamodb", span_context=parent_context) as span: + with tracer.start_as_current_span("dynamodb", context=parent_context) as span: try: span.set_attribute("dynamodb.op", args[0]) span.set_attribute("dynamodb.region", instance._client_config.region_name) diff --git a/src/instana/instrumentation/aws/lambda_inst.py b/src/instana/instrumentation/aws/lambda_inst.py index 62cabcc4..086ad933 100644 --- a/src/instana/instrumentation/aws/lambda_inst.py +++ b/src/instana/instrumentation/aws/lambda_inst.py @@ -38,7 +38,7 @@ def lambda_handler_with_instana( result = None with tracer.start_as_current_span( - "aws.lambda.entry", span_context=incoming_ctx + "aws.lambda.entry", context=incoming_ctx ) as span: enrich_lambda_span(agent, span, *args) try: diff --git a/src/instana/instrumentation/aws/s3.py b/src/instana/instrumentation/aws/s3.py index 59123ffd..9b1557b5 100644 --- a/src/instana/instrumentation/aws/s3.py +++ b/src/instana/instrumentation/aws/s3.py @@ -5,6 +5,8 @@ try: from typing import TYPE_CHECKING, Any, Callable, Dict, Sequence, Type + from opentelemetry.context import get_current + from instana.span_context import SpanContext if TYPE_CHECKING: @@ -13,9 +15,7 @@ from instana.log import logger from instana.singletons import get_tracer - from instana.util.traceutils import ( - get_tracer_tuple, - ) + from instana.util.traceutils import get_tracer_tuple operations = { "upload_file": "UploadFile", @@ -32,7 +32,7 @@ def create_s3_span( parent_context: SpanContext, ) -> None: tracer = get_tracer() - with tracer.start_as_current_span("s3", span_context=parent_context) as span: + with tracer.start_as_current_span("s3", context=parent_context) as span: try: span.set_attribute("s3.op", args[0]) if "Bucket" in args[1].keys(): @@ -52,9 +52,9 @@ def collect_s3_injected_attributes( if not tracer: return wrapped(*args, **kwargs) - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() - with tracer.start_as_current_span("s3", span_context=parent_context) as span: + with tracer.start_as_current_span("s3", context=parent_context) as span: try: span.set_attribute("s3.op", operations[wrapped.__name__]) if "Bucket" in kwargs: diff --git a/src/instana/instrumentation/cassandra.py b/src/instana/instrumentation/cassandra.py index 8feaca11..dad1b11d 100644 --- a/src/instana/instrumentation/cassandra.py +++ b/src/instana/instrumentation/cassandra.py @@ -12,6 +12,7 @@ import cassandra import wrapt + from opentelemetry.context import get_current from instana.log import logger from instana.util.traceutils import get_tracer_tuple @@ -76,7 +77,7 @@ def request_init_with_instana( if not tracer: return - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() attributes = {} if isinstance(fn.query, cassandra.query.SimpleStatement): @@ -89,7 +90,7 @@ def request_init_with_instana( with tracer.start_as_current_span( "cassandra", - span_context=parent_context, + context=parent_context, attributes=attributes, end_on_exit=False, ) as span: diff --git a/src/instana/instrumentation/celery.py b/src/instana/instrumentation/celery.py index e709950d..f0648212 100644 --- a/src/instana/instrumentation/celery.py +++ b/src/instana/instrumentation/celery.py @@ -3,13 +3,14 @@ try: - import celery # noqa: F401 import contextvars from typing import Any, Dict, Tuple from urllib import parse + import celery # noqa: F401 from celery import registry, signals from opentelemetry import context, trace + from opentelemetry.context import get_current from instana.log import logger from instana.propagators.format import Format @@ -79,7 +80,7 @@ def task_prerun( Format.HTTP_HEADERS, headers, disable_w3c_trace_context=True ) - span = tracer.start_span("celery-worker", span_context=ctx) + span = tracer.start_span("celery-worker", context=ctx) span.set_attribute("task", task.name) span.set_attribute("task_id", task_id) add_broker_attributes(span, task.app.conf["broker_url"]) @@ -148,7 +149,7 @@ def before_task_publish( if not tracer: return - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() body = kwargs["body"] headers = kwargs["headers"] @@ -156,7 +157,7 @@ def before_task_publish( task = registry.tasks.get(task_name) task_id = _get_task_id(headers, body) - span = tracer.start_span("celery-client", span_context=parent_context) + span = tracer.start_span("celery-client", context=parent_context) span.set_attribute("task", task_name) span.set_attribute("task_id", task_id) add_broker_attributes(span, task.app.conf["broker_url"]) diff --git a/src/instana/instrumentation/couchbase.py b/src/instana/instrumentation/couchbase.py index 3edd5819..ee00ce5f 100644 --- a/src/instana/instrumentation/couchbase.py +++ b/src/instana/instrumentation/couchbase.py @@ -8,6 +8,7 @@ try: import couchbase + from instana.log import logger if not ( @@ -17,12 +18,12 @@ logger.debug("Instana supports 2.3.4 <= couchbase_versions < 3.0.0. Skipping.") raise ImportError - from couchbase.bucket import Bucket - from couchbase.n1ql import N1QLQuery - from typing import Any, Callable, Dict, Tuple, Union import wrapt + from couchbase.bucket import Bucket + from couchbase.n1ql import N1QLQuery + from opentelemetry.context import get_current from instana.span.span import InstanaSpan from instana.util.traceutils import get_tracer_tuple @@ -98,10 +99,10 @@ def wrapper( if not tracer: return wrapped(*args, **kwargs) - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() with tracer.start_as_current_span( - "couchbase", span_context=parent_context + "couchbase", context=parent_context ) as span: collect_attributes(span, instance, None, op) try: @@ -124,11 +125,9 @@ def query_with_instana( if not tracer: return wrapped(*args, **kwargs) - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() - with tracer.start_as_current_span( - "couchbase", span_context=parent_context - ) as span: + with tracer.start_as_current_span("couchbase", context=parent_context) as span: try: collect_attributes(span, instance, args[0], "n1ql_query") return wrapped(*args, **kwargs) diff --git a/src/instana/instrumentation/django/middleware.py b/src/instana/instrumentation/django/middleware.py index c73d30e4..52a2981a 100644 --- a/src/instana/instrumentation/django/middleware.py +++ b/src/instana/instrumentation/django/middleware.py @@ -59,9 +59,9 @@ def process_request(self, request: Type["HttpRequest"]) -> None: tracer = get_tracer() env = request.META - span_context = tracer.extract(Format.HTTP_HEADERS, env) + parent_context = tracer.extract(Format.HTTP_HEADERS, env) - span = tracer.start_span("django", span_context=span_context) + span = tracer.start_span("django", context=parent_context) request.span = span ctx = trace.set_span_in_context(span) diff --git a/src/instana/instrumentation/flask/common.py b/src/instana/instrumentation/flask/common.py index f544eca0..66431f04 100644 --- a/src/instana/instrumentation/flask/common.py +++ b/src/instana/instrumentation/flask/common.py @@ -9,6 +9,7 @@ import flask import wrapt from opentelemetry import context, trace +from opentelemetry.context import get_current from opentelemetry.semconv.trace import SpanAttributes from instana.log import logger @@ -36,11 +37,10 @@ def render_with_instana( if not (hasattr(flask, "g") and hasattr(flask.g, "span")): return wrapped(*argv, **kwargs) - parent_span = flask.g.span - parent_context = parent_span.get_span_context() + parent_context = get_current() tracer = get_tracer() - with tracer.start_as_current_span("render", span_context=parent_context) as span: + with tracer.start_as_current_span("render", context=parent_context) as span: try: flask_version = tuple(map(int, version("flask").split("."))) template = argv[1] if flask_version >= (2, 2, 0) else argv[0] @@ -104,7 +104,7 @@ def create_span(): tracer = get_tracer() span_context = tracer.extract(Format.HTTP_HEADERS, env) - span = tracer.start_span("wsgi", span_context=span_context) + span = tracer.start_span("wsgi", context=span_context) flask.g.span = span ctx = trace.set_span_in_context(span) diff --git a/src/instana/instrumentation/google/cloud/pubsub.py b/src/instana/instrumentation/google/cloud/pubsub.py index be2af051..03b8354f 100644 --- a/src/instana/instrumentation/google/cloud/pubsub.py +++ b/src/instana/instrumentation/google/cloud/pubsub.py @@ -5,6 +5,7 @@ from typing import TYPE_CHECKING, Any, Callable, Dict, Tuple import wrapt +from opentelemetry.context import get_current from instana.log import logger from instana.propagators.format import Format @@ -54,10 +55,10 @@ def publish_with_instana( if not tracer: return wrapped(*args, **kwargs) - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() with tracer.start_as_current_span( - "gcps-producer", span_context=parent_context + "gcps-producer", context=parent_context ) as span: # trace continuity, inject to the span context headers = {} @@ -106,7 +107,7 @@ def callback_with_instana(message): parent_context = None with tracer.start_as_current_span( - "gcps-consumer", span_context=parent_context + "gcps-consumer", context=parent_context ) as span: _set_consumer_attributes(span, subscription_path=args[0]) try: diff --git a/src/instana/instrumentation/google/cloud/storage.py b/src/instana/instrumentation/google/cloud/storage.py index 8e921146..0720c353 100644 --- a/src/instana/instrumentation/google/cloud/storage.py +++ b/src/instana/instrumentation/google/cloud/storage.py @@ -2,12 +2,14 @@ # (c) Copyright Instana Inc. 2020 -import wrapt import re - from typing import Any, Callable, Dict, Tuple, Union -from instana.log import logger + +import wrapt +from opentelemetry.context import get_current + from instana.instrumentation.google.cloud.collectors import _storage_api +from instana.log import logger from instana.util.traceutils import get_tracer_tuple try: @@ -67,9 +69,9 @@ def execute_with_instana( if isinstance(instance, storage.Batch) or not tracer: return wrapped(*args, **kwargs) - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() - with tracer.start_as_current_span("gcs", span_context=parent_context) as span: + with tracer.start_as_current_span("gcs", context=parent_context) as span: try: attributes = _collect_attributes(kwargs) @@ -97,9 +99,9 @@ def download_with_instana( if not tracer: return wrapped(*args, **kwargs) - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() - with tracer.start_as_current_span("gcs", span_context=parent_context) as span: + with tracer.start_as_current_span("gcs", context=parent_context) as span: span.set_attribute("gcs.op", "objects.get") span.set_attribute("gcs.bucket", instance.bucket.name) span.set_attribute("gcs.object", instance.name) @@ -133,9 +135,9 @@ def upload_with_instana( if not tracer: return wrapped(*args, **kwargs) - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() - with tracer.start_as_current_span("gcs", span_context=parent_context) as span: + with tracer.start_as_current_span("gcs", context=parent_context) as span: span.set_attribute("gcs.op", "objects.insert") span.set_attribute("gcs.bucket", instance.bucket.name) span.set_attribute("gcs.object", instance.name) @@ -158,9 +160,9 @@ def finish_batch_with_instana( if not tracer: return wrapped(*args, **kwargs) - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() - with tracer.start_as_current_span("gcs", span_context=parent_context) as span: + with tracer.start_as_current_span("gcs", context=parent_context) as span: span.set_attribute("gcs.op", "batch") span.set_attribute("gcs.projectId", instance._client.project) span.set_attribute("gcs.numberOfOperations", len(instance._requests)) diff --git a/src/instana/instrumentation/grpcio.py b/src/instana/instrumentation/grpcio.py index 3fce14fc..c497239f 100644 --- a/src/instana/instrumentation/grpcio.py +++ b/src/instana/instrumentation/grpcio.py @@ -17,6 +17,7 @@ from grpc._server import _Server import wrapt + from opentelemetry.context import get_current from instana.log import logger from instana.propagators.format import Format @@ -76,10 +77,10 @@ def create_span( if not parent_span.is_recording(): return wrapped(*argv, **kwargs) - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() with tracer.start_as_current_span( - "rpc-client", span_context=parent_context, record_exception=record_exception + "rpc-client", context=parent_context, record_exception=record_exception ) as span: try: if "metadata" not in kwargs: @@ -196,7 +197,7 @@ def call_behavior_with_instana( Format.BINARY, metadata_dict, disable_w3c_trace_context=True ) - with tracer.start_as_current_span("rpc-server", span_context=ctx) as span: + with tracer.start_as_current_span("rpc-server", context=ctx) as span: try: collect_attributes(span, instance, argv, kwargs) rv = wrapped(*argv, **kwargs) diff --git a/src/instana/instrumentation/httpx.py b/src/instana/instrumentation/httpx.py index 854ac376..f74f1d13 100644 --- a/src/instana/instrumentation/httpx.py +++ b/src/instana/instrumentation/httpx.py @@ -1,9 +1,11 @@ # (c) Copyright IBM Corp. 2025 try: + from typing import TYPE_CHECKING, Any, Callable, Dict, Optional, Tuple + import httpx import wrapt - from typing import TYPE_CHECKING, Any, Callable, Dict, Tuple, Optional + from opentelemetry.context import get_current from opentelemetry.semconv.trace import SpanAttributes from opentelemetry.trace import SpanKind @@ -11,10 +13,7 @@ from instana.propagators.format import Format from instana.singletons import agent from instana.util.secrets import strip_secrets_from_query - from instana.util.traceutils import ( - extract_custom_headers, - get_tracer_tuple, - ) + from instana.util.traceutils import extract_custom_headers, get_tracer_tuple if TYPE_CHECKING: from instana.span.span import InstanaSpan @@ -76,10 +75,10 @@ def handle_request_with_instana( if not tracer: return wrapped(*args, **kwargs) - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() with tracer.start_as_current_span( - "httpx", span_context=parent_context, kind=SpanKind.CLIENT + "httpx", context=parent_context, kind=SpanKind.CLIENT ) as span: try: request = args[0] @@ -105,10 +104,10 @@ async def handle_async_request_with_instana( if not tracer: return await wrapped(*args, **kwargs) - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() with tracer.start_as_current_span( - "httpx", span_context=parent_context, kind=SpanKind.CLIENT + "httpx", context=parent_context, kind=SpanKind.CLIENT ) as span: try: request = args[0] diff --git a/src/instana/instrumentation/kafka/confluent_kafka_python.py b/src/instana/instrumentation/kafka/confluent_kafka_python.py index e622e661..603a5433 100644 --- a/src/instana/instrumentation/kafka/confluent_kafka_python.py +++ b/src/instana/instrumentation/kafka/confluent_kafka_python.py @@ -9,6 +9,7 @@ import wrapt from confluent_kafka import Consumer, Producer from opentelemetry import context, trace + from opentelemetry.context import get_current from opentelemetry.trace import SpanKind from instana.log import logger @@ -69,7 +70,7 @@ def trace_kafka_produce( if not tracer: return wrapped(*args, **kwargs) - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() # Get the topic from either args or kwargs topic = args[0] if args else kwargs.get("topic", "") @@ -86,7 +87,7 @@ def trace_kafka_produce( ) with tracer.start_as_current_span( - "kafka-producer", span_context=parent_context, kind=SpanKind.PRODUCER + "kafka-producer", context=parent_context, kind=SpanKind.PRODUCER ) as span: span.set_attribute("kafka.service", topic) span.set_attribute("kafka.access", "produce") @@ -161,21 +162,27 @@ def create_span( if is_suppressed: return + # parent_context = get_current() + # if tracer.exporter.options.kafka_trace_correlation and not exception: + # parent_context = tracer.extract( + # Format.KAFKA_HEADERS, + # headers, + # disable_w3c_trace_context=True, + # ) + parent_context = ( - parent_span.get_span_context() + # parent_span.get_span_context() + get_current() if parent_span - else ( - tracer.extract( - Format.KAFKA_HEADERS, - headers, - disable_w3c_trace_context=True, - ) - if tracer.exporter.options.kafka_trace_correlation - else None + else tracer.extract( + Format.KAFKA_HEADERS, + headers, + disable_w3c_trace_context=True, ) ) + span = tracer.start_span( - "kafka-consumer", span_context=parent_context, kind=SpanKind.CONSUMER + "kafka-consumer", context=parent_context, kind=SpanKind.CONSUMER ) if topic: span.set_attribute("kafka.service", topic) diff --git a/src/instana/instrumentation/kafka/kafka_python.py b/src/instana/instrumentation/kafka/kafka_python.py index fd28677d..44bd0ebd 100644 --- a/src/instana/instrumentation/kafka/kafka_python.py +++ b/src/instana/instrumentation/kafka/kafka_python.py @@ -9,6 +9,7 @@ import kafka # noqa: F401 import wrapt from opentelemetry import context, trace + from opentelemetry.context import get_current from opentelemetry.trace import SpanKind from instana.log import logger @@ -35,7 +36,7 @@ def trace_kafka_send( if not tracer: return wrapped(*args, **kwargs) - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() # Get the topic from either args or kwargs topic = args[0] if args else kwargs.get("topic", "") @@ -51,7 +52,7 @@ def trace_kafka_send( ) with tracer.start_as_current_span( - "kafka-producer", span_context=parent_context, kind=SpanKind.PRODUCER + "kafka-producer", context=parent_context, kind=SpanKind.PRODUCER ) as span: span.set_attribute("kafka.service", topic) span.set_attribute("kafka.access", "send") @@ -119,7 +120,8 @@ def create_span( return parent_context = ( - parent_span.get_span_context() + # parent_span.get_span_context() + get_current() if parent_span else tracer.extract( Format.KAFKA_HEADERS, @@ -128,7 +130,7 @@ def create_span( ) ) span = tracer.start_span( - "kafka-consumer", span_context=parent_context, kind=SpanKind.CONSUMER + "kafka-consumer", context=parent_context, kind=SpanKind.CONSUMER ) if topic: span.set_attribute("kafka.service", topic) diff --git a/src/instana/instrumentation/logging.py b/src/instana/instrumentation/logging.py index 204de0a6..0c8656d0 100644 --- a/src/instana/instrumentation/logging.py +++ b/src/instana/instrumentation/logging.py @@ -8,6 +8,7 @@ from typing import Any, Callable, Dict, Tuple import wrapt +from opentelemetry.context import get_current from instana.log import logger from instana.singletons import agent @@ -57,10 +58,10 @@ def log_with_instana( if t is not None and v is not None: parameters = "{} {}".format(t, v) - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() # create logging span - with tracer.start_as_current_span("log", span_context=parent_context) as span: + with tracer.start_as_current_span("log", context=parent_context) as span: event_attributes = {"message": msg} if parameters is not None: event_attributes.update({"parameters": parameters}) diff --git a/src/instana/instrumentation/pep0249.py b/src/instana/instrumentation/pep0249.py index 3923ef9e..c636ba5c 100644 --- a/src/instana/instrumentation/pep0249.py +++ b/src/instana/instrumentation/pep0249.py @@ -1,16 +1,17 @@ # (c) Copyright IBM Corp. 2021 # (c) Copyright Instana Inc. 2018 +from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Tuple, Union + # This is a wrapper for PEP-0249: Python Database API Specification v2.0 import wrapt -from typing import TYPE_CHECKING, Dict, Any, List, Tuple, Union, Callable, Optional -from typing_extensions import Self - +from opentelemetry.context import get_current from opentelemetry.semconv.trace import SpanAttributes +from typing_extensions import Self from instana.log import logger -from instana.util.traceutils import get_tracer_tuple from instana.util.sql import sql_sanitizer +from instana.util.traceutils import get_tracer_tuple if TYPE_CHECKING: from instana.span.span import InstanaSpan @@ -72,9 +73,9 @@ def execute( if not tracer or (operation_name == "sqlalchemy"): return self.__wrapped__.execute(sql, params) - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() with tracer.start_as_current_span( - self._module_name, span_context=parent_context + self._module_name, context=parent_context ) as span: try: self._collect_kvs(span, sql) @@ -97,9 +98,9 @@ def executemany( if not tracer or (operation_name == "sqlalchemy"): return self.__wrapped__.executemany(sql, seq_of_parameters) - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() with tracer.start_as_current_span( - self._module_name, span_context=parent_context + self._module_name, context=parent_context ) as span: try: self._collect_kvs(span, sql) @@ -122,9 +123,9 @@ def callproc( if not tracer or (operation_name == "sqlalchemy"): return self.__wrapped__.execute(proc_name, params) - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() with tracer.start_as_current_span( - self._module_name, span_context=parent_context + self._module_name, context=parent_context ) as span: try: self._collect_kvs(span, proc_name) diff --git a/src/instana/instrumentation/pika.py b/src/instana/instrumentation/pika.py index 0deb96b8..77c18063 100644 --- a/src/instana/instrumentation/pika.py +++ b/src/instana/instrumentation/pika.py @@ -18,6 +18,7 @@ import pika import wrapt + from opentelemetry.context import get_current from instana.log import logger from instana.propagators.format import Format @@ -79,15 +80,13 @@ def _bind_args( if not tracer: return wrapped(*args, **kwargs) - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() (exchange, routing_key, body, properties, args, kwargs) = _bind_args( *args, **kwargs ) - with tracer.start_as_current_span( - "rabbitmq", span_context=parent_context - ) as span: + with tracer.start_as_current_span("rabbitmq", context=parent_context) as span: try: _extract_publisher_attributes( span, @@ -155,7 +154,7 @@ def _cb_wrapper( ) with tracer.start_as_current_span( - "rabbitmq", span_context=parent_context + "rabbitmq", context=parent_context ) as span: try: _extract_consumer_tags(span, conn=instance.connection, queue=queue) @@ -208,7 +207,7 @@ def _cb_wrapper( ) with tracer.start_as_current_span( - "rabbitmq", span_context=parent_context + "rabbitmq", context=parent_context ) as span: try: _extract_consumer_tags( @@ -264,7 +263,7 @@ def _consume(gen: Iterator[object]) -> object: disable_w3c_trace_context=True, ) with tracer.start_as_current_span( - "rabbitmq", span_context=parent_context + "rabbitmq", context=parent_context ) as span: try: _extract_consumer_tags( diff --git a/src/instana/instrumentation/pymongo.py b/src/instana/instrumentation/pymongo.py index 23cbf4f7..364db85a 100644 --- a/src/instana/instrumentation/pymongo.py +++ b/src/instana/instrumentation/pymongo.py @@ -2,13 +2,14 @@ # (c) Copyright Instana Inc. 2020 -from instana.span.span import InstanaSpan from instana.log import logger +from instana.span.span import InstanaSpan from instana.util.traceutils import get_tracer_tuple try: import pymongo from bson import json_util + from opentelemetry.context import get_current from opentelemetry.semconv.trace import SpanAttributes class MongoCommandTracer(pymongo.monitoring.CommandListener): @@ -20,11 +21,9 @@ def started(self, event: pymongo.monitoring.CommandStartedEvent) -> None: # return early if we're not tracing if not tracer: return - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() - with tracer.start_as_current_span( - "mongo", span_context=parent_context - ) as span: + with tracer.start_as_current_span("mongo", context=parent_context) as span: self._collect_connection_tags(span, event) self._collect_command_tags(span, event) diff --git a/src/instana/instrumentation/pyramid.py b/src/instana/instrumentation/pyramid.py index 46f1c78e..09e71462 100644 --- a/src/instana/instrumentation/pyramid.py +++ b/src/instana/instrumentation/pyramid.py @@ -36,7 +36,7 @@ def __call__(self, request: "Request") -> Optional["Response"]: tracer = get_tracer() ctx = tracer.extract(Format.HTTP_HEADERS, dict(request.headers)) - with tracer.start_as_current_span("wsgi", span_context=ctx) as span: + with tracer.start_as_current_span("wsgi", context=ctx) as span: span.set_attribute(SpanAttributes.HTTP_HOST, request.host) span.set_attribute(SpanAttributes.HTTP_METHOD, request.method) span.set_attribute(SpanAttributes.HTTP_URL, request.path) diff --git a/src/instana/instrumentation/redis.py b/src/instana/instrumentation/redis.py index b4962581..208b0f0a 100644 --- a/src/instana/instrumentation/redis.py +++ b/src/instana/instrumentation/redis.py @@ -7,6 +7,7 @@ import redis import wrapt + from opentelemetry.context import get_current from instana.log import logger from instana.span.span import InstanaSpan @@ -49,9 +50,9 @@ def execute_command_with_instana( if not tracer or (operation_name in EXCLUDED_PARENT_SPANS): return wrapped(*args, **kwargs) - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() - with tracer.start_as_current_span("redis", span_context=parent_context) as span: + with tracer.start_as_current_span("redis", context=parent_context) as span: try: collect_attributes(span, instance, args, kwargs) if len(args) > 0: @@ -76,9 +77,9 @@ def execute_with_instana( if not tracer or (operation_name in EXCLUDED_PARENT_SPANS): return wrapped(*args, **kwargs) - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() - with tracer.start_as_current_span("redis", span_context=parent_context) as span: + with tracer.start_as_current_span("redis", context=parent_context) as span: try: collect_attributes(span, instance, args, kwargs) span.set_attribute("command", "PIPELINE") diff --git a/src/instana/instrumentation/sanic.py b/src/instana/instrumentation/sanic.py index 8d0537ad..6d5dce78 100644 --- a/src/instana/instrumentation/sanic.py +++ b/src/instana/instrumentation/sanic.py @@ -51,7 +51,7 @@ def request_with_instana(request: Request) -> None: headers = request.headers.copy() parent_context = tracer.extract(Format.HTTP_HEADERS, headers) - span = tracer.start_span("asgi", span_context=parent_context) + span = tracer.start_span("asgi", context=parent_context) request.ctx.span = span ctx = trace.set_span_in_context(span) diff --git a/src/instana/instrumentation/spyne.py b/src/instana/instrumentation/spyne.py index 6b6055e5..fe2417ad 100644 --- a/src/instana/instrumentation/spyne.py +++ b/src/instana/instrumentation/spyne.py @@ -1,31 +1,32 @@ # (c) Copyright IBM Corp. 2025 try: - import spyne # noqa: F401 - import wrapt + from types import SimpleNamespace from typing import ( TYPE_CHECKING, - Dict, Any, Callable, - Tuple, + Dict, Iterable, - Type, Optional, + Tuple, + Type, ) - from types import SimpleNamespace + import spyne # noqa: F401 + import wrapt from instana.log import logger - from instana.singletons import agent, get_tracer from instana.propagators.format import Format + from instana.singletons import agent, get_tracer from instana.util.secrets import strip_secrets_from_query if TYPE_CHECKING: - from instana.span.span import InstanaSpan from spyne.application import Application from spyne.server.wsgi import WsgiApplication + from instana.span.span import InstanaSpan + def set_span_attributes(span: "InstanaSpan", headers: Dict[str, Any]) -> None: if "PATH_INFO" in headers: span.set_attribute("rpc.call", headers["PATH_INFO"]) @@ -66,9 +67,7 @@ def handle_error_with_instana( headers = ctx.transport.req_env span_context = tracer.extract(Format.HTTP_HEADERS, headers) - with tracer.start_as_current_span( - "rpc-server", span_context=span_context - ) as span: + with tracer.start_as_current_span("rpc-server", context=span_context) as span: set_span_attributes(span, headers) response_headers = ctx.transport.resp_headers @@ -115,7 +114,7 @@ def process_request_with_instana( with tracer.start_as_current_span( "rpc-server", - span_context=span_context, + context=span_context, end_on_exit=False, ) as span: set_span_attributes(span, headers) @@ -137,3 +136,4 @@ def process_request_with_instana( except ImportError: pass + pass diff --git a/src/instana/instrumentation/sqlalchemy.py b/src/instana/instrumentation/sqlalchemy.py index 8ccda7ef..652f16bd 100644 --- a/src/instana/instrumentation/sqlalchemy.py +++ b/src/instana/instrumentation/sqlalchemy.py @@ -6,6 +6,7 @@ from typing import Any, Dict from opentelemetry import context, trace +from opentelemetry.context import get_current from instana.log import logger from instana.span.span import InstanaSpan, get_current_span @@ -30,9 +31,9 @@ def receive_before_cursor_execute( if not tracer: return - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() - span = tracer.start_span("sqlalchemy", span_context=parent_context) + span = tracer.start_span("sqlalchemy", context=parent_context) conn = kw["conn"] conn.span = span span.set_attribute("sqlalchemy.sql", kw["statement"]) diff --git a/src/instana/instrumentation/tornado/client.py b/src/instana/instrumentation/tornado/client.py index 33a4dc51..bda0501a 100644 --- a/src/instana/instrumentation/tornado/client.py +++ b/src/instana/instrumentation/tornado/client.py @@ -3,25 +3,28 @@ try: - import tornado + import functools + from typing import TYPE_CHECKING, Any, Callable, Dict, Tuple + import tornado import wrapt - import functools - from typing import TYPE_CHECKING, Dict, Any, Callable, Tuple if TYPE_CHECKING: - from instana.span.span import InstanaSpan from asyncio import Future + from tornado.httpclient import AsyncHTTPClient + from instana.span.span import InstanaSpan + + from opentelemetry.context import get_current from opentelemetry.semconv.trace import SpanAttributes from instana.log import logger + from instana.propagators.format import Format from instana.singletons import agent, get_tracer + from instana.span.span import get_current_span from instana.util.secrets import strip_secrets_from_query from instana.util.traceutils import extract_custom_headers - from instana.propagators.format import Format - from instana.span.span import get_current_span @wrapt.patch_function_wrapper("tornado.httpclient", "AsyncHTTPClient.fetch") def fetch_with_instana( @@ -53,9 +56,9 @@ def fetch_with_instana( new_kwargs[param] = kwargs.pop(param) kwargs = new_kwargs - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() tracer = get_tracer() - span = tracer.start_span("tornado-client", span_context=parent_context) + span = tracer.start_span("tornado-client", context=parent_context) extract_custom_headers(span, request.headers) @@ -99,3 +102,5 @@ def finish_tracing(future: "Future", span: "InstanaSpan") -> None: logger.debug("Instrumenting tornado client") except ImportError: pass +except ImportError: + pass diff --git a/src/instana/instrumentation/tornado/server.py b/src/instana/instrumentation/tornado/server.py index 800eab46..969bd9d0 100644 --- a/src/instana/instrumentation/tornado/server.py +++ b/src/instana/instrumentation/tornado/server.py @@ -34,7 +34,7 @@ def execute_with_instana( Format.HTTP_HEADERS, dict(instance.request.headers.items()) ) - span = tracer.start_span("tornado-server", span_context=span_context) + span = tracer.start_span("tornado-server", context=span_context) # Query param scrubbing if instance.request.query is not None and len(instance.request.query) > 0: diff --git a/src/instana/instrumentation/urllib3.py b/src/instana/instrumentation/urllib3.py index 01023071..05a7a52a 100644 --- a/src/instana/instrumentation/urllib3.py +++ b/src/instana/instrumentation/urllib3.py @@ -5,16 +5,14 @@ from typing import TYPE_CHECKING, Any, Callable, Dict, Tuple, Union import wrapt +from opentelemetry.context import get_current from opentelemetry.semconv.trace import SpanAttributes from instana.log import logger from instana.propagators.format import Format from instana.singletons import agent from instana.util.secrets import strip_secrets_from_query -from instana.util.traceutils import ( - get_tracer_tuple, - extract_custom_headers, -) +from instana.util.traceutils import extract_custom_headers, get_tracer_tuple if TYPE_CHECKING: from instana.span.span import InstanaSpan @@ -99,11 +97,9 @@ def urlopen_with_instana( if not tracer or span_name == "boto3": return wrapped(*args, **kwargs) - parent_context = parent_span.get_span_context() if parent_span else None + parent_context = get_current() - with tracer.start_as_current_span( - "urllib3", span_context=parent_context - ) as span: + with tracer.start_as_current_span("urllib3", context=parent_context) as span: try: kvs = _collect_kvs(instance, args, kwargs) if "url" in kvs: diff --git a/src/instana/instrumentation/wsgi.py b/src/instana/instrumentation/wsgi.py index 63798e89..b2413eec 100644 --- a/src/instana/instrumentation/wsgi.py +++ b/src/instana/instrumentation/wsgi.py @@ -5,10 +5,10 @@ Instana WSGI Middleware """ -from typing import Dict, Any, Callable, List, Tuple, Optional, Iterable, TYPE_CHECKING +from typing import TYPE_CHECKING, Any, Callable, Dict, Iterable, List, Optional, Tuple -from opentelemetry.semconv.trace import SpanAttributes from opentelemetry import context, trace +from opentelemetry.semconv.trace import SpanAttributes from instana.propagators.format import Format from instana.singletons import agent, get_tracer @@ -30,8 +30,8 @@ def __call__(self, environ: Dict[str, Any], start_response: Callable) -> object: tracer = get_tracer() # Extract context and start span - span_context = tracer.extract(Format.HTTP_HEADERS, env) - span = tracer.start_span("wsgi", span_context=span_context) + parent_context = tracer.extract(Format.HTTP_HEADERS, env) + span = tracer.start_span("wsgi", context=parent_context) # Attach context - this makes the span current ctx = trace.set_span_in_context(span) diff --git a/src/instana/propagators/base_propagator.py b/src/instana/propagators/base_propagator.py index a981c8a1..acb79e23 100644 --- a/src/instana/propagators/base_propagator.py +++ b/src/instana/propagators/base_propagator.py @@ -3,8 +3,15 @@ import os +from typing import Any, Dict, List, Optional, Tuple, TypeVar -from typing import Any, Optional, TypeVar, Dict, List, Tuple +from opentelemetry.context.context import Context +from opentelemetry.trace import ( + INVALID_SPAN_ID, + INVALID_TRACE_ID, + NonRecordingSpan, + set_span_in_context, +) from instana.log import logger from instana.span_context import SpanContext @@ -12,18 +19,13 @@ header_to_id, header_to_long_id, hex_id, + hex_id_limited, internal_id, internal_id_limited, - hex_id_limited, ) from instana.w3c_trace_context.traceparent import Traceparent from instana.w3c_trace_context.tracestate import Tracestate -from opentelemetry.trace import ( - INVALID_SPAN_ID, - INVALID_TRACE_ID, -) - # The carrier, typed here as CarrierT, can be a dict, a list, or a tuple. # Using the trace header as an example, it can be in the following forms # for extraction: @@ -399,7 +401,7 @@ def __extract_w3c_trace_context_headers(self, dc): def extract( self, carrier: CarrierT, disable_w3c_trace_context: bool = False - ) -> Optional[SpanContext]: + ) -> Optional[Context]: """ This method overrides one of the Base classes as with the introduction of W3C trace context for the HTTP requests more extracting steps and @@ -441,7 +443,9 @@ def extract( tracestate, disable_w3c_trace_context, ) - return span_context + + context = set_span_in_context(NonRecordingSpan(span_context), Context()) + return context except Exception: logger.debug("base_propagator extract error:", exc_info=True) diff --git a/src/instana/propagators/binary_propagator.py b/src/instana/propagators/binary_propagator.py index d5b31e16..d94f77fc 100644 --- a/src/instana/propagators/binary_propagator.py +++ b/src/instana/propagators/binary_propagator.py @@ -2,11 +2,13 @@ # (c) Copyright Instana Inc. 2020 -from instana.log import logger -from instana.propagators.base_propagator import BasePropagator +from typing import Optional from opentelemetry.trace.span import format_span_id +from instana.log import logger +from instana.propagators.base_propagator import BasePropagator, CarrierT +from instana.span_context import SpanContext from instana.util.ids import define_server_timing @@ -17,17 +19,22 @@ class BinaryPropagator(BasePropagator): """ # ByteArray variations from base class - HEADER_KEY_T = b'x-instana-t' - HEADER_KEY_S = b'x-instana-s' - HEADER_KEY_L = b'x-instana-l' - HEADER_SERVER_TIMING = b'server-timing' - HEADER_KEY_TRACEPARENT = b'traceparent' - HEADER_KEY_TRACESTATE = b'tracestate' + HEADER_KEY_T = b"x-instana-t" + HEADER_KEY_S = b"x-instana-s" + HEADER_KEY_L = b"x-instana-l" + HEADER_SERVER_TIMING = b"server-timing" + HEADER_KEY_TRACEPARENT = b"traceparent" + HEADER_KEY_TRACESTATE = b"tracestate" - def __init__(self): + def __init__(self) -> None: super(BinaryPropagator, self).__init__() - def inject(self, span_context, carrier, disable_w3c_trace_context=True): + def inject( + self, + span_context: SpanContext, + carrier: CarrierT, + disable_w3c_trace_context: bool = True, + ) -> Optional[CarrierT]: try: trace_id = format_span_id(span_context.trace_id).encode() span_id = format_span_id(span_context.span_id).encode() @@ -37,21 +44,23 @@ def inject(self, span_context, carrier, disable_w3c_trace_context=True): if disable_w3c_trace_context: traceparent, tracestate = [None] * 2 else: - traceparent, tracestate = self._get_participating_trace_context(span_context) + traceparent, tracestate = self._get_participating_trace_context( + span_context + ) try: - traceparent = str.encode(traceparent) - tracestate = str.encode(tracestate) + traceparent = str.encode(traceparent) # type: ignore[arg-type] + tracestate = str.encode(tracestate) # type: ignore[arg-type] except Exception: traceparent, tracestate = [None] * 2 if isinstance(carrier, dict) or hasattr(carrier, "__dict__"): if traceparent and tracestate: - carrier[self.HEADER_KEY_TRACEPARENT] = traceparent - carrier[self.HEADER_KEY_TRACESTATE] = tracestate - carrier[self.HEADER_KEY_T] = trace_id - carrier[self.HEADER_KEY_S] = span_id - carrier[self.HEADER_KEY_L] = level - carrier[self.HEADER_SERVER_TIMING] = server_timing + carrier[self.HEADER_KEY_TRACEPARENT] = traceparent # type: ignore[index] + carrier[self.HEADER_KEY_TRACESTATE] = tracestate # type: ignore[index] + carrier[self.HEADER_KEY_T] = trace_id # type: ignore[index] + carrier[self.HEADER_KEY_S] = span_id # type: ignore[index] + carrier[self.HEADER_KEY_L] = level # type: ignore[index] + carrier[self.HEADER_SERVER_TIMING] = server_timing # type: ignore[index] elif isinstance(carrier, list): if traceparent and tracestate: carrier.append((self.HEADER_KEY_TRACEPARENT, traceparent)) @@ -62,13 +71,17 @@ def inject(self, span_context, carrier, disable_w3c_trace_context=True): carrier.append((self.HEADER_SERVER_TIMING, server_timing)) elif isinstance(carrier, tuple): if traceparent and tracestate: - carrier = carrier.__add__(((self.HEADER_KEY_TRACEPARENT, traceparent),)) - carrier = carrier.__add__(((self.HEADER_KEY_TRACESTATE, tracestate),)) + carrier = carrier.__add__( + ((self.HEADER_KEY_TRACEPARENT, traceparent),) + ) + carrier = carrier.__add__( + ((self.HEADER_KEY_TRACESTATE, tracestate),) + ) carrier = carrier.__add__(((self.HEADER_KEY_T, trace_id),)) carrier = carrier.__add__(((self.HEADER_KEY_S, span_id),)) carrier = carrier.__add__(((self.HEADER_KEY_L, level),)) carrier = carrier.__add__(((self.HEADER_SERVER_TIMING, server_timing),)) - elif hasattr(carrier, '__setitem__'): + elif hasattr(carrier, "__setitem__"): if traceparent and tracestate: carrier.__setitem__(self.HEADER_KEY_TRACEPARENT, traceparent) carrier.__setitem__(self.HEADER_KEY_TRACESTATE, tracestate) @@ -82,5 +95,3 @@ def inject(self, span_context, carrier, disable_w3c_trace_context=True): return carrier except Exception: logger.debug("inject error:", exc_info=True) - - diff --git a/src/instana/propagators/http_propagator.py b/src/instana/propagators/http_propagator.py index c6491076..667b1ddd 100644 --- a/src/instana/propagators/http_propagator.py +++ b/src/instana/propagators/http_propagator.py @@ -2,12 +2,15 @@ # (c) Copyright Instana Inc. 2020 +from typing import Any + +from opentelemetry.trace.span import format_span_id + from instana.log import logger -from instana.propagators.base_propagator import BasePropagator -from instana.util.ids import define_server_timing, hex_id_limited +from instana.propagators.base_propagator import BasePropagator, CarrierT from instana.span_context import SpanContext +from instana.util.ids import define_server_timing, hex_id_limited -from opentelemetry.trace.span import format_span_id class HTTPPropagator(BasePropagator): """ @@ -17,17 +20,24 @@ class HTTPPropagator(BasePropagator): The character set should be restricted to HTTP compatible. """ - def __init__(self): + def __init__(self) -> None: super(HTTPPropagator, self).__init__() - def inject(self, span_context, carrier, disable_w3c_trace_context=False): + def inject( + self, + span_context: SpanContext, + carrier: CarrierT, + disable_w3c_trace_context: bool = False, + ) -> None: trace_id = span_context.trace_id span_id = span_context.span_id dictionary_carrier = self.extract_headers_dict(carrier) if dictionary_carrier: # Suppression `level` made in the child context or in the parent context # has priority over any non-suppressed `level` setting - child_level = int(self.extract_instana_headers(dictionary_carrier)[2] or "1") + child_level = int( + self.extract_instana_headers(dictionary_carrier)[2] or "1" + ) new_level = min(child_level, span_context.level) if new_level != span_context.level: @@ -46,7 +56,7 @@ def inject(self, span_context, carrier, disable_w3c_trace_context=False): correlation_type=span_context.correlation_type, correlation_id=span_context.correlation_id, traceparent=span_context.traceparent, - tracestate=span_context.tracestate + tracestate=span_context.tracestate, ) serializable_level = str(span_context.level) @@ -54,13 +64,15 @@ def inject(self, span_context, carrier, disable_w3c_trace_context=False): if disable_w3c_trace_context: traceparent, tracestate = [None] * 2 else: - traceparent, tracestate = self._get_participating_trace_context(span_context) + traceparent, tracestate = self._get_participating_trace_context( + span_context + ) - def inject_key_value(carrier, key, value): + def inject_key_value(carrier: CarrierT, key: str, value: Any) -> None: if isinstance(carrier, list): carrier.append((key, value)) - elif isinstance(carrier, dict) or '__setitem__' in dir(carrier): - carrier[key] = value + elif isinstance(carrier, dict) or "__setitem__" in dir(carrier): + carrier[key] = value # type: ignore[index] else: raise Exception("Unsupported carrier type", type(carrier)) diff --git a/src/instana/propagators/kafka_propagator.py b/src/instana/propagators/kafka_propagator.py index 97bae58c..2be77fe1 100644 --- a/src/instana/propagators/kafka_propagator.py +++ b/src/instana/propagators/kafka_propagator.py @@ -1,12 +1,14 @@ # (c) Copyright IBM Corp. 2025 from typing import Any, Dict, Optional +from opentelemetry.context.context import Context from opentelemetry.trace.span import format_span_id from instana.log import logger from instana.propagators.base_propagator import BasePropagator, CarrierT -from instana.util.ids import hex_id_limited from instana.span_context import SpanContext +from instana.util.ids import hex_id_limited + class KafkaPropagator(BasePropagator): """ @@ -50,7 +52,7 @@ def extract_carrier_headers(self, carrier: CarrierT) -> Dict[str, Any]: def extract( self, carrier: CarrierT, disable_w3c_trace_context: bool = False - ) -> Optional[SpanContext]: + ) -> Optional[Context]: """ This method overrides one of the Base classes as with the introduction of W3C trace context for the Kafka requests more extracting steps and @@ -61,7 +63,7 @@ def extract( disable_w3c_trace_context (bool): A flag to disable the W3C trace context. Returns: - Optional[SpanContext]: The extracted span context or None. + Optional[Context]: The extracted span context or None. """ try: headers = self.extract_carrier_headers(carrier=carrier) @@ -118,7 +120,7 @@ def inject( correlation_type=span_context.correlation_type, correlation_id=span_context.correlation_id, traceparent=span_context.traceparent, - tracestate=span_context.tracestate + tracestate=span_context.tracestate, ) def inject_key_value(carrier, key, value): diff --git a/src/instana/propagators/text_propagator.py b/src/instana/propagators/text_propagator.py index 59c2b3ab..96f6e8e0 100644 --- a/src/instana/propagators/text_propagator.py +++ b/src/instana/propagators/text_propagator.py @@ -2,11 +2,13 @@ # (c) Copyright Instana Inc. 2020 -from instana.log import logger -from instana.propagators.base_propagator import BasePropagator +from typing import Optional from opentelemetry.trace.span import format_span_id +from instana.log import logger +from instana.propagators.base_propagator import BasePropagator, CarrierT +from instana.span_context import SpanContext from instana.util.ids import define_server_timing @@ -18,17 +20,22 @@ class TextPropagator(BasePropagator): The character set is unrestricted. """ - def inject(self, span_context, carrier, disable_w3c_trace_context=True): + def inject( + self, + span_context: SpanContext, + carrier: CarrierT, + disable_w3c_trace_context: bool = True, + ) -> Optional[CarrierT]: try: trace_id = format_span_id(span_context.trace_id) span_id = format_span_id(span_context.span_id) server_timing = define_server_timing(span_context.trace_id).encode() if isinstance(carrier, dict) or hasattr(carrier, "__dict__"): - carrier[self.LC_HEADER_KEY_T] = trace_id - carrier[self.LC_HEADER_KEY_S] = span_id - carrier[self.LC_HEADER_KEY_L] = "1" - carrier[self.LC_HEADER_KEY_SERVER_TIMING] = server_timing + carrier[self.LC_HEADER_KEY_T] = trace_id # type: ignore[index] + carrier[self.LC_HEADER_KEY_S] = span_id # type: ignore[index] + carrier[self.LC_HEADER_KEY_L] = "1" # type: ignore[index] + carrier[self.LC_HEADER_KEY_SERVER_TIMING] = server_timing # type: ignore[index] elif isinstance(carrier, list): carrier.append((self.LC_HEADER_KEY_T, trace_id)) carrier.append((self.LC_HEADER_KEY_S, span_id)) @@ -38,8 +45,10 @@ def inject(self, span_context, carrier, disable_w3c_trace_context=True): carrier = carrier.__add__(((self.LC_HEADER_KEY_T, trace_id),)) carrier = carrier.__add__(((self.LC_HEADER_KEY_S, span_id),)) carrier = carrier.__add__(((self.LC_HEADER_KEY_L, "1"),)) - carrier = carrier.__add__(((self.LC_HEADER_KEY_SERVER_TIMING, server_timing),)) - elif hasattr(carrier, '__setitem__'): + carrier = carrier.__add__( + ((self.LC_HEADER_KEY_SERVER_TIMING, server_timing),) + ) + elif hasattr(carrier, "__setitem__"): carrier.__setitem__(self.LC_HEADER_KEY_T, trace_id) carrier.__setitem__(self.LC_HEADER_KEY_S, span_id) carrier.__setitem__(self.LC_HEADER_KEY_L, "1") diff --git a/src/instana/span/span.py b/src/instana/span/span.py index 0319569f..721e5bb0 100644 --- a/src/instana/span/span.py +++ b/src/instana/span/span.py @@ -241,7 +241,7 @@ def assure_errored(self) -> None: INVALID_SPAN = NonRecordingSpan(INVALID_SPAN_CONTEXT) -def get_current_span(context: Optional[Context] = None) -> InstanaSpan: +def get_current_span(context: Optional[Context] = None) -> Union[InstanaSpan, Span]: """Retrieve the current span. Args: @@ -252,6 +252,8 @@ def get_current_span(context: Optional[Context] = None) -> InstanaSpan: The Span set in the context if it exists. INVALID_SPAN otherwise. """ span = get_value(_SPAN_KEY, context=context) - if span is None or not isinstance(span, InstanaSpan): + if span is None or ( + not isinstance(span, InstanaSpan) and not isinstance(span, Span) + ): return INVALID_SPAN return span diff --git a/src/instana/tracer.py b/src/instana/tracer.py index 8546a8a1..795f0d53 100644 --- a/src/instana/tracer.py +++ b/src/instana/tracer.py @@ -3,7 +3,6 @@ import time -from contextlib import contextmanager from typing import TYPE_CHECKING, Iterator, Mapping, Optional, Type, Union from opentelemetry.context.context import Context @@ -16,6 +15,7 @@ use_span, ) from opentelemetry.util import types +from opentelemetry.util._decorator import _agnosticcontextmanager from instana.agent.host import HostAgent from instana.log import logger @@ -32,6 +32,8 @@ from instana.util.ids import generate_id if TYPE_CHECKING: + from opentelemetry.trace import Span + from instana.agent.base import BaseAgent from instana.propagators.base_propagator import BasePropagator, CarrierT @@ -108,7 +110,7 @@ def exporter(self) -> Optional[Type["BaseAgent"]]: def start_span( self, name: str, - span_context: Optional[SpanContext] = None, + context: Optional[Context] = None, kind: SpanKind = SpanKind.INTERNAL, attributes: types.Attributes = None, links: _Links = None, @@ -116,9 +118,7 @@ def start_span( record_exception: bool = True, set_status_on_exception: bool = True, ) -> InstanaSpan: - parent_context = ( - span_context if span_context else get_current_span().get_span_context() - ) + parent_context = get_current_span(context).get_span_context() if parent_context and not isinstance(parent_context, SpanContext): raise TypeError("parent_context must be an Instana SpanContext or None.") @@ -136,11 +136,11 @@ def start_span( return span - @contextmanager + @_agnosticcontextmanager def start_as_current_span( self, name: str, - span_context: Optional[SpanContext] = None, + context: Optional[Context] = None, kind: SpanKind = SpanKind.INTERNAL, attributes: types.Attributes = None, links: _Links = None, @@ -148,10 +148,10 @@ def start_as_current_span( record_exception: bool = True, set_status_on_exception: bool = True, end_on_exit: bool = True, - ) -> Iterator[InstanaSpan]: + ) -> Iterator["Span"]: span = self.start_span( name=name, - span_context=span_context, + context=context, kind=kind, attributes=attributes, links=links, @@ -167,7 +167,9 @@ def start_as_current_span( ) as span: yield span - def _create_span_context(self, parent_context: SpanContext) -> SpanContext: + def _create_span_context( + self, parent_context: Optional[SpanContext] = None + ) -> SpanContext: """Creates a new SpanContext based on the given parent context.""" if parent_context and parent_context.is_valid: diff --git a/tests/collector/test_utils.py b/tests/collector/test_utils.py index 6d233934..f6eba0ff 100644 --- a/tests/collector/test_utils.py +++ b/tests/collector/test_utils.py @@ -1,15 +1,16 @@ # (c) Copyright IBM Corp. 2025 -import pytest from typing import Generator + +import pytest +from opentelemetry.context.context import Context +from opentelemetry.trace.span import format_span_id + from instana.collector.utils import format_span from instana.singletons import get_tracer from instana.span.registered_span import RegisteredSpan from instana.span.span import get_current_span -from opentelemetry.trace.span import format_span_id - -from instana.span_context import SpanContext class TestUtils: @@ -17,13 +18,13 @@ class TestUtils: def _resource(self) -> Generator[None, None, None]: self.tracer = get_tracer() self.recorder = self.tracer.span_processor - self.span_context = None + self.context = None yield - def test_format_span(self, span_context: SpanContext) -> None: - self.span_context = span_context + def test_format_span(self, context: Context) -> None: + self.context = context with self.tracer.start_as_current_span( - name="span1", span_context=self.span_context + name="span1", context=self.context ) as pspan: expected_trace_id = format_span_id(pspan.context.trace_id) expected_span_id = format_span_id(pspan.context.span_id) @@ -47,3 +48,4 @@ def test_format_span(self, span_context: SpanContext) -> None: assert formatted_spans[1].k == 1 assert formatted_spans[1].s != formatted_spans[0].s assert formatted_spans[1].n == "span2" + assert formatted_spans[1].n == "span2" diff --git a/tests/propagators/test_http_propagator.py b/tests/propagators/test_http_propagator.py index bac0a173..76014345 100644 --- a/tests/propagators/test_http_propagator.py +++ b/tests/propagators/test_http_propagator.py @@ -5,6 +5,7 @@ from typing import Any, Dict, Generator import pytest +from opentelemetry.context.context import Context from opentelemetry.trace import ( INVALID_SPAN_ID, INVALID_TRACE_ID, @@ -13,6 +14,7 @@ ) from instana.propagators.http_propagator import HTTPPropagator +from instana.span.span import get_current_span from instana.span_context import SpanContext from instana.util.ids import header_to_long_id, internal_id @@ -76,18 +78,22 @@ def test_extract_carrier_dict( } ctx = self.hptc.extract(carrier) - - assert ctx.correlation_id == str(span_id) - assert ctx.correlation_type == "web" - assert not ctx.instana_ancestor - assert ctx.level == 1 - assert ctx.long_trace_id == header_to_long_id(_instana_long_tracer_id) - assert ctx.span_id == _span_id - assert not ctx.synthetic - assert ctx.trace_id == _trace_id - assert ctx.trace_parent - assert ctx.traceparent == f"00-{_instana_long_tracer_id}-{_instana_span_id}-01" - assert ctx.tracestate == _tracestate + span_ctx = get_current_span(ctx).get_span_context() + + assert span_ctx.correlation_id == str(span_id) + assert span_ctx.correlation_type == "web" + assert span_ctx.level == 1 + assert span_ctx.long_trace_id == header_to_long_id(_instana_long_tracer_id) + assert span_ctx.span_id == _span_id + assert span_ctx.trace_id == _trace_id + assert span_ctx.trace_parent + assert ( + span_ctx.traceparent + == f"00-{_instana_long_tracer_id}-{_instana_span_id}-01" + ) + assert span_ctx.tracestate == _tracestate + assert not span_ctx.synthetic + assert not span_ctx.instana_ancestor def test_extract_carrier_list( self, @@ -112,18 +118,22 @@ def test_extract_carrier_list( ] ctx = self.hptc.extract(carrier) - - assert not ctx.correlation_id - assert not ctx.correlation_type - assert not ctx.instana_ancestor - assert ctx.level == 1 - assert not ctx.long_trace_id - assert ctx.span_id == _span_id - assert not ctx.synthetic - assert ctx.trace_id == internal_id(_trace_id) - assert not ctx.trace_parent - assert ctx.traceparent == f"00-{_instana_long_tracer_id}-{_instana_span_id}-01" - assert ctx.tracestate == _tracestate + span_ctx = get_current_span(ctx).get_span_context() + + assert not span_ctx.correlation_id + assert not span_ctx.correlation_type + assert not span_ctx.instana_ancestor + assert span_ctx.level == 1 + assert not span_ctx.long_trace_id + assert span_ctx.span_id == _span_id + assert not span_ctx.synthetic + assert span_ctx.trace_id == internal_id(_trace_id) + assert not span_ctx.trace_parent + assert ( + span_ctx.traceparent + == f"00-{_instana_long_tracer_id}-{_instana_span_id}-01" + ) + assert span_ctx.tracestate == _tracestate def test_extract_carrier_dict_validate_Exception_None_returned( self, @@ -147,13 +157,15 @@ def test_extract_carrier_dict_validate_Exception_None_returned( } ctx = self.hptc.extract(carrier) + span_ctx = get_current_span(ctx).get_span_context() - assert isinstance(ctx, SpanContext) - assert ctx.trace_id == INVALID_TRACE_ID - assert ctx.span_id == INVALID_SPAN_ID - assert not ctx.synthetic - assert ctx.correlation_id == str(span_id) - assert ctx.correlation_type == "web" + assert isinstance(ctx, Context) + assert isinstance(span_ctx, SpanContext) + assert span_ctx.trace_id == INVALID_TRACE_ID + assert span_ctx.span_id == INVALID_SPAN_ID + assert not span_ctx.synthetic + assert span_ctx.correlation_id == str(span_id) + assert span_ctx.correlation_type == "web" def test_extract_fake_exception( self, @@ -194,18 +206,19 @@ def test_extract_carrier_dict_corrupted_level_header( } ctx = self.hptc.extract(carrier) - - assert not ctx.correlation_id - assert ctx.correlation_type == "web" - assert not ctx.instana_ancestor - assert ctx.level == 1 - assert ctx.long_trace_id == header_to_long_id(_instana_long_tracer_id) - assert ctx.span_id == _span_id - assert not ctx.synthetic - assert ctx.trace_id == _trace_id - assert ctx.trace_parent - assert ctx.traceparent == _traceparent - assert ctx.tracestate == _tracestate + span_ctx = get_current_span(ctx).get_span_context() + + assert not span_ctx.correlation_id + assert span_ctx.correlation_type == "web" + assert not span_ctx.instana_ancestor + assert span_ctx.level == 1 + assert span_ctx.long_trace_id == header_to_long_id(_instana_long_tracer_id) + assert span_ctx.span_id == _span_id + assert not span_ctx.synthetic + assert span_ctx.trace_id == _trace_id + assert span_ctx.trace_parent + assert span_ctx.traceparent == _traceparent + assert span_ctx.tracestate == _tracestate def test_extract_carrier_dict_level_header_not_splitable( self, @@ -224,18 +237,19 @@ def test_extract_carrier_dict_level_header_not_splitable( } ctx = self.hptc.extract(carrier) - - assert not ctx.correlation_id - assert not ctx.correlation_type - assert not ctx.instana_ancestor - assert ctx.level == 1 - assert not ctx.long_trace_id - assert ctx.span_id == _span_id - assert not ctx.synthetic - assert ctx.trace_id == internal_id(_trace_id) - assert not ctx.trace_parent - assert ctx.traceparent == _traceparent - assert ctx.tracestate == _tracestate + span_ctx = get_current_span(ctx).get_span_context() + + assert not span_ctx.correlation_id + assert not span_ctx.correlation_type + assert not span_ctx.instana_ancestor + assert span_ctx.level == 1 + assert not span_ctx.long_trace_id + assert span_ctx.span_id == _span_id + assert not span_ctx.synthetic + assert span_ctx.trace_id == internal_id(_trace_id) + assert not span_ctx.trace_parent + assert span_ctx.traceparent == _traceparent + assert span_ctx.tracestate == _tracestate # The following tests are based on the test cases defined in the # tracer_compliance_test_cases.json file. @@ -283,48 +297,49 @@ def test_w3c_off_x_instana_l_0( os.environ["INSTANA_DISABLE_W3C_TRACE_CORRELATION"] = disable_w3c ctx = self.hptc.extract(carrier_header) + span_ctx = get_current_span(ctx).get_span_context() # Assert the level is (zero) int, not str - assert isinstance(ctx.level, int) - assert ctx.level == 0 + assert isinstance(span_ctx.level, int) + assert span_ctx.level == 0 # Assert the suppression is on - assert ctx.suppression + assert span_ctx.suppression # Assert the rest of the attributes are on their default value - assert ctx.trace_id == INVALID_TRACE_ID - assert ctx.span_id == INVALID_SPAN_ID - assert not ctx.synthetic - assert not ctx.correlation_id - assert not ctx.trace_parent - assert not ctx.instana_ancestor - assert not ctx.long_trace_id - assert not ctx.correlation_type - assert not ctx.correlation_id + assert span_ctx.trace_id == INVALID_TRACE_ID + assert span_ctx.span_id == INVALID_SPAN_ID + assert not span_ctx.synthetic + assert not span_ctx.correlation_id + assert not span_ctx.trace_parent + assert not span_ctx.instana_ancestor + assert not span_ctx.long_trace_id + assert not span_ctx.correlation_type + assert not span_ctx.correlation_id # Assert that the traceparent is propagated when it is enabled if "traceparent" in carrier_header.keys(): - assert ctx.traceparent + assert span_ctx.traceparent tp_trace_id = header_to_long_id(carrier_header["traceparent"].split("-")[1]) else: - assert not ctx.traceparent - tp_trace_id = ctx.trace_id + assert not span_ctx.traceparent + tp_trace_id = span_ctx.trace_id # Assert that the tracestate is propagated when it is enabled if "tracestate" in carrier_header.keys(): - assert ctx.tracestate + assert span_ctx.tracestate else: - assert not ctx.tracestate + assert not span_ctx.tracestate # Simulate the side-effect of starting a span, getting a trace_id and span_id. # Actually, with OTel API using a Tuple to store the SpanContext info, # this will not change the values. - ctx.trace_id = ctx.span_id = trace_id + span_ctx.trace_id = span_ctx.span_id = trace_id # Test propagation downstream_carrier = {} - self.hptc.inject(ctx, downstream_carrier) + self.hptc.inject(span_ctx, downstream_carrier) # Assert the 'X-INSTANA-L' has been injected with the correct 0 value assert "X-INSTANA-L" in downstream_carrier @@ -333,7 +348,7 @@ def test_w3c_off_x_instana_l_0( assert "traceparent" in downstream_carrier assert ( downstream_carrier.get("traceparent") - == f"00-{format_trace_id(tp_trace_id)}-{format_span_id(ctx.span_id)}-00" + == f"00-{format_trace_id(tp_trace_id)}-{format_span_id(span_ctx.span_id)}-00" ) # Assert that the tracestate is propagated when it is enabled @@ -347,7 +362,8 @@ def test_suppression_when_child_level_is_lower( _span_id: int, ) -> None: """ - Test that span_context.level is updated when the child level (extracted from carrier) is lower than the current span_context.level. + Test that span_context.level is updated when the child level (extracted from carrier) is lower than the + current span_context.level. """ # Create a span context with level=1 original_span_context = SpanContext( @@ -365,16 +381,17 @@ def test_suppression_when_child_level_is_lower( # Extract the span context from the carrier to verify the level was updated extracted_context = self.hptc.extract(carrier_header) + span_ctx = get_current_span(extracted_context).get_span_context() # Verify that the level is 0 (suppressed) - assert extracted_context.level == 0 - assert extracted_context.suppression + assert span_ctx.level == 0 + assert span_ctx.suppression # Create a new carrier to test the propagation downstream_carrier = {} # Inject the extracted context into the downstream carrier - self.hptc.inject(extracted_context, downstream_carrier) + self.hptc.inject(span_ctx, downstream_carrier) # Verify that the downstream carrier has the correct level assert downstream_carrier.get("X-INSTANA-L") == "0" diff --git a/tests/span/test_span.py b/tests/span/test_span.py index 15479a7b..63afb5cd 100644 --- a/tests/span/test_span.py +++ b/tests/span/test_span.py @@ -6,11 +6,14 @@ from unittest.mock import patch import pytest +from opentelemetry.context.context import Context +from opentelemetry.trace.span import NonRecordingSpan, Span from opentelemetry.trace.status import Status, StatusCode from instana.recorder import StanRecorder from instana.span.span import INVALID_SPAN, Event, InstanaSpan, get_current_span from instana.span_context import SpanContext +from instana.tracer import InstanaTracerProvider class TestSpan: @@ -837,7 +840,7 @@ def test_span_assure_errored_exception( self.span.assure_errored() assert not self.span.attributes - def test_get_current_span(self, context: SpanContext) -> None: + def test_get_current_span(self, context: Context) -> None: self.span = get_current_span(context) assert isinstance(self.span, InstanaSpan) @@ -847,6 +850,44 @@ def test_get_current_span_INVALID_SPAN(self) -> None: assert self.span assert self.span == INVALID_SPAN + def test_get_current_span_OtelSpan( + self, + span_context: SpanContext, + ) -> None: + """Test get_current_span when get_value returns an OpenTelemetry Span object. + + This test verifies that get_current_span() properly handles when get_value() + returns a generic OpenTelemetry Span (NonRecordingSpan) that is not an InstanaSpan. + """ + # Create a mock OpenTelemetry Span (NonRecordingSpan) + mock_otel_span = NonRecordingSpan(span_context) + + # Mock get_value to return the OpenTelemetry Span + with patch("instana.span.span.get_value", return_value=mock_otel_span): + self.span = get_current_span() + + assert self.span + assert self.span == mock_otel_span + assert isinstance(self.span, NonRecordingSpan) + assert isinstance(self.span, Span) + assert not isinstance(self.span, InstanaSpan) + + def test_get_current_span_NoSpan( + self, + tracer_provider: InstanaTracerProvider, + ) -> None: + """Test get_current_span when get_value returns an different object. + + This test verifies that get_current_span() properly handles when get_value() + returns a generic object that is not an OpenTelemetry Span nor an InstanaSpan. + """ + # Mock get_value to return something that is not an OpenTelemetry Span nor an InstanaSpan. + with patch("instana.span.span.get_value", return_value=tracer_provider): + self.span = get_current_span() + + assert self.span + assert self.span == INVALID_SPAN + def test_span_duration_default( self, span_context: SpanContext, diff --git a/tests/test_tracer.py b/tests/test_tracer.py index 13ed495e..55b94be7 100644 --- a/tests/test_tracer.py +++ b/tests/test_tracer.py @@ -1,17 +1,14 @@ # (c) Copyright IBM Corp. 2024 import pytest +from opentelemetry.context.context import Context from opentelemetry.trace.span import _SPAN_ID_MAX_VALUE from instana.agent.host import HostAgent from instana.recorder import StanRecorder from instana.sampling import InstanaSampler -from instana.span.span import ( - INVALID_SPAN, - INVALID_SPAN_ID, - InstanaSpan, - get_current_span, -) +from instana.span.span import (INVALID_SPAN, INVALID_SPAN_ID, InstanaSpan, + get_current_span) from instana.span_context import SpanContext from instana.tracer import InstanaTracer, InstanaTracerProvider @@ -31,7 +28,7 @@ def test_tracer_defaults(tracer_provider: InstanaTracerProvider) -> None: def test_tracer_start_span( - tracer_provider: InstanaTracerProvider, span_context: SpanContext + tracer_provider: InstanaTracerProvider, context: Context ) -> None: span_name = "test-span" tracer = InstanaTracer( @@ -40,7 +37,7 @@ def test_tracer_start_span( tracer_provider._exporter, tracer_provider._propagators, ) - span = tracer.start_span(name=span_name, span_context=span_context) + span = tracer.start_span(name=span_name, context=context) assert span assert isinstance(span, InstanaSpan) @@ -49,7 +46,7 @@ def test_tracer_start_span( def test_tracer_start_span_Exception( - mocker, tracer_provider: InstanaTracerProvider, span_context: SpanContext + mocker, tracer_provider: InstanaTracerProvider, context: Context ) -> None: span_name = "test-span" tracer = InstanaTracer( @@ -64,7 +61,7 @@ def test_tracer_start_span_Exception( return_value={"key": "value"}, ) with pytest.raises(AttributeError): - tracer.start_span(name=span_name, span_context=span_context) + tracer.start_span(name=span_name, context=context) def test_tracer_start_as_current_span(tracer_provider: InstanaTracerProvider) -> None: @@ -140,5 +137,3 @@ def test_tracer_create_span_context_root( assert new_span_context.trace_id <= _SPAN_ID_MAX_VALUE assert new_span_context.trace_id == new_span_context.span_id - -