letta-nightly 0.8.15.dev20250720104313__py3-none-any.whl → 0.8.16.dev20250721104533__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.
- letta/__init__.py +1 -1
- letta/agent.py +27 -11
- letta/agents/helpers.py +1 -1
- letta/agents/letta_agent.py +518 -322
- letta/agents/letta_agent_batch.py +1 -2
- letta/agents/voice_agent.py +15 -17
- letta/client/client.py +3 -3
- letta/constants.py +5 -0
- letta/embeddings.py +0 -2
- letta/errors.py +8 -0
- letta/functions/function_sets/base.py +3 -3
- letta/functions/helpers.py +2 -3
- letta/groups/sleeptime_multi_agent.py +0 -1
- letta/helpers/composio_helpers.py +2 -2
- letta/helpers/converters.py +1 -1
- letta/helpers/pinecone_utils.py +8 -0
- letta/helpers/tool_rule_solver.py +13 -18
- letta/llm_api/aws_bedrock.py +16 -2
- letta/llm_api/cohere.py +1 -1
- letta/llm_api/openai_client.py +1 -1
- letta/local_llm/grammars/gbnf_grammar_generator.py +1 -1
- letta/local_llm/llm_chat_completion_wrappers/zephyr.py +14 -14
- letta/local_llm/utils.py +1 -2
- letta/orm/agent.py +3 -3
- letta/orm/block.py +4 -4
- letta/orm/files_agents.py +0 -1
- letta/orm/identity.py +2 -0
- letta/orm/mcp_server.py +0 -2
- letta/orm/message.py +140 -14
- letta/orm/organization.py +5 -5
- letta/orm/passage.py +4 -4
- letta/orm/source.py +1 -1
- letta/orm/sqlalchemy_base.py +61 -39
- letta/orm/step.py +2 -0
- letta/otel/db_pool_monitoring.py +308 -0
- letta/otel/metric_registry.py +94 -1
- letta/otel/sqlalchemy_instrumentation.py +548 -0
- letta/otel/sqlalchemy_instrumentation_integration.py +124 -0
- letta/otel/tracing.py +37 -1
- letta/schemas/agent.py +0 -3
- letta/schemas/agent_file.py +283 -0
- letta/schemas/block.py +0 -3
- letta/schemas/file.py +28 -26
- letta/schemas/letta_message.py +15 -4
- letta/schemas/memory.py +1 -1
- letta/schemas/message.py +31 -26
- letta/schemas/openai/chat_completion_response.py +0 -1
- letta/schemas/providers.py +20 -0
- letta/schemas/source.py +11 -13
- letta/schemas/step.py +12 -0
- letta/schemas/tool.py +0 -4
- letta/serialize_schemas/marshmallow_agent.py +14 -1
- letta/serialize_schemas/marshmallow_block.py +23 -1
- letta/serialize_schemas/marshmallow_message.py +1 -3
- letta/serialize_schemas/marshmallow_tool.py +23 -1
- letta/server/db.py +110 -6
- letta/server/rest_api/app.py +85 -73
- letta/server/rest_api/routers/v1/agents.py +68 -53
- letta/server/rest_api/routers/v1/blocks.py +2 -2
- letta/server/rest_api/routers/v1/jobs.py +3 -0
- letta/server/rest_api/routers/v1/organizations.py +2 -2
- letta/server/rest_api/routers/v1/sources.py +18 -2
- letta/server/rest_api/routers/v1/tools.py +11 -12
- letta/server/rest_api/routers/v1/users.py +1 -1
- letta/server/rest_api/streaming_response.py +13 -5
- letta/server/rest_api/utils.py +8 -25
- letta/server/server.py +11 -4
- letta/server/ws_api/server.py +2 -2
- letta/services/agent_file_manager.py +616 -0
- letta/services/agent_manager.py +133 -46
- letta/services/block_manager.py +38 -17
- letta/services/file_manager.py +106 -21
- letta/services/file_processor/file_processor.py +93 -0
- letta/services/files_agents_manager.py +28 -0
- letta/services/group_manager.py +4 -5
- letta/services/helpers/agent_manager_helper.py +57 -9
- letta/services/identity_manager.py +22 -0
- letta/services/job_manager.py +210 -91
- letta/services/llm_batch_manager.py +9 -6
- letta/services/mcp/stdio_client.py +1 -2
- letta/services/mcp_manager.py +0 -1
- letta/services/message_manager.py +49 -26
- letta/services/passage_manager.py +0 -1
- letta/services/provider_manager.py +1 -1
- letta/services/source_manager.py +114 -5
- letta/services/step_manager.py +36 -4
- letta/services/telemetry_manager.py +9 -2
- letta/services/tool_executor/builtin_tool_executor.py +5 -1
- letta/services/tool_executor/core_tool_executor.py +3 -3
- letta/services/tool_manager.py +95 -20
- letta/services/user_manager.py +4 -12
- letta/settings.py +23 -6
- letta/system.py +1 -1
- letta/utils.py +26 -2
- {letta_nightly-0.8.15.dev20250720104313.dist-info → letta_nightly-0.8.16.dev20250721104533.dist-info}/METADATA +3 -2
- {letta_nightly-0.8.15.dev20250720104313.dist-info → letta_nightly-0.8.16.dev20250721104533.dist-info}/RECORD +99 -94
- {letta_nightly-0.8.15.dev20250720104313.dist-info → letta_nightly-0.8.16.dev20250721104533.dist-info}/LICENSE +0 -0
- {letta_nightly-0.8.15.dev20250720104313.dist-info → letta_nightly-0.8.16.dev20250721104533.dist-info}/WHEEL +0 -0
- {letta_nightly-0.8.15.dev20250720104313.dist-info → letta_nightly-0.8.16.dev20250721104533.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,308 @@
|
|
1
|
+
import time
|
2
|
+
from typing import Any
|
3
|
+
|
4
|
+
from sqlalchemy import Engine, PoolProxiedConnection, QueuePool, event
|
5
|
+
from sqlalchemy.engine.interfaces import DBAPIConnection
|
6
|
+
from sqlalchemy.ext.asyncio import AsyncEngine
|
7
|
+
from sqlalchemy.pool import ConnectionPoolEntry, Pool
|
8
|
+
|
9
|
+
from letta.helpers.datetime_helpers import get_utc_timestamp_ns, ns_to_ms
|
10
|
+
from letta.log import get_logger
|
11
|
+
from letta.otel.context import get_ctx_attributes
|
12
|
+
|
13
|
+
logger = get_logger(__name__)
|
14
|
+
|
15
|
+
|
16
|
+
class DatabasePoolMonitor:
|
17
|
+
"""Monitor database connection pool metrics and events using SQLAlchemy event listeners."""
|
18
|
+
|
19
|
+
def __init__(self):
|
20
|
+
self._active_connections: dict[int, dict[str, Any]] = {}
|
21
|
+
self._pool_stats: dict[str, dict[str, Any]] = {}
|
22
|
+
|
23
|
+
def setup_monitoring(self, engine: Engine | AsyncEngine, engine_name: str = "default") -> None:
|
24
|
+
"""Set up connection pool monitoring for the given engine."""
|
25
|
+
if not hasattr(engine, "pool"):
|
26
|
+
logger.warning(f"Engine {engine_name} does not have a pool attribute")
|
27
|
+
return
|
28
|
+
|
29
|
+
try:
|
30
|
+
self._setup_pool_listeners(engine.pool, engine_name)
|
31
|
+
logger.info(f"Database pool monitoring initialized for engine: {engine_name}")
|
32
|
+
except Exception as e:
|
33
|
+
logger.error(f"Failed to setup pool monitoring for {engine_name}: {e}")
|
34
|
+
|
35
|
+
def _setup_pool_listeners(self, pool: Pool, engine_name: str) -> None:
|
36
|
+
"""Set up event listeners for the connection pool."""
|
37
|
+
|
38
|
+
@event.listens_for(pool, "connect")
|
39
|
+
def on_connect(dbapi_connection: DBAPIConnection, connection_record: ConnectionPoolEntry):
|
40
|
+
"""Called when a new connection is created."""
|
41
|
+
connection_id = id(connection_record)
|
42
|
+
|
43
|
+
self._active_connections[connection_id] = {
|
44
|
+
"engine_name": engine_name,
|
45
|
+
"created_at": time.time(),
|
46
|
+
"checked_out_at": None,
|
47
|
+
"checked_in_at": None,
|
48
|
+
"checkout_count": 0,
|
49
|
+
}
|
50
|
+
|
51
|
+
try:
|
52
|
+
from letta.otel.metric_registry import MetricRegistry
|
53
|
+
|
54
|
+
attrs = {
|
55
|
+
"engine_name": engine_name,
|
56
|
+
"event": "connect",
|
57
|
+
**get_ctx_attributes(),
|
58
|
+
}
|
59
|
+
MetricRegistry().db_pool_connection_events_counter.add(1, attributes=attrs)
|
60
|
+
except Exception as e:
|
61
|
+
logger.info(f"Failed to record connection event metric: {e}")
|
62
|
+
|
63
|
+
@event.listens_for(pool, "first_connect")
|
64
|
+
def on_first_connect(dbapi_connection: DBAPIConnection, connection_record: ConnectionPoolEntry):
|
65
|
+
"""Called when the first connection is created."""
|
66
|
+
try:
|
67
|
+
from letta.otel.metric_registry import MetricRegistry
|
68
|
+
|
69
|
+
attrs = {
|
70
|
+
"engine_name": engine_name,
|
71
|
+
"event": "first_connect",
|
72
|
+
**get_ctx_attributes(),
|
73
|
+
}
|
74
|
+
MetricRegistry().db_pool_connection_events_counter.add(1, attributes=attrs)
|
75
|
+
logger.info(f"First connection established for engine: {engine_name}")
|
76
|
+
except Exception as e:
|
77
|
+
logger.info(f"Failed to record first_connect event metric: {e}")
|
78
|
+
|
79
|
+
@event.listens_for(pool, "checkout")
|
80
|
+
def on_checkout(dbapi_connection: DBAPIConnection, connection_record: ConnectionPoolEntry, connection_proxy: PoolProxiedConnection):
|
81
|
+
"""Called when a connection is checked out from the pool."""
|
82
|
+
connection_id = id(connection_record)
|
83
|
+
checkout_start_ns = get_utc_timestamp_ns()
|
84
|
+
|
85
|
+
if connection_id in self._active_connections:
|
86
|
+
self._active_connections[connection_id]["checked_out_at_ns"] = checkout_start_ns
|
87
|
+
self._active_connections[connection_id]["checkout_count"] += 1
|
88
|
+
|
89
|
+
try:
|
90
|
+
from letta.otel.metric_registry import MetricRegistry
|
91
|
+
|
92
|
+
# Record current pool statistics
|
93
|
+
pool_stats = self._get_pool_stats(pool)
|
94
|
+
attrs = {
|
95
|
+
"engine_name": engine_name,
|
96
|
+
**get_ctx_attributes(),
|
97
|
+
}
|
98
|
+
|
99
|
+
MetricRegistry().db_pool_connections_checked_out_gauge.set(pool_stats["checked_out"], attributes=attrs)
|
100
|
+
MetricRegistry().db_pool_connections_available_gauge.set(pool_stats["available"], attributes=attrs)
|
101
|
+
MetricRegistry().db_pool_connections_total_gauge.set(pool_stats["total"], attributes=attrs)
|
102
|
+
if pool_stats["overflow"] is not None:
|
103
|
+
MetricRegistry().db_pool_connections_overflow_gauge.set(pool_stats["overflow"], attributes=attrs)
|
104
|
+
|
105
|
+
# Record checkout event
|
106
|
+
attrs["event"] = "checkout"
|
107
|
+
MetricRegistry().db_pool_connection_events_counter.add(1, attributes=attrs)
|
108
|
+
|
109
|
+
except Exception as e:
|
110
|
+
logger.info(f"Failed to record checkout event metric: {e}")
|
111
|
+
|
112
|
+
@event.listens_for(pool, "checkin")
|
113
|
+
def on_checkin(dbapi_connection: DBAPIConnection, connection_record: ConnectionPoolEntry):
|
114
|
+
"""Called when a connection is checked back into the pool."""
|
115
|
+
connection_id = id(connection_record)
|
116
|
+
checkin_time_ns = get_utc_timestamp_ns()
|
117
|
+
|
118
|
+
if connection_id in self._active_connections:
|
119
|
+
conn_info = self._active_connections[connection_id]
|
120
|
+
conn_info["checkin_time_ns"] = checkin_time_ns
|
121
|
+
|
122
|
+
# Calculate connection duration if we have checkout time
|
123
|
+
if conn_info["checked_out_at_ns"]:
|
124
|
+
duration_ms = ns_to_ms(checkin_time_ns - conn_info["checked_out_at_ns"])
|
125
|
+
|
126
|
+
try:
|
127
|
+
from letta.otel.metric_registry import MetricRegistry
|
128
|
+
|
129
|
+
attrs = {
|
130
|
+
"engine_name": engine_name,
|
131
|
+
**get_ctx_attributes(),
|
132
|
+
}
|
133
|
+
MetricRegistry().db_pool_connection_duration_ms_histogram.record(duration_ms, attributes=attrs)
|
134
|
+
except Exception as e:
|
135
|
+
logger.info(f"Failed to record connection duration metric: {e}")
|
136
|
+
|
137
|
+
try:
|
138
|
+
from letta.otel.metric_registry import MetricRegistry
|
139
|
+
|
140
|
+
# Record current pool statistics after checkin
|
141
|
+
pool_stats = self._get_pool_stats(pool)
|
142
|
+
attrs = {
|
143
|
+
"engine_name": engine_name,
|
144
|
+
**get_ctx_attributes(),
|
145
|
+
}
|
146
|
+
|
147
|
+
MetricRegistry().db_pool_connections_checked_out_gauge.set(pool_stats["checked_out"], attributes=attrs)
|
148
|
+
MetricRegistry().db_pool_connections_available_gauge.set(pool_stats["available"], attributes=attrs)
|
149
|
+
|
150
|
+
# Record checkin event
|
151
|
+
attrs["event"] = "checkin"
|
152
|
+
MetricRegistry().db_pool_connection_events_counter.add(1, attributes=attrs)
|
153
|
+
|
154
|
+
except Exception as e:
|
155
|
+
logger.info(f"Failed to record checkin event metric: {e}")
|
156
|
+
|
157
|
+
@event.listens_for(pool, "invalidate")
|
158
|
+
def on_invalidate(dbapi_connection: DBAPIConnection, connection_record: ConnectionPoolEntry, exception):
|
159
|
+
"""Called when a connection is invalidated."""
|
160
|
+
connection_id = id(connection_record)
|
161
|
+
|
162
|
+
if connection_id in self._active_connections:
|
163
|
+
del self._active_connections[connection_id]
|
164
|
+
|
165
|
+
try:
|
166
|
+
from letta.otel.metric_registry import MetricRegistry
|
167
|
+
|
168
|
+
attrs = {
|
169
|
+
"engine_name": engine_name,
|
170
|
+
"event": "invalidate",
|
171
|
+
"exception_type": type(exception).__name__ if exception else "unknown",
|
172
|
+
**get_ctx_attributes(),
|
173
|
+
}
|
174
|
+
MetricRegistry().db_pool_connection_events_counter.add(1, attributes=attrs)
|
175
|
+
MetricRegistry().db_pool_connection_errors_counter.add(1, attributes=attrs)
|
176
|
+
except Exception as e:
|
177
|
+
logger.info(f"Failed to record invalidate event metric: {e}")
|
178
|
+
|
179
|
+
@event.listens_for(pool, "soft_invalidate")
|
180
|
+
def on_soft_invalidate(dbapi_connection: DBAPIConnection, connection_record: ConnectionPoolEntry, exception):
|
181
|
+
"""Called when a connection is soft invalidated."""
|
182
|
+
try:
|
183
|
+
from letta.otel.metric_registry import MetricRegistry
|
184
|
+
|
185
|
+
attrs = {
|
186
|
+
"engine_name": engine_name,
|
187
|
+
"event": "soft_invalidate",
|
188
|
+
"exception_type": type(exception).__name__ if exception else "unknown",
|
189
|
+
**get_ctx_attributes(),
|
190
|
+
}
|
191
|
+
MetricRegistry().db_pool_connection_events_counter.add(1, attributes=attrs)
|
192
|
+
logger.debug(f"Connection soft invalidated for engine: {engine_name}")
|
193
|
+
except Exception as e:
|
194
|
+
logger.info(f"Failed to record soft_invalidate event metric: {e}")
|
195
|
+
|
196
|
+
@event.listens_for(pool, "close")
|
197
|
+
def on_close(dbapi_connection: DBAPIConnection, connection_record: ConnectionPoolEntry):
|
198
|
+
"""Called when a connection is closed."""
|
199
|
+
connection_id = id(connection_record)
|
200
|
+
|
201
|
+
if connection_id in self._active_connections:
|
202
|
+
del self._active_connections[connection_id]
|
203
|
+
|
204
|
+
try:
|
205
|
+
from letta.otel.metric_registry import MetricRegistry
|
206
|
+
|
207
|
+
attrs = {
|
208
|
+
"engine_name": engine_name,
|
209
|
+
"event": "close",
|
210
|
+
**get_ctx_attributes(),
|
211
|
+
}
|
212
|
+
MetricRegistry().db_pool_connection_events_counter.add(1, attributes=attrs)
|
213
|
+
except Exception as e:
|
214
|
+
logger.info(f"Failed to record close event metric: {e}")
|
215
|
+
|
216
|
+
@event.listens_for(pool, "close_detached")
|
217
|
+
def on_close_detached(dbapi_connection: DBAPIConnection):
|
218
|
+
"""Called when a detached connection is closed."""
|
219
|
+
try:
|
220
|
+
from letta.otel.metric_registry import MetricRegistry
|
221
|
+
|
222
|
+
attrs = {
|
223
|
+
"engine_name": engine_name,
|
224
|
+
"event": "close_detached",
|
225
|
+
**get_ctx_attributes(),
|
226
|
+
}
|
227
|
+
MetricRegistry().db_pool_connection_events_counter.add(1, attributes=attrs)
|
228
|
+
logger.debug(f"Detached connection closed for engine: {engine_name}")
|
229
|
+
except Exception as e:
|
230
|
+
logger.info(f"Failed to record close_detached event metric: {e}")
|
231
|
+
|
232
|
+
@event.listens_for(pool, "detach")
|
233
|
+
def on_detach(dbapi_connection: DBAPIConnection, connection_record: ConnectionPoolEntry):
|
234
|
+
"""Called when a connection is detached from the pool."""
|
235
|
+
connection_id = id(connection_record)
|
236
|
+
|
237
|
+
if connection_id in self._active_connections:
|
238
|
+
self._active_connections[connection_id]["detached"] = True
|
239
|
+
|
240
|
+
try:
|
241
|
+
from letta.otel.metric_registry import MetricRegistry
|
242
|
+
|
243
|
+
attrs = {
|
244
|
+
"engine_name": engine_name,
|
245
|
+
"event": "detach",
|
246
|
+
**get_ctx_attributes(),
|
247
|
+
}
|
248
|
+
MetricRegistry().db_pool_connection_events_counter.add(1, attributes=attrs)
|
249
|
+
logger.debug(f"Connection detached from pool for engine: {engine_name}")
|
250
|
+
except Exception as e:
|
251
|
+
logger.info(f"Failed to record detach event metric: {e}")
|
252
|
+
|
253
|
+
@event.listens_for(pool, "reset")
|
254
|
+
def on_reset(dbapi_connection: DBAPIConnection, connection_record: ConnectionPoolEntry):
|
255
|
+
"""Called when a connection is reset."""
|
256
|
+
try:
|
257
|
+
from letta.otel.metric_registry import MetricRegistry
|
258
|
+
|
259
|
+
attrs = {
|
260
|
+
"engine_name": engine_name,
|
261
|
+
"event": "reset",
|
262
|
+
**get_ctx_attributes(),
|
263
|
+
}
|
264
|
+
MetricRegistry().db_pool_connection_events_counter.add(1, attributes=attrs)
|
265
|
+
logger.debug(f"Connection reset for engine: {engine_name}")
|
266
|
+
except Exception as e:
|
267
|
+
logger.info(f"Failed to record reset event metric: {e}")
|
268
|
+
|
269
|
+
# Note: dispatch is not a listenable event, it's a method for custom events
|
270
|
+
# If you need to track custom dispatch events, you would need to implement them separately
|
271
|
+
|
272
|
+
# noinspection PyProtectedMember
|
273
|
+
@staticmethod
|
274
|
+
def _get_pool_stats(pool: Pool) -> dict[str, Any]:
|
275
|
+
"""Get current pool statistics."""
|
276
|
+
stats = {
|
277
|
+
"total": 0,
|
278
|
+
"checked_out": 0,
|
279
|
+
"available": 0,
|
280
|
+
"overflow": None,
|
281
|
+
}
|
282
|
+
|
283
|
+
try:
|
284
|
+
if not isinstance(pool, QueuePool):
|
285
|
+
logger.info("Not currently supported for non-QueuePools")
|
286
|
+
|
287
|
+
stats["total"] = pool._pool.maxsize
|
288
|
+
stats["available"] = pool._pool.qsize()
|
289
|
+
stats["overflow"] = pool._overflow
|
290
|
+
stats["checked_out"] = stats["total"] - stats["available"]
|
291
|
+
|
292
|
+
except Exception as e:
|
293
|
+
logger.info(f"Failed to get pool stats: {e}")
|
294
|
+
return stats
|
295
|
+
|
296
|
+
|
297
|
+
# Global instance
|
298
|
+
_pool_monitor = DatabasePoolMonitor()
|
299
|
+
|
300
|
+
|
301
|
+
def get_pool_monitor() -> DatabasePoolMonitor:
|
302
|
+
"""Get the global database pool monitor instance."""
|
303
|
+
return _pool_monitor
|
304
|
+
|
305
|
+
|
306
|
+
def setup_pool_monitoring(engine: Engine | AsyncEngine, engine_name: str = "default") -> None:
|
307
|
+
"""Set up connection pool monitoring for the given engine."""
|
308
|
+
_pool_monitor.setup_monitoring(engine, engine_name)
|
letta/otel/metric_registry.py
CHANGED
@@ -3,6 +3,7 @@ from functools import partial
|
|
3
3
|
|
4
4
|
from opentelemetry import metrics
|
5
5
|
from opentelemetry.metrics import Counter, Histogram
|
6
|
+
from opentelemetry.metrics._internal import Gauge
|
6
7
|
|
7
8
|
from letta.helpers.singleton import singleton
|
8
9
|
from letta.otel.metrics import get_letta_meter
|
@@ -27,7 +28,7 @@ class MetricRegistry:
|
|
27
28
|
agent_id -1:N -> tool_name
|
28
29
|
"""
|
29
30
|
|
30
|
-
Instrument = Counter | Histogram
|
31
|
+
Instrument = Counter | Histogram | Gauge
|
31
32
|
_metrics: dict[str, Instrument] = field(default_factory=dict, init=False)
|
32
33
|
_meter: metrics.Meter = field(init=False)
|
33
34
|
|
@@ -180,3 +181,95 @@ class MetricRegistry:
|
|
180
181
|
unit="By",
|
181
182
|
),
|
182
183
|
)
|
184
|
+
|
185
|
+
# Database connection pool metrics
|
186
|
+
# (includes engine_name)
|
187
|
+
@property
|
188
|
+
def db_pool_connections_total_gauge(self) -> Gauge:
|
189
|
+
return self._get_or_create_metric(
|
190
|
+
"gauge_db_pool_connections_total",
|
191
|
+
partial(
|
192
|
+
self._meter.create_gauge,
|
193
|
+
name="gauge_db_pool_connections_total",
|
194
|
+
description="Total number of connections in the database pool",
|
195
|
+
unit="1",
|
196
|
+
),
|
197
|
+
)
|
198
|
+
|
199
|
+
# (includes engine_name)
|
200
|
+
@property
|
201
|
+
def db_pool_connections_checked_out_gauge(self) -> Gauge:
|
202
|
+
return self._get_or_create_metric(
|
203
|
+
"gauge_db_pool_connections_checked_out",
|
204
|
+
partial(
|
205
|
+
self._meter.create_gauge,
|
206
|
+
name="gauge_db_pool_connections_checked_out",
|
207
|
+
description="Number of connections currently checked out from the pool",
|
208
|
+
unit="1",
|
209
|
+
),
|
210
|
+
)
|
211
|
+
|
212
|
+
# (includes engine_name)
|
213
|
+
@property
|
214
|
+
def db_pool_connections_available_gauge(self) -> Gauge:
|
215
|
+
return self._get_or_create_metric(
|
216
|
+
"gauge_db_pool_connections_available",
|
217
|
+
partial(
|
218
|
+
self._meter.create_gauge,
|
219
|
+
name="gauge_db_pool_connections_available",
|
220
|
+
description="Number of available connections in the pool",
|
221
|
+
unit="1",
|
222
|
+
),
|
223
|
+
)
|
224
|
+
|
225
|
+
# (includes engine_name)
|
226
|
+
@property
|
227
|
+
def db_pool_connections_overflow_gauge(self) -> Gauge:
|
228
|
+
return self._get_or_create_metric(
|
229
|
+
"gauge_db_pool_connections_overflow",
|
230
|
+
partial(
|
231
|
+
self._meter.create_gauge,
|
232
|
+
name="gauge_db_pool_connections_overflow",
|
233
|
+
description="Number of overflow connections in the pool",
|
234
|
+
unit="1",
|
235
|
+
),
|
236
|
+
)
|
237
|
+
|
238
|
+
# (includes engine_name)
|
239
|
+
@property
|
240
|
+
def db_pool_connection_duration_ms_histogram(self) -> Histogram:
|
241
|
+
return self._get_or_create_metric(
|
242
|
+
"hist_db_pool_connection_duration_ms",
|
243
|
+
partial(
|
244
|
+
self._meter.create_histogram,
|
245
|
+
name="hist_db_pool_connection_duration_ms",
|
246
|
+
description="Duration of database connection usage in milliseconds",
|
247
|
+
unit="ms",
|
248
|
+
),
|
249
|
+
)
|
250
|
+
|
251
|
+
# (includes engine_name, event)
|
252
|
+
@property
|
253
|
+
def db_pool_connection_events_counter(self) -> Counter:
|
254
|
+
return self._get_or_create_metric(
|
255
|
+
"count_db_pool_connection_events",
|
256
|
+
partial(
|
257
|
+
self._meter.create_counter,
|
258
|
+
name="count_db_pool_connection_events",
|
259
|
+
description="Count of database connection pool events (connect, checkout, checkin, invalidate)",
|
260
|
+
unit="1",
|
261
|
+
),
|
262
|
+
)
|
263
|
+
|
264
|
+
# (includes engine_name, exception_type)
|
265
|
+
@property
|
266
|
+
def db_pool_connection_errors_counter(self) -> Counter:
|
267
|
+
return self._get_or_create_metric(
|
268
|
+
"count_db_pool_connection_errors",
|
269
|
+
partial(
|
270
|
+
self._meter.create_counter,
|
271
|
+
name="count_db_pool_connection_errors",
|
272
|
+
description="Count of database connection pool errors",
|
273
|
+
unit="1",
|
274
|
+
),
|
275
|
+
)
|