Instruction file imported from winsenlabs/platos (
.cursor/rules/otel-metrics.mdc). Copyright stays with the author.
OpenTelemetry Metrics Guidelines
When creating or editing OTEL metrics (counters, histograms, gauges), always ensure metric attributes have low cardinality.
What is Cardinality?
Cardinality refers to the number of unique values an attribute can have. Each unique combination of attribute values creates a new time series, which consumes memory and storage in your metrics backend.
Rules
DO use low-cardinality attributes:
- Enums:
environment_type(PRODUCTION, STAGING, DEVELOPMENT, PREVIEW) - Booleans:
hasFailures,streaming,success - Bounded error codes: A finite, controlled set of error types
- Shard IDs: When sharding is bounded (e.g., 0-15)
DO NOT use high-cardinality attributes:
- UUIDs/IDs:
envId,userId,runId,projectId,organizationId - Unbounded integers:
itemCount,batchSize,retryCount - Timestamps:
createdAt,startTime - Free-form strings:
errorMessage,taskName,queueName
Example
// BAD - High cardinality
this.counter.add(1, {
envId: options.environmentId, // UUID - unbounded
itemCount: options.runCount, // Integer - unbounded
});
// GOOD - Low cardinality
this.counter.add(1, {
environment_type: options.environmentType, // Enum - 4 values
streaming: true, // Boolean - 2 values
});
Prometheus Metric Naming
When metrics are exported via OTLP to Prometheus, the exporter automatically adds unit suffixes to metric names:
| OTel Metric Name | Unit | Prometheus Name |
|---|---|---|
my_duration_ms |
ms |
my_duration_ms_milliseconds |
my_counter |
counter | my_counter_total |
items_inserted |
counter | items_inserted_inserts_total |
batch_size |
histogram | batch_size_items_bucket |
Keep this in mind when writing Grafana dashboards or Prometheus queries—the metric names in Prometheus will differ from the names defined in code.
Reference
See the schedule engine (internal-packages/schedule-engine/src/engine/index.ts) for a good example of low-cardinality metric attributes.
High cardinality metrics can cause:
- Memory bloat in metrics backends (Axiom, Prometheus, etc.)
- Slow queries and dashboard timeouts
- Increased costs (many backends charge per time series)
- Potential data loss or crashes at scale
