From 030a95ddbdc037161f42a71997ea23cadb392ada Mon Sep 17 00:00:00 2001 From: Jean Scherf Date: Thu, 9 Apr 2026 09:22:29 -0300 Subject: [PATCH 1/2] feat: enable batch for instrumentation --- src/sap_cloud_sdk/core/telemetry/auto_instrument.py | 9 +++++++-- src/sap_cloud_sdk/core/telemetry/user-guide.md | 8 ++++++++ tests/core/unit/telemetry/test_auto_instrument.py | 13 ++++++++++++- uv.lock | 2 +- 4 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/sap_cloud_sdk/core/telemetry/auto_instrument.py b/src/sap_cloud_sdk/core/telemetry/auto_instrument.py index 74998af..414c15f 100644 --- a/src/sap_cloud_sdk/core/telemetry/auto_instrument.py +++ b/src/sap_cloud_sdk/core/telemetry/auto_instrument.py @@ -30,12 +30,17 @@ @record_metrics(Module.AICORE, Operation.AICORE_AUTO_INSTRUMENT) -def auto_instrument(): +def auto_instrument(disable_batch: bool = False): """ Initialize meta-instrumentation for GenAI tracing. Should be initialized before any AI frameworks. Traces are exported to the OTEL collector endpoint configured in environment with OTEL_EXPORTER_OTLP_ENDPOINT, or printed to console when OTEL_TRACES_EXPORTER=console. + + Args: + disable_batch: If True, uses SimpleSpanProcessor (synchronous, lower throughput). + Defaults to False, which uses BatchSpanProcessor (asynchronous, + recommended for production workloads). """ otel_endpoint = os.getenv(ENV_OTLP_ENDPOINT, "") console_traces = os.getenv(ENV_TRACES_EXPORTER, "").lower() == "console" @@ -54,7 +59,7 @@ def auto_instrument(): exporter=exporter, resource_attributes=resource, should_enrich_metrics=True, - disable_batch=True, + disable_batch=disable_batch, ) _set_baggage_processor() diff --git a/src/sap_cloud_sdk/core/telemetry/user-guide.md b/src/sap_cloud_sdk/core/telemetry/user-guide.md index dc88f52..b8a9e6d 100644 --- a/src/sap_cloud_sdk/core/telemetry/user-guide.md +++ b/src/sap_cloud_sdk/core/telemetry/user-guide.md @@ -210,6 +210,14 @@ Use an OTLP collector: export OTEL_EXPORTER_OTLP_ENDPOINT="https://otel-collector.example.com" ``` +### Span processor + +By default, `auto_instrument` uses `BatchSpanProcessor`, which exports spans asynchronously in a background thread and is recommended for production workloads. If you need synchronous span processing (e.g. in short-lived scripts or tests where the process may exit before the batch is flushed), pass `disable_batch=True`: + +```python +auto_instrument(disable_batch=True) +``` + ### System role ```bash diff --git a/tests/core/unit/telemetry/test_auto_instrument.py b/tests/core/unit/telemetry/test_auto_instrument.py index 1b39331..82a1957 100644 --- a/tests/core/unit/telemetry/test_auto_instrument.py +++ b/tests/core/unit/telemetry/test_auto_instrument.py @@ -43,7 +43,7 @@ def test_auto_instrument_with_endpoint_success(self, mock_traceloop_components): call_kwargs = mock_traceloop_components['traceloop'].init.call_args[1] assert call_kwargs['app_name'] == 'test-app' assert call_kwargs['should_enrich_metrics'] is True - assert call_kwargs['disable_batch'] is True + assert call_kwargs['disable_batch'] is False def test_auto_instrument_uses_grpc_exporter_by_default(self, mock_traceloop_components): """Test that auto_instrument uses gRPC exporter by default, letting it read endpoint from env.""" @@ -209,6 +209,17 @@ def test_auto_instrument_without_endpoint_or_console(self): warning_message = mock_logger.warning.call_args[0][0] assert "OTEL_EXPORTER_OTLP_ENDPOINT not set" in warning_message + def test_auto_instrument_disable_batch_can_be_set_to_true(self, mock_traceloop_components): + """Test that disable_batch=True can be explicitly passed to use SimpleSpanProcessor.""" + mock_traceloop_components['get_app_name'].return_value = 'test-app' + mock_traceloop_components['create_resource'].return_value = {} + + with patch.dict('os.environ', {'OTEL_EXPORTER_OTLP_ENDPOINT': 'http://localhost:4317'}, clear=True): + auto_instrument(disable_batch=True) + + call_kwargs = mock_traceloop_components['traceloop'].init.call_args[1] + assert call_kwargs['disable_batch'] is True + def test_auto_instrument_passes_baggage_span_processor(self, mock_traceloop_components): """Test that auto_instrument registers a BaggageSpanProcessor on the tracer provider.""" mock_traceloop_components['get_app_name'].return_value = 'test-app' diff --git a/uv.lock b/uv.lock index e2252cf..508314a 100644 --- a/uv.lock +++ b/uv.lock @@ -2424,7 +2424,7 @@ wheels = [ [[package]] name = "sap-cloud-sdk" -version = "0.5.0" +version = "0.6.0" source = { editable = "." } dependencies = [ { name = "hatchling" }, From 6b9ca892a8ad2bea41fb03f22433ad959b8f0ff1 Mon Sep 17 00:00:00 2001 From: Jean Scherf Date: Thu, 9 Apr 2026 10:06:05 -0300 Subject: [PATCH 2/2] chore: bump version --- pyproject.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 4888fd7..b469140 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [project] name = "sap-cloud-sdk" -version = "0.6.0" +version = "0.6.1" description = "SAP Cloud SDK for Python" readme = "README.md" license = "Apache-2.0"