Skip to content

Commit 5d337b6

Browse files
authoredNov 28, 2024··
Small debugging enhancements (#1119)
- **Fix wrong term in battery formula documentation** - **Give more receivers/tasks a name for easier debugging** - **Add a nice str representation for `Sample`** - **Actually log the error that caused a source to be removed** - **Add more details to "no relevant sample found" warning log** - **Add change log**
2 parents 9cec69a + d46efcf commit 5d337b6

File tree

7 files changed

+51
-6
lines changed

7 files changed

+51
-6
lines changed
 

‎RELEASE_NOTES.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010

1111
## New Features
1212

13-
<!-- Here goes the main new features and examples or instructions on how to use them -->
13+
* Many tasks, senders and receivers now have proper names for easier debugging.
14+
* The resample log was improved to show more details.
15+
* The `Sample` class now has a nice `__str__` representation.
1416

1517
## Bug Fixes
1618

‎src/frequenz/sdk/microgrid/_data_pipeline.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -479,7 +479,8 @@ def _resampling_request_sender(self) -> Sender[ComponentMetricRequest]:
479479
channel_registry=self._channel_registry,
480480
data_sourcing_request_sender=self._data_sourcing_request_sender(),
481481
resampling_request_receiver=channel.new_receiver(
482-
limit=_REQUEST_RECV_BUFFER_SIZE
482+
limit=_REQUEST_RECV_BUFFER_SIZE,
483+
name=channel.name + " Receiver",
483484
),
484485
config=self._resampler_config,
485486
)

‎src/frequenz/sdk/microgrid/_data_sourcing/microgrid_api_source.py

+2-1
Original file line numberDiff line numberDiff line change
@@ -461,7 +461,8 @@ async def _update_streams(
461461
self.comp_data_tasks[comp_id].cancel()
462462

463463
self.comp_data_tasks[comp_id] = asyncio.create_task(
464-
run_forever(lambda: self._handle_data_stream(comp_id, category))
464+
run_forever(lambda: self._handle_data_stream(comp_id, category)),
465+
name=f"{type(self).__name__}._update_stream({comp_id=}, {category.name})",
465466
)
466467

467468
async def add_metric(self, request: ComponentMetricRequest) -> None:

‎src/frequenz/sdk/microgrid/_resampling.py

+5-1
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,11 @@ def _log_resampling_task_error(self, resampling_task: asyncio.Task[None]) -> Non
179179
resampling_task.result()
180180
except ResamplingError as error:
181181
for source, source_error in error.exceptions.items():
182-
_logger.error("Error resampling source %s, removing source...", source)
182+
_logger.error(
183+
"Error resampling source %s, removing source",
184+
source,
185+
exc_info=source_error,
186+
)
183187
removed = self._resampler.remove_timeseries(source)
184188
if not removed:
185189
_logger.error(

‎src/frequenz/sdk/timeseries/_base_types.py

+8
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,14 @@ class Sample(Generic[QuantityT]):
3434
value: QuantityT | None = None
3535
"""The value of this sample."""
3636

37+
def __str__(self) -> str:
38+
"""Return a string representation of the sample."""
39+
return f"{type(self).__name__}({self.timestamp}, {self.value})"
40+
41+
def __repr__(self) -> str:
42+
"""Return a string representation of the sample."""
43+
return f"{type(self).__name__}({self.timestamp=}, {self.value=})"
44+
3745

3846
@dataclass(frozen=True)
3947
class Sample3Phase(Generic[QuantityT]):

‎src/frequenz/sdk/timeseries/_resampling.py

+30-1
Original file line numberDiff line numberDiff line change
@@ -744,14 +744,43 @@ def resample(self, timestamp: datetime) -> Sample[Quantity]:
744744
# resort to some C (or similar) implementation.
745745
relevant_samples = list(itertools.islice(self._buffer, min_index, max_index))
746746
if not relevant_samples:
747-
_logger.warning("No relevant samples found for: %s", self._name)
747+
self._log_no_relevant_samples(minimum_relevant_timestamp, timestamp)
748+
748749
value = (
749750
conf.resampling_function(relevant_samples, conf, props)
750751
if relevant_samples
751752
else None
752753
)
753754
return Sample(timestamp, None if value is None else Quantity(value))
754755

756+
def _log_no_relevant_samples(
757+
self, minimum_relevant_timestamp: datetime, timestamp: datetime
758+
) -> None:
759+
"""Log that no relevant samples were found.
760+
761+
Args:
762+
minimum_relevant_timestamp: Minimum timestamp that was requested
763+
timestamp: Timestamp that was requested
764+
"""
765+
if not _logger.isEnabledFor(logging.WARNING):
766+
return
767+
768+
if self._buffer:
769+
buffer_info = (
770+
f"{self._buffer[0].timestamp} - "
771+
f"{self._buffer[-1].timestamp} ({len(self._buffer)} samples)"
772+
)
773+
else:
774+
buffer_info = "Empty"
775+
776+
_logger.warning(
777+
"No relevant samples found for: %s\n Requested: %s - %s\n Buffer: %s",
778+
self._name,
779+
minimum_relevant_timestamp,
780+
timestamp,
781+
buffer_info,
782+
)
783+
755784

756785
class _StreamingHelper:
757786
"""Resample data coming from a source, sending the results to a sink."""

‎src/frequenz/sdk/timeseries/formula_engine/_formula_generators/_battery_power_formula.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424

2525

2626
class BatteryPowerFormula(FormulaGenerator[Power]):
27-
"""Creates a formula engine from the component graph for calculating grid power."""
27+
"""Creates a formula engine from the component graph for calculating battery power."""
2828

2929
def generate(
3030
self,

0 commit comments

Comments
 (0)
Please sign in to comment.