# Application metrics > https://docs.errora.org/en/products/metrics Metrics are the numbers your app produces that aren't errors: orders placed, queue depth, checkout latency, unique visitors. Errora accepts both wire formats Sentry SDKs emit. ## Metric types | Type | Use it for | Aggregates | | --- | --- | --- | | **counter** (`c`) | Things that happen | `sum`, `count` | | **distribution** (`d`) | Measured values (latency, size) | `avg`, `min`, `max`, `p50`–`p99` | | **gauge** (`g`) | A level at a point in time | `last`, `min`, `max`, `avg` | | **set** (`s`) | Distinct things | `count_unique` | ## Setup — SDK ```javascript Sentry.metrics.increment("checkout.attempt", 1, { tags: { plan: "pro" } }); Sentry.metrics.distribution("checkout.latency", 421, { unit: "millisecond", tags: { route: "/pay" } }); Sentry.metrics.gauge("queue.depth", 128, { tags: { queue: "ingest" } }); Sentry.metrics.set("visitors", userId); ``` ```python sentry_sdk.metrics.incr("orders.created", 1, tags={"plan": "pro"}) sentry_sdk.metrics.distribution("orders.total", order.total, unit="none") sentry_sdk.metrics.gauge("queue.depth", depth, tags={"queue": "ingest"}) sentry_sdk.metrics.set("visitors", user_id) ``` SDKs **pre-aggregate** before sending: what arrives is a bucket per metric per time window, not one message per call. Distributions keep a sample of the raw values, so percentiles stay answerable, and sets count distinct members as a union across buckets rather than a sum. ## Setup — raw wire formats **1. `trace_metric` items** (current SDKs) — the same batch container as logs: ```json {"items":[{"timestamp":1767225600.0,"name":"checkout.latency","type":"distribution", "value":42.0,"unit":"millisecond","trace_id":"7b3f…", "attributes":{"route":{"value":"/pay","type":"string"}}}]} ``` **2. statsd text** (the original protocol), newline-delimited: ```text [@]:[:…]||#:,…|T endpoint.response_time@millisecond:57:92|d|#route:/pay,method:POST|T1615889449 button_click@none:1|c|#browser:firefox ``` A statsd **gauge with exactly five values** is the pre-aggregated `last:min:max:sum:count` form, not five readings. Limits per batch: 1000 items, 30 tags per metric, 200 characters per name or tag value. ## Querying **Metrics** in the sidebar lists the metric names a project has sent. Pick one and choose: - an **aggregate** — `sum`, `avg`, `min`, `max`, `p50`, `p75`, `p90`, `p95`, `p99`, `count_unique` - a **group-by tag**, to split the series - an **environment** and a **time period** `avg` is always the true `sum / count` over the window, never the average of per-bucket averages. ```text GET /api/v1/projects/{project_id}/metrics?q=&stats_period=24h GET /api/v1/projects/{project_id}/metrics/series?name=&op=&group_by=&environment=&stats_period=24h GET /api/v1/projects/{project_id}/metrics/summary?name=&environment=&stats_period=24h GET /api/v1/projects/{project_id}/metrics/tags?name=&stats_period=24h ``` ## Naming that stays useful - `domain.thing.measure` — `checkout.latency`, `queue.depth`, `sms.sent`. - Put varying values in **tags**, never in the name. `orders.created` tagged `plan:pro` beats `orders.created.pro`. - Keep tag cardinality low: `route` yes, `user_id` no. Every distinct tag combination is its own bucket, and you are billed per metric bucket. ## Dashboards Metrics are a [dashboard](/en/products/dashboards) dataset — put a metric series next to your error counts and transaction latency on the same board. ## Troubleshooting | Symptom | Cause / fix | | --- | --- | | Metric name missing from the list | Nothing has arrived in the selected period — widen it | | Percentiles look approximate | A bucket kept a bounded sample (100 values); that is by design | | `count_unique` lower than expected | Set members are hashed and bounded per bucket (500) | | Tag missing | It exceeded the 30-tag or 200-character limit and was dropped | | Costs climbing | Tag cardinality — one high-cardinality tag multiplies your bucket count |