Skip to content

Commit 8a04229

Browse files
authored
Test OpenTelemetry SDK 1.33 (#1124)
1 parent c889d27 commit 8a04229

File tree

4 files changed

+87
-43
lines changed

4 files changed

+87
-43
lines changed

.github/workflows/main.yml

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -56,16 +56,21 @@ jobs:
5656
ALGOLIA_WRITE_API_KEY: ${{ secrets.ALGOLIA_WRITE_API_KEY }}
5757
5858
test:
59-
name: test on Python ${{ matrix.python-version }} and pydantic ${{ matrix.pydantic-version }}
59+
name: test on Python ${{ matrix.python-version }}, pydantic ${{ matrix.pydantic-version }}, otel ${{ matrix.otel-version }}
6060
runs-on: ubuntu-latest
6161
strategy:
6262
fail-fast: false
6363
matrix:
6464
python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"]
65-
pydantic-version: ['2.11']
65+
pydantic-version: ['2'] # i.e. the latest
66+
otel-version: ['1'] # i.e. the latest
6667
include:
6768
- python-version: '3.12'
6869
pydantic-version: '2.4'
70+
otel-version: '1' # i.e. the latest
71+
- python-version: '3.12'
72+
pydantic-version: '2' # i.e. the latest
73+
otel-version: '1.33'
6974
env:
7075
PYTHON: ${{ matrix.python-version }}
7176
steps:
@@ -90,14 +95,18 @@ jobs:
9095
# installs the most recent patch on the minor version's track, ex 2.6.* -> 2.6.4
9196
run: uv pip install 'pydantic==${{ matrix.pydantic-version }}.*'
9297

98+
- name: Install OTel SDK ${{ matrix.otel-version }}
99+
# installs the most recent patch on the minor version's track, ex 2.6.* -> 2.6.4
100+
run: uv pip install 'opentelemetry-sdk==${{ matrix.otel-version }}.*'
101+
93102
- run: mkdir coverage
94103
- run: uv run --no-sync coverage run -m pytest --junitxml=coverage/test-results.xml
95104
env:
96-
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}-pydantic-${{ matrix.pydantic-version }}
105+
COVERAGE_FILE: coverage/.coverage.${{ runner.os }}-py${{ matrix.python-version }}-pydantic-${{ matrix.pydantic-version }}-otel-${{ matrix.otel-version }}
97106
- name: store coverage files
98107
uses: actions/upload-artifact@v4
99108
with:
100-
name: coverage-py${{ matrix.python-version }}-pydantic-${{ matrix.pydantic-version }}
109+
name: coverage-py${{ matrix.python-version }}-pydantic-${{ matrix.pydantic-version }}-otel-${{ matrix.otel-version }}
101110
path: coverage
102111
include-hidden-files: true
103112

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,17 @@
11
from __future__ import annotations
22

33
import os
4-
from typing import TYPE_CHECKING
54

65
from opentelemetry.sdk.environment_variables import OTEL_BSP_SCHEDULE_DELAY
7-
from opentelemetry.sdk.trace import ReadableSpan, Span
6+
from opentelemetry.sdk.trace import ReadableSpan
87
from opentelemetry.sdk.trace.export import BatchSpanProcessor, SpanExporter
98

109
from logfire._internal.exporters.wrapper import WrapperSpanProcessor
1110

12-
if TYPE_CHECKING:
11+
try:
1312
from opentelemetry.sdk._shared_internal import BatchProcessor
13+
except ImportError:
14+
BatchProcessor = None
1415

1516

1617
class DynamicBatchSpanProcessor(WrapperSpanProcessor):
@@ -33,19 +34,40 @@ def __init__(self, exporter: SpanExporter) -> None:
3334
def on_end(self, span: ReadableSpan) -> None:
3435
self.num_processed += 1
3536
if self.num_processed == 10:
36-
if hasattr(self.batch_processor, '_schedule_delay_millis'):
37-
self.batch_processor._schedule_delay = self.final_delay / 1e3 # type: ignore
38-
else: # pragma: no cover
39-
self.processor.schedule_delay_millis = self.final_delay # type: ignore
37+
self.schedule_delay_millis = self.final_delay
4038
super().on_end(span)
4139

42-
@property
43-
def batch_processor(self) -> BatchSpanProcessor | BatchProcessor[Span]:
44-
return getattr(self.processor, '_batch_processor', self.processor)
40+
if BatchProcessor:
4541

46-
@property
47-
def span_exporter(self) -> SpanExporter:
48-
if isinstance(self.batch_processor, BatchSpanProcessor): # pragma: no cover
49-
return self.batch_processor.span_exporter # type: ignore
50-
else:
42+
@property
43+
def batch_processor(self): # type: ignore
44+
return self.processor._batch_processor # type: ignore
45+
46+
@property
47+
def span_exporter(self) -> SpanExporter: # type: ignore
5148
return self.batch_processor._exporter # type: ignore
49+
50+
@property
51+
def schedule_delay_millis(self) -> float: # type: ignore
52+
return self.batch_processor._schedule_delay * 1000 # type: ignore
53+
54+
@schedule_delay_millis.setter
55+
def schedule_delay_millis(self, value: float): # type: ignore
56+
self.batch_processor._schedule_delay = value / 1000 # type: ignore
57+
else:
58+
59+
@property
60+
def batch_processor(self):
61+
return self.processor
62+
63+
@property
64+
def span_exporter(self) -> SpanExporter:
65+
return self.processor.span_exporter # type: ignore
66+
67+
@property
68+
def schedule_delay_millis(self) -> float:
69+
return self.processor.schedule_delay_millis # type: ignore
70+
71+
@schedule_delay_millis.setter
72+
def schedule_delay_millis(self, value: float):
73+
self.processor.schedule_delay_millis = value # type: ignore

tests/exporters/test_dynamic_batch_span_processor.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ def test_dynamic_batch_span_processor(exporter: TestExporter, config_kwargs: dic
1717
logfire.info('test')
1818
assert processor.num_processed == 9
1919
assert isinstance(processor.processor, BatchSpanProcessor)
20-
assert processor.batch_processor._schedule_delay == 0.1 # type: ignore
20+
assert processor.schedule_delay_millis == 100
21+
2122
logfire.info('test')
2223
assert processor.num_processed == 10
23-
assert processor.batch_processor._schedule_delay == 0.5 # type: ignore
24+
assert processor.schedule_delay_millis == 500
2425
logfire.force_flush()
2526
assert len(exporter.exported_spans) == 10

tests/test_configure.py

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@
2828
from opentelemetry.propagators.composite import CompositePropagator
2929
from opentelemetry.sdk._logs import LogRecordProcessor
3030
from opentelemetry.sdk._logs._internal import SynchronousMultiLogRecordProcessor
31-
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor, SimpleLogRecordProcessor
31+
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor, LogExporter, SimpleLogRecordProcessor
3232
from opentelemetry.sdk.metrics.export import InMemoryMetricReader, PeriodicExportingMetricReader
3333
from opentelemetry.sdk.trace import ReadableSpan, SpanProcessor, SynchronousMultiSpanProcessor
3434
from opentelemetry.sdk.trace.export import (
@@ -572,6 +572,24 @@ def test_logfire_config_console_options() -> None:
572572
assert LogfireConfig().console == ConsoleOptions(verbose=False)
573573

574574

575+
def get_batch_span_exporter(processor: SpanProcessor) -> SpanExporter:
576+
assert isinstance(processor, BatchSpanProcessor)
577+
try:
578+
exporter = processor._batch_processor._exporter # type: ignore
579+
except AttributeError:
580+
exporter = processor.span_exporter # type: ignore
581+
return exporter # type: ignore
582+
583+
584+
def get_batch_log_exporter(processor: LogRecordProcessor) -> LogExporter:
585+
assert isinstance(processor, BatchLogRecordProcessor)
586+
try:
587+
exporter = processor._batch_processor._exporter # type: ignore
588+
except AttributeError:
589+
exporter = processor._exporter # type: ignore
590+
return exporter # type: ignore
591+
592+
575593
def test_configure_export_delay() -> None:
576594
class TrackingExporter(SpanExporter):
577595
def __init__(self) -> None:
@@ -602,8 +620,8 @@ def configure_tracking_exporter():
602620

603621
dynamic_batch_span_processor, *_ = get_span_processors()
604622
assert isinstance(dynamic_batch_span_processor, DynamicBatchSpanProcessor)
605-
ex = dynamic_batch_span_processor.batch_processor._exporter = TrackingExporter() # type: ignore
606-
assert dynamic_batch_span_processor.span_exporter is ex
623+
batch_processor = dynamic_batch_span_processor.batch_processor
624+
ex = batch_processor.span_exporter = batch_processor._exporter = TrackingExporter() # type: ignore
607625
return ex
608626

609627
def check_delays(exp: TrackingExporter, min_delay: float, max_delay: float) -> None:
@@ -612,7 +630,6 @@ def check_delays(exp: TrackingExporter, min_delay: float, max_delay: float) -> N
612630

613631
# test the default behaviour
614632
exporter = configure_tracking_exporter()
615-
assert get_span_processors()[0].batch_processor._schedule_delay == 0.1 # type: ignore
616633
for _ in range(10):
617634
logfire.info('test')
618635
sleep(0.1)
@@ -625,7 +642,6 @@ def check_delays(exp: TrackingExporter, min_delay: float, max_delay: float) -> N
625642
pass
626643
sleep(0.1)
627644
# After the first 10 spans, we increase to 500ms by default
628-
assert get_span_processors()[0].batch_processor._schedule_delay == 0.5 # type: ignore
629645
check_delays(exporter, 0.4, 1.0)
630646

631647
# test a very small value
@@ -1566,9 +1582,9 @@ def test_default_exporters(monkeypatch: pytest.MonkeyPatch):
15661582
assert isinstance(console_log_processor._exporter, ConsoleLogExporter) # type: ignore
15671583
assert console_log_processor._exporter.span_exporter is console_span_processor.span_exporter # type: ignore
15681584

1569-
assert isinstance(logfire_log_processor, BatchLogRecordProcessor)
1570-
assert isinstance(logfire_log_processor._batch_processor._exporter, QuietLogExporter) # type: ignore
1571-
assert isinstance(logfire_log_processor._batch_processor._exporter.exporter, OTLPLogExporter) # type: ignore
1585+
exporter = get_batch_log_exporter(logfire_log_processor)
1586+
assert isinstance(exporter, QuietLogExporter)
1587+
assert isinstance(exporter.exporter, OTLPLogExporter)
15721588

15731589

15741590
def test_custom_exporters():
@@ -1600,8 +1616,7 @@ def test_otel_exporter_otlp_endpoint_env_var():
16001616
logfire.configure(send_to_logfire=False, console=False)
16011617

16021618
[otel_processor] = get_span_processors()
1603-
assert isinstance(otel_processor, BatchSpanProcessor)
1604-
exporter = otel_processor._batch_processor._exporter # type: ignore
1619+
exporter = get_batch_span_exporter(otel_processor)
16051620
assert isinstance(exporter, OTLPSpanExporter)
16061621
assert exporter._endpoint == 'otel_endpoint/v1/traces' # type: ignore
16071622

@@ -1611,9 +1626,9 @@ def test_otel_exporter_otlp_endpoint_env_var():
16111626
assert otel_metric_reader._exporter._endpoint == 'otel_endpoint/v1/metrics' # type: ignore
16121627

16131628
[otel_log_processor] = get_log_record_processors()
1614-
assert isinstance(otel_log_processor, BatchLogRecordProcessor)
1615-
assert isinstance(otel_log_processor._batch_processor._exporter, OTLPLogExporter) # type: ignore
1616-
assert otel_log_processor._batch_processor._exporter._endpoint == 'otel_endpoint/v1/logs' # type: ignore
1629+
log_exporter = get_batch_log_exporter(otel_log_processor)
1630+
assert isinstance(log_exporter, OTLPLogExporter)
1631+
assert log_exporter._endpoint == 'otel_endpoint/v1/logs' # type: ignore
16171632

16181633

16191634
def test_otel_traces_exporter_env_var():
@@ -1644,8 +1659,7 @@ def test_otel_metrics_exporter_env_var():
16441659
logfire.configure(send_to_logfire=False, console=False)
16451660

16461661
[otel_processor] = get_span_processors()
1647-
assert isinstance(otel_processor, BatchSpanProcessor)
1648-
exporter = otel_processor._batch_processor._exporter # type: ignore
1662+
exporter = get_batch_span_exporter(otel_processor)
16491663
assert isinstance(exporter, OTLPSpanExporter)
16501664
assert exporter._endpoint == 'otel_endpoint3/v1/traces' # type: ignore
16511665

@@ -1658,8 +1672,7 @@ def test_otel_logs_exporter_env_var():
16581672
logfire.configure(send_to_logfire=False, console=False)
16591673

16601674
[otel_processor] = get_span_processors()
1661-
assert isinstance(otel_processor, BatchSpanProcessor)
1662-
exporter = otel_processor._batch_processor._exporter # type: ignore
1675+
exporter = get_batch_span_exporter(otel_processor)
16631676
assert isinstance(exporter, OTLPSpanExporter)
16641677
assert exporter._endpoint == 'otel_endpoint4/v1/traces' # type: ignore
16651678

@@ -1672,8 +1685,7 @@ def test_otel_exporter_otlp_traces_endpoint_env_var():
16721685
logfire.configure(send_to_logfire=False, console=False)
16731686

16741687
[otel_processor] = get_span_processors()
1675-
assert isinstance(otel_processor, BatchSpanProcessor)
1676-
exporter = otel_processor._batch_processor._exporter # type: ignore
1688+
exporter = get_batch_span_exporter(otel_processor)
16771689
assert isinstance(exporter, OTLPSpanExporter)
16781690
assert exporter._endpoint == 'otel_traces_endpoint' # type: ignore
16791691

@@ -1704,9 +1716,9 @@ def test_otel_exporter_otlp_logs_endpoint_env_var():
17041716
assert len(list(get_metric_readers())) == 0
17051717

17061718
[otel_log_processor] = get_log_record_processors()
1707-
assert isinstance(otel_log_processor, BatchLogRecordProcessor)
1708-
assert isinstance(otel_log_processor._batch_processor._exporter, OTLPLogExporter) # type: ignore
1709-
assert otel_log_processor._batch_processor._exporter._endpoint == 'otel_logs_endpoint' # type: ignore
1719+
exporter = get_batch_log_exporter(otel_log_processor)
1720+
assert isinstance(exporter, OTLPLogExporter)
1721+
assert exporter._endpoint == 'otel_logs_endpoint' # type: ignore
17101722

17111723

17121724
def test_metrics_false(monkeypatch: pytest.MonkeyPatch):

0 commit comments

Comments
 (0)