rebrandly-otel 0.1.16__py3-none-any.whl → 0.1.18__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.

@@ -1,5 +1,7 @@
1
1
  # src/__init__.py
2
2
  from .rebrandly_otel import *
3
+ from .flask_support import setup_flask
4
+ from .fastapi_support import setup_fastapi
3
5
 
4
6
  # Explicitly define what's available
5
7
  __all__ = [
@@ -12,5 +14,7 @@ __all__ = [
12
14
  'logger',
13
15
  'force_flush',
14
16
  'aws_message_handler',
15
- 'shutdown'
17
+ 'shutdown',
18
+ 'setup_flask',
19
+ 'setup_fastapi'
16
20
  ]
@@ -0,0 +1,198 @@
1
+ # fastapi_integration.py
2
+ """FastAPI integration for Rebrandly OTEL SDK."""
3
+ import json
4
+ from rebrandly_otel import Status, StatusCode, SpanKind
5
+ from fastapi import HTTPException, Depends
6
+ from starlette.requests import Request
7
+ from starlette.middleware.base import BaseHTTPMiddleware
8
+ from fastapi.responses import JSONResponse
9
+
10
+ import time
11
+
12
+ def setup_fastapi(otel , app):
13
+ """
14
+ Setup FastAPI application with OTEL instrumentation.
15
+
16
+ Example:
17
+ from fastapi import FastAPI
18
+ from rebrandly_otel import otel
19
+ from rebrandly_otel.fastapi_integration import setup_fastapi
20
+
21
+ app = FastAPI()
22
+ setup_fastapi(otel, app)
23
+ """
24
+
25
+ # Add middleware
26
+ add_otel_middleware(otel, app)
27
+
28
+ # Add exception handlers
29
+ app.add_exception_handler(HTTPException, lambda request, exc: fastapi_exception_handler(otel, request, exc))
30
+ app.add_exception_handler(Exception, lambda request, exc: fastapi_exception_handler(otel, request, exc))
31
+
32
+ return app
33
+
34
+ def add_otel_middleware(otel, app):
35
+ """
36
+ Add OTEL middleware to FastAPI application.
37
+ """
38
+
39
+ class OTELMiddleware(BaseHTTPMiddleware):
40
+ def __init__(self, app):
41
+ super().__init__(app)
42
+ self.otel = otel
43
+
44
+ async def dispatch(self, request: Request, call_next):
45
+ # Extract trace context from headers
46
+ headers = dict(request.headers)
47
+ token = self.otel.attach_context(headers)
48
+
49
+ # Start span for request
50
+ span_name = f"{request.method} {request.url.path}"
51
+
52
+ # Use start_as_current_span for proper context propagation
53
+ with self.otel.tracer.tracer.start_as_current_span(
54
+ span_name,
55
+ attributes={
56
+ # Required HTTP attributes per semantic conventions
57
+ "http.request.method": request.method,
58
+ "http.request.headers": json.dumps(request.headers, default=str),
59
+ "http.response.status_code": None, # Will be set after response
60
+ "url.full": str(request.url),
61
+ "url.scheme": request.url.scheme,
62
+ "url.path": request.url.path,
63
+ "url.query": request.url.query if request.url.query else None,
64
+ "user_agent.original": request.headers.get("user-agent"),
65
+ "http.route": None, # Will be set after routing
66
+ "network.protocol.version": "1.1", # FastAPI/Starlette typically uses HTTP/1.1
67
+ "server.address": request.url.hostname,
68
+ "server.port": request.url.port or (443 if request.url.scheme == 'https' else 80),
69
+ "client.address": request.client.host if request.client else None,
70
+ },
71
+ kind=SpanKind.SERVER
72
+ ) as span:
73
+ # Log request start
74
+ self.otel.logger.logger.info(f"Request started: {request.method} {request.url.path}",
75
+ extra={"http.method": request.method, "http.path": request.url.path})
76
+
77
+ # Store span in request state for access in routes
78
+ request.state.span = span
79
+ request.state.trace_token = token
80
+
81
+ start_time = time.time()
82
+
83
+ try:
84
+ # Process request
85
+ response = await call_next(request)
86
+
87
+ # After routing, update span name and route if available
88
+ if hasattr(request, 'scope') and 'path' in request.scope:
89
+ route = request.scope.get('path', request.url.path)
90
+ span.update_name(f"{request.method} {route}")
91
+ span.set_attribute("http.route", route)
92
+
93
+ # Set response attributes using new semantic conventions
94
+ span.set_attribute("http.response.status_code", response.status_code)
95
+ span.set_attribute("http.status_code", response.status_code) # Deprecated
96
+
97
+ # Set span status based on HTTP status code
98
+ if response.status_code >= 400:
99
+ span.set_status(Status(StatusCode.ERROR, f"HTTP {response.status_code}"))
100
+ else:
101
+ span.set_status(Status(StatusCode.OK))
102
+
103
+ # Log request completion
104
+ self.otel.logger.logger.info(f"Request completed: {response.status_code}",
105
+ extra={"http.status_code": response.status_code})
106
+ otel.force_flush(timeout_millis=100)
107
+ return response
108
+
109
+ except Exception as e:
110
+ # Record exception
111
+ span.record_exception(e)
112
+ span.set_status(Status(StatusCode.ERROR, str(e)))
113
+ span.add_event("exception", {
114
+ "exception.type": type(e).__name__,
115
+ "exception.message": str(e)
116
+ })
117
+
118
+ # Log error
119
+ self.otel.logger.logger.error(f"Unhandled exception: {e}",
120
+ exc_info=True,
121
+ extra={"exception.type": type(e).__name__})
122
+
123
+ raise
124
+
125
+ finally:
126
+ # Detach context
127
+ self.otel.detach_context(token)
128
+
129
+ # Add middleware to app
130
+ app.add_middleware(OTELMiddleware)
131
+
132
+ def fastapi_exception_handler(otel, request, exc):
133
+ """
134
+ Handle FastAPI exceptions and record them in the current span.
135
+ """
136
+
137
+ # Determine the status code
138
+ if isinstance(exc, HTTPException):
139
+ status_code = exc.status_code
140
+ error_detail = exc.detail
141
+ elif hasattr(exc, 'status_code'):
142
+ status_code = exc.status_code
143
+ error_detail = str(exc)
144
+ elif hasattr(exc, 'code'):
145
+ status_code = exc.code if isinstance(exc.code, int) else 500
146
+ error_detail = str(exc)
147
+ else:
148
+ status_code = 500
149
+ error_detail = str(exc)
150
+
151
+ # Record exception in span if available and still recording
152
+ if hasattr(request.state, 'span') and request.state.span.is_recording():
153
+ # Update both new and old attribute names for compatibility
154
+ request.state.span.set_attribute("http.response.status_code", status_code)
155
+ request.state.span.set_attribute("error.type", type(exc).__name__)
156
+
157
+ request.state.span.record_exception(exc)
158
+ request.state.span.set_status(Status(StatusCode.ERROR, str(exc)))
159
+ request.state.span.add_event("exception", {
160
+ "exception.type": type(exc).__name__,
161
+ "exception.message": str(exc)
162
+ })
163
+
164
+ # Log the error
165
+ otel.logger.logger.error(f"Unhandled exception: {exc} (status: {status_code})",
166
+ exc_info=True,
167
+ extra={
168
+ "exception.type": type(exc).__name__,
169
+ "http.status_code": status_code
170
+ })
171
+
172
+ # Return error response
173
+ return JSONResponse(
174
+ status_code=status_code,
175
+ content={
176
+ "error": error_detail,
177
+ "type": type(exc).__name__
178
+ }
179
+ )
180
+
181
+ # Optional: Dependency injection helper for accessing the span in routes
182
+ def get_current_span(request: Request):
183
+ """
184
+ FastAPI dependency to get the current span in route handlers.
185
+
186
+ Example:
187
+ from fastapi import Depends
188
+ from rebrandly_otel.fastapi_integration import get_current_span
189
+
190
+ @app.get("/example")
191
+ async def example(span = Depends(get_current_span)):
192
+ if span:
193
+ span.add_event("custom_event", {"key": "value"})
194
+ return {"status": "ok"}
195
+ """
196
+ if hasattr(request.state, 'span'):
197
+ return request.state.span
198
+ return None
@@ -0,0 +1,153 @@
1
+ # flask_integration.py
2
+ """Flask integration for Rebrandly OTEL SDK."""
3
+
4
+ import json
5
+ from rebrandly_otel import Status, StatusCode, SpanKind
6
+
7
+ from flask import request, jsonify
8
+ from werkzeug.exceptions import HTTPException
9
+
10
+
11
+ def setup_flask(otel, app):
12
+ """
13
+ Setup Flask application with OTEL instrumentation.
14
+
15
+ Example:
16
+ from flask import Flask
17
+ from rebrandly_otel import otel
18
+ from rebrandly_otel.flask_integration import setup_flask
19
+
20
+ app = Flask(__name__)
21
+ setup_flask(otel, app)
22
+ """
23
+ app.before_request(lambda: app_before_request(otel))
24
+ app.after_request(lambda response: app_after_request(otel, response))
25
+ app.register_error_handler(Exception, lambda e: flask_error_handler(otel, e))
26
+ return app
27
+
28
+ def app_before_request(otel):
29
+ """
30
+ Setup tracing for incoming Flask request.
31
+ To be used with Flask's before_request hook.
32
+ """
33
+
34
+ # Extract trace context from headers
35
+ headers = dict(request.headers)
36
+ token = otel.attach_context(headers)
37
+ request.trace_token = token
38
+
39
+ # Determine span name - use route if available, otherwise just method
40
+ # Route will be available after request routing is done
41
+ span_name = f"{request.method} {request.path}"
42
+
43
+ # Start span for request using start_as_current_span to make it the active span
44
+ span = otel.tracer.tracer.start_as_current_span(
45
+ span_name,
46
+ attributes={
47
+ # Required HTTP attributes per semantic conventions
48
+ "http.request.method": request.method,
49
+ "http.request.headers": json.dumps(request.headers, default=str),
50
+ "http.response.status_code": None, # Will be set in after_request
51
+ "url.full": request.url,
52
+ "url.scheme": request.scheme,
53
+ "url.path": request.path,
54
+ "url.query": request.query_string.decode('utf-8') if request.query_string else None,
55
+ "user_agent.original": request.user_agent.string if request.user_agent else None,
56
+ "http.route": request.path, # Flask doesn't expose route pattern easily
57
+ "network.protocol.version": request.environ.get('SERVER_PROTOCOL', 'HTTP/1.1').split('/')[-1],
58
+ "server.address": request.host.split(':')[0],
59
+ "server.port": request.host.split(':')[1] if ':' in request.host else (443 if request.scheme == 'https' else 80),
60
+ "client.address": request.remote_addr,
61
+ },
62
+ kind=SpanKind.SERVER
63
+ )
64
+ # Store both the span context manager and the span itself
65
+ request.span_context = span
66
+ request.span = span.__enter__() # This activates the span and returns the span object
67
+
68
+ # Log request start
69
+ otel.logger.logger.info(f"Request started: {request.method} {request.path}",
70
+ extra={"http.method": request.method, "http.path": request.path})
71
+
72
+ def app_after_request(otel, response):
73
+ """
74
+ Cleanup tracing after Flask request completes.
75
+ To be used with Flask's after_request hook.
76
+ """
77
+
78
+ # Check if we have a span and it's still recording
79
+ if hasattr(request, 'span') and request.span.is_recording():
80
+ # Update both new and old attribute names for compatibility
81
+ request.span.set_attribute("http.response.status_code", response.status_code)
82
+
83
+ # Set span status based on HTTP status code
84
+ if response.status_code >= 400:
85
+ request.span.set_status(Status(StatusCode.ERROR, f"HTTP {response.status_code}"))
86
+ else:
87
+ request.span.set_status(Status(StatusCode.OK))
88
+
89
+ # Properly close the span context manager
90
+ if hasattr(request, 'span_context'):
91
+ request.span_context.__exit__(None, None, None)
92
+ else:
93
+ # Fallback if we don't have the context manager
94
+ request.span.end()
95
+
96
+ # Detach context
97
+ if hasattr(request, 'trace_token'):
98
+ otel.detach_context(request.trace_token)
99
+
100
+ # Log request completion
101
+ otel.logger.logger.info(f"Request completed: {response.status_code}",
102
+ extra={"http.status_code": response.status_code})
103
+
104
+ otel.force_flush(timeout_millis=100)
105
+ return response
106
+
107
+ def flask_error_handler(otel, exception):
108
+ """
109
+ Handle Flask exceptions and record them in the current span.
110
+ To be used with Flask's errorhandler decorator.
111
+ """
112
+
113
+ # Determine the status code
114
+ if isinstance(exception, HTTPException):
115
+ status_code = exception.code
116
+ elif hasattr(exception, 'status_code'):
117
+ status_code = exception.status_code
118
+ elif hasattr(exception, 'code'):
119
+ status_code = exception.code if isinstance(exception.code, int) else 500
120
+ else:
121
+ status_code = 500
122
+
123
+ # Record exception in span if available and still recording
124
+ if hasattr(request, 'span') and request.span.is_recording():
125
+ request.span.set_attribute("http.response.status_code", status_code)
126
+ request.span.set_attribute("error.type", type(exception).__name__)
127
+
128
+ request.span.record_exception(exception)
129
+ request.span.set_status(Status(StatusCode.ERROR, str(exception)))
130
+ request.span.add_event("exception", {
131
+ "exception.type": type(exception).__name__,
132
+ "exception.message": str(exception)
133
+ })
134
+
135
+ # Only close the span if it's still recording (not already ended)
136
+ if hasattr(request, 'span_context'):
137
+ request.span_context.__exit__(type(exception), exception, None)
138
+ else:
139
+ request.span.end()
140
+
141
+ # Log the error with status code
142
+ otel.logger.logger.error(f"Unhandled exception: {exception} (status: {status_code})",
143
+ exc_info=True,
144
+ extra={
145
+ "exception.type": type(exception).__name__,
146
+ "http.status_code": status_code
147
+ })
148
+
149
+ # Return error response with the determined status code
150
+ return jsonify({
151
+ "error": str(exception),
152
+ "type": type(exception).__name__
153
+ }), status_code
rebrandly_otel/metrics.py CHANGED
@@ -34,51 +34,24 @@ class RebrandlyMeter:
34
34
 
35
35
  # Standardized metric definitions aligned with Node.js
36
36
  DEFAULT_METRICS = {
37
- 'invocations': MetricDefinition(
38
- name='invocations',
39
- description='Number of invocations',
40
- unit='1',
41
- type=MetricType.COUNTER
42
- ),
43
- 'successful_invocations': MetricDefinition(
44
- name='successful_invocations',
45
- description='Number of successful invocations',
46
- unit='1',
47
- type=MetricType.COUNTER
48
- ),
49
- 'error_invocations': MetricDefinition(
50
- name='error_invocations',
51
- description='Number of error invocations',
52
- unit='1',
53
- type=MetricType.COUNTER
54
- ),
55
- 'duration': MetricDefinition(
56
- name='duration',
57
- description='Duration of execution in milliseconds',
58
- unit='ms',
59
- type=MetricType.HISTOGRAM
60
- ),
37
+ ## PROCESS
61
38
  'cpu_usage_percentage': MetricDefinition(
62
- name='cpu_usage_percentage',
63
- description='CPU usage percentage',
64
- unit='%',
39
+ name='process.cpu.utilization',
40
+ description='Difference in process.cpu.time since the last measurement, divided by the elapsed time and number of CPUs available to the process.',
41
+ unit='1',
65
42
  type=MetricType.GAUGE
66
43
  ),
67
44
  'memory_usage_bytes': MetricDefinition(
68
- name='memory_usage_bytes',
69
- description='Memory usage in bytes',
45
+ name='process.memory.used',
46
+ description='The amount of physical memory in use.',
70
47
  unit='By',
71
48
  type=MetricType.GAUGE
72
- ),
49
+ )
73
50
  }
74
51
 
75
52
  class GlobalMetrics:
76
53
  def __init__(self, rebrandly_meter):
77
54
  self.__rebrandly_meter = rebrandly_meter
78
- self.invocations: Counter = self.__rebrandly_meter.get_metric('invocations')
79
- self.successful_invocations: Counter = self.__rebrandly_meter.get_metric('successful_invocations')
80
- self.error_invocations: Counter = self.__rebrandly_meter.get_metric('error_invocations')
81
- self.duration: Histogram = self.__rebrandly_meter.get_metric('duration')
82
55
  self.cpu_usage_percentage: Gauge = self.__rebrandly_meter.get_metric('cpu_usage_percentage')
83
56
  self.memory_usage_bytes: Gauge = self.__rebrandly_meter.get_metric('memory_usage_bytes')
84
57
 
@@ -25,6 +25,17 @@ def create_resource(name: str = None, version: str = None) -> Resource:
25
25
  )
26
26
  return resource
27
27
 
28
+ def get_package_version():
29
+ try:
30
+ from importlib.metadata import version, PackageNotFoundError # Python 3.8+
31
+ return version('rebrandly_otel')
32
+ except ImportError:
33
+ try:
34
+ from importlib_metadata import version, PackageNotFoundError
35
+ return version('rebrandly_otel')
36
+ except:
37
+ return '?.0.1'
38
+
28
39
 
29
40
  def get_service_name(service_name: str = None) -> str:
30
41
  if service_name is None:
@@ -34,7 +45,7 @@ def get_service_name(service_name: str = None) -> str:
34
45
 
35
46
  def get_service_version(service_version: str = None) -> str:
36
47
  if service_version is None:
37
- return os.environ.get('OTEL_SERVICE_VERSION', '1.0.0')
48
+ return os.environ.get('OTEL_SERVICE_VERSION', get_package_version())
38
49
  return service_version
39
50
 
40
51
 
@@ -1,8 +1,5 @@
1
- # rebrandly_otel.py
2
- """
3
- Rebrandly OpenTelemetry SDK - Simplified instrumentation for Rebrandly services.
4
- Updated for consistency with Node.js SDK
5
- """
1
+
2
+ import json
6
3
  import time
7
4
  import psutil
8
5
  import functools
@@ -163,8 +160,6 @@ class RebrandlyOTEL:
163
160
 
164
161
  result = None
165
162
  try:
166
- # Increment invocation counter with standardized metric name
167
- self.meter.GlobalMetrics.invocations.add(1, {'function': span_name})
168
163
 
169
164
  # Create and execute within span
170
165
  with self.tracer.start_span(
@@ -207,17 +202,9 @@ class RebrandlyOTEL:
207
202
  complete_event_attrs['success'] = success
208
203
  span.add_event("lambda.invocation.complete", complete_event_attrs)
209
204
 
210
- # Increment success counter with standardized metric
211
- self.meter.GlobalMetrics.successful_invocations.add(1, {'function': span_name})
212
-
213
205
  return result
214
206
 
215
207
  except Exception as e:
216
- # Increment error counter with standardized metric
217
- self.meter.GlobalMetrics.error_invocations.add(1, {
218
- 'function': span_name,
219
- 'error': type(e).__name__
220
- })
221
208
 
222
209
  # Add failed completion event with error attribute (THIS IS THE KEY ADDITION)
223
210
  span.add_event("lambda.invocation.complete", {
@@ -239,13 +226,9 @@ class RebrandlyOTEL:
239
226
  from opentelemetry import context as otel_context
240
227
  otel_context.detach(token)
241
228
 
242
- # Record duration with standardized metric
243
- duration = (datetime.now() - start_time).total_seconds() * 1000
244
- self.meter.GlobalMetrics.duration.record(duration, {'function': span_name})
245
-
246
229
  # Force flush if enabled
247
230
  if auto_flush:
248
- self.logger.logger.info(f"[Rebrandly OTEL] Lambda '{span_name}' completed in {duration:.2f}ms, flushing...")
231
+ self.logger.logger.info(f"[Rebrandly OTEL] Lambda '{span_name}', flushing...")
249
232
  flush_success = self.force_flush(timeout_millis=1000)
250
233
  if not flush_success:
251
234
  self.logger.logger.warning("[Rebrandly OTEL] Force flush may not have completed fully")
@@ -275,9 +258,6 @@ class RebrandlyOTEL:
275
258
 
276
259
  result = None
277
260
  try:
278
- # Increment invocations counter with standardized metric
279
- self.meter.GlobalMetrics.invocations.add(1, {'handler': span_name})
280
-
281
261
  # Create span and execute function
282
262
  span_function = self.span
283
263
  if record is not None and ('MessageAttributes' in record or 'messageAttributes' in record):
@@ -321,18 +301,9 @@ class RebrandlyOTEL:
321
301
  complete_event_attrs['success'] = success
322
302
  span_context.add_event("message.processing.complete", complete_event_attrs)
323
303
 
324
- # Increment success counter with standardized metric
325
- self.meter.GlobalMetrics.successful_invocations.add(1, {'handler': span_name})
326
-
327
304
  return result
328
305
 
329
306
  except Exception as e:
330
- # Increment error counter with standardized metric
331
- self.meter.GlobalMetrics.error_invocations.add(1, {
332
- 'handler': span_name,
333
- 'error': type(e).__name__
334
- })
335
-
336
307
  # Record the exception in the span
337
308
  if 'span_context' in locals():
338
309
  span_context.record_exception(e)
@@ -348,10 +319,6 @@ class RebrandlyOTEL:
348
319
  raise
349
320
 
350
321
  finally:
351
- # Record duration with standardized metric
352
- duration = (datetime.now() - start_func).total_seconds() * 1000
353
- self.meter.GlobalMetrics.duration.record(duration, {'handler': span_name})
354
-
355
322
  if auto_flush:
356
323
  self.force_flush(start_datetime=start_func)
357
324
 
@@ -374,16 +341,14 @@ class RebrandlyOTEL:
374
341
 
375
342
  if start_datetime is not None:
376
343
  end_func = datetime.now()
377
- duration = (end_func - start_datetime).total_seconds() * 1000
378
344
  cpu_percent = psutil.cpu_percent(interval=0.1) # Shorter interval for Lambda
379
345
  memory = psutil.virtual_memory()
380
346
 
381
347
  # Record metrics using standardized names
382
- self.meter.GlobalMetrics.duration.record(duration, {'source': 'force_flush'})
383
348
  self.meter.GlobalMetrics.memory_usage_bytes.set(memory.used)
384
349
  self.meter.GlobalMetrics.cpu_usage_percentage.set(cpu_percent)
385
350
 
386
- print(f"Function duration: {duration}ms, Memory usage: {memory.percent}%, CPU usage: {cpu_percent}%")
351
+ print(f"Function Memory usage: {memory.percent}%, CPU usage: {cpu_percent}%")
387
352
 
388
353
  try:
389
354
  # Flush traces
@@ -550,4 +515,4 @@ extract_context = otel.extract_context
550
515
  attach_context = otel.attach_context
551
516
  detach_context = otel.detach_context
552
517
  force_flush = otel.force_flush
553
- shutdown = otel.shutdown
518
+ shutdown = otel.shutdown
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rebrandly_otel
3
- Version: 0.1.16
3
+ Version: 0.1.18
4
4
  Summary: Python OTEL wrapper by Rebrandly
5
5
  Home-page: https://github.com/rebrandly/rebrandly-otel-python
6
6
  Author: Antonio Romano
@@ -102,21 +102,23 @@ The SDK automatically registers and tracks the following metrics:
102
102
 
103
103
  ### Standard Metrics
104
104
 
105
- - **`invocations`** (Counter): Total number of function invocations
106
- - **`successful_invocations`** (Counter): Number of successful completions
107
- - **`error_invocations`** (Counter): Number of failed invocations
108
- - **`duration`** (Histogram): Execution duration in milliseconds
109
105
  - **`cpu_usage_percentage`** (Gauge): CPU utilization percentage
110
106
  - **`memory_usage_bytes`** (Gauge): Memory usage in bytes
111
107
 
112
- ### Global Metrics Access
113
108
 
114
- ```python
115
- from rebrandly_otel import meter
109
+ ### Custom Metrics
110
+
111
+ You can create the custom metrics you need using the default open telemetry metrics
116
112
 
117
- # Access pre-configured metrics
118
- meter.GlobalMetrics.invocations.add(1, {'function': 'my_function'})
119
- meter.GlobalMetrics.duration.record(150.5, {'source': 'api'})
113
+ ```python
114
+ from src.rebrandly_otel import meter
115
+
116
+ sqs_counter = meter.meter.create_counter(
117
+ name="sqs_sender_counter",
118
+ description="Number of messages sent",
119
+ unit="1"
120
+ )
121
+ sqs_counter.add(1)
120
122
  ```
121
123
 
122
124
  ## Tracing Features
@@ -180,10 +182,8 @@ Automatically detects and labels Lambda triggers:
180
182
  ### Automatic Metrics
181
183
 
182
184
  For Lambda functions, the SDK automatically captures:
183
- - Function duration
184
185
  - Memory usage
185
186
  - CPU utilization
186
- - Invocation counts by status
187
187
 
188
188
  ### Context Extraction
189
189
 
@@ -319,6 +319,47 @@ def process_message(record):
319
319
  logger.info(f"Message data: {body}")
320
320
  ```
321
321
 
322
+ ###
323
+ Flask
324
+
325
+ ```python
326
+
327
+ from flask import Flask, jsonify
328
+ from src.rebrandly_otel import otel, logger, app_before_request, app_after_request, flask_error_handler
329
+ from datetime import datetime
330
+
331
+ app = Flask(__name__)
332
+
333
+ # Register the centralized OTEL handlers
334
+ app.before_request(app_before_request)
335
+ app.after_request(app_after_request)
336
+ app.register_error_handler(Exception, flask_error_handler)
337
+
338
+ @app.route('/health')
339
+ def health():
340
+ logger.info("Health check requested")
341
+ return jsonify({"status": "healthy"}), 200
342
+
343
+ @app.route('/process', methods=['POST', 'GET'])
344
+ def process():
345
+ with otel.span("process_request"):
346
+ logger.info("Processing POST request")
347
+
348
+ # Simulate processing
349
+ result = {"processed": True, "timestamp": datetime.now().isoformat()}
350
+
351
+ logger.info(f"Returning result: {result}")
352
+ return jsonify(result), 200
353
+
354
+ @app.route('/error')
355
+ def error():
356
+ logger.error("Error endpoint called")
357
+ raise Exception("Simulated error")
358
+
359
+ if __name__ == '__main__':
360
+ app.run(debug=True)
361
+ ```
362
+
322
363
  ### More examples
323
364
  You can find More examples [here](examples)
324
365
 
@@ -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=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,,
@@ -1,11 +0,0 @@
1
- rebrandly_otel/__init__.py,sha256=NUlroPhnHAKVC_q2BiyhtURXw23w1YY1BJd45gtc9qM,275
2
- rebrandly_otel/logs.py,sha256=92jaxzI5hCHnKHu3lsSAa7K_SPHQgL46AlQUESsYNds,3724
3
- rebrandly_otel/metrics.py,sha256=57jwrn8e1u66BOO7fcFrmtE3Rpt4VDixG9I78K-zrUU,8537
4
- rebrandly_otel/otel_utils.py,sha256=uJmfz2NspSnTVJXGKoaLUl1CYb0ow6VHKjmg2d_rdwg,1704
5
- rebrandly_otel/rebrandly_otel.py,sha256=A_TGyCQq752EeEvcsMmBWaWx2Z12J1hfmyYTFpYJFbo,23232
6
- rebrandly_otel/traces.py,sha256=v582WtJv3t4Bn92vlDyZouibHtgWNxdRo_XmQCmSOEA,7126
7
- rebrandly_otel-0.1.16.dist-info/licenses/LICENSE,sha256=KMXHvTwP62S2q-SG7CFfMU_09rUwxiqlM0izaYGdcgY,1103
8
- rebrandly_otel-0.1.16.dist-info/METADATA,sha256=4SxmMqhoIM6EyjZc8_JWCvHHxATQvuXcKztv5gEqc18,9628
9
- rebrandly_otel-0.1.16.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
10
- rebrandly_otel-0.1.16.dist-info/top_level.txt,sha256=26PSC1gjVUl8tTH5QfKbFevjVV4E2yojoukEfiTScvM,15
11
- rebrandly_otel-0.1.16.dist-info/RECORD,,