Skip to content

Commit fab489d

Browse files
committed
feat(opentracing-shim): Map OpenTracing span.kind tag to OTel SpanKind
The OpenTracing shim was not translating the span.kind tag to the OpenTelemetry SpanKind argument. This change extracts the span.kind tag from OpenTracing spans and maps it to the corresponding OTel SpanKind (CLIENT, SERVER, CONSUMER, PRODUCER, or INTERNAL). The span.kind tag is preserved in the span attributes to avoid modifying user data within the shim. Fixes open-telemetry#2549 Made-with: Cursor
1 parent fb94553 commit fab489d

3 files changed

Lines changed: 85 additions & 9 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
1212

1313
## Unreleased
1414

15+
- `opentelemetry-opentracing-shim`: Map OpenTracing `span.kind` tag to OpenTelemetry SpanKind
16+
([#2549](https://github.com/open-telemetry/opentelemetry-python/issues/2549))
1517
- `opentelemetry-sdk`: fix type annotations on `MetricReader` and related types
1618
([#4938](https://github.com/open-telemetry/opentelemetry-python/pull/4938/))
1719

1820
## Version 1.40.0/0.61b0 (2026-03-04)
1921

2022
- `opentelemetry-sdk`: deprecate `LoggingHandler` in favor of `opentelemetry-instrumentation-logging`, see `opentelemetry-instrumentation-logging` documentation
2123
([#4919](https://github.com/open-telemetry/opentelemetry-python/pull/4919))
22-
- `opentelemetry-sdk`: Clarify log processor error handling expectations in documentation
23-
([#4915](https://github.com/open-telemetry/opentelemetry-python/pull/4915))
2424
- bump semantic-conventions to v1.40.0
2525
([#4941](https://github.com/open-telemetry/opentelemetry-python/pull/4941))
2626
- Add stale PR GitHub Action

shim/opentelemetry-opentracing-shim/src/opentelemetry/shim/opentracing_shim/__init__.py

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
Tracer,
100100
UnsupportedFormatException,
101101
)
102+
from opentracing import tags as ot_tags
102103
from typing_extensions import deprecated
103104

104105
from opentelemetry.baggage import get_baggage, set_baggage
@@ -117,6 +118,7 @@
117118
INVALID_SPAN_CONTEXT,
118119
Link,
119120
NonRecordingSpan,
121+
SpanKind,
120122
TracerProvider,
121123
get_current_span,
122124
set_span_in_context,
@@ -130,6 +132,14 @@
130132
logger = logging.getLogger(__name__)
131133
_SHIM_KEY = create_key("scope_shim")
132134

135+
# Mapping from OpenTracing span kind tag values to OpenTelemetry SpanKind.
136+
_OPENTRACING_TO_OTEL_KIND = {
137+
ot_tags.SPAN_KIND_RPC_CLIENT: SpanKind.CLIENT,
138+
ot_tags.SPAN_KIND_RPC_SERVER: SpanKind.SERVER,
139+
ot_tags.SPAN_KIND_CONSUMER: SpanKind.CONSUMER,
140+
ot_tags.SPAN_KIND_PRODUCER: SpanKind.PRODUCER,
141+
}
142+
133143

134144
def create_tracer(otel_tracer_provider: TracerProvider) -> "TracerShim":
135145
"""Creates a :class:`TracerShim` object from the provided OpenTelemetry
@@ -674,13 +684,21 @@ def start_span(
674684
if start_time_ns is not None:
675685
start_time_ns = util.time_seconds_to_ns(start_time)
676686

677-
span = self._otel_tracer.start_span(
678-
operation_name,
679-
context=parent_span_context,
680-
links=valid_links,
681-
attributes=tags,
682-
start_time=start_time_ns,
683-
)
687+
# Extract span kind from OpenTracing tags if present.
688+
# OpenTracing uses the "span.kind" tag to indicate span kind, while
689+
# OpenTelemetry uses a dedicated kind argument.
690+
span_kwargs: dict = {
691+
"context": parent_span_context,
692+
"links": valid_links,
693+
"attributes": tags,
694+
"start_time": start_time_ns,
695+
}
696+
if tags is not None and ot_tags.SPAN_KIND in tags:
697+
span_kwargs["kind"] = _OPENTRACING_TO_OTEL_KIND.get(
698+
tags[ot_tags.SPAN_KIND], SpanKind.INTERNAL
699+
)
700+
701+
span = self._otel_tracer.start_span(operation_name, **span_kwargs)
684702

685703
context = SpanContextShim(span.get_span_context())
686704
return SpanShim(self, context, span)

shim/opentelemetry-opentracing-shim/tests/test_shim.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -668,3 +668,61 @@ def test_mixed_mode(self):
668668
scope.span.unwrap().parent,
669669
opentelemetry_span.context,
670670
)
671+
672+
def test_span_kind_from_tags(self):
673+
"""Test that span.kind OpenTracing tag is converted to OTel SpanKind."""
674+
675+
# Test consumer kind
676+
with self.shim.start_active_span(
677+
"TestSpanKind1", tags={"span.kind": "consumer"}
678+
) as scope:
679+
self.assertEqual(scope.span.unwrap().kind, trace.SpanKind.CONSUMER)
680+
681+
# Test producer kind
682+
with self.shim.start_active_span(
683+
"TestSpanKind2", tags={"span.kind": "producer"}
684+
) as scope:
685+
self.assertEqual(scope.span.unwrap().kind, trace.SpanKind.PRODUCER)
686+
687+
# Test client kind
688+
with self.shim.start_active_span(
689+
"TestSpanKind3", tags={"span.kind": "client"}
690+
) as scope:
691+
self.assertEqual(scope.span.unwrap().kind, trace.SpanKind.CLIENT)
692+
693+
# Test server kind
694+
with self.shim.start_active_span(
695+
"TestSpanKind4", tags={"span.kind": "server"}
696+
) as scope:
697+
self.assertEqual(scope.span.unwrap().kind, trace.SpanKind.SERVER)
698+
699+
# Test unknown kind defaults to INTERNAL
700+
with self.shim.start_active_span(
701+
"TestSpanKind5", tags={"span.kind": "unknown"}
702+
) as scope:
703+
self.assertEqual(scope.span.unwrap().kind, trace.SpanKind.INTERNAL)
704+
705+
# Test no span.kind tag defaults to INTERNAL
706+
with self.shim.start_active_span(
707+
"TestSpanKind6", tags={"foo": "bar"}
708+
) as scope:
709+
self.assertEqual(scope.span.unwrap().kind, trace.SpanKind.INTERNAL)
710+
711+
# Test no tags at all defaults to INTERNAL
712+
with self.shim.start_active_span("TestSpanKind7") as scope:
713+
self.assertEqual(scope.span.unwrap().kind, trace.SpanKind.INTERNAL)
714+
715+
def test_span_kind_tag_preserved_in_attributes(self):
716+
"""Test that span.kind tag is also preserved as an attribute."""
717+
718+
with self.shim.start_active_span(
719+
"TestSpanKindAttr", tags={"span.kind": "consumer", "foo": "bar"}
720+
) as scope:
721+
# Verify the span kind is correctly set
722+
self.assertEqual(scope.span.unwrap().kind, trace.SpanKind.CONSUMER)
723+
# Verify the span.kind tag is preserved as an attribute
724+
self.assertEqual(
725+
scope.span.unwrap().attributes.get("span.kind"), "consumer"
726+
)
727+
# Verify other attributes are also preserved
728+
self.assertEqual(scope.span.unwrap().attributes.get("foo"), "bar")

0 commit comments

Comments
 (0)