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.
- {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/PKG-INFO +1 -1
- {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/rebrandly_otel.egg-info/PKG-INFO +1 -1
- {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/rebrandly_otel.egg-info/SOURCES.txt +1 -0
- {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/setup.py +1 -1
- {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/src/fastapi_support.py +5 -1
- {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/src/flask_support.py +5 -1
- rebrandly_otel-0.2.3/src/http_utils.py +28 -0
- {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/src/otel_utils.py +39 -8
- {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/src/pymysql_instrumentation.py +1 -1
- {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/src/rebrandly_otel.py +10 -8
- {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/LICENSE +0 -0
- {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/README.md +0 -0
- {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/rebrandly_otel.egg-info/dependency_links.txt +0 -0
- {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/rebrandly_otel.egg-info/requires.txt +0 -0
- {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/rebrandly_otel.egg-info/top_level.txt +0 -0
- {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/setup.cfg +0 -0
- {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/src/__init__.py +0 -0
- {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/src/logs.py +0 -0
- {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/src/metrics.py +0 -0
- {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/src/traces.py +0 -0
- {rebrandly_otel-0.1.36 → rebrandly_otel-0.2.3}/tests/test_usage.py +0 -0
|
@@ -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(
|
|
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(
|
|
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.
|
|
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 (
|
|
211
|
-
span
|
|
212
|
-
|
|
213
|
-
|
|
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
|
-
|
|
217
|
-
|
|
218
|
-
|
|
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
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|