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 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
- if get_otlp_endpoint():
38
- otlp_exporter = OTLPLogExporter(endpoint=get_otlp_endpoint())
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
- if get_otlp_endpoint() is not None:
82
- otlp_exporter = OTLPMetricExporter(endpoint=get_otlp_endpoint())
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
 
@@ -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
- return '?.0.1'
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
- if otlp_endpoint is None:
54
- return os.environ.get('OTEL_EXPORTER_OTLP_ENDPOINT', None)
55
- return otlp_endpoint
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
- return 5000
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
@@ -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
- if get_otlp_endpoint() is not None:
36
- otlp_exporter = OTLPSpanExporter(endpoint=get_otlp_endpoint())
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
- target_span.set_status(trace.Status(trace.StatusCode.ERROR, str(exception)), description=msg)
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 an exception on a span."""
150
+ """Record success on a span."""
147
151
  target_span = span or self.get_current_span()
148
- if target_span and hasattr(target_span, 'record_exception'):
149
- target_span.set_status(trace.Status(trace.StatusCode.OK), description=msg)
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.18
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,,