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

@@ -3,7 +3,6 @@ from .rebrandly_otel import *
3
3
 
4
4
  # Explicitly define what's available
5
5
  __all__ = [
6
- 'otel',
7
6
  'lambda_handler',
8
7
  'span',
9
8
  'aws_message_span',
@@ -101,101 +101,118 @@ class RebrandlyOTEL:
101
101
 
102
102
  # Fix for the lambda_handler method in rebrandly_otel.py
103
103
  # Replace the lambda_handler method (around line 132) with this fixed version:
104
-
105
104
  def lambda_handler(self,
106
105
  name: Optional[str] = None,
107
106
  attributes: Optional[Dict[str, Any]] = None,
108
- kind: SpanKind = SpanKind.CONSUMER,
107
+ kind: SpanKind = SpanKind.SERVER,
109
108
  auto_flush: bool = True,
110
- skip_aws_link: bool = True):
109
+ skip_aws_link: bool = False):
111
110
  """
112
111
  Decorator specifically for Lambda handlers with automatic flushing.
113
112
  """
114
113
  def decorator(func):
115
114
  @functools.wraps(func)
116
- def wrapper(event=None, context=None):
115
+ def wrapper(event=None, lambda_context=None):
117
116
  # Determine span name
118
117
  span_name = name or f"lambda.{func.__name__}"
119
- start_func = datetime.now()
118
+ start_time = datetime.now()
120
119
 
121
120
  # Build span attributes
122
121
  span_attributes = attributes or {}
123
-
124
122
  span_attributes['faas.trigger'] = self._detect_lambda_trigger(event)
125
123
 
126
124
  # Add Lambda-specific attributes if context is available
127
- if context is not None:
125
+ if lambda_context is not None:
128
126
  span_attributes.update({
129
- "faas.execution": getattr(context, 'aws_request_id', 'unknown'),
130
- "faas.id": getattr(context, 'function_name', 'unknown'),
131
- "cloud.provider": "aws",
132
- "cloud.platform": "aws_lambda"
127
+ "faas.execution": getattr(lambda_context, 'aws_request_id', 'unknown'),
128
+ "faas.id": getattr(lambda_context, 'function_arn', 'unknown'),
129
+ "faas.name": getattr(lambda_context, 'function_name', 'unknown'),
130
+ "faas.version": getattr(lambda_context, 'function_version', 'unknown')
133
131
  })
134
132
 
133
+ # Handle context extraction from AWS events
134
+ token = None
135
+ if not skip_aws_link and event and isinstance(event, dict) and 'Records' in event:
136
+ first_record = event['Records'][0] if event['Records'] else None
137
+ if first_record:
138
+ carrier = {}
139
+
140
+ # Extract from SQS
141
+ if 'MessageAttributes' in first_record:
142
+ for key, value in first_record['MessageAttributes'].items():
143
+ if isinstance(value, dict) and 'StringValue' in value:
144
+ carrier[key] = value['StringValue']
145
+ if ('messageAttributes' in first_record and 'traceparent' in first_record['messageAttributes']
146
+ and 'stringValue' in first_record['messageAttributes']['traceparent']):
147
+ carrier['traceparent'] = first_record['messageAttributes']['traceparent']['stringValue']
148
+
149
+ # Extract from SNS
150
+ elif 'Sns' in first_record and 'MessageAttributes' in first_record['Sns']:
151
+ for key, value in first_record['Sns']['MessageAttributes'].items():
152
+ if isinstance(value, dict):
153
+ if 'Value' in value:
154
+ carrier[key] = value['Value']
155
+ elif 'StringValue' in value:
156
+ carrier[key] = value['StringValue']
157
+
158
+ # Attach extracted context
159
+ if carrier:
160
+ from opentelemetry import propagate, context as otel_context
161
+ extracted_context = propagate.extract(carrier)
162
+ token = otel_context.attach(extracted_context)
163
+ span_attributes['message.has_trace_context'] = True
164
+
135
165
  result = None
136
166
  try:
137
- # Increment invocations counter
167
+ # Increment invocation counter
138
168
  self.meter.GlobalMetrics.invocations.add(1, {'function': span_name})
139
169
 
140
- # Determine if we should extract context from AWS message
141
- extracted_context = None
142
- if not skip_aws_link and event is not None and 'Records' in event and len(event['Records']) > 0:
143
- first_record = event['Records'][0]
170
+ # Create and execute within span
171
+ with self.tracer.start_span(
172
+ name=span_name,
173
+ attributes=span_attributes,
174
+ kind=kind
175
+ ) as span:
176
+ # Add invocation start event
177
+ span.add_event("lambda.invocation.start", {
178
+ 'event.type': type(event).__name__ if event else 'None'
179
+ })
144
180
 
145
- # Check different message formats
146
- if 'messageAttributes' in first_record or 'MessageAttributes' in first_record:
147
- # SQS format
148
- extracted_context = self.extract_context(first_record)
149
- elif 'Sns' in first_record and 'MessageAttributes' in first_record['Sns']:
150
- # SNS format
151
- extracted_context = self.extract_context(first_record)
152
-
153
- # Create span - use extracted context if available
154
- if extracted_context:
155
- token = self.attach_context(extracted_context)
156
- try:
157
- with self.span(span_name, attributes=span_attributes, kind=kind) as span_context:
158
- # Add event type as span event
159
- if event is not None:
160
- span_context.add_event("lambda.invocation.start",
161
- attributes={"event.type": type(event).__name__})
162
-
163
- result = func(event, context) if event is not None else func()
164
-
165
- # Handle result
166
- self._process_lambda_result(result, span_context, span_name)
167
- finally:
168
- self.detach_context(token)
169
- else:
170
- # No context extraction needed
171
- with self.span(span_name, attributes=span_attributes, kind=kind) as span_context:
172
- # Add event type as span event
173
- if event is not None:
174
- span_context.add_event("lambda.invocation.start",
175
- attributes={"event.type": type(event).__name__})
176
-
177
- result = func(event, context) if event is not None else func()
178
-
179
- # Handle result
180
- self._process_lambda_result(result, span_context, span_name)
181
+ # Execute handler
182
+ result = func(event, lambda_context)
183
+
184
+ # Process result
185
+ self._process_lambda_result(result, span, span_name)
181
186
 
182
187
  return result
183
188
 
184
189
  except Exception as e:
185
190
  # Increment error counter
186
- self.meter.GlobalMetrics.error_invocations.add(1, {'function': span_name, 'error': type(e).__name__})
191
+ self.meter.GlobalMetrics.error_invocations.add(1, {
192
+ 'function': span_name,
193
+ 'error': type(e).__name__
194
+ })
187
195
 
188
- # Log error with context
196
+ # Log error
189
197
  self.logger.logger.error(f"Lambda execution failed: {e}", exc_info=True)
190
198
  raise
191
199
 
192
200
  finally:
201
+ # Always detach context if we attached it
202
+ if token is not None:
203
+ from opentelemetry import context as otel_context
204
+ otel_context.detach(token)
205
+
193
206
  # Record duration
194
- duration = (datetime.now() - start_func).total_seconds() * 1000
207
+ duration = (datetime.now() - start_time).total_seconds() * 1000
195
208
  self.meter.GlobalMetrics.duration.record(duration, {'function': span_name})
196
209
 
210
+ # Force flush if enabled
197
211
  if auto_flush:
198
- self.force_flush(start_datetime=start_func)
212
+ self.logger.logger.info(f"[OTEL] Lambda '{span_name}' completed in {duration:.2f}ms, flushing...")
213
+ flush_success = self.force_flush(timeout_millis=1000)
214
+ if not flush_success:
215
+ self.logger.logger.warning("[OTEL] Force flush may not have completed fully")
199
216
 
200
217
  return wrapper
201
218
  return decorator
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: rebrandly_otel
3
- Version: 0.1.11
3
+ Version: 0.1.14
4
4
  Summary: Python OTEL wrapper by Rebrandly
5
5
  Home-page: https://github.com/rebrandly/rebrandly-otel-python
6
6
  Author: Antonio Romano
@@ -0,0 +1,11 @@
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=KwUWW1kiWZrB9B2xwioBsA_c-Xm4YoH51_HyX6HBvQY,21778
6
+ rebrandly_otel/traces.py,sha256=v582WtJv3t4Bn92vlDyZouibHtgWNxdRo_XmQCmSOEA,7126
7
+ rebrandly_otel-0.1.14.dist-info/licenses/LICENSE,sha256=KMXHvTwP62S2q-SG7CFfMU_09rUwxiqlM0izaYGdcgY,1103
8
+ rebrandly_otel-0.1.14.dist-info/METADATA,sha256=tfOB74T9zcU5aXOj3Rw6bHChrd9-wexzkq7FqQUWptY,9628
9
+ rebrandly_otel-0.1.14.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
10
+ rebrandly_otel-0.1.14.dist-info/top_level.txt,sha256=26PSC1gjVUl8tTH5QfKbFevjVV4E2yojoukEfiTScvM,15
11
+ rebrandly_otel-0.1.14.dist-info/RECORD,,
@@ -1,11 +0,0 @@
1
- rebrandly_otel/__init__.py,sha256=AHcEKWq4KRafIF_x_Y8jiIQ5crNG1nbEwdnlGaEP6js,287
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=LwzBVY67N12X--8js8yFixusFs7T--1BVog0jOVds4k,20790
6
- rebrandly_otel/traces.py,sha256=v582WtJv3t4Bn92vlDyZouibHtgWNxdRo_XmQCmSOEA,7126
7
- rebrandly_otel-0.1.11.dist-info/licenses/LICENSE,sha256=KMXHvTwP62S2q-SG7CFfMU_09rUwxiqlM0izaYGdcgY,1103
8
- rebrandly_otel-0.1.11.dist-info/METADATA,sha256=aRJJR4nhuJiocpBS1hpXuIWpUv-KEX0R_b3wkq7ns3g,9628
9
- rebrandly_otel-0.1.11.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
10
- rebrandly_otel-0.1.11.dist-info/top_level.txt,sha256=26PSC1gjVUl8tTH5QfKbFevjVV4E2yojoukEfiTScvM,15
11
- rebrandly_otel-0.1.11.dist-info/RECORD,,