@@ -74,6 +74,8 @@ class LatestMetricsFetcher(ComponentMetricFetcher, Generic[T], ABC):
74
74
75
75
_receiver : Receiver [T ]
76
76
_max_waiting_time : float
77
+ _outage : bool
78
+ _last_log_time : datetime | None
77
79
78
80
@classmethod
79
81
async def async_new (
@@ -107,6 +109,8 @@ async def async_new(
107
109
# pylint: disable=protected-access
108
110
self ._receiver = await self ._subscribe ()
109
111
self ._max_waiting_time = MAX_BATTERY_DATA_AGE_SEC
112
+ self ._outage = False
113
+ self ._last_log_time = None
110
114
return self
111
115
112
116
async def fetch_next (self ) -> ComponentMetricsData | None :
@@ -120,15 +124,27 @@ async def fetch_next(self) -> ComponentMetricsData | None:
120
124
data = await asyncio .wait_for (
121
125
self ._receiver .receive (), self ._max_waiting_time
122
126
)
127
+ self ._outage = False
128
+ self ._last_log_time = None
123
129
124
130
except ChannelClosedError :
125
131
_logger .exception (
126
132
"Channel for component %d was closed." , self ._component_id
127
133
)
128
134
return None
129
135
except asyncio .TimeoutError :
130
- # Next time wait infinitely until we receive any message.
131
- _logger .debug ("Component %d stopped sending data." , self ._component_id )
136
+ now = datetime .now (tz = timezone .utc )
137
+ if (
138
+ not self ._outage
139
+ or not self ._last_log_time
140
+ or (now - self ._last_log_time ).total_seconds () > 60
141
+ ):
142
+ self ._outage = True
143
+ self ._last_log_time = now
144
+ _logger .warning (
145
+ "Component %d stopped sending data." ,
146
+ self ._component_id ,
147
+ )
132
148
return ComponentMetricsData (
133
149
self ._component_id , datetime .now (tz = timezone .utc ), {}
134
150
)
0 commit comments