1818
1919import asyncio
2020import atexit
21- import logging
2221import time
2322import weakref
2423from typing import TYPE_CHECKING , Any , Optional
2524
2625from pymongo import common , periodic_executor
2726from pymongo ._csot import MovingMinimum
27+ from pymongo ._telemetry import _HeartbeatTelemetry , _monotonic_duration , log_srv_monitor_failure
2828from pymongo .asynchronous .srv_resolver import _SrvResolver
2929from pymongo .errors import NetworkTimeout , _OperationCancelled
3030from pymongo .hello import Hello
3131from pymongo .lock import _async_create_lock
32- from pymongo .logger import _SDAM_LOGGER , _debug_log , _SDAMStatusMessage
3332from pymongo .periodic_executor import _shutdown_executors
3433from pymongo .pool_options import _is_faas
3534from pymongo .read_preferences import MovingAverage
@@ -54,15 +53,6 @@ def _sanitize(error: Exception) -> None:
5453 error .__cause__ = None
5554
5655
57- def _monotonic_duration (start : float ) -> float :
58- """Return the duration since the given start time.
59-
60- Accounts for buggy platforms where time.monotonic() is not monotonic.
61- See PYTHON-4600.
62- """
63- return max (0.0 , time .monotonic () - start )
64-
65-
6656class MonitorBase :
6757 def __init__ (self , topology : Topology , name : str , interval : int , min_interval : float ):
6858 """Base class to do periodic work on a background thread.
@@ -151,9 +141,10 @@ def __init__(
151141 self ._pool = pool
152142 self ._settings = topology_settings
153143 self ._listeners = self ._settings ._pool_options ._event_listeners
154- self ._publish = self ._listeners is not None and self ._listeners .enabled_for_server_heartbeat
155144 self ._cancel_context : Optional [_CancellationContext ] = None
156145 self ._conn_id : Optional [int ] = None
146+ self ._current_hb : Optional [_HeartbeatTelemetry ] = None
147+ self ._awaited : bool = False
157148 self ._rtt_monitor = _RttMonitor (
158149 topology ,
159150 topology_settings ,
@@ -257,32 +248,17 @@ async def _check_server(self) -> ServerDescription:
257248 Returns a ServerDescription.
258249 """
259250 self ._conn_id = None
260- start = time .monotonic ()
251+ self ._current_hb = None
252+ self ._awaited = False
261253 try :
262254 return await self ._check_once ()
263255 except ReferenceError :
264256 raise
265257 except Exception as error :
266258 _sanitize (error )
267- sd = self ._server_description
268- address = sd .address
269- duration = _monotonic_duration (start )
270- awaited = bool (self ._stream and sd .is_server_type_known and sd .topology_version )
271- if self ._publish :
272- assert self ._listeners is not None
273- self ._listeners .publish_server_heartbeat_failed (address , duration , error , awaited )
274- if _SDAM_LOGGER .isEnabledFor (logging .DEBUG ):
275- _debug_log (
276- _SDAM_LOGGER ,
277- message = _SDAMStatusMessage .HEARTBEAT_FAIL ,
278- topologyId = self ._topology ._topology_id ,
279- serverHost = address [0 ],
280- serverPort = address [1 ],
281- awaited = awaited ,
282- durationMS = duration * 1000 ,
283- failure = error ,
284- driverConnectionId = self ._conn_id ,
285- )
259+ address = self ._server_description .address
260+ if self ._current_hb is not None :
261+ self ._current_hb .failed (error , self ._conn_id , self ._awaited )
286262 await self ._reset_connection ()
287263 if isinstance (error , _OperationCancelled ):
288264 raise
@@ -300,28 +276,17 @@ async def _check_once(self) -> ServerDescription:
300276
301277 # XXX: "awaited" could be incorrectly set to True in the rare case
302278 # the pool checkout closes and recreates a connection.
303- awaited = bool (
279+ self . _awaited = bool (
304280 self ._pool .conns and self ._stream and sd .is_server_type_known and sd .topology_version
305281 )
306- if self ._publish :
307- assert self ._listeners is not None
308- self . _listeners . publish_server_heartbeat_started ( address , awaited )
282+ hb = _HeartbeatTelemetry ( self ._topology . _topology_id , address , self . _listeners )
283+ self ._current_hb = hb
284+ hb . started ( self . _awaited )
309285
310286 if self ._cancel_context and self ._cancel_context .cancelled :
311287 await self ._reset_connection ()
312288 async with self ._pool .checkout () as conn :
313- if _SDAM_LOGGER .isEnabledFor (logging .DEBUG ):
314- _debug_log (
315- _SDAM_LOGGER ,
316- message = _SDAMStatusMessage .HEARTBEAT_START ,
317- topologyId = self ._topology ._topology_id ,
318- driverConnectionId = conn .id ,
319- serverConnectionId = conn .server_connection_id ,
320- serverHost = address [0 ],
321- serverPort = address [1 ],
322- awaited = awaited ,
323- )
324-
289+ hb .emit_started_log (conn .id , conn .server_connection_id , self ._awaited )
325290 self ._cancel_context = conn .cancel_context
326291 # Record the connection id so we can later attach it to the failed log message.
327292 self ._conn_id = conn .id
@@ -331,24 +296,7 @@ async def _check_once(self) -> ServerDescription:
331296
332297 avg_rtt , min_rtt = await self ._rtt_monitor .get ()
333298 sd = ServerDescription (address , response , avg_rtt , min_round_trip_time = min_rtt )
334- if self ._publish :
335- assert self ._listeners is not None
336- self ._listeners .publish_server_heartbeat_succeeded (
337- address , round_trip_time , response , response .awaitable
338- )
339- if _SDAM_LOGGER .isEnabledFor (logging .DEBUG ):
340- _debug_log (
341- _SDAM_LOGGER ,
342- message = _SDAMStatusMessage .HEARTBEAT_SUCCESS ,
343- topologyId = self ._topology ._topology_id ,
344- driverConnectionId = conn .id ,
345- serverConnectionId = conn .server_connection_id ,
346- serverHost = address [0 ],
347- serverPort = address [1 ],
348- awaited = awaited ,
349- durationMS = round_trip_time * 1000 ,
350- reply = response .document ,
351- )
299+ hb .succeeded (round_trip_time , response , conn .id , conn .server_connection_id )
352300 return sd
353301
354302 async def _check_with_socket (self , conn : AsyncConnection ) -> tuple [Hello , float ]: # type: ignore[type-arg]
@@ -429,7 +377,7 @@ async def _get_seedlist(self) -> Optional[list[tuple[str, Any]]]:
429377 # - SRV records must be rescanned every heartbeatFrequencyMS
430378 # - Topology must be left unchanged
431379 self .request_check ()
432- _debug_log ( _SDAM_LOGGER , message = "SRV monitor check failed" , failure = repr ( exc ) )
380+ log_srv_monitor_failure ( exc )
433381 return None
434382 else :
435383 self ._executor .update_interval (max (ttl , common .MIN_SRV_RESCAN_INTERVAL ))
0 commit comments