Skip to content

Emit a quality score to Prometheus

Surface a /metrics endpoint that exposes one counter for failures and one gauge for the latest score per contract. Requires pip install pycharter[server].

from http.server import BaseHTTPRequestHandler, HTTPServer

from pycharter import (
    QualityPrometheusObserver,
    QualityThresholds,
    SlackWebhookSink,
    check_quality,
    notify_on_breach,
)

observer = QualityPrometheusObserver()
slack = SlackWebhookSink(webhook_url="https://hooks.slack.invalid/T/B/abc")
thresholds = QualityThresholds(min_overall_score=95.0, max_violation_rate=0.05)


def evaluate(records: list[dict], *, contract_id: str) -> None:
    report = check_quality(
        data=records,
        contract_file=f"contracts/{contract_id}.yaml",
        thresholds=thresholds,
    )
    observer.record(report, contract=contract_id)
    notify_on_breach(report, contract=contract_id, sinks=[slack])


class MetricsHandler(BaseHTTPRequestHandler):
    def do_GET(self):  # noqa: N802 — http.server convention
        if self.path != "/metrics":
            self.send_response(404)
            self.end_headers()
            return
        body = observer.render_metrics()
        self.send_response(200)
        self.send_header("Content-Type", "text/plain; version=0.0.4")
        self.end_headers()
        self.wfile.write(body)


HTTPServer(("0.0.0.0", 9100), MetricsHandler).serve_forever()

Metrics exposed (all labelled by contract):

Name Type What it tracks
pycharter_quality_records_total counter Records evaluated
pycharter_quality_violations_total counter Records that failed validation
pycharter_quality_threshold_breaches_total counter QualityThresholds breaches
pycharter_quality_score gauge Latest overall score (0–100)
pycharter_quality_completeness gauge Latest completeness (0–1)
pycharter_quality_accuracy gauge Latest accuracy (0–1)
pycharter_quality_violation_rate gauge Latest violation rate (0–1)

To share a single /metrics endpoint with pycharter.streaming's observer, pass the same CollectorRegistry to both:

from prometheus_client import CollectorRegistry

registry = CollectorRegistry()
quality_observer = QualityPrometheusObserver(registry=registry)
# streaming_observer = PrometheusObserver(registry=registry)

Other available sinks: GenericWebhookSink (any URL), PagerDutyWebhookSink (Events API v2).

See also: Validate a pandas DataFrame, Gate a FastAPI endpoint.