> ## Documentation Index
> Fetch the complete documentation index at: https://filament.getgalaxy.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Metrics

> Query run totals and time series for dashboards and reports

Use `metrics.v1.MetricsService` to answer questions such as “how many records
moved today?” and “how has the failure count changed by hour?” The built-in
dashboard uses the same two read-only RPCs. Their messages are defined in
`protos/metrics/v1/metrics.proto`.

The service is backed by the same runs table the
[ingestion API](/pages/api-reference/service/runs) writes, through the store
selected by `METRICSSTORE_PROVIDER` (default `postgres`). When no metrics
store is configured, both RPCs return `unimplemented`.

## RPCs

| RPC               | Description                                                    |
| ----------------- | -------------------------------------------------------------- |
| `QueryTimeseries` | Return hourly or daily values for charts                       |
| `QueryAggregate`  | Return one value per metric and group for totals or stat cards |

## Metrics

| Metric                    | Aggregation                                      |
| ------------------------- | ------------------------------------------------ |
| `METRIC_RUN_COUNT`        | Sum of runs                                      |
| `METRIC_RUN_RECORDS`      | Sum of records moved                             |
| `METRIC_RUN_BYTES`        | Sum of bytes moved                               |
| `METRIC_RUN_DURATION`     | Mean duration in milliseconds over terminal runs |
| `METRIC_RUN_MEMORY_USAGE` | Mean peak working set in bytes                   |
| `METRIC_RUN_CPU_USAGE`    | Mean CPU seconds                                 |

CPU and memory averages exclude runs that did not report those values. For
example, a run outside a cgroup does not reduce the average by contributing a
zero.

## QueryTimeseries

Provide:

* `tenant_id`: `""` matches every tenant, mirroring `ListRuns`
* `metrics`: a repeated list computed together per bucket
* `since_ms`/`until_ms`: the time window, inclusive / exclusive (`until_ms`
  unset means now)
* `granularity`: `HOUR` or `DAY`
* `tz_offset_minutes`: the viewer's offset east of UTC, which shifts bucket
  boundaries so `DAY` buckets match the local calendar day

`group_by` splits the result into one series per value of a dimension
(`TENANT_ID`, `PIPELINE_ID`, or `STATUS`). Unspecified returns a single total
series with key `""`. `filters` narrow the input rows, with values given as
dimension-value strings (a pipeline id, or a `RunStatus` enum number such as
`"3"` for `COMPLETED`). When a filter covers the `group_by` dimension, the
response returns one series per requested value in request order.

Every series contains the same ordered set of buckets. Missing values are
filled with zero, so points at the same index always refer to the same hour or
day. Values follow the order of `request.metrics`.

```sh theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
curl -X POST http://localhost:8080/metrics.v1.MetricsService/QueryTimeseries \
  -H "Content-Type: application/json" \
  -d '{
    "metrics": ["METRIC_RUN_COUNT", "METRIC_RUN_BYTES"],
    "sinceMs": 1755561600000,
    "granularity": "METRIC_GRANULARITY_DAY",
    "tzOffsetMinutes": -420,
    "groupBy": "METRIC_DIMENSION_STATUS"
  }'
```

A shortened response looks like this:

```json theme={"theme":{"light":"github-light-default","dark":"github-dark-default"}}
{
  "series": [{
    "key": "3",
    "points": [
      {"timestampMs": "1755561600000", "values": [12, 84520]}
    ]
  }]
}
```

Here `key` is the grouped status value, and the two values correspond to run
count and bytes in the order requested.

## QueryAggregate

Use the same fields except `granularity` and `tz_offset_minutes`. The response
contains one row per group for the entire window, with values in the same order
as `metrics`. Use this RPC for totals and stat cards instead of charts.
