pint
repository·main·Indexed 21 days ago
https://github.com/cloudflare/pintA specialized linter and validator for Prometheus rules designed to ensure rule files follow best practices and are syntactically correct. It provides a CLI with commands for linting, parsing PromQL queries, and continuous monitoring via a watch mode that exposes metrics via HTTP. Pint supports both online checks requiring Prometheus server connectivity and offline structural checks.
What's inside pint
- pint is a linter designed specifically for Prometheus rules. It helps ensure that Prometheus rule files follow best practices and are syntactically correct.
What is the alerts/comparison check?
mainThe
alerts/comparisoncheck enforces the use of comparison operators (e.g.,> 10or> 0) in alert queries.Without a comparison operator, an alerting rule query returns the metric series itself. Since any returned result triggers an alert, a query like
errorswill trigger an alert even when the value is0. Using a condition likeerrors > 10ensures alerts only trigger when the specific threshold is met.While some metrics (like
http_responses_total{code="504"}) might only exist in Prometheus when they have non-zero values, relying on this behavior is fragile. It is a best practice to always include a comparison operator to ensure predictable alerting behavior.Understand the promql/impossible check
mainThe
promql/impossiblecheck identifies PromQL queries that are logically guaranteed to return no results. This typically happens when operators or aggregations strip away labels required for matching, joining, or grouping.Common scenarios detected include:
- Label Mismatches in Set Operators: Using
unless,and, ororwhere the right-hand side has a different label set than the left-hand side (e.g., usingsum()on the right side removes all labels, making it impossible to match a left-hand side that has labels). - Aggregating on Missing Labels: Using
group byon labels that were already removed by an inner aggregation (e.g.,group by(cluster) (sum(errors))fails becausesum()removes theclusterlabel). - Joins on Missing Labels: Performing a join
on(label)where the label has been stripped by an aggregation on one or both sides. - Binary Aggregations blocking label propagation: Using
group_leftorgroup_rightin a way that prevents labels from propagating to the final result when combined with other operators (e.g., missing parentheses around a complex expression).
// Example of an impossible query: labels on LHS won't match empty labels on RHS foo{job="bar"} unless sum(foo) // Example of aggregating on a label that was already removed group by(cluster) ( sum(errors) ) // Example of a join on a label that was already removed sum(foo) / on(cluster) count(bar)- Label Mismatches in Set Operators: Using
Assign owners to rules using control comments
mainYou can use special comments to assign owners to rules, which allows you to route alerts based on the
ownerlabel in thepint_problemmetric.Requirements:
- Comments must start with
# pint(with a space). - Use
# pint file/owner <owner>to set an owner for an entire file. - Use
# pint rule/owner <owner>to set an owner for a specific rule.
Example:
# pint file/owner bob - alert: AlertName expr: up == 0 # pint rule/owner alice - alert: OtherAlert expr: up == 1- Comments must start with
What the `promql/range_query` check does
mainThe
promql/range_querycheck inspects range query selectors (e.g.,foo[40d]) in PromQL queries. It warns if a query requests a time range that exceeds the configured Prometheus retention limits.By default, Pint checks the
--storage.tsdb.retention.timeflag passed to Prometheus. If Prometheus is configured with--storage.tsdb.retention.time=30d, a query likefoo[40d]will trigger a warning because the query can only ever return up to 30 days of data, potentially leading to a mismatch between user expectations and actual results.Understand the `rule/dependency` check
mainThe
rule/dependencycheck validates Prometheus rule dependencies to prevent broken metrics or data inconsistency. It covers two main scenarios:- Removed dependencies: When running
pint ci, it ensures that if you delete a recording rule, it isn't still being used as a dependency by other rules. Deleting a dependency without updating its consumers will cause those consumers to fail. - Cross-group dependencies: It warns when a recording rule in one group uses a metric produced by a recording rule in a different group. Because groups run at different times, the dependent rule will see stale data (up to one evaluation interval of lag), causing metric mismatches.
Best Practice: To avoid lag and ensure consistency, move dependent recording rules into the same group so they execute sequentially within the same evaluation cycle.
- Removed dependencies: When running
Understand the alerts/external_labels check
mainThe
alerts/external_labelscheck ensures that Prometheus alerting rules referencing$externalLabelsvariables actually correspond to labels configured in the Prometheus server'sglobal:external_labelssection.If an alert rule uses a variable like
{{ $externalLabels.cluster }}butclusteris not defined in the Prometheus server'sglobal:external_labelsconfiguration, this check will report an error because the variable will resolve to an empty value.# Prometheus configuration global: external_labels: cluster: mycluster # Alert rule using the label - alert: Abc Is Down expr: up{job="abc"} == 0 annotations: summary: "{{ $labels.job }} is down in {{ $externalLabels.cluster }} cluster"What the promql/features check does
mainThe
promql/featurescheck ensures that PromQL queries do not use experimental features that require specific--enable-feature=...flags which are not currently enabled on the target Prometheus server.To perform this validation, Pint:
- Queries the Prometheus flags API to retrieve enabled feature flags.
- Queries the build info API to check the Prometheus version against feature requirements.
Currently monitored features include:
promql-experimental-functions: Required for functions likemad_over_time,sort_by_label,sort_by_label_desc,info,double_exponential_smoothing,min_of,max_of,start,end,range,step,limitk, andlimit_ratio.promql-duration-expr: Required for arithmetic expressions in time durations.promql-extended-range-selectors: Required foranchoredorsmoothedrange selector modifiers.promql-binop-fill-modifiers: Required for thefill()binary operator modifier.
Understand the promql/counter check
mainThe
promql/countercheck identifies Prometheus alerting rules that use counter metrics incorrectly.The Concept: Counters track the number of events over time and can only grow (until they overflow or the service restarts and resets to zero). Because the absolute value of a counter is essentially a random number depending on the application's uptime, using a raw counter in an expression like
errors_total > 10is usually invalid for alerting. Such an alert would fire once the threshold is hit and stay firing until a restart, regardless of whether errors are still occurring.The Correct Pattern: To track the health of an application in real-time, you should use counter-safe functions like
rate()to calculate the frequency of events over a specific time window. This ensures that if the error rate drops, the alert stops firing.Example of an invalid rule:
- alert: Too many errors expr: errors_total > 10Example of a valid rule:
- alert: Too many errors expr: rate(errors_total[1h]) > 10Ignored cases for the promql/series check
mainThe
promql/seriescheck ignores certain query patterns to avoid false positives:vector() fallback
Metrics wrapped in
... or vector(0)are ignored because theor vector(0)pattern is explicitly intended to provide a fallback value when no time series match.- alert: Foo expr: sum(my_metric or vector(0)) > 1foo unless bar
In a query using
foo unless bar, only thefooselector is tested. The check assumes the presence ofbaris not guaranteed, so it only verifies ifbarexists rather than checking if it matches the query's intent.Note: If the query is
foo unless bar > 5, bothfooandbarwill be tested because the presence of a value comparison (> 5) implies the metric is assumed to be present.Understand the `ignore/file` informational report
mainWhen a file is excluded from Pint using an
ignore/filecomment, Pint will still report on that file. These reports are purely informational and serve to make it obvious that Pint did not run any actual checks on the specified file.For details on how to use exclusion comments, refer to the ignoring documentation.
What the promql/rate check inspects
mainThe
promql/ratecheck analyzesrate()andirate()function calls in PromQL queries to ensure they are used correctly. It validates three main criteria:- Valid Time Durations: It ensures range queries use a duration of at least 2x the global
scrape_intervalof the selected Prometheus servers. This is necessary because Prometheus requires at least two samples to calculate a rate. - Counter Metric Usage: It verifies that metrics passed to
rate()orirate()are counters. Using non-counter metrics (like gauges) can lead to incorrect results due to how counter overflows are handled. For gauge metrics, usedelta()orderiv()instead. - Correct Chaining: It checks that
rate()is not called on the result ofsum(counter), as this produces invalid results. It specifically looks for the patternrate(sum(...))and flags it unless the metric is produced via recording rules.
Note on Metadata Mismatches: Metric type checks rely on the Prometheus metadata API. If a metric name is exported with multiple different types across different targets, or if a type change hasn't propagated to all targets yet, the check may report errors due to ambiguous metadata.
- Valid Time Durations: It ensures range queries use a duration of at least 2x the global