diff --git a/app.py b/app.py index b3f7756..115af68 100644 --- a/app.py +++ b/app.py @@ -16,9 +16,29 @@ import shutil from datetime import datetime from threading import Lock -from flask import Flask, jsonify, render_template, send_from_directory, request, redirect +from flask import Flask, jsonify, render_template, send_from_directory, request, redirect, g, Response import psutil +# Gunicorn -w N: set PROMETHEUS_MULTIPROC_DIR before workers import this module (e.g. in gunicorn.conf.py). +if os.environ.get("PROMETHEUS_MULTIPROC_DIR", "").strip(): + _prom_mpdir = os.environ["PROMETHEUS_MULTIPROC_DIR"].strip() + os.makedirs(_prom_mpdir, exist_ok=True) + +try: + from prometheus_client import ( + CONTENT_TYPE_LATEST, + CollectorRegistry, + Counter, + Histogram, + generate_latest, + multiprocess, + ) + + _PROMETHEUS_AVAILABLE = True +except ImportError: + _PROMETHEUS_AVAILABLE = False + CONTENT_TYPE_LATEST = "text/plain; version=0.0.4; charset=utf-8" + # Try to import OpenAI (optional) try: import openai @@ -27,6 +47,60 @@ OPENAI_AVAILABLE = False app = Flask(__name__) + +if _PROMETHEUS_AVAILABLE: + REQUEST_COUNT = Counter( + "http_requests_total", + "Total HTTP requests", + ["method", "endpoint", "status"], + ) + REQUEST_LATENCY = Histogram( + "http_request_duration_seconds", + "HTTP request latency in seconds", + buckets=(0.005, 0.01, 0.025, 0.05, 0.1, 0.25, 0.5, 1.0, 2.5, 5.0, 10.0, float("inf")), + ) + + @app.before_request + def _prometheus_before_request(): + g._prom_start = time.perf_counter() + + @app.after_request + def _prometheus_after_request(response): + start = getattr(g, "_prom_start", None) + if start is not None: + REQUEST_LATENCY.observe(time.perf_counter() - start) + ep = request.endpoint + rule = request.url_rule.rule if request.url_rule else None + endpoint_label = ep or rule or "unmatched" + try: + REQUEST_COUNT.labels( + method=request.method, + endpoint=endpoint_label, + status=str(response.status_code), + ).inc() + except Exception: + pass + return response + + @app.route("/metrics") + def prometheus_metrics(): + if os.environ.get("PROMETHEUS_MULTIPROC_DIR", "").strip(): + registry = CollectorRegistry() + multiprocess.MultiProcessCollector(registry) + data = generate_latest(registry) + else: + data = generate_latest() + return Response(data, mimetype=CONTENT_TYPE_LATEST) + +else: + + @app.route("/metrics") + def prometheus_metrics_disabled(): + return jsonify( + {"error": "prometheus_client not installed; pip install prometheus-client"} + ), 503 + + TRACEROUTE_CACHE = {} TRACEROUTE_CACHE_TTL_SECONDS = 60 NETWORK_STACK_PREV = { diff --git a/templates/linux-memory-subsystem.html b/templates/linux-memory-subsystem.html index 29c2526..1cad269 100644 --- a/templates/linux-memory-subsystem.html +++ b/templates/linux-memory-subsystem.html @@ -87,7 +87,7 @@

Linux kernel memory management

- +