Skip to content

Commit

Permalink
Small debugging enhancements (#1119)
Browse files Browse the repository at this point in the history
- **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**
  • Loading branch information
Marenz authored Nov 28, 2024
2 parents 9cec69a + d46efcf commit 5d337b6
Show file tree
Hide file tree
Showing 7 changed files with 51 additions and 6 deletions.
4 changes: 3 additions & 1 deletion RELEASE_NOTES.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,9 @@

## New Features

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

## Bug Fixes

Expand Down
3 changes: 2 additions & 1 deletion src/frequenz/sdk/microgrid/_data_pipeline.py
Original file line number Diff line number Diff line change
Expand Up @@ -479,7 +479,8 @@ def _resampling_request_sender(self) -> Sender[ComponentMetricRequest]:
channel_registry=self._channel_registry,
data_sourcing_request_sender=self._data_sourcing_request_sender(),
resampling_request_receiver=channel.new_receiver(
limit=_REQUEST_RECV_BUFFER_SIZE
limit=_REQUEST_RECV_BUFFER_SIZE,
name=channel.name + " Receiver",
),
config=self._resampler_config,
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -461,7 +461,8 @@ async def _update_streams(
self.comp_data_tasks[comp_id].cancel()

self.comp_data_tasks[comp_id] = asyncio.create_task(
run_forever(lambda: self._handle_data_stream(comp_id, category))
run_forever(lambda: self._handle_data_stream(comp_id, category)),
name=f"{type(self).__name__}._update_stream({comp_id=}, {category.name})",
)

async def add_metric(self, request: ComponentMetricRequest) -> None:
Expand Down
6 changes: 5 additions & 1 deletion src/frequenz/sdk/microgrid/_resampling.py
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,11 @@ def _log_resampling_task_error(self, resampling_task: asyncio.Task[None]) -> Non
resampling_task.result()
except ResamplingError as error:
for source, source_error in error.exceptions.items():
_logger.error("Error resampling source %s, removing source...", source)
_logger.error(
"Error resampling source %s, removing source",
source,
exc_info=source_error,
)
removed = self._resampler.remove_timeseries(source)
if not removed:
_logger.error(
Expand Down
8 changes: 8 additions & 0 deletions src/frequenz/sdk/timeseries/_base_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ class Sample(Generic[QuantityT]):
value: QuantityT | None = None
"""The value of this sample."""

def __str__(self) -> str:
"""Return a string representation of the sample."""
return f"{type(self).__name__}({self.timestamp}, {self.value})"

def __repr__(self) -> str:
"""Return a string representation of the sample."""
return f"{type(self).__name__}({self.timestamp=}, {self.value=})"


@dataclass(frozen=True)
class Sample3Phase(Generic[QuantityT]):
Expand Down
31 changes: 30 additions & 1 deletion src/frequenz/sdk/timeseries/_resampling.py
Original file line number Diff line number Diff line change
Expand Up @@ -744,14 +744,43 @@ def resample(self, timestamp: datetime) -> Sample[Quantity]:
# resort to some C (or similar) implementation.
relevant_samples = list(itertools.islice(self._buffer, min_index, max_index))
if not relevant_samples:
_logger.warning("No relevant samples found for: %s", self._name)
self._log_no_relevant_samples(minimum_relevant_timestamp, timestamp)

value = (
conf.resampling_function(relevant_samples, conf, props)
if relevant_samples
else None
)
return Sample(timestamp, None if value is None else Quantity(value))

def _log_no_relevant_samples(
self, minimum_relevant_timestamp: datetime, timestamp: datetime
) -> None:
"""Log that no relevant samples were found.
Args:
minimum_relevant_timestamp: Minimum timestamp that was requested
timestamp: Timestamp that was requested
"""
if not _logger.isEnabledFor(logging.WARNING):
return

if self._buffer:
buffer_info = (
f"{self._buffer[0].timestamp} - "
f"{self._buffer[-1].timestamp} ({len(self._buffer)} samples)"
)
else:
buffer_info = "Empty"

_logger.warning(
"No relevant samples found for: %s\n Requested: %s - %s\n Buffer: %s",
self._name,
minimum_relevant_timestamp,
timestamp,
buffer_info,
)


class _StreamingHelper:
"""Resample data coming from a source, sending the results to a sink."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@


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

def generate(
self,
Expand Down

0 comments on commit 5d337b6

Please sign in to comment.