rebrandly-otel 0.1.36__tar.gz → 0.2.3__tar.gz

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.

Files changed (21) hide show
  1. {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/PKG-INFO +1 -1
  2. {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/rebrandly_otel.egg-info/PKG-INFO +1 -1
  3. {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/rebrandly_otel.egg-info/SOURCES.txt +1 -0
  4. {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/setup.py +1 -1
  5. {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/src/fastapi_support.py +5 -1
  6. {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/src/flask_support.py +5 -1
  7. rebrandly_otel-0.2.3/src/http_utils.py +28 -0
  8. {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/src/otel_utils.py +39 -8
  9. {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/src/pymysql_instrumentation.py +1 -1
  10. {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/src/rebrandly_otel.py +10 -8
  11. {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/LICENSE +0 -0
  12. {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/README.md +0 -0
  13. {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/rebrandly_otel.egg-info/dependency_links.txt +0 -0
  14. {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/rebrandly_otel.egg-info/requires.txt +0 -0
  15. {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/rebrandly_otel.egg-info/top_level.txt +0 -0
  16. {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/setup.cfg +0 -0
  17. {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/src/__init__.py +0 -0
  18. {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/src/logs.py +0 -0
  19. {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/src/metrics.py +0 -0
  20. {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/src/traces.py +0 -0
  21. {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/tests/test_usage.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rebrandly_otel
3
- Version: 0.1.36
3
+ Version: 0.2.3
4
4
  Summary: Python OTEL wrapper by Rebrandly
5
5
  Home-page: https://github.com/rebrandly/rebrandly-otel-python
6
6
  Author: Antonio Romano
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rebrandly_otel
3
- Version: 0.1.36
3
+ Version: 0.2.3
4
4
  Summary: Python OTEL wrapper by Rebrandly
5
5
  Home-page: https://github.com/rebrandly/rebrandly-otel-python
6
6
  Author: Antonio Romano
@@ -9,6 +9,7 @@ rebrandly_otel.egg-info/top_level.txt
9
9
  src/__init__.py
10
10
  src/fastapi_support.py
11
11
  src/flask_support.py
12
+ src/http_utils.py
12
13
  src/logs.py
13
14
  src/metrics.py
14
15
  src/otel_utils.py
@@ -5,7 +5,7 @@ with open("README.md", "r") as fh:
5
5
 
6
6
  setuptools.setup(
7
7
  name="rebrandly_otel",
8
- version="0.1.36",
8
+ version="0.2.3",
9
9
  author="Antonio Romano",
10
10
  author_email="antonio@rebrandly.com",
11
11
  description="Python OTEL wrapper by Rebrandly",
@@ -2,6 +2,7 @@
2
2
  """FastAPI integration for Rebrandly OTEL SDK."""
3
3
  import json
4
4
  from rebrandly_otel import Status, StatusCode, SpanKind
5
+ from .http_utils import filter_important_headers
5
6
  from fastapi import HTTPException, Depends
6
7
  from starlette.requests import Request
7
8
  from starlette.middleware.base import BaseHTTPMiddleware
@@ -49,11 +50,14 @@ def add_otel_middleware(otel, app):
49
50
  # Start span for request
50
51
  span_name = f"{request.method} {request.url.path}"
51
52
 
53
+ # Filter headers to keep only important ones
54
+ filtered_headers = filter_important_headers(headers)
55
+
52
56
  # Build attributes dict, excluding None values
53
57
  attributes = {
54
58
  # Required HTTP attributes per semantic conventions
55
59
  "http.request.method": request.method,
56
- "http.request.headers": json.dumps(request.headers, default=str),
60
+ "http.request.headers": json.dumps(filtered_headers, default=str),
57
61
  "url.full": str(request.url),
58
62
  "url.scheme": request.url.scheme,
59
63
  "url.path": request.url.path,
@@ -3,6 +3,7 @@
3
3
 
4
4
  import json
5
5
  from rebrandly_otel import Status, StatusCode, SpanKind
6
+ from .http_utils import filter_important_headers
6
7
 
7
8
  from flask import request, jsonify
8
9
  from werkzeug.exceptions import HTTPException
@@ -40,11 +41,14 @@ def app_before_request(otel):
40
41
  # Route will be available after request routing is done
41
42
  span_name = f"{request.method} {request.path}"
42
43
 
44
+ # Filter headers to keep only important ones
45
+ filtered_headers = filter_important_headers(headers)
46
+
43
47
  # Build attributes dict, excluding None values
44
48
  attributes = {
45
49
  # Required HTTP attributes per semantic conventions
46
50
  "http.request.method": request.method,
47
- "http.request.headers": json.dumps(request.headers, default=str),
51
+ "http.request.headers": json.dumps(filtered_headers, default=str),
48
52
  "url.full": request.url,
49
53
  "url.scheme": request.scheme,
50
54
  "url.path": request.path,
@@ -0,0 +1,28 @@
1
+ # http_utils.py
2
+ """Shared HTTP utilities for Rebrandly OTEL SDK."""
3
+
4
+
5
+ def filter_important_headers(headers):
6
+ """
7
+ Filter headers to keep only important ones for observability.
8
+ Excludes sensitive headers like authorization, cookies, and tokens.
9
+ """
10
+ important_headers = [
11
+ 'content-type',
12
+ 'content-length',
13
+ 'accept',
14
+ 'accept-encoding',
15
+ 'accept-language',
16
+ 'host',
17
+ 'x-forwarded-for',
18
+ 'x-forwarded-proto',
19
+ 'x-request-id',
20
+ 'x-correlation-id',
21
+ 'x-trace-id'
22
+ ]
23
+
24
+ filtered = {}
25
+ for key, value in headers.items():
26
+ if key.lower() in important_headers:
27
+ filtered[key] = value
28
+ return filtered
@@ -7,7 +7,8 @@ import grpc
7
7
  import json
8
8
 
9
9
  from opentelemetry.sdk.resources import Resource, SERVICE_NAMESPACE
10
- from opentelemetry.semconv.attributes import service_attributes
10
+ from opentelemetry.semconv.attributes import service_attributes, telemetry_attributes
11
+ from opentelemetry.semconv.resource import ResourceAttributes
11
12
  from opentelemetry.semconv._incubating.attributes import process_attributes, deployment_attributes
12
13
 
13
14
  def create_resource(name: str = None, version: str = None) -> Resource:
@@ -17,13 +18,28 @@ def create_resource(name: str = None, version: str = None) -> Resource:
17
18
  if version is None:
18
19
  version = get_service_version()
19
20
 
21
+ resources_attributes = {
22
+ service_attributes.SERVICE_NAME: name,
23
+ service_attributes.SERVICE_VERSION: version,
24
+ process_attributes.PROCESS_RUNTIME_VERSION: f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
25
+ SERVICE_NAMESPACE: os.environ.get('ENV', os.environ.get('ENVIRONMENT', os.environ.get('NODE_ENV', 'local'))),
26
+ telemetry_attributes.TELEMETRY_SDK_LANGUAGE: "python"
27
+ }
28
+
29
+ if os.environ.get('OTEL_RESOURCE_ATTRIBUTES', None) is not None and os.environ.get('OTEL_RESOURCE_ATTRIBUTES', None).strip() != "":
30
+ try:
31
+ ora = os.environ.get('OTEL_RESOURCE_ATTRIBUTES')
32
+ spl = ora.split(',')
33
+ for attr in spl:
34
+ attr = attr.strip()
35
+ if attr != "" and '=' in attr:
36
+ # Split on first '=' only, in case value contains '='
37
+ k, v = attr.split('=', 1)
38
+ resources_attributes[k.strip()] = v.strip()
39
+ except Exception as e:
40
+ print(f"[OTEL Utils] Warning: Invalid OTEL_RESOURCE_ATTRIBUTES value: {e}")
20
41
  resource = Resource.create(
21
- {
22
- service_attributes.SERVICE_NAME: name,
23
- service_attributes.SERVICE_VERSION: version,
24
- process_attributes.PROCESS_RUNTIME_VERSION: f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
25
- SERVICE_NAMESPACE: os.environ.get('ENV', os.environ.get('ENVIRONMENT', os.environ.get('NODE_ENV', 'local')))
26
- }
42
+ resources_attributes
27
43
  )
28
44
  return resource
29
45
 
@@ -55,6 +71,17 @@ def get_service_version(service_version: str = None) -> str:
55
71
  return service_version
56
72
 
57
73
 
74
+ def get_subsystem_name(subsystem_name: str = None) -> str:
75
+ """
76
+ Get subsystem name for Coralogix.
77
+ Checks OTEL_SUBSYSTEM_NAME or CX_SUBSYSTEM_NAME environment variables.
78
+ Returns None if not set (subsystem is optional).
79
+ """
80
+ if subsystem_name is None:
81
+ return os.environ.get('OTEL_SUBSYSTEM_NAME', os.environ.get('CX_SUBSYSTEM_NAME', None))
82
+ return subsystem_name
83
+
84
+
58
85
  def get_otlp_endpoint(otlp_endpoint: str = None) -> str | None:
59
86
  endpoint = otlp_endpoint or os.environ.get('OTEL_EXPORTER_OTLP_ENDPOINT', None)
60
87
 
@@ -80,8 +107,12 @@ def get_otlp_endpoint(otlp_endpoint: str = None) -> str | None:
80
107
  finally:
81
108
  channel.close()
82
109
 
110
+ except grpc.FutureTimeoutError:
111
+ print(f"[OTEL] Error: Connection timeout to OTLP endpoint {endpoint}. Check if the collector is running and accessible.")
112
+ return None
83
113
  except Exception as e:
84
- print(f"[OTEL] Failed to connect to OTLP endpoint {endpoint}: {e}")
114
+ print(f"[OTEL] Error: Failed to connect to OTLP endpoint {endpoint}: {type(e).__name__}: {e}")
115
+ print(f"[OTEL] Telemetry data will not be exported. Verify endpoint configuration and network connectivity.")
85
116
  return None
86
117
  return endpoint
87
118
 
@@ -110,7 +110,7 @@ def _trace_query(func, tracer, slow_query_threshold_ms, capture_bindings, db_nam
110
110
  # Start span
111
111
  span_name = f"pymysql.{'executemany' if many else 'execute'}"
112
112
 
113
- with tracer.start_span(
113
+ with tracer.start_as_current_span(
114
114
  name=span_name,
115
115
  kind=SpanKind.CLIENT
116
116
  ) as span:
@@ -160,6 +160,7 @@ class RebrandlyOTEL:
160
160
  token = otel_context.attach(extracted_context)
161
161
 
162
162
  result = None
163
+ span = None
163
164
  try:
164
165
 
165
166
  # Create and execute within span
@@ -207,15 +208,16 @@ class RebrandlyOTEL:
207
208
 
208
209
  except Exception as e:
209
210
 
210
- # Add failed completion event with error attribute (THIS IS THE KEY ADDITION)
211
- span.add_event("lambda.invocation.complete", {
212
- 'success': False,
213
- 'error': type(e).__name__
214
- })
211
+ # Add failed completion event with error attribute (only if span exists)
212
+ if span is not None and hasattr(span, 'is_recording') and span.is_recording():
213
+ span.add_event("lambda.invocation.complete", {
214
+ 'success': False,
215
+ 'error': type(e).__name__
216
+ })
215
217
 
216
- # Record the exception in the span
217
- span.record_exception(e)
218
- span.set_status(Status(StatusCode.ERROR, str(e)))
218
+ # Record the exception in the span
219
+ span.record_exception(e)
220
+ span.set_status(Status(StatusCode.ERROR, str(e)))
219
221
 
220
222
  # Log error
221
223
  print(f"Lambda execution failed: {e}")
File without changes