Un tableau de bord Grafana bien conçu vous montre en un coup d'œil exactement ce qui se passe dans votre pipeline CAPTCHA : taux de résolution, percentiles de latence, tendances d'équilibrage et répartition des erreurs. Ces modèles sont prêts à être importés avec Prometheus comme source de données.
Disposition du tableau de bord
┌───────────────────────────────────────────────┐
│ Row 1: Overview │
│ [Solve Rate %] [Balance $] [Queue Depth] [TPM]│
├───────────────────────────────────────────────┤
│ Row 2: Performance │
│ [Latency P50/P95/P99] [Solve Rate Over Time] │
├───────────────────────────────────────────────┤
│ Row 3: Errors │
│ [Error Rate %] [Error Breakdown by Type] │
├───────────────────────────────────────────────┤
│ Row 4: Workers │
│ [Active Workers] [Tasks Per Worker] │
└───────────────────────────────────────────────┘
Configuration des métriques Prometheus
Tout d’abord, exposez les métriques de votre solveur CAPTCHA :
Python – Client Prometheus
import os
import time
import requests
from prometheus_client import (
Counter, Histogram, Gauge, start_http_server
)
API_KEY = os.environ["CAPTCHAAI_API_KEY"]
# Define metrics
captcha_solves = Counter(
"captcha_solves_total",
"Total CAPTCHA solve attempts",
["captcha_type", "status"]
)
captcha_latency = Histogram(
"captcha_solve_duration_seconds",
"CAPTCHA solve latency",
["captcha_type"],
buckets=[5, 10, 15, 20, 30, 45, 60, 90, 120, 180, 300]
)
captcha_balance = Gauge(
"captcha_balance_dollars",
"CaptchaAI account balance"
)
captcha_queue_depth = Gauge(
"captcha_queue_depth",
"Pending tasks in queue"
)
captcha_workers_active = Gauge(
"captcha_workers_active",
"Number of active workers"
)
session = requests.Session()
def solve_with_metrics(sitekey, pageurl, captcha_type="recaptcha_v2"):
start = time.time()
resp = session.post("https://ocr.captchaai.com/in.php", data={
"key": API_KEY,
"method": "userrecaptcha",
"googlekey": sitekey,
"pageurl": pageurl,
"json": 1
})
data = resp.json()
if data.get("status") != 1:
captcha_solves.labels(captcha_type, "error").inc()
return {"error": data.get("request")}
captcha_id = data["request"]
for _ in range(60):
time.sleep(5)
result = session.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get", "id": captcha_id, "json": 1
}).json()
if result.get("status") == 1:
elapsed = time.time() - start
captcha_solves.labels(captcha_type, "success").inc()
captcha_latency.labels(captcha_type).observe(elapsed)
return {"solution": result["request"]}
if result.get("request") != "CAPCHA_NOT_READY":
captcha_solves.labels(captcha_type, "error").inc()
return {"error": result.get("request")}
captcha_solves.labels(captcha_type, "timeout").inc()
return {"error": "TIMEOUT"}
def update_balance():
resp = session.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "getbalance", "json": 1
})
if resp.json().get("status") == 1:
captcha_balance.set(float(resp.json()["request"]))
# Start metrics server on port 9090
start_http_server(9090)
Javascript
const promClient = require("prom-client");
const axios = require("axios");
const API_KEY = process.env.CAPTCHAAI_API_KEY;
const register = new promClient.Registry();
const solvesTotal = new promClient.Counter({
name: "captcha_solves_total",
help: "Total CAPTCHA solve attempts",
labelNames: ["captcha_type", "status"],
registers: [register],
});
const solveLatency = new promClient.Histogram({
name: "captcha_solve_duration_seconds",
help: "CAPTCHA solve latency",
labelNames: ["captcha_type"],
buckets: [5, 10, 15, 20, 30, 45, 60, 90, 120, 180, 300],
registers: [register],
});
const balance = new promClient.Gauge({
name: "captcha_balance_dollars",
help: "CaptchaAI account balance",
registers: [register],
});
const queueDepth = new promClient.Gauge({
name: "captcha_queue_depth",
help: "Pending tasks in queue",
registers: [register],
});
async function solveWithMetrics(sitekey, pageurl, captchaType = "recaptcha_v2") {
const end = solveLatency.startTimer({ captcha_type: captchaType });
try {
const resp = await axios.post("https://ocr.captchaai.com/in.php", null, {
params: {
key: API_KEY, method: "userrecaptcha",
googlekey: sitekey, pageurl, json: 1,
},
});
if (resp.data.status !== 1) {
solvesTotal.inc({ captcha_type: captchaType, status: "error" });
return { error: resp.data.request };
}
const captchaId = resp.data.request;
for (let i = 0; i < 60; i++) {
await new Promise((r) => setTimeout(r, 5000));
const poll = await axios.get("https://ocr.captchaai.com/res.php", {
params: { key: API_KEY, action: "get", id: captchaId, json: 1 },
});
if (poll.data.status === 1) {
end();
solvesTotal.inc({ captcha_type: captchaType, status: "success" });
return { solution: poll.data.request };
}
if (poll.data.request !== "CAPCHA_NOT_READY") {
solvesTotal.inc({ captcha_type: captchaType, status: "error" });
return { error: poll.data.request };
}
}
solvesTotal.inc({ captcha_type: captchaType, status: "timeout" });
return { error: "TIMEOUT" };
} catch (err) {
solvesTotal.inc({ captcha_type: captchaType, status: "error" });
throw err;
}
}
// Expose metrics endpoint
const express = require("express");
const app = express();
app.get("/metrics", async (req, res) => {
res.set("Content-Type", register.contentType);
res.end(await register.metrics());
});
app.listen(9090);
Requêtes du panneau Grafana (PromQL)
Ligne 1 : Statistiques générales
Taux de résolution (panneau Stat) :
sum(rate(captcha_solves_total{status="success"}[5m]))
/
sum(rate(captcha_solves_total[5m]))
* 100
Balance (panneau de jauge) :
captcha_balance_dollars
Profondeur de la file d'attente (panneau Stat) :
captcha_queue_depth
Tâches par minute (panneau Stat) :
sum(rate(captcha_solves_total[5m])) * 60
Ligne 2 : Performances
Percentiles de latence (séries chronologiques) :
# p50
histogram_quantile(0.50, rate(captcha_solve_duration_seconds_bucket[5m]))
# p95
histogram_quantile(0.95, rate(captcha_solve_duration_seconds_bucket[5m]))
# p99
histogram_quantile(0.99, rate(captcha_solve_duration_seconds_bucket[5m]))
Taux de résolution au fil du temps (série chronologique) :
sum(rate(captcha_solves_total{status="success"}[5m])) by (captcha_type) * 60
Ligne 3 : erreurs
Taux d'erreur (série chronologique) :
sum(rate(captcha_solves_total{status!="success"}[5m]))
/
sum(rate(captcha_solves_total[5m]))
* 100
Répartition des erreurs (graphique circulaire) :
sum by (status) (increase(captcha_solves_total{status!="success"}[1h]))
Rangée 4 : Travailleurs
Travailleurs actifs (séries chronologiques) :
captcha_workers_active
Règles d'alerte (alerte Grafana)
# Grafana alert rules
groups:
- name: captcha-alerts
rules:
- alert: LowBalance
expr: captcha_balance_dollars < 10
for: 5m
labels:
severity: warning
annotations:
summary: "CaptchaAI balance low: {{ $value }}"
- alert: HighErrorRate
expr: |
sum(rate(captcha_solves_total{status!="success"}[5m]))
/ sum(rate(captcha_solves_total[5m]))
> 0.1
for: 5m
labels:
severity: critical
- alert: HighLatency
expr: |
histogram_quantile(0.95,
rate(captcha_solve_duration_seconds_bucket[5m])
) > 120
for: 10m
labels:
severity: warning
Dépannage
| Problème | Parce que | Corriger |
|---|---|---|
| "Aucune donnée" sur les panneaux | Prometheus ne supprime pas le point de terminaison des métriques | Vérifiez les cibles prometheus.yml ; vérifier que /metrics renvoie des données |
| Les percentiles de latence sont erronés | Mauvaise fenêtre rate() ou compartiments manquants |
Utilisez la fenêtre de taux [5m] ; ajouter des buckets plus fins |
| Les variables du tableau de bord ne fonctionnent pas | Requête de variable de modèle incorrecte | Utiliser label_values(captcha_solves_total, captcha_type) |
| Les alertes ne se déclenchent pas | Intervalle d'évaluation des alertes trop long | Définir l'intervalle d'évaluation sur 1 minute |
FAQ
Puis-je importer ces tableaux de bord depuis un fichier JSON ?
Oui. Grafana prend en charge l'importation de tableaux de bord JSON. Créez le tableau de bord dans l'interface utilisateur, exportez-le au format JSON, puis importez-le sur d'autres instances Grafana.
Quel intervalle de grattage Prometheus dois-je utiliser ?
15 secondes sont la norme. Pour les pipelines CAPTCHA avec un volume inférieur, 30 secondes fonctionnent très bien. Ne descendez pas en dessous de 10 secondes, sauf si vous avez besoin d'une visibilité en temps réel.
Puis-je utiliser Grafana Cloud au lieu d'un hébergement auto-hébergé ?
Oui. Grafana Cloud prend en charge l'écriture à distance Prometheus. Envoyez les métriques de votre Prometheus vers Grafana Cloud et utilisez les mêmes requêtes PromQL.
Articles connexes
- Créer un tableau de bord d'analyse des concurrents Captchaai
- Alertes de métriques Datadog de surveillance Captchaai
- Création d'une surveillance du tableau de bord d'utilisation de Captchaai
Prochaines étapes
Visualisez votre pipeline CAPTCHA -récupérez votre clé API CaptchaAIet mettre en place des tableaux de bord Grafana.
Guides associés :
- Surveillance Prometheus et Grafana
- Surveillance Datadog
- Surveillance SLI/SLO