rebrandly-otel 0.2.17__py3-none-any.whl → 0.2.20__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of rebrandly-otel might be problematic. Click here for more details.

@@ -25,6 +25,7 @@ def create_resource(name: str = None, version: str = None) -> Resource:
25
25
 
26
26
  resources_attributes = {
27
27
  service_attributes.SERVICE_NAME: name,
28
+ "application.name": name,
28
29
  service_attributes.SERVICE_VERSION: version,
29
30
  process_attributes.PROCESS_RUNTIME_VERSION: f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
30
31
  SERVICE_NAMESPACE: get_application_name(),
@@ -0,0 +1,106 @@
1
+ """
2
+ Span Attributes Processor for Rebrandly OTEL SDK
3
+ Automatically adds attributes from OTEL_SPAN_ATTRIBUTES environment variable to all spans
4
+ """
5
+
6
+ import os
7
+ from typing import Optional
8
+ from opentelemetry.context import Context
9
+ from opentelemetry.sdk.trace import ReadableSpan, Span
10
+ from opentelemetry.sdk.trace.export import SpanProcessor
11
+
12
+
13
+ class SpanAttributesProcessor(SpanProcessor):
14
+ """
15
+ Span processor that automatically adds attributes from OTEL_SPAN_ATTRIBUTES
16
+ environment variable to all spans at creation time.
17
+ """
18
+
19
+ def __init__(self):
20
+ """Initialize the processor and parse OTEL_SPAN_ATTRIBUTES."""
21
+ self.name = 'SpanAttributesProcessor'
22
+ self.span_attributes = self._parse_span_attributes()
23
+
24
+ # Log parsed attributes in debug mode
25
+ if os.environ.get('OTEL_DEBUG', 'false').lower() == 'true' and self.span_attributes:
26
+ print(f'[SpanAttributesProcessor] Parsed OTEL_SPAN_ATTRIBUTES: {self.span_attributes}')
27
+
28
+ def _parse_span_attributes(self) -> dict:
29
+ """
30
+ Parse OTEL_SPAN_ATTRIBUTES environment variable.
31
+ Format: key1=value1,key2=value2
32
+
33
+ Returns:
34
+ Dictionary of parsed attributes as key-value pairs
35
+ """
36
+ attributes = {}
37
+ otel_span_attrs = os.environ.get('OTEL_SPAN_ATTRIBUTES', None)
38
+
39
+ if not otel_span_attrs or otel_span_attrs.strip() == '':
40
+ return attributes
41
+
42
+ try:
43
+ pairs = otel_span_attrs.split(',')
44
+ for attr in pairs:
45
+ trimmed_attr = attr.strip()
46
+ if trimmed_attr and '=' in trimmed_attr:
47
+ # Split on first '=' only, in case value contains '='
48
+ key, value = trimmed_attr.split('=', 1)
49
+ key = key.strip()
50
+ value = value.strip()
51
+ if key:
52
+ attributes[key] = value
53
+ except Exception as e:
54
+ print(f'[SpanAttributesProcessor] Warning: Invalid OTEL_SPAN_ATTRIBUTES value: {e}')
55
+
56
+ return attributes
57
+
58
+ def on_start(self, span: Span, parent_context: Optional[Context] = None) -> None:
59
+ """
60
+ Called when a span is started.
61
+ Adds configured attributes to the span.
62
+
63
+ Args:
64
+ span: The span that was just started
65
+ parent_context: The parent context (optional)
66
+ """
67
+ try:
68
+ # Add all parsed attributes to the span
69
+ if self.span_attributes:
70
+ for key, value in self.span_attributes.items():
71
+ span.set_attribute(key, value)
72
+ except Exception as e:
73
+ # Fail silently to avoid breaking the entire tracing pipeline
74
+ # Log only in debug mode to avoid noise
75
+ if os.environ.get('OTEL_DEBUG', 'false').lower() == 'true':
76
+ print(f'[SpanAttributesProcessor] Error processing span: {e}')
77
+
78
+ def on_end(self, span: ReadableSpan) -> None:
79
+ """
80
+ Called when a span is ended.
81
+ No-op for this processor.
82
+
83
+ Args:
84
+ span: The span that was just ended
85
+ """
86
+ pass
87
+
88
+ def shutdown(self) -> None:
89
+ """
90
+ Shutdown the processor.
91
+ No-op for this processor.
92
+ """
93
+ pass
94
+
95
+ def force_flush(self, timeout_millis: int = 30000) -> bool:
96
+ """
97
+ Force flush the processor.
98
+ No-op for this processor.
99
+
100
+ Args:
101
+ timeout_millis: Maximum time to wait for flush in milliseconds
102
+
103
+ Returns:
104
+ Always returns True as there's nothing to flush
105
+ """
106
+ return True
rebrandly_otel/traces.py CHANGED
@@ -12,6 +12,7 @@ from opentelemetry.sdk.trace.export import (
12
12
  )
13
13
 
14
14
  from .otel_utils import *
15
+ from .span_attributes_processor import SpanAttributesProcessor
15
16
 
16
17
  class RebrandlyTracer:
17
18
  """Wrapper for OpenTelemetry tracing with Rebrandly-specific features."""
@@ -26,6 +27,9 @@ class RebrandlyTracer:
26
27
  # Create provider with resource
27
28
  self._provider = TracerProvider(resource=create_resource())
28
29
 
30
+ # Add span attributes processor to automatically add OTEL_SPAN_ATTRIBUTES to all spans
31
+ self._provider.add_span_processor(SpanAttributesProcessor())
32
+
29
33
  # Add console exporter for local debugging
30
34
  if is_otel_debug():
31
35
  console_exporter = ConsoleSpanExporter()
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rebrandly_otel
3
- Version: 0.2.17
3
+ Version: 0.2.20
4
4
  Summary: Python OTEL wrapper by Rebrandly
5
5
  Home-page: https://github.com/rebrandly/rebrandly-otel-python
6
6
  Author: Antonio Romano
@@ -58,6 +58,7 @@ The SDK is configured through environment variables:
58
58
  | `OTEL_SERVICE_APPLICATION` | Application namespace (groups multiple services under one application) | Fallback to `OTEL_SERVICE_NAME` |
59
59
  | `OTEL_EXPORTER_OTLP_ENDPOINT` | OTLP collector endpoint | `None` |
60
60
  | `OTEL_DEBUG` | Enable console debugging | `false` |
61
+ | `OTEL_SPAN_ATTRIBUTES` | Attributes automatically added to all spans (format: `key1=value1,key2=value2`) | `None` |
61
62
  | `BATCH_EXPORT_TIME_MILLIS` | Batch export interval | `100` |
62
63
  | `ENV` or `ENVIRONMENT` or `NODE_ENV` | Deployment environment | `local` |
63
64
 
@@ -147,6 +148,67 @@ Lambda spans automatically include:
147
148
  - `cloud.provider`: Always "aws" for Lambda
148
149
  - `cloud.platform`: Always "aws_lambda" for Lambda
149
150
 
151
+ ## Automatic Span Attributes
152
+
153
+ The SDK supports automatically adding custom attributes to all spans via the `OTEL_SPAN_ATTRIBUTES` environment variable. This is useful for adding metadata that applies to all telemetry in a service, such as team ownership, deployment environment, or version information.
154
+
155
+ ### Configuration
156
+
157
+ Set the `OTEL_SPAN_ATTRIBUTES` environment variable with a comma-separated list of key-value pairs:
158
+
159
+ ```bash
160
+ export OTEL_SPAN_ATTRIBUTES="team=backend,environment=production,version=1.2.3"
161
+ ```
162
+
163
+ ### Behavior
164
+
165
+ - **Universal Application**: Attributes are added to ALL spans, including:
166
+ - Manually created spans (`tracer.start_span()`, `tracer.start_as_current_span()`)
167
+ - Lambda handler spans (`@lambda_handler`)
168
+ - AWS message handler spans (`@aws_message_handler`)
169
+ - Flask/FastAPI middleware spans
170
+ - Auto-instrumented spans (database queries, HTTP requests, etc.)
171
+
172
+ - **Format**: Same as `OTEL_RESOURCE_ATTRIBUTES` - comma-separated `key=value` pairs
173
+ - **Value Handling**: Supports values containing `=` characters (e.g., URLs)
174
+ - **Whitespace**: Leading/trailing whitespace is automatically trimmed
175
+
176
+ ### Example
177
+
178
+ ```python
179
+ import os
180
+
181
+ # Set environment variable
182
+ os.environ['OTEL_SPAN_ATTRIBUTES'] = "team=backend,service.owner=platform-team,deployment.region=us-east-1"
183
+
184
+ # Initialize SDK
185
+ from rebrandly_otel import otel, logger
186
+
187
+ # Create any span - attributes are added automatically
188
+ with otel.span('my-operation'):
189
+ logger.info('Processing request')
190
+ # The span will include:
191
+ # - team: "backend"
192
+ # - service.owner: "platform-team"
193
+ # - deployment.region: "us-east-1"
194
+ # ... plus any other attributes you set manually
195
+ ```
196
+
197
+ ### Use Cases
198
+
199
+ - **Team/Ownership Tagging**: `team=backend,owner=john@example.com`
200
+ - **Environment Metadata**: `environment=production,region=us-east-1,availability_zone=us-east-1a`
201
+ - **Version Tracking**: `version=1.2.3,build=12345,commit=abc123def`
202
+ - **Cost Attribution**: `cost_center=engineering,project=customer-api`
203
+ - **Multi-Tenancy**: `tenant=acme-corp,customer_tier=enterprise`
204
+
205
+ ### Difference from OTEL_RESOURCE_ATTRIBUTES
206
+
207
+ - **OTEL_RESOURCE_ATTRIBUTES**: Service-level metadata (set once, applies to the entire service instance)
208
+ - **OTEL_SPAN_ATTRIBUTES**: Span-level metadata (added to each individual span at creation time)
209
+
210
+ Both use the same format but serve different purposes in the OpenTelemetry data model.
211
+
150
212
  ### Exception Handling
151
213
 
152
214
  Spans automatically capture exceptions with:
@@ -522,6 +584,7 @@ pytest tests/test_usage.py -v
522
584
  pytest tests/test_pymysql_instrumentation.py -v
523
585
  pytest tests/test_metrics_and_logs.py -v
524
586
  pytest tests/test_decorators.py -v
587
+ pytest tests/test_span_attributes_processor.py -v
525
588
  ```
526
589
 
527
590
  Run with coverage:
@@ -538,6 +601,7 @@ The test suite includes:
538
601
  - **PyMySQL instrumentation tests** (`test_pymysql_instrumentation.py`): Database connection instrumentation, query tracing, helper functions
539
602
  - **Metrics and logs tests** (`test_metrics_and_logs.py`): Custom metrics creation (counter, histogram, gauge), logging levels (info, warning, debug, error)
540
603
  - **Decorators tests** (`test_decorators.py`): Lambda handler decorator, AWS message handler decorator, traces decorator, aws_message_span context manager
604
+ - **Span attributes processor tests** (`test_span_attributes_processor.py`): Automatic span attributes from OTEL_SPAN_ATTRIBUTES (31 tests)
541
605
 
542
606
  ## License
543
607
 
@@ -4,12 +4,13 @@ rebrandly_otel/flask_support.py,sha256=wn_Esiu4cy48f_rD-kwIypRKeJ8D1pT6x4WZacEQm
4
4
  rebrandly_otel/http_utils.py,sha256=5mHJQCJ-5FDFNmTRPaErtdQLAEzCbcDJqoiGzjgKe0w,741
5
5
  rebrandly_otel/logs.py,sha256=9uwWbQ9h9YcxF7-2vtsYucaMGCqMoPnO2vWf6qkyGgI,5328
6
6
  rebrandly_otel/metrics.py,sha256=jZPygTY7vie33sadXc7P1DwLTKe9tJvAWSZuGHW1xQg,7743
7
- rebrandly_otel/otel_utils.py,sha256=wbtr2_eZTRHmgqo6PY6nCe81zdyQXo9bpDd9D2NehrM,6028
7
+ rebrandly_otel/otel_utils.py,sha256=ZlmkoJlMbo-ELqHsPgoOm7eGuSjO0LQeMEb-3BSRdcQ,6062
8
8
  rebrandly_otel/pymysql_instrumentation.py,sha256=lS_V5DcsnpQ3f2PoNqeM7t3Jyiew2KNURM9iILfz8sM,6477
9
9
  rebrandly_otel/rebrandly_otel.py,sha256=8EArZTkeqV0IWLrgKH4vZzY-6VhJBNRp4_c99mpjJNw,22785
10
- rebrandly_otel/traces.py,sha256=JY_3RWbzpUxzEx3GqTVgggsyA2DB4oR-zDftIFFJha4,7174
11
- rebrandly_otel-0.2.17.dist-info/licenses/LICENSE,sha256=KMXHvTwP62S2q-SG7CFfMU_09rUwxiqlM0izaYGdcgY,1103
12
- rebrandly_otel-0.2.17.dist-info/METADATA,sha256=EoVItL24JDVXK_gUTs_yGRS_0dx_RNM9u7EULpK3TM0,16065
13
- rebrandly_otel-0.2.17.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
14
- rebrandly_otel-0.2.17.dist-info/top_level.txt,sha256=26PSC1gjVUl8tTH5QfKbFevjVV4E2yojoukEfiTScvM,15
15
- rebrandly_otel-0.2.17.dist-info/RECORD,,
10
+ rebrandly_otel/span_attributes_processor.py,sha256=eB5oeHwNw4gtD3opQYOJVOq7VQYRj2YEv4oL7BV0t9Q,3609
11
+ rebrandly_otel/traces.py,sha256=Y7WE17vGYvetoxzjUBmAEnwI9r7GBMFffBww4zcnFbk,7402
12
+ rebrandly_otel-0.2.20.dist-info/licenses/LICENSE,sha256=KMXHvTwP62S2q-SG7CFfMU_09rUwxiqlM0izaYGdcgY,1103
13
+ rebrandly_otel-0.2.20.dist-info/METADATA,sha256=q2VDyQLjTNy7vuYz-61Z04b4HeWXUX5jqU__821t8bw,18807
14
+ rebrandly_otel-0.2.20.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
15
+ rebrandly_otel-0.2.20.dist-info/top_level.txt,sha256=26PSC1gjVUl8tTH5QfKbFevjVV4E2yojoukEfiTScvM,15
16
+ rebrandly_otel-0.2.20.dist-info/RECORD,,