Skip to content

Commit 66d7402

Browse files
authored
Merge branch 'main' into get-tracer-doc-string
2 parents 35ed8b8 + 1f9ea47 commit 66d7402

43 files changed

Lines changed: 1527 additions & 209 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CHANGELOG.md

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

1313
## Unreleased
1414

15+
- `opentelemetry-sdk`: Drop unused Jaeger exporter environment variables (exporter removed in 1.22.0)
16+
([#4918](https://github.com/open-telemetry/opentelemetry-python/issues/4918))
17+
- `opentelemetry-sdk`: Clarify timeout units in environment variable documentation
18+
([#4906](https://github.com/open-telemetry/opentelemetry-python/pull/4906))
1519
- `opentelemetry-exporter-otlp-proto-grpc`: Fix re-initialization of gRPC channel on UNAVAILABLE error
1620
([#4825](https://github.com/open-telemetry/opentelemetry-python/pull/4825))
1721
- `opentelemetry-exporter-prometheus`: Fix duplicate HELP/TYPE declarations for metrics with different label sets
@@ -30,6 +34,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3034
([#4806](https://github.com/open-telemetry/opentelemetry-python/pull/4806))
3135
- Prevent possible endless recursion from happening in `SimpleLogRecordProcessor.on_emit`,
3236
([#4799](https://github.com/open-telemetry/opentelemetry-python/pull/4799)) and ([#4867](https://github.com/open-telemetry/opentelemetry-python/pull/4867)).
37+
- Implement span start/end metrics
38+
([#4880](https://github.com/open-telemetry/opentelemetry-python/pull/4880))
3339
- Add environment variable carriers to API
3440
([#4609](https://github.com/open-telemetry/opentelemetry-python/pull/4609))
3541
- Add experimental composable rule based sampler
@@ -38,6 +44,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3844
([#4862](https://github.com/open-telemetry/opentelemetry-python/pull/4862))
3945
- `opentelemetry-exporter-otlp-proto-http`: fix retry logic and error handling for connection failures in trace, metric, and log exporters
4046
([#4709](https://github.com/open-telemetry/opentelemetry-python/pull/4709))
47+
- `opentelemetry-sdk`: avoid RuntimeError during iteration of view instrument match dictionary in MetricReaderStorage.collect()
48+
([#4891](https://github.com/open-telemetry/opentelemetry-python/pull/4891))
49+
- Implement experimental TracerConfigurator
50+
([#4861](https://github.com/open-telemetry/opentelemetry-python/pull/4861))
51+
- bump semantic-conventions to v1.39.0
52+
([#4914](https://github.com/open-telemetry/opentelemetry-python/pull/4914))
4153

4254
## Version 1.39.0/0.60b0 (2025-12-03)
4355

@@ -47,8 +59,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4759
([#4734](https://github.com/open-telemetry/opentelemetry-python/pull/4734))
4860
- build: bump ruff to 0.14.1
4961
([#4782](https://github.com/open-telemetry/opentelemetry-python/pull/4782))
50-
- Add `opentelemetry-exporter-credential-provider-gcp` as an optional dependency to `opentelemetry-exporter-otlp-proto-grpc`
51-
and `opentelemetry-exporter-otlp-proto-http`
62+
- Add `opentelemetry-exporter-credential-provider-gcp` as an optional dependency to `opentelemetry-exporter-otlp-proto-grpc`
63+
and `opentelemetry-exporter-otlp-proto-http`
5264
([#4760](https://github.com/open-telemetry/opentelemetry-python/pull/4760))
5365
- feat: implement on ending in span processor
5466
([#4775](https://github.com/open-telemetry/opentelemetry-python/pull/4775))
@@ -60,29 +72,29 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
6072
([#4647](https://github.com/open-telemetry/opentelemetry-python/pull/4647))
6173

6274
**Migration Guide:**
63-
75+
6476
`LogData` has been removed. Users should update their code as follows:
65-
77+
6678
- **For Log Exporters:** Change from `Sequence[LogData]` to `Sequence[ReadableLogRecord]`
6779
```python
6880
# Before
6981
from opentelemetry.sdk._logs import LogData
7082
def export(self, batch: Sequence[LogData]) -> LogRecordExportResult:
7183
...
72-
84+
7385
# After
7486
from opentelemetry.sdk._logs import ReadableLogRecord
7587
def export(self, batch: Sequence[ReadableLogRecord]) -> LogRecordExportResult:
7688
...
7789
```
78-
90+
7991
- **For Log Processors:** Use `ReadWriteLogRecord` for processing, `ReadableLogRecord` for exporting
8092
```python
8193
# Before
8294
from opentelemetry.sdk._logs import LogData
8395
def on_emit(self, log_data: LogData):
8496
...
85-
97+
8698
# After
8799
from opentelemetry.sdk._logs import ReadWriteLogRecord, ReadableLogRecord
88100
def on_emit(self, log_record: ReadWriteLogRecord):
@@ -95,7 +107,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
95107
)
96108
...
97109
```
98-
110+
99111
- **Accessing log data:** Use the same attributes on `ReadableLogRecord`/`ReadWriteLogRecord`
100112
- `log_record.log_record` - The API LogRecord (contains body, severity, attributes, etc.)
101113
- `log_record.resource` - The Resource

docs/examples/opencensus-shim/app.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@
3131

3232
# Set up OpenTelemetry
3333
tracer_provider = TracerProvider(
34-
resource=Resource(
34+
resource=Resource.create(
3535
{
3636
"service.name": "opencensus-shim-example-flask",
3737
}

docs/exporter/prometheus/prometheus.rst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ Prometheus text format on request::
2727
from opentelemetry.sdk.resources import SERVICE_NAME, Resource
2828

2929
# Service name is required for most backends
30-
resource = Resource(attributes={
30+
resource = Resource.create(attributes={
3131
SERVICE_NAME: "your-service-name"
3232
})
3333

exporter/opentelemetry-exporter-otlp-proto-grpc/src/opentelemetry/exporter/otlp/proto/grpc/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
5151
# Resource can be required for some backends, e.g. Jaeger
5252
# If resource wouldn't be set - traces wouldn't appears in Jaeger
53-
resource = Resource(attributes={
53+
resource = Resource.create({
5454
"service.name": "service"
5555
})
5656

exporter/opentelemetry-exporter-otlp-proto-http/src/opentelemetry/exporter/otlp/proto/http/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
5151
# Resource can be required for some backends, e.g. Jaeger
5252
# If resource wouldn't be set - traces wouldn't appears in Jaeger
53-
resource = Resource(attributes={
53+
resource = Resource.create({
5454
"service.name": "service"
5555
})
5656

opentelemetry-api/src/opentelemetry/trace/__init__.py

Lines changed: 32 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,21 @@ def start_span(
471471
record_exception: bool = True,
472472
set_status_on_exception: bool = True,
473473
) -> "Span":
474-
return INVALID_SPAN
474+
current_span = get_current_span(context)
475+
if isinstance(current_span, NonRecordingSpan):
476+
return current_span
477+
parent_span_context = current_span.get_span_context()
478+
if parent_span_context is not None and not isinstance(
479+
parent_span_context, SpanContext
480+
):
481+
logger.warning(
482+
"Invalid span context for %s: %s",
483+
current_span,
484+
parent_span_context,
485+
)
486+
return INVALID_SPAN
487+
488+
return NonRecordingSpan(context=parent_span_context)
475489

476490
@_agnosticcontextmanager
477491
def start_as_current_span(
@@ -486,7 +500,23 @@ def start_as_current_span(
486500
set_status_on_exception: bool = True,
487501
end_on_exit: bool = True,
488502
) -> Iterator["Span"]:
489-
yield INVALID_SPAN
503+
span = self.start_span(
504+
name=name,
505+
context=context,
506+
kind=kind,
507+
attributes=attributes,
508+
links=links,
509+
start_time=start_time,
510+
record_exception=record_exception,
511+
set_status_on_exception=set_status_on_exception,
512+
)
513+
with use_span(
514+
span,
515+
end_on_exit=end_on_exit,
516+
record_exception=record_exception,
517+
set_status_on_exception=set_status_on_exception,
518+
) as span:
519+
yield span
490520

491521

492522
@deprecated("You should use NoOpTracer. Deprecated since version 1.9.0.")

opentelemetry-api/tests/test_implementation.py

Lines changed: 92 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,63 @@
1717
from opentelemetry import trace
1818

1919

20+
class RecordingSpan(trace.Span):
21+
def __init__(self, context: trace.SpanContext) -> None:
22+
self._context = context
23+
24+
def get_span_context(self) -> trace.SpanContext:
25+
return self._context
26+
27+
def is_recording(self) -> bool:
28+
return True
29+
30+
def end(self, end_time=None) -> None:
31+
pass
32+
33+
def set_attributes(self, attributes) -> None:
34+
pass
35+
36+
def set_attribute(self, key, value) -> None:
37+
pass
38+
39+
def add_event(
40+
self,
41+
name: str,
42+
attributes=None,
43+
timestamp=None,
44+
) -> None:
45+
pass
46+
47+
def add_link(
48+
self,
49+
context,
50+
attributes=None,
51+
) -> None:
52+
pass
53+
54+
def update_name(self, name) -> None:
55+
pass
56+
57+
def set_status(
58+
self,
59+
status,
60+
description=None,
61+
) -> None:
62+
pass
63+
64+
def record_exception(
65+
self,
66+
exception,
67+
attributes=None,
68+
timestamp=None,
69+
escaped=False,
70+
) -> None:
71+
pass
72+
73+
def __repr__(self) -> str:
74+
return f"RecordingSpan({self._context!r})"
75+
76+
2077
class TestAPIOnlyImplementation(unittest.TestCase):
2178
"""
2279
This test is in place to ensure the API is returning values that
@@ -36,18 +93,45 @@ def test_default_tracer(self):
3693
tracer_provider = trace.NoOpTracerProvider()
3794
tracer = tracer_provider.get_tracer(__name__)
3895
with tracer.start_span("test") as span:
39-
self.assertEqual(
40-
span.get_span_context(), trace.INVALID_SPAN_CONTEXT
41-
)
42-
self.assertEqual(span, trace.INVALID_SPAN)
96+
self.assertFalse(span.get_span_context().is_valid)
4397
self.assertIs(span.is_recording(), False)
4498
with tracer.start_span("test2") as span2:
45-
self.assertEqual(
46-
span2.get_span_context(), trace.INVALID_SPAN_CONTEXT
47-
)
48-
self.assertEqual(span2, trace.INVALID_SPAN)
99+
self.assertFalse(span2.get_span_context().is_valid)
49100
self.assertIs(span2.is_recording(), False)
50101

102+
def test_default_tracer_context_propagation_recording_span(self):
103+
tracer_provider = trace.NoOpTracerProvider()
104+
tracer = tracer_provider.get_tracer(__name__)
105+
span_context = trace.SpanContext(
106+
2604504634922341076776623263868986797,
107+
5213367945872657620,
108+
False,
109+
trace.TraceFlags(0x01),
110+
)
111+
ctx = trace.set_span_in_context(RecordingSpan(context=span_context))
112+
with tracer.start_span("test", context=ctx) as span:
113+
self.assertTrue(span.get_span_context().is_valid)
114+
self.assertEqual(span.get_span_context(), span_context)
115+
self.assertIs(span.is_recording(), False)
116+
117+
def test_default_tracer_context_propagation_non_recording_span(self):
118+
tracer_provider = trace.NoOpTracerProvider()
119+
tracer = tracer_provider.get_tracer(__name__)
120+
ctx = trace.set_span_in_context(trace.INVALID_SPAN)
121+
with tracer.start_span("test", context=ctx) as span:
122+
self.assertFalse(span.get_span_context().is_valid)
123+
self.assertIs(span, trace.INVALID_SPAN)
124+
125+
def test_default_tracer_context_propagation_with_invalid_context(self):
126+
tracer_provider = trace.NoOpTracerProvider()
127+
tracer = tracer_provider.get_tracer(__name__)
128+
ctx = trace.set_span_in_context(
129+
RecordingSpan(context="invalid_context") # type: ignore[reportArgumentType]
130+
)
131+
with tracer.start_span("test", context=ctx) as span:
132+
self.assertFalse(span.get_span_context().is_valid)
133+
self.assertIs(span, trace.INVALID_SPAN)
134+
51135
def test_span(self):
52136
with self.assertRaises(TypeError):
53137
# pylint: disable=abstract-class-instantiated

opentelemetry-api/tests/trace/test_proxy.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,8 @@ def my_function() -> Span:
9696
return trace.get_current_span()
9797

9898
# call function before configuring tracing provider, should
99-
# return INVALID_SPAN from the NoOpTracer
100-
self.assertEqual(my_function(), trace.INVALID_SPAN)
99+
# return NonRecordingSpan from the NoOpTracer
100+
self.assertFalse(my_function().is_recording())
101101

102102
# configure tracing provider
103103
trace.set_tracer_provider(TestProvider())

opentelemetry-api/tests/trace/test_tracer.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,5 +79,5 @@ async def function_async(data: str) -> int:
7979
def test_get_current_span(self):
8080
with self.tracer.start_as_current_span("test") as span:
8181
get_current_span().set_attribute("test", "test")
82-
self.assertEqual(span, INVALID_SPAN)
82+
self.assertFalse(span.is_recording())
8383
self.assertFalse(hasattr("span", "attributes"))

opentelemetry-sdk/benchmarks/trace/test_benchmark_trace.py

Lines changed: 59 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,19 @@
1212
# See the License for the specific language governing permissions and
1313
# limitations under the License.
1414

15+
from functools import lru_cache
16+
17+
import pytest
18+
1519
from opentelemetry.sdk.resources import Resource
16-
from opentelemetry.sdk.trace import TracerProvider, sampling
20+
from opentelemetry.sdk.trace import (
21+
TracerProvider,
22+
_default_tracer_configurator,
23+
_RuleBasedTracerConfigurator,
24+
_scope_name_matches_glob,
25+
_TracerConfig,
26+
sampling,
27+
)
1728

1829
tracer = TracerProvider(
1930
sampler=sampling.DEFAULT_ON,
@@ -27,16 +38,61 @@
2738
).get_tracer("sdk_tracer_provider")
2839

2940

41+
@pytest.fixture(params=[None, 0, 1, 10, 50])
42+
def num_tracer_configurator_rules(request):
43+
return request.param
44+
45+
3046
def test_simple_start_span(benchmark):
31-
def benchmark_start_as_current_span():
47+
def benchmark_start_span():
3248
span = tracer.start_span(
3349
"benchmarkedSpan",
3450
attributes={"long.attribute": -10000000001000000000},
3551
)
3652
span.add_event("benchmarkEvent")
3753
span.end()
3854

39-
benchmark(benchmark_start_as_current_span)
55+
benchmark(benchmark_start_span)
56+
57+
58+
# pylint: disable=protected-access,redefined-outer-name
59+
def test_simple_start_span_with_tracer_configurator_rules(
60+
benchmark, num_tracer_configurator_rules
61+
):
62+
def benchmark_start_span():
63+
span = tracer.start_span(
64+
"benchmarkedSpan",
65+
attributes={"long.attribute": -10000000001000000000},
66+
)
67+
span.add_event("benchmarkEvent")
68+
span.end()
69+
70+
@lru_cache
71+
def tracer_configurator(tracer_scope):
72+
# this is testing 100 rules that is an extreme case
73+
return _RuleBasedTracerConfigurator(
74+
rules=[
75+
(
76+
_scope_name_matches_glob(glob_pattern=str(i)),
77+
_TracerConfig(is_enabled=True),
78+
)
79+
for i in range(num_tracer_configurator_rules)
80+
],
81+
default_config=_TracerConfig(is_enabled=True),
82+
)(tracer_scope=tracer_scope)
83+
84+
tracer_provider = tracer._tracer_provider
85+
tracer_provider._set_tracer_configurator(
86+
tracer_configurator=tracer_configurator
87+
)
88+
if num_tracer_configurator_rules is None:
89+
tracer._tracer_provider = None
90+
benchmark(benchmark_start_span)
91+
tracer_provider._set_tracer_configurator(
92+
tracer_configurator=_default_tracer_configurator
93+
)
94+
if num_tracer_configurator_rules is None:
95+
tracer._tracer_provider = tracer_provider
4096

4197

4298
def test_simple_start_as_current_span(benchmark):

0 commit comments

Comments
 (0)