diff --git a/kiosk_show_replacement/metrics.py b/kiosk_show_replacement/metrics.py index 0132526..83c2d3b 100644 --- a/kiosk_show_replacement/metrics.py +++ b/kiosk_show_replacement/metrics.py @@ -23,6 +23,13 @@ metrics_bp = Blueprint("metrics", __name__) +# Content type for the Prometheus text exposition format, version 0.0.4. +# The ``version`` parameter is required: Prometheus v3 fails a scrape whose +# Content-Type carries no recognized format version, where v2 silently fell +# back to this format. This is the same value as prometheus_client's +# ``CONTENT_TYPE_LATEST``. +PROMETHEUS_CONTENT_TYPE = "text/plain; version=0.0.4; charset=utf-8" + class MetricsCollector: """Thread-safe metrics collector for Prometheus-style metrics.""" @@ -504,7 +511,9 @@ def metrics_endpoint() -> Response: # Add summary metrics output += "\n" + get_summary_metrics() - return Response(output, mimetype="text/plain; charset=utf-8") + # Use content_type rather than mimetype: Werkzeug appends its own + # charset to a mimetype, which would duplicate the parameter here. + return Response(output, content_type=PROMETHEUS_CONTENT_TYPE) # Convenience functions for recording metrics from other modules diff --git a/tests/unit/test_metrics.py b/tests/unit/test_metrics.py index f2773b9..3f17f7d 100644 --- a/tests/unit/test_metrics.py +++ b/tests/unit/test_metrics.py @@ -8,6 +8,7 @@ """ from kiosk_show_replacement.metrics import ( + PROMETHEUS_CONTENT_TYPE, MetricsCollector, metrics_collector, record_database_error, @@ -28,6 +29,18 @@ def test_metrics_endpoint_returns_text_plain(self, app, client): response = client.get("/metrics") assert "text/plain" in response.content_type + def test_metrics_endpoint_declares_exposition_format_version(self, app, client): + """Test the Content-Type names the Prometheus 0.0.4 text format. + + Prometheus v3 fails a scrape whose Content-Type carries no recognized + format version, so the ``version`` parameter must be present, and the + header must not repeat ``charset``. + """ + response = client.get("/metrics") + + assert response.content_type == "text/plain; version=0.0.4; charset=utf-8" + assert response.content_type == PROMETHEUS_CONTENT_TYPE + def test_metrics_contains_http_requests_help(self, app, client): """Test metrics output contains HTTP requests metric definition.""" response = client.get("/metrics")