rebrandly-otel 0.1.35__tar.gz → 0.2.2__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.35 → rebrandly_otel-0.2.2}/PKG-INFO +1 -1
  2. {rebrandly_otel-0.1.35 → rebrandly_otel-0.2.2}/rebrandly_otel.egg-info/PKG-INFO +1 -1
  3. {rebrandly_otel-0.1.35 → rebrandly_otel-0.2.2}/rebrandly_otel.egg-info/SOURCES.txt +1 -0
  4. {rebrandly_otel-0.1.35 → rebrandly_otel-0.2.2}/setup.py +1 -1
  5. {rebrandly_otel-0.1.35 → rebrandly_otel-0.2.2}/src/fastapi_support.py +5 -1
  6. {rebrandly_otel-0.1.35 → rebrandly_otel-0.2.2}/src/flask_support.py +5 -1
  7. rebrandly_otel-0.2.2/src/http_utils.py +28 -0
  8. {rebrandly_otel-0.1.35 → rebrandly_otel-0.2.2}/src/otel_utils.py +36 -7
  9. {rebrandly_otel-0.1.35 → rebrandly_otel-0.2.2}/src/rebrandly_otel.py +13 -11
  10. {rebrandly_otel-0.1.35 → rebrandly_otel-0.2.2}/LICENSE +0 -0
  11. {rebrandly_otel-0.1.35 → rebrandly_otel-0.2.2}/README.md +0 -0
  12. {rebrandly_otel-0.1.35 → rebrandly_otel-0.2.2}/rebrandly_otel.egg-info/dependency_links.txt +0 -0
  13. {rebrandly_otel-0.1.35 → rebrandly_otel-0.2.2}/rebrandly_otel.egg-info/requires.txt +0 -0
  14. {rebrandly_otel-0.1.35 → rebrandly_otel-0.2.2}/rebrandly_otel.egg-info/top_level.txt +0 -0
  15. {rebrandly_otel-0.1.35 → rebrandly_otel-0.2.2}/setup.cfg +0 -0
  16. {rebrandly_otel-0.1.35 → rebrandly_otel-0.2.2}/src/__init__.py +0 -0
  17. {rebrandly_otel-0.1.35 → rebrandly_otel-0.2.2}/src/logs.py +0 -0
  18. {rebrandly_otel-0.1.35 → rebrandly_otel-0.2.2}/src/metrics.py +0 -0
  19. {rebrandly_otel-0.1.35 → rebrandly_otel-0.2.2}/src/pymysql_instrumentation.py +0 -0
  20. {rebrandly_otel-0.1.35 → rebrandly_otel-0.2.2}/src/traces.py +0 -0
  21. {rebrandly_otel-0.1.35 → rebrandly_otel-0.2.2}/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.35
3
+ Version: 0.2.2
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.35
3
+ Version: 0.2.2
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.35",
8
+ version="0.2.2",
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
@@ -17,13 +17,27 @@ def create_resource(name: str = None, version: str = None) -> Resource:
17
17
  if version is None:
18
18
  version = get_service_version()
19
19
 
20
+ resources_attributes = {
21
+ service_attributes.SERVICE_NAME: name,
22
+ service_attributes.SERVICE_VERSION: version,
23
+ process_attributes.PROCESS_RUNTIME_VERSION: f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}",
24
+ SERVICE_NAMESPACE: os.environ.get('ENV', os.environ.get('ENVIRONMENT', os.environ.get('NODE_ENV', 'local')))
25
+ }
26
+
27
+ if os.environ.get('OTEL_RESOURCE_ATTRIBUTES', None) is not None and os.environ.get('OTEL_RESOURCE_ATTRIBUTES', None).strip() != "":
28
+ try:
29
+ ora = os.environ.get('OTEL_RESOURCE_ATTRIBUTES')
30
+ spl = ora.split(',')
31
+ for attr in spl:
32
+ attr = attr.strip()
33
+ if attr != "" and '=' in attr:
34
+ # Split on first '=' only, in case value contains '='
35
+ k, v = attr.split('=', 1)
36
+ resources_attributes[k.strip()] = v.strip()
37
+ except Exception as e:
38
+ print(f"[OTEL Utils] Warning: Invalid OTEL_RESOURCE_ATTRIBUTES value: {e}")
20
39
  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
- }
40
+ resources_attributes
27
41
  )
28
42
  return resource
29
43
 
@@ -55,6 +69,17 @@ def get_service_version(service_version: str = None) -> str:
55
69
  return service_version
56
70
 
57
71
 
72
+ def get_subsystem_name(subsystem_name: str = None) -> str:
73
+ """
74
+ Get subsystem name for Coralogix.
75
+ Checks OTEL_SUBSYSTEM_NAME or CX_SUBSYSTEM_NAME environment variables.
76
+ Returns None if not set (subsystem is optional).
77
+ """
78
+ if subsystem_name is None:
79
+ return os.environ.get('OTEL_SUBSYSTEM_NAME', os.environ.get('CX_SUBSYSTEM_NAME', None))
80
+ return subsystem_name
81
+
82
+
58
83
  def get_otlp_endpoint(otlp_endpoint: str = None) -> str | None:
59
84
  endpoint = otlp_endpoint or os.environ.get('OTEL_EXPORTER_OTLP_ENDPOINT', None)
60
85
 
@@ -80,8 +105,12 @@ def get_otlp_endpoint(otlp_endpoint: str = None) -> str | None:
80
105
  finally:
81
106
  channel.close()
82
107
 
108
+ except grpc.FutureTimeoutError:
109
+ print(f"[OTEL] Error: Connection timeout to OTLP endpoint {endpoint}. Check if the collector is running and accessible.")
110
+ return None
83
111
  except Exception as e:
84
- print(f"[OTEL] Failed to connect to OTLP endpoint {endpoint}: {e}")
112
+ print(f"[OTEL] Error: Failed to connect to OTLP endpoint {endpoint}: {type(e).__name__}: {e}")
113
+ print(f"[OTEL] Telemetry data will not be exported. Verify endpoint configuration and network connectivity.")
85
114
  return None
86
115
  return endpoint
87
116
 
@@ -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,18 +208,19 @@ 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
- self.logger.logger.error(f"Lambda execution failed: {e}", exc_info=True)
223
+ print(f"Lambda execution failed: {e}")
222
224
  raise
223
225
 
224
226
  finally:
@@ -229,10 +231,10 @@ class RebrandlyOTEL:
229
231
 
230
232
  # Force flush if enabled
231
233
  if auto_flush:
232
- self.logger.logger.info(f"[Rebrandly OTEL] Lambda '{span_name}', flushing...")
234
+ print(f"[Rebrandly OTEL] Lambda '{span_name}', flushing...")
233
235
  flush_success = self.force_flush(timeout_millis=1000)
234
236
  if not flush_success:
235
- self.logger.logger.warning("[Rebrandly OTEL] Force flush may not have completed fully")
237
+ print("[Rebrandly OTEL] Force flush may not have completed fully")
236
238
 
237
239
  return wrapper
238
240
  return decorator
File without changes