rebrandly-otel 0.1.18__py3-none-any.whl → 0.1.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.
- rebrandly_otel/logs.py +7 -3
- rebrandly_otel/metrics.py +6 -2
- rebrandly_otel/otel_utils.py +52 -8
- rebrandly_otel/rebrandly_otel.py +10 -1
- rebrandly_otel/traces.py +10 -6
- {rebrandly_otel-0.1.18.dist-info → rebrandly_otel-0.1.20.dist-info}/METADATA +88 -1
- rebrandly_otel-0.1.20.dist-info/RECORD +13 -0
- rebrandly_otel-0.1.18.dist-info/RECORD +0 -13
- {rebrandly_otel-0.1.18.dist-info → rebrandly_otel-0.1.20.dist-info}/WHEEL +0 -0
- {rebrandly_otel-0.1.18.dist-info → rebrandly_otel-0.1.20.dist-info}/licenses/LICENSE +0 -0
- {rebrandly_otel-0.1.18.dist-info → rebrandly_otel-0.1.20.dist-info}/top_level.txt +0 -0
rebrandly_otel/logs.py
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
# logs.py
|
|
2
2
|
"""Logging implementation for Rebrandly OTEL SDK."""
|
|
3
3
|
import logging
|
|
4
|
+
import sys
|
|
4
5
|
from typing import Optional
|
|
5
6
|
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
|
|
6
7
|
from opentelemetry.sdk._logs.export import (
|
|
@@ -34,12 +35,15 @@ class RebrandlyLogger:
|
|
|
34
35
|
self._provider.add_log_record_processor(SimpleLogRecordProcessor(console_exporter))
|
|
35
36
|
|
|
36
37
|
# Add OTLP exporter if configured
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
otel_endpoint = get_otlp_endpoint()
|
|
39
|
+
if otel_endpoint:
|
|
40
|
+
otlp_exporter = OTLPLogExporter(
|
|
41
|
+
timeout=5,
|
|
42
|
+
endpoint=otel_endpoint
|
|
43
|
+
)
|
|
39
44
|
batch_processor = BatchLogRecordProcessor(otlp_exporter, export_timeout_millis=get_millis_batch_time())
|
|
40
45
|
self._provider.add_log_record_processor(batch_processor)
|
|
41
46
|
|
|
42
|
-
# Set as global provider
|
|
43
47
|
set_logger_provider(self._provider)
|
|
44
48
|
|
|
45
49
|
# Configure standard logging
|
rebrandly_otel/metrics.py
CHANGED
|
@@ -78,8 +78,12 @@ class RebrandlyMeter:
|
|
|
78
78
|
readers.append(console_reader)
|
|
79
79
|
|
|
80
80
|
# Add OTLP exporter if configured
|
|
81
|
-
|
|
82
|
-
|
|
81
|
+
otel_endpoint = get_otlp_endpoint()
|
|
82
|
+
if otel_endpoint is not None:
|
|
83
|
+
otlp_exporter = OTLPMetricExporter(
|
|
84
|
+
endpoint=otel_endpoint,
|
|
85
|
+
timeout=5
|
|
86
|
+
)
|
|
83
87
|
otlp_reader = PeriodicExportingMetricReader(otlp_exporter, export_interval_millis=get_millis_batch_time())
|
|
84
88
|
readers.append(otlp_reader)
|
|
85
89
|
|
rebrandly_otel/otel_utils.py
CHANGED
|
@@ -3,6 +3,8 @@
|
|
|
3
3
|
|
|
4
4
|
import os
|
|
5
5
|
import sys
|
|
6
|
+
import grpc
|
|
7
|
+
import json
|
|
6
8
|
|
|
7
9
|
from opentelemetry.sdk.resources import Resource
|
|
8
10
|
from opentelemetry.semconv.attributes import service_attributes
|
|
@@ -33,8 +35,9 @@ def get_package_version():
|
|
|
33
35
|
try:
|
|
34
36
|
from importlib_metadata import version, PackageNotFoundError
|
|
35
37
|
return version('rebrandly_otel')
|
|
36
|
-
except:
|
|
37
|
-
|
|
38
|
+
except Exception as e:
|
|
39
|
+
print(f"[OTEL Utils] Warning: Could not get package version: {e}")
|
|
40
|
+
return '0.1.0'
|
|
38
41
|
|
|
39
42
|
|
|
40
43
|
def get_service_name(service_name: str = None) -> str:
|
|
@@ -49,10 +52,31 @@ def get_service_version(service_version: str = None) -> str:
|
|
|
49
52
|
return service_version
|
|
50
53
|
|
|
51
54
|
|
|
52
|
-
def get_otlp_endpoint(otlp_endpoint: str = None) -> str:
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
55
|
+
def get_otlp_endpoint(otlp_endpoint: str = None) -> str | None:
|
|
56
|
+
endpoint = otlp_endpoint or os.environ.get('OTEL_EXPORTER_OTLP_ENDPOINT', None)
|
|
57
|
+
|
|
58
|
+
if endpoint is not None:
|
|
59
|
+
try:
|
|
60
|
+
from urllib.parse import urlparse
|
|
61
|
+
|
|
62
|
+
# Parse the endpoint
|
|
63
|
+
parsed = urlparse(endpoint if '://' in endpoint else f'http://{endpoint}')
|
|
64
|
+
host = parsed.hostname
|
|
65
|
+
port = parsed.port
|
|
66
|
+
|
|
67
|
+
# Test gRPC connection
|
|
68
|
+
channel = grpc.insecure_channel(f'{host}:{port}')
|
|
69
|
+
try:
|
|
70
|
+
# Wait for the channel to be ready
|
|
71
|
+
grpc.channel_ready_future(channel).result(timeout=3)
|
|
72
|
+
return endpoint
|
|
73
|
+
finally:
|
|
74
|
+
channel.close()
|
|
75
|
+
|
|
76
|
+
except Exception as e:
|
|
77
|
+
print(f"[OTEL] Failed to connect to OTLP endpoint {endpoint}: {e}")
|
|
78
|
+
return None
|
|
79
|
+
return endpoint
|
|
56
80
|
|
|
57
81
|
def is_otel_debug() -> bool:
|
|
58
82
|
return os.environ.get('OTEL_DEBUG', 'false').lower() == 'true'
|
|
@@ -61,5 +85,25 @@ def is_otel_debug() -> bool:
|
|
|
61
85
|
def get_millis_batch_time():
|
|
62
86
|
try:
|
|
63
87
|
return int(os.environ.get('BATCH_EXPORT_TIME_MILLIS', 100))
|
|
64
|
-
except:
|
|
65
|
-
|
|
88
|
+
except Exception as e:
|
|
89
|
+
print(f"[OTEL Utils] Warning: Invalid BATCH_EXPORT_TIME_MILLIS value, using default 5000ms: {e}")
|
|
90
|
+
return 5000
|
|
91
|
+
|
|
92
|
+
def extract_event_from(message) -> str | None:
|
|
93
|
+
body = None
|
|
94
|
+
if 'body' in message:
|
|
95
|
+
body = message['body']
|
|
96
|
+
if 'Body' in message:
|
|
97
|
+
body = message['Body']
|
|
98
|
+
if 'Message' in message:
|
|
99
|
+
body = message['Message']
|
|
100
|
+
if 'Sns' in message and 'Message' in message['Sns']:
|
|
101
|
+
body = message['Sns']['Message']
|
|
102
|
+
if body is not None:
|
|
103
|
+
try:
|
|
104
|
+
jbody = json.loads(body)
|
|
105
|
+
if 'event' in jbody:
|
|
106
|
+
return jbody['event']
|
|
107
|
+
except:
|
|
108
|
+
pass
|
|
109
|
+
return None
|
rebrandly_otel/rebrandly_otel.py
CHANGED
|
@@ -12,6 +12,7 @@ from opentelemetry import baggage, propagate, context
|
|
|
12
12
|
from .traces import RebrandlyTracer
|
|
13
13
|
from .metrics import RebrandlyMeter
|
|
14
14
|
from .logs import RebrandlyLogger
|
|
15
|
+
from .otel_utils import extract_event_from
|
|
15
16
|
|
|
16
17
|
|
|
17
18
|
T = TypeVar('T')
|
|
@@ -260,8 +261,11 @@ class RebrandlyOTEL:
|
|
|
260
261
|
try:
|
|
261
262
|
# Create span and execute function
|
|
262
263
|
span_function = self.span
|
|
263
|
-
if record is not None and ('MessageAttributes' in record or 'messageAttributes' in record):
|
|
264
|
+
if record is not None and (('MessageAttributes' in record or 'messageAttributes' in record) or ('Sns' in record and 'MessageAttributes' in record['Sns'])):
|
|
264
265
|
span_function = self.aws_message_span
|
|
266
|
+
evt = extract_event_from(record)
|
|
267
|
+
if evt:
|
|
268
|
+
span_attributes['event.type'] = evt
|
|
265
269
|
|
|
266
270
|
with span_function(span_name, message=record, attributes=span_attributes, kind=kind) as span_context:
|
|
267
271
|
# Add processing start event with standardized name
|
|
@@ -370,6 +374,7 @@ class RebrandlyOTEL:
|
|
|
370
374
|
time.sleep(0.1)
|
|
371
375
|
|
|
372
376
|
except Exception as e:
|
|
377
|
+
print(f"[Rebrandly OTEL] Error during force flush: {e}")
|
|
373
378
|
success = False
|
|
374
379
|
|
|
375
380
|
return success
|
|
@@ -486,6 +491,10 @@ class RebrandlyOTEL:
|
|
|
486
491
|
if 'awsRegion' in message:
|
|
487
492
|
combined_attributes['cloud.region'] = message['awsRegion']
|
|
488
493
|
|
|
494
|
+
evt = extract_event_from(message)
|
|
495
|
+
if evt:
|
|
496
|
+
combined_attributes['event.type'] = evt
|
|
497
|
+
|
|
489
498
|
|
|
490
499
|
# Use the tracer's start_span method directly to ensure it works
|
|
491
500
|
# This creates a child span of whatever is currently active
|
rebrandly_otel/traces.py
CHANGED
|
@@ -32,8 +32,12 @@ class RebrandlyTracer:
|
|
|
32
32
|
self._provider.add_span_processor(SimpleSpanProcessor(console_exporter))
|
|
33
33
|
|
|
34
34
|
# Add OTLP exporter if configured
|
|
35
|
-
|
|
36
|
-
|
|
35
|
+
otel_endpoint = get_otlp_endpoint()
|
|
36
|
+
if otel_endpoint is not None:
|
|
37
|
+
otlp_exporter = OTLPSpanExporter(
|
|
38
|
+
endpoint=otel_endpoint,
|
|
39
|
+
timeout=5
|
|
40
|
+
)
|
|
37
41
|
|
|
38
42
|
# Use batch processor for production
|
|
39
43
|
batch_processor = BatchSpanProcessor(otlp_exporter, export_timeout_millis=get_millis_batch_time())
|
|
@@ -139,14 +143,14 @@ class RebrandlyTracer:
|
|
|
139
143
|
if target_span and hasattr(target_span, 'record_exception'):
|
|
140
144
|
if exception is not None:
|
|
141
145
|
target_span.record_exception(exception)
|
|
142
|
-
|
|
146
|
+
target_span.set_status(trace.Status(trace.StatusCode.ERROR, str(exception)))
|
|
143
147
|
|
|
144
148
|
|
|
145
149
|
def record_span_success(self, span: Optional[Span] = None, msg: Optional[str] = None):
|
|
146
|
-
"""Record
|
|
150
|
+
"""Record success on a span."""
|
|
147
151
|
target_span = span or self.get_current_span()
|
|
148
|
-
if target_span and hasattr(target_span, '
|
|
149
|
-
target_span.set_status(trace.Status(trace.StatusCode.OK)
|
|
152
|
+
if target_span and hasattr(target_span, 'set_status'):
|
|
153
|
+
target_span.set_status(trace.Status(trace.StatusCode.OK))
|
|
150
154
|
|
|
151
155
|
|
|
152
156
|
def add_event(self, name: str, attributes: Optional[Dict[str, Any]] = None, span: Optional[Span] = None):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: rebrandly_otel
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.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
|
|
@@ -360,6 +360,93 @@ if __name__ == '__main__':
|
|
|
360
360
|
app.run(debug=True)
|
|
361
361
|
```
|
|
362
362
|
|
|
363
|
+
###
|
|
364
|
+
FastAPI
|
|
365
|
+
|
|
366
|
+
```python
|
|
367
|
+
|
|
368
|
+
# main_fastapi.py
|
|
369
|
+
from fastapi import FastAPI, HTTPException, Depends
|
|
370
|
+
from contextlib import asynccontextmanager
|
|
371
|
+
from src.rebrandly_otel import otel, logger, force_flush
|
|
372
|
+
from src.fastapi_support import setup_fastapi, get_current_span
|
|
373
|
+
from datetime import datetime
|
|
374
|
+
from typing import Optional
|
|
375
|
+
import uvicorn
|
|
376
|
+
|
|
377
|
+
@asynccontextmanager
|
|
378
|
+
async def lifespan(app: FastAPI):
|
|
379
|
+
# Startup
|
|
380
|
+
logger.info("FastAPI application starting up")
|
|
381
|
+
yield
|
|
382
|
+
# Shutdown
|
|
383
|
+
logger.info("FastAPI application shutting down")
|
|
384
|
+
force_flush()
|
|
385
|
+
|
|
386
|
+
app = FastAPI(title="FastAPI OTEL Example", lifespan=lifespan)
|
|
387
|
+
|
|
388
|
+
# Setup FastAPI with OTEL
|
|
389
|
+
setup_fastapi(otel, app)
|
|
390
|
+
|
|
391
|
+
@app.get("/health")
|
|
392
|
+
async def health():
|
|
393
|
+
"""Health check endpoint."""
|
|
394
|
+
logger.info("Health check requested")
|
|
395
|
+
return {"status": "healthy"}
|
|
396
|
+
|
|
397
|
+
@app.post("/process")
|
|
398
|
+
@app.get("/process")
|
|
399
|
+
async def process(span = Depends(get_current_span)):
|
|
400
|
+
"""Process endpoint with custom span."""
|
|
401
|
+
with otel.span("process_request"):
|
|
402
|
+
logger.info("Processing request")
|
|
403
|
+
|
|
404
|
+
# You can also use the injected span directly
|
|
405
|
+
if span:
|
|
406
|
+
span.add_event("custom_processing_event", {
|
|
407
|
+
"timestamp": datetime.now().isoformat()
|
|
408
|
+
})
|
|
409
|
+
|
|
410
|
+
# Simulate some processing
|
|
411
|
+
result = {
|
|
412
|
+
"processed": True,
|
|
413
|
+
"timestamp": datetime.now().isoformat()
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
logger.info(f"Returning result: {result}")
|
|
417
|
+
return result
|
|
418
|
+
|
|
419
|
+
@app.get("/error")
|
|
420
|
+
async def error():
|
|
421
|
+
"""Endpoint that raises an error."""
|
|
422
|
+
logger.error("Error endpoint called")
|
|
423
|
+
raise HTTPException(status_code=400, detail="Simulated error")
|
|
424
|
+
|
|
425
|
+
@app.get("/exception")
|
|
426
|
+
async def exception():
|
|
427
|
+
"""Endpoint that raises an unhandled exception."""
|
|
428
|
+
logger.error("Exception endpoint called")
|
|
429
|
+
raise ValueError("Simulated unhandled exception")
|
|
430
|
+
|
|
431
|
+
@app.get("/items/{item_id}")
|
|
432
|
+
async def get_item(item_id: int, q: Optional[str] = None):
|
|
433
|
+
"""Example endpoint with path and query parameters."""
|
|
434
|
+
with otel.span("fetch_item", attributes={"item_id": item_id, "query": q}):
|
|
435
|
+
logger.info(f"Fetching item {item_id} with query: {q}")
|
|
436
|
+
|
|
437
|
+
if item_id == 999:
|
|
438
|
+
raise HTTPException(status_code=404, detail="Item not found")
|
|
439
|
+
|
|
440
|
+
return {
|
|
441
|
+
"item_id": item_id,
|
|
442
|
+
"name": f"Item {item_id}",
|
|
443
|
+
"query": q
|
|
444
|
+
}
|
|
445
|
+
|
|
446
|
+
if __name__ == "__main__":
|
|
447
|
+
uvicorn.run(app, host="0.0.0.0", port=8000)
|
|
448
|
+
```
|
|
449
|
+
|
|
363
450
|
### More examples
|
|
364
451
|
You can find More examples [here](examples)
|
|
365
452
|
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
rebrandly_otel/__init__.py,sha256=tkZQJo5hR4FJ4dIRc-3b_YGxGo-uq-DsiSz8shdac-k,397
|
|
2
|
+
rebrandly_otel/fastapi_support.py,sha256=RuBBZJuzr3osBDrkHZ0oQPV70pmvnqxTfBBDVFBFQlo,8019
|
|
3
|
+
rebrandly_otel/flask_support.py,sha256=cUVMGTjN6N8xZD4Zyng2LRWhNj62C5nmTH91SnYBp2A,6072
|
|
4
|
+
rebrandly_otel/logs.py,sha256=5byeN-cDmBRpeZDw9IBz_vuiJm3wsGEbcAk5pwYHNAU,3791
|
|
5
|
+
rebrandly_otel/metrics.py,sha256=8aAqdr3SAcX_rVivTl_aHeD_BRByt-Qnfij_51Y7Fn0,7561
|
|
6
|
+
rebrandly_otel/otel_utils.py,sha256=DodoBBqzDlo3pC5TLmWT2eAAE8NvW6MxxAoBrfVaxc4,3572
|
|
7
|
+
rebrandly_otel/rebrandly_otel.py,sha256=wT1GiOQQMZl2sVG8MGJ9qM9gx_zRwS7U5oAe77RbJdk,21618
|
|
8
|
+
rebrandly_otel/traces.py,sha256=JY_3RWbzpUxzEx3GqTVgggsyA2DB4oR-zDftIFFJha4,7174
|
|
9
|
+
rebrandly_otel-0.1.20.dist-info/licenses/LICENSE,sha256=KMXHvTwP62S2q-SG7CFfMU_09rUwxiqlM0izaYGdcgY,1103
|
|
10
|
+
rebrandly_otel-0.1.20.dist-info/METADATA,sha256=II9R2GM1UWTYg-JmUbQewcXR9J_jww3eiKVOApklmkM,12854
|
|
11
|
+
rebrandly_otel-0.1.20.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
12
|
+
rebrandly_otel-0.1.20.dist-info/top_level.txt,sha256=26PSC1gjVUl8tTH5QfKbFevjVV4E2yojoukEfiTScvM,15
|
|
13
|
+
rebrandly_otel-0.1.20.dist-info/RECORD,,
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
rebrandly_otel/__init__.py,sha256=tkZQJo5hR4FJ4dIRc-3b_YGxGo-uq-DsiSz8shdac-k,397
|
|
2
|
-
rebrandly_otel/fastapi_support.py,sha256=RuBBZJuzr3osBDrkHZ0oQPV70pmvnqxTfBBDVFBFQlo,8019
|
|
3
|
-
rebrandly_otel/flask_support.py,sha256=cUVMGTjN6N8xZD4Zyng2LRWhNj62C5nmTH91SnYBp2A,6072
|
|
4
|
-
rebrandly_otel/logs.py,sha256=92jaxzI5hCHnKHu3lsSAa7K_SPHQgL46AlQUESsYNds,3724
|
|
5
|
-
rebrandly_otel/metrics.py,sha256=8Mgz_VcgsGQaBeYH2y6FtLGSqMqTwa2ilAa1pbYYymU,7472
|
|
6
|
-
rebrandly_otel/otel_utils.py,sha256=tKelaETEeZxxvddKDpY8ESsGS77CcBbQku4oQjmiJx0,2078
|
|
7
|
-
rebrandly_otel/rebrandly_otel.py,sha256=Es6ZQC2hr6qJ_M4Y-7nUvcAnGADj6-ofKn8hKFw7P1k,21166
|
|
8
|
-
rebrandly_otel/traces.py,sha256=v582WtJv3t4Bn92vlDyZouibHtgWNxdRo_XmQCmSOEA,7126
|
|
9
|
-
rebrandly_otel-0.1.18.dist-info/licenses/LICENSE,sha256=KMXHvTwP62S2q-SG7CFfMU_09rUwxiqlM0izaYGdcgY,1103
|
|
10
|
-
rebrandly_otel-0.1.18.dist-info/METADATA,sha256=CyoVBpLByY8eqh5C7UTmKTBH62thbZww1GxPjYKSV6Y,10410
|
|
11
|
-
rebrandly_otel-0.1.18.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
12
|
-
rebrandly_otel-0.1.18.dist-info/top_level.txt,sha256=26PSC1gjVUl8tTH5QfKbFevjVV4E2yojoukEfiTScvM,15
|
|
13
|
-
rebrandly_otel-0.1.18.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|
|
File without changes
|