dory-processor-sdk 0.0.1__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.
- dory/__init__.py +101 -0
- dory/auth/__init__.py +10 -0
- dory/auth/oauth2.py +153 -0
- dory/auto_instrument.py +142 -0
- dory/cli/__init__.py +5 -0
- dory/cli/main.py +137 -0
- dory/cli/templates.py +123 -0
- dory/config/__init__.py +23 -0
- dory/config/defaults.py +24 -0
- dory/config/loader.py +430 -0
- dory/config/presets.py +73 -0
- dory/config/schema.py +84 -0
- dory/core/__init__.py +27 -0
- dory/core/app.py +434 -0
- dory/core/context.py +209 -0
- dory/core/lifecycle.py +214 -0
- dory/core/meta.py +121 -0
- dory/core/modes.py +479 -0
- dory/core/processor.py +564 -0
- dory/core/signals.py +122 -0
- dory/decorators.py +142 -0
- dory/edge/__init__.py +88 -0
- dory/edge/adaptive.py +644 -0
- dory/edge/detector.py +546 -0
- dory/edge/fencing.py +488 -0
- dory/edge/heartbeat.py +598 -0
- dory/edge/role.py +419 -0
- dory/errors/__init__.py +139 -0
- dory/errors/classification.py +362 -0
- dory/errors/codes.py +498 -0
- dory/geo/__init__.py +40 -0
- dory/geo/geolocalizer.py +1034 -0
- dory/health/__init__.py +12 -0
- dory/health/probes.py +210 -0
- dory/health/server.py +635 -0
- dory/k8s/__init__.py +80 -0
- dory/k8s/annotation_watcher.py +184 -0
- dory/k8s/client.py +251 -0
- dory/k8s/labels.py +505 -0
- dory/k8s/pod_metadata.py +182 -0
- dory/logging/__init__.py +9 -0
- dory/logging/logger.py +148 -0
- dory/metrics/__init__.py +7 -0
- dory/metrics/collector.py +301 -0
- dory/middleware/__init__.py +46 -0
- dory/middleware/connection_tracker.py +608 -0
- dory/middleware/request_id.py +325 -0
- dory/middleware/request_tracker.py +511 -0
- dory/migration/__init__.py +33 -0
- dory/migration/configmap.py +232 -0
- dory/migration/s3_store.py +594 -0
- dory/migration/serialization.py +135 -0
- dory/migration/state_manager.py +286 -0
- dory/migration/transfer.py +382 -0
- dory/monitoring/__init__.py +29 -0
- dory/monitoring/opentelemetry.py +489 -0
- dory/output/__init__.py +31 -0
- dory/output/envelope.py +137 -0
- dory/output/formatter.py +113 -0
- dory/output/rabbitmq.py +632 -0
- dory/output/routing.py +318 -0
- dory/output/validator.py +199 -0
- dory/py.typed +2 -0
- dory/recovery/__init__.py +60 -0
- dory/recovery/golden_image.py +487 -0
- dory/recovery/golden_snapshot.py +713 -0
- dory/recovery/golden_validator.py +518 -0
- dory/recovery/partial_recovery.py +482 -0
- dory/recovery/recovery_decision.py +242 -0
- dory/recovery/restart_detector.py +142 -0
- dory/recovery/state_validator.py +183 -0
- dory/resilience/__init__.py +45 -0
- dory/resilience/circuit_breaker.py +457 -0
- dory/resilience/retry.py +389 -0
- dory/simple.py +342 -0
- dory/types.py +68 -0
- dory/utils/__init__.py +31 -0
- dory/utils/errors.py +59 -0
- dory/utils/retry.py +115 -0
- dory/utils/timeout.py +80 -0
- dory_processor_sdk-0.0.1.dist-info/METADATA +424 -0
- dory_processor_sdk-0.0.1.dist-info/RECORD +86 -0
- dory_processor_sdk-0.0.1.dist-info/WHEEL +5 -0
- dory_processor_sdk-0.0.1.dist-info/entry_points.txt +2 -0
- dory_processor_sdk-0.0.1.dist-info/licenses/LICENSE +201 -0
- dory_processor_sdk-0.0.1.dist-info/top_level.txt +1 -0
dory/logging/logger.py
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Structured logging for Dory SDK.
|
|
3
|
+
|
|
4
|
+
Provides JSON-formatted logging suitable for Kubernetes
|
|
5
|
+
and log aggregation systems.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import json
|
|
9
|
+
import logging
|
|
10
|
+
import sys
|
|
11
|
+
import time
|
|
12
|
+
from typing import Any
|
|
13
|
+
|
|
14
|
+
class JsonFormatter(logging.Formatter):
|
|
15
|
+
"""
|
|
16
|
+
JSON log formatter for structured logging.
|
|
17
|
+
|
|
18
|
+
Output format:
|
|
19
|
+
{
|
|
20
|
+
"timestamp": "2024-01-15T10:30:45.123456Z",
|
|
21
|
+
"level": "INFO",
|
|
22
|
+
"logger": "dory.core.app",
|
|
23
|
+
"message": "Processor starting",
|
|
24
|
+
"extra": {...}
|
|
25
|
+
}
|
|
26
|
+
"""
|
|
27
|
+
|
|
28
|
+
def format(self, record: logging.LogRecord) -> str:
|
|
29
|
+
"""Format log record as JSON."""
|
|
30
|
+
log_dict = {
|
|
31
|
+
"timestamp": self._format_timestamp(record.created),
|
|
32
|
+
"level": record.levelname,
|
|
33
|
+
"logger": record.name,
|
|
34
|
+
"message": record.getMessage(),
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
# Add exception info if present
|
|
38
|
+
if record.exc_info:
|
|
39
|
+
log_dict["exception"] = self.formatException(record.exc_info)
|
|
40
|
+
|
|
41
|
+
# Add extra fields
|
|
42
|
+
extra = {}
|
|
43
|
+
for key, value in record.__dict__.items():
|
|
44
|
+
if key not in (
|
|
45
|
+
"name", "msg", "args", "created", "levelname", "levelno",
|
|
46
|
+
"pathname", "filename", "module", "exc_info", "exc_text",
|
|
47
|
+
"stack_info", "lineno", "funcName", "relativeCreated",
|
|
48
|
+
"thread", "threadName", "processName", "process", "message",
|
|
49
|
+
"msecs", "taskName",
|
|
50
|
+
):
|
|
51
|
+
extra[key] = self._serialize_value(value)
|
|
52
|
+
|
|
53
|
+
if extra:
|
|
54
|
+
log_dict["extra"] = extra
|
|
55
|
+
|
|
56
|
+
return json.dumps(log_dict)
|
|
57
|
+
|
|
58
|
+
def _format_timestamp(self, created: float) -> str:
|
|
59
|
+
"""Format timestamp as ISO 8601."""
|
|
60
|
+
return time.strftime(
|
|
61
|
+
"%Y-%m-%dT%H:%M:%S",
|
|
62
|
+
time.gmtime(created)
|
|
63
|
+
) + f".{int((created % 1) * 1000000):06d}Z"
|
|
64
|
+
|
|
65
|
+
def _serialize_value(self, value: Any) -> Any:
|
|
66
|
+
"""Serialize value for JSON."""
|
|
67
|
+
if isinstance(value, (str, int, float, bool, type(None))):
|
|
68
|
+
return value
|
|
69
|
+
if isinstance(value, (list, tuple)):
|
|
70
|
+
return [self._serialize_value(v) for v in value]
|
|
71
|
+
if isinstance(value, dict):
|
|
72
|
+
return {k: self._serialize_value(v) for k, v in value.items()}
|
|
73
|
+
return str(value)
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
class DoryLoggerAdapter(logging.LoggerAdapter):
|
|
77
|
+
"""
|
|
78
|
+
Logger adapter that adds context to all log messages.
|
|
79
|
+
|
|
80
|
+
Usage:
|
|
81
|
+
logger = DoryLoggerAdapter(
|
|
82
|
+
logging.getLogger(__name__),
|
|
83
|
+
{"processor_id": "my-processor", "pod": "my-pod-abc123"}
|
|
84
|
+
)
|
|
85
|
+
logger.info("Processing started")
|
|
86
|
+
# Output includes processor_id and pod in extra fields
|
|
87
|
+
"""
|
|
88
|
+
|
|
89
|
+
def process(self, msg: str, kwargs: dict) -> tuple[str, dict]:
|
|
90
|
+
"""Add extra context to log kwargs."""
|
|
91
|
+
extra = kwargs.get("extra", {})
|
|
92
|
+
extra.update(self.extra)
|
|
93
|
+
kwargs["extra"] = extra
|
|
94
|
+
return msg, kwargs
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def setup_logging(
|
|
98
|
+
level: str = "INFO",
|
|
99
|
+
) -> None:
|
|
100
|
+
"""
|
|
101
|
+
Setup logging configuration for Dory SDK.
|
|
102
|
+
|
|
103
|
+
Always uses JSON-structured logging.
|
|
104
|
+
|
|
105
|
+
Args:
|
|
106
|
+
level: Log level (DEBUG, INFO, WARNING, ERROR)
|
|
107
|
+
"""
|
|
108
|
+
# Get root logger
|
|
109
|
+
root_logger = logging.getLogger()
|
|
110
|
+
root_logger.setLevel(getattr(logging, level.upper()))
|
|
111
|
+
|
|
112
|
+
# Remove existing handlers
|
|
113
|
+
for handler in root_logger.handlers[:]:
|
|
114
|
+
root_logger.removeHandler(handler)
|
|
115
|
+
|
|
116
|
+
# Create console handler with JSON formatter
|
|
117
|
+
handler = logging.StreamHandler(sys.stdout)
|
|
118
|
+
handler.setLevel(getattr(logging, level.upper()))
|
|
119
|
+
handler.setFormatter(JsonFormatter())
|
|
120
|
+
|
|
121
|
+
root_logger.addHandler(handler)
|
|
122
|
+
|
|
123
|
+
# Set levels for noisy libraries
|
|
124
|
+
logging.getLogger("kubernetes").setLevel(logging.WARNING)
|
|
125
|
+
logging.getLogger("urllib3").setLevel(logging.WARNING)
|
|
126
|
+
logging.getLogger("aiohttp").setLevel(logging.WARNING)
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def get_logger(
|
|
130
|
+
name: str,
|
|
131
|
+
extra: dict[str, Any] | None = None,
|
|
132
|
+
) -> logging.Logger | DoryLoggerAdapter:
|
|
133
|
+
"""
|
|
134
|
+
Get a logger with optional extra context.
|
|
135
|
+
|
|
136
|
+
Args:
|
|
137
|
+
name: Logger name (usually __name__)
|
|
138
|
+
extra: Optional extra context to include in all logs
|
|
139
|
+
|
|
140
|
+
Returns:
|
|
141
|
+
Logger or LoggerAdapter
|
|
142
|
+
"""
|
|
143
|
+
logger = logging.getLogger(name)
|
|
144
|
+
|
|
145
|
+
if extra:
|
|
146
|
+
return DoryLoggerAdapter(logger, extra)
|
|
147
|
+
|
|
148
|
+
return logger
|
dory/metrics/__init__.py
ADDED
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Prometheus metrics collector.
|
|
3
|
+
|
|
4
|
+
Collects and exports metrics in Prometheus format.
|
|
5
|
+
"""
|
|
6
|
+
|
|
7
|
+
import logging
|
|
8
|
+
import time
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from typing import Dict, List
|
|
11
|
+
|
|
12
|
+
logger = logging.getLogger(__name__)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@dataclass
|
|
16
|
+
class MetricValue:
|
|
17
|
+
"""A single metric value with labels."""
|
|
18
|
+
value: float
|
|
19
|
+
labels: Dict[str, str] = field(default_factory=dict)
|
|
20
|
+
timestamp: float = field(default_factory=time.time)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass
|
|
24
|
+
class MetricDefinition:
|
|
25
|
+
"""Definition of a metric."""
|
|
26
|
+
name: str
|
|
27
|
+
help: str
|
|
28
|
+
type: str # counter, gauge, histogram, summary
|
|
29
|
+
values: List[MetricValue] = field(default_factory=list)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
class MetricsCollector:
|
|
33
|
+
"""
|
|
34
|
+
Collects and exports Prometheus metrics.
|
|
35
|
+
|
|
36
|
+
Metrics collected:
|
|
37
|
+
- dory_startup_duration_seconds: Time to complete startup
|
|
38
|
+
- dory_shutdown_duration_seconds: Time to complete shutdown
|
|
39
|
+
- dory_state_save_duration_seconds: Time to save state
|
|
40
|
+
- dory_state_load_duration_seconds: Time to load state
|
|
41
|
+
- dory_restart_count: Number of restarts
|
|
42
|
+
- dory_golden_image_resets: Number of golden image resets
|
|
43
|
+
- dory_health_check_failures: Number of health check failures
|
|
44
|
+
"""
|
|
45
|
+
|
|
46
|
+
def __init__(self, prefix: str = "dory"):
|
|
47
|
+
"""
|
|
48
|
+
Initialize metrics collector.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
prefix: Metric name prefix
|
|
52
|
+
"""
|
|
53
|
+
self._prefix = prefix
|
|
54
|
+
self._metrics: Dict[str, MetricDefinition] = {}
|
|
55
|
+
|
|
56
|
+
# Timing state
|
|
57
|
+
self._startup_start: float | None = None
|
|
58
|
+
self._startup_completed_at: float | None = None
|
|
59
|
+
self._shutdown_start: float | None = None
|
|
60
|
+
self._request_count: int = 0
|
|
61
|
+
|
|
62
|
+
# Initialize standard metrics
|
|
63
|
+
self._init_standard_metrics()
|
|
64
|
+
|
|
65
|
+
def _init_standard_metrics(self) -> None:
|
|
66
|
+
"""Initialize standard Dory metrics."""
|
|
67
|
+
self._register_metric(
|
|
68
|
+
"startup_duration_seconds",
|
|
69
|
+
"Time to complete processor startup",
|
|
70
|
+
"gauge",
|
|
71
|
+
)
|
|
72
|
+
self._register_metric(
|
|
73
|
+
"shutdown_duration_seconds",
|
|
74
|
+
"Time to complete processor shutdown",
|
|
75
|
+
"gauge",
|
|
76
|
+
)
|
|
77
|
+
self._register_metric(
|
|
78
|
+
"state_save_duration_seconds",
|
|
79
|
+
"Time to save processor state",
|
|
80
|
+
"gauge",
|
|
81
|
+
)
|
|
82
|
+
self._register_metric(
|
|
83
|
+
"state_load_duration_seconds",
|
|
84
|
+
"Time to load processor state",
|
|
85
|
+
"gauge",
|
|
86
|
+
)
|
|
87
|
+
self._register_metric(
|
|
88
|
+
"restart_count",
|
|
89
|
+
"Total number of processor restarts",
|
|
90
|
+
"counter",
|
|
91
|
+
)
|
|
92
|
+
self._register_metric(
|
|
93
|
+
"golden_image_resets_total",
|
|
94
|
+
"Total number of golden image resets",
|
|
95
|
+
"counter",
|
|
96
|
+
)
|
|
97
|
+
self._register_metric(
|
|
98
|
+
"health_check_failures_total",
|
|
99
|
+
"Total number of health check failures",
|
|
100
|
+
"counter",
|
|
101
|
+
)
|
|
102
|
+
self._register_metric(
|
|
103
|
+
"state_size_bytes",
|
|
104
|
+
"Size of processor state in bytes",
|
|
105
|
+
"gauge",
|
|
106
|
+
)
|
|
107
|
+
self._register_metric(
|
|
108
|
+
"processor_info",
|
|
109
|
+
"Processor information",
|
|
110
|
+
"gauge",
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
def _register_metric(self, name: str, help: str, type: str) -> None:
|
|
114
|
+
"""Register a metric definition."""
|
|
115
|
+
full_name = f"{self._prefix}_{name}"
|
|
116
|
+
self._metrics[full_name] = MetricDefinition(
|
|
117
|
+
name=full_name,
|
|
118
|
+
help=help,
|
|
119
|
+
type=type,
|
|
120
|
+
)
|
|
121
|
+
|
|
122
|
+
def _metric_name(self, name: str) -> str:
|
|
123
|
+
"""Get full metric name."""
|
|
124
|
+
return f"{self._prefix}_{name}"
|
|
125
|
+
|
|
126
|
+
def set_gauge(
|
|
127
|
+
self,
|
|
128
|
+
name: str,
|
|
129
|
+
value: float,
|
|
130
|
+
labels: Dict[str, str] | None = None,
|
|
131
|
+
) -> None:
|
|
132
|
+
"""
|
|
133
|
+
Set a gauge metric value.
|
|
134
|
+
|
|
135
|
+
Args:
|
|
136
|
+
name: Metric name (without prefix)
|
|
137
|
+
value: Metric value
|
|
138
|
+
labels: Optional labels
|
|
139
|
+
"""
|
|
140
|
+
full_name = self._metric_name(name)
|
|
141
|
+
if full_name not in self._metrics:
|
|
142
|
+
self._register_metric(name, f"Custom gauge {name}", "gauge")
|
|
143
|
+
|
|
144
|
+
metric = self._metrics[full_name]
|
|
145
|
+
metric.values = [MetricValue(value=value, labels=labels or {})]
|
|
146
|
+
|
|
147
|
+
def inc_counter(
|
|
148
|
+
self,
|
|
149
|
+
name: str,
|
|
150
|
+
value: float = 1.0,
|
|
151
|
+
labels: Dict[str, str] | None = None,
|
|
152
|
+
) -> None:
|
|
153
|
+
"""
|
|
154
|
+
Increment a counter metric.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
name: Metric name (without prefix)
|
|
158
|
+
value: Amount to increment (default 1)
|
|
159
|
+
labels: Optional labels
|
|
160
|
+
"""
|
|
161
|
+
full_name = self._metric_name(name)
|
|
162
|
+
if full_name not in self._metrics:
|
|
163
|
+
self._register_metric(name, f"Custom counter {name}", "counter")
|
|
164
|
+
|
|
165
|
+
metric = self._metrics[full_name]
|
|
166
|
+
|
|
167
|
+
# Find existing value with same labels or create new
|
|
168
|
+
labels = labels or {}
|
|
169
|
+
for mv in metric.values:
|
|
170
|
+
if mv.labels == labels:
|
|
171
|
+
mv.value += value
|
|
172
|
+
mv.timestamp = time.time()
|
|
173
|
+
return
|
|
174
|
+
|
|
175
|
+
metric.values.append(MetricValue(value=value, labels=labels))
|
|
176
|
+
|
|
177
|
+
# Convenience methods for standard metrics
|
|
178
|
+
|
|
179
|
+
def record_startup_started(self) -> None:
|
|
180
|
+
"""Record that startup has started."""
|
|
181
|
+
self._startup_start = time.time()
|
|
182
|
+
|
|
183
|
+
def record_startup_completed(self) -> None:
|
|
184
|
+
"""Record that startup has completed."""
|
|
185
|
+
self._startup_completed_at = time.time()
|
|
186
|
+
if self._startup_start:
|
|
187
|
+
duration = self._startup_completed_at - self._startup_start
|
|
188
|
+
self.set_gauge("startup_duration_seconds", duration)
|
|
189
|
+
logger.debug(f"Startup completed in {duration:.3f}s")
|
|
190
|
+
|
|
191
|
+
def record_shutdown_started(self) -> None:
|
|
192
|
+
"""Record that shutdown has started."""
|
|
193
|
+
self._shutdown_start = time.time()
|
|
194
|
+
|
|
195
|
+
def record_shutdown_completed(self) -> None:
|
|
196
|
+
"""Record that shutdown has completed."""
|
|
197
|
+
if self._shutdown_start:
|
|
198
|
+
duration = time.time() - self._shutdown_start
|
|
199
|
+
self.set_gauge("shutdown_duration_seconds", duration)
|
|
200
|
+
logger.debug(f"Shutdown completed in {duration:.3f}s")
|
|
201
|
+
|
|
202
|
+
def record_state_save(self, duration: float, size_bytes: int = 0) -> None:
|
|
203
|
+
"""Record state save operation."""
|
|
204
|
+
self.set_gauge("state_save_duration_seconds", duration)
|
|
205
|
+
if size_bytes > 0:
|
|
206
|
+
self.set_gauge("state_size_bytes", size_bytes)
|
|
207
|
+
|
|
208
|
+
def record_state_load(self, duration: float) -> None:
|
|
209
|
+
"""Record state load operation."""
|
|
210
|
+
self.set_gauge("state_load_duration_seconds", duration)
|
|
211
|
+
|
|
212
|
+
def record_restart(self) -> None:
|
|
213
|
+
"""Record a restart event."""
|
|
214
|
+
self.inc_counter("restart_count")
|
|
215
|
+
|
|
216
|
+
def record_golden_image_reset(self) -> None:
|
|
217
|
+
"""Record a golden image reset."""
|
|
218
|
+
self.inc_counter("golden_image_resets_total")
|
|
219
|
+
|
|
220
|
+
def record_health_check_failure(self) -> None:
|
|
221
|
+
"""Record a health check failure."""
|
|
222
|
+
self.inc_counter("health_check_failures_total")
|
|
223
|
+
|
|
224
|
+
def set_processor_info(
|
|
225
|
+
self,
|
|
226
|
+
processor_id: str,
|
|
227
|
+
version: str = "",
|
|
228
|
+
pod_name: str = "",
|
|
229
|
+
) -> None:
|
|
230
|
+
"""Set processor information metric."""
|
|
231
|
+
self.set_gauge(
|
|
232
|
+
"processor_info",
|
|
233
|
+
1.0,
|
|
234
|
+
labels={
|
|
235
|
+
"processor_id": processor_id,
|
|
236
|
+
"version": version,
|
|
237
|
+
"pod": pod_name,
|
|
238
|
+
},
|
|
239
|
+
)
|
|
240
|
+
|
|
241
|
+
def export_prometheus(self) -> str:
|
|
242
|
+
"""
|
|
243
|
+
Export metrics in Prometheus text format.
|
|
244
|
+
|
|
245
|
+
Returns:
|
|
246
|
+
Prometheus-formatted metrics string
|
|
247
|
+
"""
|
|
248
|
+
lines = []
|
|
249
|
+
|
|
250
|
+
for metric in self._metrics.values():
|
|
251
|
+
# HELP line
|
|
252
|
+
lines.append(f"# HELP {metric.name} {metric.help}")
|
|
253
|
+
# TYPE line
|
|
254
|
+
lines.append(f"# TYPE {metric.name} {metric.type}")
|
|
255
|
+
|
|
256
|
+
# Metric values
|
|
257
|
+
for mv in metric.values:
|
|
258
|
+
if mv.labels:
|
|
259
|
+
label_str = ",".join(
|
|
260
|
+
f'{k}="{v}"' for k, v in mv.labels.items()
|
|
261
|
+
)
|
|
262
|
+
lines.append(f"{metric.name}{{{label_str}}} {mv.value}")
|
|
263
|
+
else:
|
|
264
|
+
lines.append(f"{metric.name} {mv.value}")
|
|
265
|
+
|
|
266
|
+
return "\n".join(lines) + "\n"
|
|
267
|
+
|
|
268
|
+
def flush(self) -> None:
|
|
269
|
+
"""Flush any buffered metrics."""
|
|
270
|
+
# In this implementation, metrics are not buffered
|
|
271
|
+
# This is a hook for implementations that buffer
|
|
272
|
+
pass
|
|
273
|
+
|
|
274
|
+
def get_uptime_seconds(self) -> float:
|
|
275
|
+
"""
|
|
276
|
+
Get the processor uptime in seconds.
|
|
277
|
+
|
|
278
|
+
Returns:
|
|
279
|
+
Uptime in seconds since startup completed, or 0 if not started
|
|
280
|
+
"""
|
|
281
|
+
if self._startup_completed_at is None:
|
|
282
|
+
return 0.0
|
|
283
|
+
return time.time() - self._startup_completed_at
|
|
284
|
+
|
|
285
|
+
def get_request_count(self) -> int:
|
|
286
|
+
"""
|
|
287
|
+
Get the total request count.
|
|
288
|
+
|
|
289
|
+
Returns:
|
|
290
|
+
Total number of requests processed
|
|
291
|
+
"""
|
|
292
|
+
return self._request_count
|
|
293
|
+
|
|
294
|
+
def increment_request_count(self, count: int = 1) -> None:
|
|
295
|
+
"""
|
|
296
|
+
Increment the request counter.
|
|
297
|
+
|
|
298
|
+
Args:
|
|
299
|
+
count: Amount to increment (default 1)
|
|
300
|
+
"""
|
|
301
|
+
self._request_count += count
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Dory Middleware
|
|
3
|
+
|
|
4
|
+
Automatic bookkeeping middleware for request tracking, connection management,
|
|
5
|
+
and observability.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from dory.middleware.request_tracker import (
|
|
9
|
+
RequestTracker,
|
|
10
|
+
track_request,
|
|
11
|
+
RequestMetrics,
|
|
12
|
+
RequestInfo,
|
|
13
|
+
RequestStatus,
|
|
14
|
+
)
|
|
15
|
+
from dory.middleware.request_id import (
|
|
16
|
+
RequestIdMiddleware,
|
|
17
|
+
with_request_id,
|
|
18
|
+
get_current_request_id,
|
|
19
|
+
set_request_id,
|
|
20
|
+
generate_request_id,
|
|
21
|
+
)
|
|
22
|
+
from dory.middleware.connection_tracker import (
|
|
23
|
+
ConnectionTracker,
|
|
24
|
+
track_connection,
|
|
25
|
+
ConnectionInfo,
|
|
26
|
+
ConnectionStatus,
|
|
27
|
+
ConnectionType,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
__all__ = [
|
|
31
|
+
"RequestTracker",
|
|
32
|
+
"track_request",
|
|
33
|
+
"RequestMetrics",
|
|
34
|
+
"RequestInfo",
|
|
35
|
+
"RequestStatus",
|
|
36
|
+
"RequestIdMiddleware",
|
|
37
|
+
"with_request_id",
|
|
38
|
+
"get_current_request_id",
|
|
39
|
+
"set_request_id",
|
|
40
|
+
"generate_request_id",
|
|
41
|
+
"ConnectionTracker",
|
|
42
|
+
"track_connection",
|
|
43
|
+
"ConnectionInfo",
|
|
44
|
+
"ConnectionStatus",
|
|
45
|
+
"ConnectionType",
|
|
46
|
+
]
|