Reporting#
ECLYPSE provides a reporting pipeline with three moving parts:
Define what to report through metrics,
choose how to persist it through report formats and reporters,
load and query the results through
Report.
Defining Metrics#
Metrics are reportable events that collect and return structured data during the simulation.
Technically, they are events with the
METRIC role, exposed through the
@metric.<type> decorators provided in eclypse.report.metrics.
Metrics, callbacks, and regular events all share the same event engine, but they serve different purposes:
regular events drive the workflow,
callbacks attach post-event logic without making reporting the primary goal,
metrics attach post-event logic whose output is intended to be written by reporters.
This distinction matters because a metric is both:
a post-event hook, so it can access the triggering event payload,
a reporting primitive, so its return value is prepared for the reporting pipeline.
By contrast, a plain callback can inspect the same context, but it is mainly meant for workflow composition or transient post-processing.
There are 7 decorators corresponding to different metric types:
See Scheduled event decorators for details on scheduled event helpers. A metric lets you specify:
What data to collect
How often to report (using triggers)
How to report it (via the report argument)
Example:
from eclypse.report.metrics import metric
@metric.application(activates_on=["step"], report=["csv", "json"])
def my_metric(application, placement, infrastructure):
return len(application.nodes)
Note
Metrics are executed like events, and use the same underlying logic, including support for cascade triggers and trigger conditions.
Custom metric recipe#
Use the metric decorator that matches the object you want to inspect, choose when it activates, and return either a scalar or a mapping.
from eclypse.report.metrics import metric
@metric.service(activates_on="step", report=["csv"])
def requested_cpu(service_id, requirements, placement, infrastructure):
return {
"service": service_id,
"value": requirements["cpu"],
}
The reporting pipeline records the event metadata and stores the returned
value. For multi-field mappings, keep a stable value key for the main
measurement and use the other keys as context.
Metric and callback types#
The metric decorators mirror the same event types used by regular events. The event type still determines what your logic receives:
simulationmetrics receivetriggering_event,applicationmetrics receiveapplication,placement, andinfrastructure,servicemetrics receiveservice_id,requirements,placement, andinfrastructure,servicemetrics withremote=Truereceive a remoteServiceinstance,interactionmetrics receivesource_id,target_id,requirements,placement, andinfrastructure,infrastructuremetrics receiveinfrastructureandplacement_view,nodemetrics receivenode_id,resources,placements,infrastructure, andplacement_view,linkmetrics receivesource_id,target_id,resources,placements,infrastructure, andplacement_view.
Callbacks follow the same type-based signatures. The difference is therefore not the arguments they receive, but the role they play in the workflow and whether their output is meant to be reported.
Report formats and reporters#
The report argument on an event or metric selects one or more reporter
types. Built-in reporters cover the most common outputs:
csvfor human-readable tabular files,jsonfor JSONL outputs,parquetfor analytical workloads and larger runs,gmlfor graph exports,tensorboardfor TensorBoard-compatible metrics.
You can also implement your own
Reporter subclass if you want to persist data
to a different sink such as a database or a live dashboard.
Default Reporters#
ECLYPSE includes a set of built-in reporters:
In most cases you do not instantiate them manually. Instead, you select the desired reporter types through metric/event configuration and let the simulation resolve the built-in reporters automatically.
Important
When implementing custom reporters that write to the filesystem, use aiofiles for asynchronous file operations. This keeps reporting from blocking the simulation loop.
import aiofiles
async with aiofiles.open(self.report_path / "data.csv", "a") as f:
await f.write("some,data,to,write\n")
Choosing a report backend#
The Report class can use multiple dataframe
backends:
pandaspolarspolars_lazy
Choose the backend through
SimulationConfig or directly when loading a
report from disk.
Accessing reports#
After the simulation, you can access the results using the
Report object.
Example usage:
from eclypse.report import Report
report = Report("./output")
df = report.service(application_ids="app1", service_ids="srv2")
For a quick run summary, call
describe():
print(report.describe())
# 84 rows x 10 steps x 12 metrics | 5 applications | ...
Each accessor method supports filtering by:
report_range (e.g., only events between 10 and 100)
report_step (e.g., one point every N events)
event IDs, application IDs, node/service/link IDs, etc.