ergon-framework-python 0.1.0__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.
- ergon/__init__.py +13 -0
- ergon/bootstrap/src/__project__/__init__.py +0 -0
- ergon/bootstrap/src/__project__/_observability/docker-compose.telemetry.yml +124 -0
- ergon/bootstrap/src/__project__/_observability/grafana.yaml +17 -0
- ergon/bootstrap/src/__project__/_observability/loki.yaml +48 -0
- ergon/bootstrap/src/__project__/_observability/otel-collector-config.yaml +53 -0
- ergon/bootstrap/src/__project__/_observability/prometheus.yaml +11 -0
- ergon/bootstrap/src/__project__/_observability/tempo.yaml +24 -0
- ergon/bootstrap/src/__project__/connectors/__init__.py +0 -0
- ergon/bootstrap/src/__project__/main.py +9 -0
- ergon/bootstrap/src/__project__/tasks/__init__.py +0 -0
- ergon/bootstrap/src/__project__/tasks/constants.py +13 -0
- ergon/bootstrap/src/__project__/tasks/example_task/__init__.py +0 -0
- ergon/bootstrap/src/__project__/tasks/example_task/config.py +4 -0
- ergon/bootstrap/src/__project__/tasks/example_task/exceptions.py +4 -0
- ergon/bootstrap/src/__project__/tasks/example_task/helpers.py +4 -0
- ergon/bootstrap/src/__project__/tasks/example_task/schemas.py +5 -0
- ergon/bootstrap/src/__project__/tasks/example_task/task.py +1 -0
- ergon/bootstrap/src/__project__/tasks/exceptions.py +0 -0
- ergon/bootstrap/src/__project__/tasks/helpers.py +0 -0
- ergon/bootstrap/src/__project__/tasks/schemas.py +0 -0
- ergon/bootstrap/src/__project__/tasks/settings.py +5 -0
- ergon/cli.py +174 -0
- ergon/connector/__init__.py +64 -0
- ergon/connector/connector.py +97 -0
- ergon/connector/excel/__init__.py +18 -0
- ergon/connector/excel/connector.py +175 -0
- ergon/connector/excel/models.py +24 -0
- ergon/connector/excel/service.py +98 -0
- ergon/connector/pipefy/__init__.py +21 -0
- ergon/connector/pipefy/async_connector.py +48 -0
- ergon/connector/pipefy/async_service.py +907 -0
- ergon/connector/pipefy/connector.py +36 -0
- ergon/connector/pipefy/models.py +48 -0
- ergon/connector/pipefy/service.py +1016 -0
- ergon/connector/pipefy/version.py +1 -0
- ergon/connector/postgres/__init__.py +11 -0
- ergon/connector/postgres/async_connector.py +119 -0
- ergon/connector/postgres/async_service.py +116 -0
- ergon/connector/postgres/models.py +34 -0
- ergon/connector/rabbitmq/__init__.py +25 -0
- ergon/connector/rabbitmq/async_connector.py +120 -0
- ergon/connector/rabbitmq/async_service.py +417 -0
- ergon/connector/rabbitmq/connector.py +54 -0
- ergon/connector/rabbitmq/helper.py +14 -0
- ergon/connector/rabbitmq/models.py +92 -0
- ergon/connector/rabbitmq/service.py +199 -0
- ergon/connector/sqs/__init__.py +15 -0
- ergon/connector/sqs/async_connector.py +120 -0
- ergon/connector/sqs/async_service.py +246 -0
- ergon/connector/sqs/connector.py +120 -0
- ergon/connector/sqs/models.py +36 -0
- ergon/connector/sqs/service.py +219 -0
- ergon/connector/transaction.py +14 -0
- ergon/py.typed +0 -0
- ergon/service/__init__.py +5 -0
- ergon/service/service.py +17 -0
- ergon/task/__init__.py +13 -0
- ergon/task/base.py +222 -0
- ergon/task/exceptions.py +217 -0
- ergon/task/helpers.py +691 -0
- ergon/task/manager.py +85 -0
- ergon/task/mixins/__init__.py +13 -0
- ergon/task/mixins/consumer.py +858 -0
- ergon/task/mixins/metrics.py +457 -0
- ergon/task/mixins/producer.py +486 -0
- ergon/task/policies.py +229 -0
- ergon/task/runner.py +386 -0
- ergon/task/utils.py +64 -0
- ergon/telemetry/__init__.py +7 -0
- ergon/telemetry/_resource.py +13 -0
- ergon/telemetry/logging.py +370 -0
- ergon/telemetry/metrics.py +101 -0
- ergon/telemetry/tracing.py +152 -0
- ergon/utils/__init__.py +5 -0
- ergon/utils/env.py +26 -0
- ergon_framework_python-0.1.0.dist-info/METADATA +449 -0
- ergon_framework_python-0.1.0.dist-info/RECORD +82 -0
- ergon_framework_python-0.1.0.dist-info/WHEEL +5 -0
- ergon_framework_python-0.1.0.dist-info/entry_points.txt +2 -0
- ergon_framework_python-0.1.0.dist-info/licenses/LICENSE +21 -0
- ergon_framework_python-0.1.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,457 @@
|
|
|
1
|
+
# metrics.py
|
|
2
|
+
"""
|
|
3
|
+
Ergon Task Framework - Built-in Metrics Instrumentation
|
|
4
|
+
|
|
5
|
+
This module provides pre-configured OpenTelemetry metrics for Consumer and Producer
|
|
6
|
+
lifecycle stages. Metrics are lazily initialized on first use after the MeterProvider
|
|
7
|
+
has been configured by the task runner.
|
|
8
|
+
|
|
9
|
+
All metrics follow OpenTelemetry semantic conventions where applicable.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import time
|
|
13
|
+
from contextlib import contextmanager
|
|
14
|
+
from typing import Callable
|
|
15
|
+
|
|
16
|
+
from ... import telemetry
|
|
17
|
+
|
|
18
|
+
# ============================================================
|
|
19
|
+
# LAZY METER INITIALIZATION
|
|
20
|
+
# ============================================================
|
|
21
|
+
|
|
22
|
+
_meter = None
|
|
23
|
+
_instruments = {}
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _get_meter():
|
|
27
|
+
"""
|
|
28
|
+
Lazily initialize the meter after telemetry configuration is applied.
|
|
29
|
+
Must be called AFTER runner initializes telemetry.
|
|
30
|
+
"""
|
|
31
|
+
global _meter
|
|
32
|
+
if _meter is None:
|
|
33
|
+
_meter = telemetry.metrics.get_metric_meter("ergon.task")
|
|
34
|
+
return _meter
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _get_instrument(name: str, factory: Callable):
|
|
38
|
+
"""
|
|
39
|
+
Lazily create and cache metric instruments.
|
|
40
|
+
"""
|
|
41
|
+
if name not in _instruments:
|
|
42
|
+
_instruments[name] = factory(_get_meter())
|
|
43
|
+
return _instruments[name]
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
# ============================================================
|
|
47
|
+
# CONSUMER METRICS
|
|
48
|
+
# ============================================================
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
def _consumer_transactions_total():
|
|
52
|
+
return _get_instrument(
|
|
53
|
+
"consumer.transactions.total",
|
|
54
|
+
lambda m: m.create_counter(
|
|
55
|
+
name="consumer.transactions.total",
|
|
56
|
+
description="Total number of transactions processed by the consumer",
|
|
57
|
+
unit="1",
|
|
58
|
+
),
|
|
59
|
+
)
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _consumer_transactions_duration():
|
|
63
|
+
return _get_instrument(
|
|
64
|
+
"consumer.transactions.duration",
|
|
65
|
+
lambda m: m.create_histogram(
|
|
66
|
+
name="consumer.transactions.duration",
|
|
67
|
+
description="Time spent processing each transaction end-to-end",
|
|
68
|
+
unit="s",
|
|
69
|
+
),
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _consumer_fetch_total():
|
|
74
|
+
return _get_instrument(
|
|
75
|
+
"consumer.fetch.total",
|
|
76
|
+
lambda m: m.create_counter(
|
|
77
|
+
name="consumer.fetch.total",
|
|
78
|
+
description="Total number of fetch operations performed",
|
|
79
|
+
unit="1",
|
|
80
|
+
),
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
def _consumer_fetch_batch_size():
|
|
85
|
+
return _get_instrument(
|
|
86
|
+
"consumer.fetch.batch_size",
|
|
87
|
+
lambda m: m.create_histogram(
|
|
88
|
+
name="consumer.fetch.batch_size",
|
|
89
|
+
description="Number of transactions fetched per fetch operation",
|
|
90
|
+
unit="1",
|
|
91
|
+
),
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def _consumer_fetch_duration():
|
|
96
|
+
return _get_instrument(
|
|
97
|
+
"consumer.fetch.duration",
|
|
98
|
+
lambda m: m.create_histogram(
|
|
99
|
+
name="consumer.fetch.duration",
|
|
100
|
+
description="Time spent on each fetch operation",
|
|
101
|
+
unit="s",
|
|
102
|
+
),
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
def _consumer_lifecycle_duration():
|
|
107
|
+
return _get_instrument(
|
|
108
|
+
"consumer.lifecycle.duration",
|
|
109
|
+
lambda m: m.create_histogram(
|
|
110
|
+
name="consumer.lifecycle.duration",
|
|
111
|
+
description="Duration of each consumer lifecycle stage",
|
|
112
|
+
unit="s",
|
|
113
|
+
),
|
|
114
|
+
)
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def _consumer_lifecycle_outcome():
|
|
118
|
+
return _get_instrument(
|
|
119
|
+
"consumer.lifecycle.outcome",
|
|
120
|
+
lambda m: m.create_counter(
|
|
121
|
+
name="consumer.lifecycle.outcome",
|
|
122
|
+
description="Outcome counts for each consumer lifecycle stage",
|
|
123
|
+
unit="1",
|
|
124
|
+
),
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def _consumer_batches_total():
|
|
129
|
+
return _get_instrument(
|
|
130
|
+
"consumer.batches.total",
|
|
131
|
+
lambda m: m.create_counter(
|
|
132
|
+
name="consumer.batches.total",
|
|
133
|
+
description="Total number of batches processed",
|
|
134
|
+
unit="1",
|
|
135
|
+
),
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def _consumer_empty_queue_waits():
|
|
140
|
+
return _get_instrument(
|
|
141
|
+
"consumer.empty_queue.waits",
|
|
142
|
+
lambda m: m.create_counter(
|
|
143
|
+
name="consumer.empty_queue.waits",
|
|
144
|
+
description="Number of times consumer waited on empty queue",
|
|
145
|
+
unit="1",
|
|
146
|
+
),
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
# ============================================================
|
|
151
|
+
# PRODUCER METRICS
|
|
152
|
+
# ============================================================
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
def _producer_transactions_total():
|
|
156
|
+
return _get_instrument(
|
|
157
|
+
"producer.transactions.total",
|
|
158
|
+
lambda m: m.create_counter(
|
|
159
|
+
name="producer.transactions.total",
|
|
160
|
+
description="Total number of transactions produced",
|
|
161
|
+
unit="1",
|
|
162
|
+
),
|
|
163
|
+
)
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _producer_transactions_duration():
|
|
167
|
+
return _get_instrument(
|
|
168
|
+
"producer.transactions.duration",
|
|
169
|
+
lambda m: m.create_histogram(
|
|
170
|
+
name="producer.transactions.duration",
|
|
171
|
+
description="Time spent producing each transaction end-to-end",
|
|
172
|
+
unit="s",
|
|
173
|
+
),
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
def _producer_lifecycle_duration():
|
|
178
|
+
return _get_instrument(
|
|
179
|
+
"producer.lifecycle.duration",
|
|
180
|
+
lambda m: m.create_histogram(
|
|
181
|
+
name="producer.lifecycle.duration",
|
|
182
|
+
description="Duration of each producer lifecycle stage",
|
|
183
|
+
unit="s",
|
|
184
|
+
),
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
|
|
188
|
+
def _producer_lifecycle_outcome():
|
|
189
|
+
return _get_instrument(
|
|
190
|
+
"producer.lifecycle.outcome",
|
|
191
|
+
lambda m: m.create_counter(
|
|
192
|
+
name="producer.lifecycle.outcome",
|
|
193
|
+
description="Outcome counts for each producer lifecycle stage",
|
|
194
|
+
unit="1",
|
|
195
|
+
),
|
|
196
|
+
)
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
def _producer_batches_total():
|
|
200
|
+
return _get_instrument(
|
|
201
|
+
"producer.batches.total",
|
|
202
|
+
lambda m: m.create_counter(
|
|
203
|
+
name="producer.batches.total",
|
|
204
|
+
description="Total number of batches produced",
|
|
205
|
+
unit="1",
|
|
206
|
+
),
|
|
207
|
+
)
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
# ============================================================
|
|
211
|
+
# CONSUMER RECORDING HELPERS
|
|
212
|
+
# ============================================================
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def record_consumer_transaction(
|
|
216
|
+
task_name: str,
|
|
217
|
+
transaction_id: str,
|
|
218
|
+
duration: float,
|
|
219
|
+
status: str, # "success" | "exception"
|
|
220
|
+
):
|
|
221
|
+
"""Record a completed consumer transaction."""
|
|
222
|
+
attrs = {"task": task_name, "status": status}
|
|
223
|
+
_consumer_transactions_total().add(1, attrs)
|
|
224
|
+
_consumer_transactions_duration().record(duration, attrs)
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def record_consumer_fetch(
|
|
228
|
+
task_name: str,
|
|
229
|
+
connector_name: str,
|
|
230
|
+
batch_size: int,
|
|
231
|
+
fetched_count: int,
|
|
232
|
+
duration: float,
|
|
233
|
+
success: bool,
|
|
234
|
+
):
|
|
235
|
+
"""Record a fetch operation."""
|
|
236
|
+
attrs = {
|
|
237
|
+
"task": task_name,
|
|
238
|
+
"connector": connector_name,
|
|
239
|
+
"success": str(success).lower(),
|
|
240
|
+
}
|
|
241
|
+
_consumer_fetch_total().add(1, attrs)
|
|
242
|
+
_consumer_fetch_duration().record(duration, attrs)
|
|
243
|
+
|
|
244
|
+
if success and fetched_count > 0:
|
|
245
|
+
_consumer_fetch_batch_size().record(fetched_count, {"task": task_name, "connector": connector_name})
|
|
246
|
+
|
|
247
|
+
|
|
248
|
+
def record_consumer_lifecycle(
|
|
249
|
+
task_name: str,
|
|
250
|
+
stage: str, # "process" | "success" | "exception"
|
|
251
|
+
duration: float,
|
|
252
|
+
outcome: str, # "ok" | "error" | "timeout" | "retry"
|
|
253
|
+
attempt: int = 1,
|
|
254
|
+
):
|
|
255
|
+
"""Record a consumer lifecycle stage execution."""
|
|
256
|
+
attrs = {
|
|
257
|
+
"task": task_name,
|
|
258
|
+
"stage": stage,
|
|
259
|
+
"outcome": outcome,
|
|
260
|
+
"attempt": attempt,
|
|
261
|
+
}
|
|
262
|
+
_consumer_lifecycle_duration().record(duration, {"task": task_name, "stage": stage})
|
|
263
|
+
_consumer_lifecycle_outcome().add(1, attrs)
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def record_consumer_batch(task_name: str, batch_number: int, batch_size: int, streaming: bool):
|
|
267
|
+
"""Record a batch being processed."""
|
|
268
|
+
attrs = {
|
|
269
|
+
"task": task_name,
|
|
270
|
+
"streaming": str(streaming).lower(),
|
|
271
|
+
}
|
|
272
|
+
_consumer_batches_total().add(1, attrs)
|
|
273
|
+
|
|
274
|
+
|
|
275
|
+
def record_consumer_empty_queue_wait(task_name: str, wait_count: int):
|
|
276
|
+
"""Record an empty queue wait event."""
|
|
277
|
+
_consumer_empty_queue_waits().add(1, {"task": task_name, "consecutive_waits": wait_count})
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
# ============================================================
|
|
281
|
+
# PRODUCER RECORDING HELPERS
|
|
282
|
+
# ============================================================
|
|
283
|
+
|
|
284
|
+
|
|
285
|
+
def record_producer_transaction(
|
|
286
|
+
task_name: str,
|
|
287
|
+
transaction_id: str,
|
|
288
|
+
duration: float,
|
|
289
|
+
status: str, # "success" | "exception"
|
|
290
|
+
):
|
|
291
|
+
"""Record a completed producer transaction."""
|
|
292
|
+
attrs = {"task": task_name, "status": status}
|
|
293
|
+
_producer_transactions_total().add(1, attrs)
|
|
294
|
+
_producer_transactions_duration().record(duration, attrs)
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
def record_producer_lifecycle(
|
|
298
|
+
task_name: str,
|
|
299
|
+
stage: str, # "prepare" | "success" | "exception"
|
|
300
|
+
duration: float,
|
|
301
|
+
outcome: str, # "ok" | "error" | "timeout" | "retry"
|
|
302
|
+
attempt: int = 1,
|
|
303
|
+
):
|
|
304
|
+
"""Record a producer lifecycle stage execution."""
|
|
305
|
+
attrs = {
|
|
306
|
+
"task": task_name,
|
|
307
|
+
"stage": stage,
|
|
308
|
+
"outcome": outcome,
|
|
309
|
+
"attempt": attempt,
|
|
310
|
+
}
|
|
311
|
+
_producer_lifecycle_duration().record(duration, {"task": task_name, "stage": stage})
|
|
312
|
+
_producer_lifecycle_outcome().add(1, attrs)
|
|
313
|
+
|
|
314
|
+
|
|
315
|
+
def record_producer_batch(task_name: str, batch_number: int, batch_size: int):
|
|
316
|
+
"""Record a batch being produced."""
|
|
317
|
+
attrs = {"task": task_name, "batch_size": batch_size}
|
|
318
|
+
_producer_batches_total().add(1, attrs)
|
|
319
|
+
|
|
320
|
+
|
|
321
|
+
# ============================================================
|
|
322
|
+
# CONTEXT MANAGERS FOR TIMING
|
|
323
|
+
# ============================================================
|
|
324
|
+
|
|
325
|
+
|
|
326
|
+
@contextmanager
|
|
327
|
+
def timed_consumer_transaction(task_name: str, transaction_id: str):
|
|
328
|
+
"""
|
|
329
|
+
Context manager to time and record a consumer transaction.
|
|
330
|
+
|
|
331
|
+
Usage:
|
|
332
|
+
with timed_consumer_transaction("MyTask", tx.id) as ctx:
|
|
333
|
+
# process transaction
|
|
334
|
+
ctx["status"] = "success" # or "exception"
|
|
335
|
+
"""
|
|
336
|
+
ctx = {"status": "success", "start": time.perf_counter()}
|
|
337
|
+
try:
|
|
338
|
+
yield ctx
|
|
339
|
+
except Exception:
|
|
340
|
+
ctx["status"] = "exception"
|
|
341
|
+
raise
|
|
342
|
+
finally:
|
|
343
|
+
duration = time.perf_counter() - ctx["start"]
|
|
344
|
+
record_consumer_transaction(task_name, transaction_id, duration, ctx["status"])
|
|
345
|
+
|
|
346
|
+
|
|
347
|
+
@contextmanager
|
|
348
|
+
def timed_consumer_lifecycle(task_name: str, stage: str, attempt: int = 1):
|
|
349
|
+
"""
|
|
350
|
+
Context manager to time and record a consumer lifecycle stage.
|
|
351
|
+
|
|
352
|
+
Usage:
|
|
353
|
+
with timed_consumer_lifecycle("MyTask", "process") as ctx:
|
|
354
|
+
# do work
|
|
355
|
+
ctx["outcome"] = "ok" # or "error", "timeout", "retry"
|
|
356
|
+
"""
|
|
357
|
+
ctx = {"outcome": "ok", "start": time.perf_counter()}
|
|
358
|
+
try:
|
|
359
|
+
yield ctx
|
|
360
|
+
except Exception:
|
|
361
|
+
ctx["outcome"] = "error"
|
|
362
|
+
raise
|
|
363
|
+
finally:
|
|
364
|
+
duration = time.perf_counter() - ctx["start"]
|
|
365
|
+
record_consumer_lifecycle(task_name, stage, duration, ctx["outcome"], attempt)
|
|
366
|
+
|
|
367
|
+
|
|
368
|
+
@contextmanager
|
|
369
|
+
def timed_consumer_fetch(task_name: str, connector_name: str, batch_size: int):
|
|
370
|
+
"""
|
|
371
|
+
Context manager to time and record a fetch operation.
|
|
372
|
+
|
|
373
|
+
Usage:
|
|
374
|
+
with timed_consumer_fetch("MyTask", "rabbitmq", 100) as ctx:
|
|
375
|
+
transactions = connector.fetch()
|
|
376
|
+
ctx["fetched_count"] = len(transactions)
|
|
377
|
+
ctx["success"] = True
|
|
378
|
+
"""
|
|
379
|
+
ctx = {"fetched_count": 0, "success": False, "start": time.perf_counter()}
|
|
380
|
+
try:
|
|
381
|
+
yield ctx
|
|
382
|
+
finally:
|
|
383
|
+
duration = time.perf_counter() - ctx["start"]
|
|
384
|
+
record_consumer_fetch(
|
|
385
|
+
task_name,
|
|
386
|
+
connector_name,
|
|
387
|
+
batch_size,
|
|
388
|
+
ctx["fetched_count"],
|
|
389
|
+
duration,
|
|
390
|
+
ctx["success"],
|
|
391
|
+
)
|
|
392
|
+
|
|
393
|
+
|
|
394
|
+
@contextmanager
|
|
395
|
+
def timed_producer_transaction(task_name: str, transaction_id: str):
|
|
396
|
+
"""
|
|
397
|
+
Context manager to time and record a producer transaction.
|
|
398
|
+
|
|
399
|
+
Usage:
|
|
400
|
+
with timed_producer_transaction("MyTask", tx.id) as ctx:
|
|
401
|
+
# produce transaction
|
|
402
|
+
ctx["status"] = "success" # or "exception"
|
|
403
|
+
"""
|
|
404
|
+
ctx = {"status": "success", "start": time.perf_counter()}
|
|
405
|
+
try:
|
|
406
|
+
yield ctx
|
|
407
|
+
except Exception:
|
|
408
|
+
ctx["status"] = "exception"
|
|
409
|
+
raise
|
|
410
|
+
finally:
|
|
411
|
+
duration = time.perf_counter() - ctx["start"]
|
|
412
|
+
record_producer_transaction(task_name, transaction_id, duration, ctx["status"])
|
|
413
|
+
|
|
414
|
+
|
|
415
|
+
@contextmanager
|
|
416
|
+
def timed_producer_lifecycle(task_name: str, stage: str, attempt: int = 1):
|
|
417
|
+
"""
|
|
418
|
+
Context manager to time and record a producer lifecycle stage.
|
|
419
|
+
|
|
420
|
+
Usage:
|
|
421
|
+
with timed_producer_lifecycle("MyTask", "prepare") as ctx:
|
|
422
|
+
# do work
|
|
423
|
+
ctx["outcome"] = "ok" # or "error", "timeout", "retry"
|
|
424
|
+
"""
|
|
425
|
+
ctx = {"outcome": "ok", "start": time.perf_counter()}
|
|
426
|
+
try:
|
|
427
|
+
yield ctx
|
|
428
|
+
except Exception:
|
|
429
|
+
ctx["outcome"] = "error"
|
|
430
|
+
raise
|
|
431
|
+
finally:
|
|
432
|
+
duration = time.perf_counter() - ctx["start"]
|
|
433
|
+
record_producer_lifecycle(task_name, stage, duration, ctx["outcome"], attempt)
|
|
434
|
+
|
|
435
|
+
|
|
436
|
+
# ============================================================
|
|
437
|
+
# PUBLIC API
|
|
438
|
+
# ============================================================
|
|
439
|
+
|
|
440
|
+
__all__ = [
|
|
441
|
+
# Consumer recording
|
|
442
|
+
"record_consumer_transaction",
|
|
443
|
+
"record_consumer_fetch",
|
|
444
|
+
"record_consumer_lifecycle",
|
|
445
|
+
"record_consumer_batch",
|
|
446
|
+
"record_consumer_empty_queue_wait",
|
|
447
|
+
# Producer recording
|
|
448
|
+
"record_producer_transaction",
|
|
449
|
+
"record_producer_lifecycle",
|
|
450
|
+
"record_producer_batch",
|
|
451
|
+
# Context managers
|
|
452
|
+
"timed_consumer_transaction",
|
|
453
|
+
"timed_consumer_lifecycle",
|
|
454
|
+
"timed_consumer_fetch",
|
|
455
|
+
"timed_producer_transaction",
|
|
456
|
+
"timed_producer_lifecycle",
|
|
457
|
+
]
|