DevOps & Mise à l'Échelle

Surveillance CaptchaAI avec Datadog : métriques et alertes

Vous ne pouvez pas réparer ce que vous ne pouvez pas voir. Datadog vous offre une visibilité en temps réel sur votre pipeline de résolution de CAPTCHA : taux de résolution, percentiles de latence, pannes d'erreurs et alertes d'anomalies qui se déclenchent avant la rupture de votre pipeline.

Indicateurs clés à suivre

Métrique Tapez Pourquoi c'est important
captcha.solve.count Compteur Total des tâches soumises
captcha.solve.success Compteur Des solutions réussies
captcha.solve.error Compteur Résolutions ayant échoué (par type d'erreur)
captcha.solve.latency Histogramme Délai entre la soumission et la solution
captcha.queue.depth Jauge Tâches en attente dans la file d'attente
captcha.balance Jauge Solde API restant
captcha.worker.active Jauge Processus de travail actifs

Python – Intégration DogStatsD

import os
import time
import functools
import requests
from datadog import initialize, statsd

# Initialize Datadog
initialize(
    statsd_host=os.environ.get("DD_AGENT_HOST", "localhost"),
    statsd_port=int(os.environ.get("DD_DOGSTATSD_PORT", "8125"))
)

API_KEY = os.environ["CAPTCHAAI_API_KEY"]
session = requests.Session()


def track_captcha_metrics(captcha_type="recaptcha_v2"):
    """Decorator to track solve metrics."""
    def decorator(func):
        @functools.wraps(func)
        def wrapper(*args, **kwargs):
            tags = [f"captcha_type:{captcha_type}"]
            statsd.increment("captcha.solve.count", tags=tags)

            start = time.time()
            try:
                result = func(*args, **kwargs)
                elapsed = time.time() - start

                if "solution" in result:
                    statsd.increment("captcha.solve.success", tags=tags)
                    statsd.histogram("captcha.solve.latency", elapsed, tags=tags)
                else:
                    error = result.get("error", "unknown")
                    statsd.increment(
                        "captcha.solve.error",
                        tags=tags + [f"error:{error}"]
                    )
                return result
            except Exception as e:
                statsd.increment(
                    "captcha.solve.error",
                    tags=tags + [f"error:{type(e).__name__}"]
                )
                raise
        return wrapper
    return decorator


@track_captcha_metrics(captcha_type="recaptcha_v2")
def solve_recaptcha(sitekey, pageurl):
    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:
        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:
            return {"solution": result["request"]}
        if result.get("request") != "CAPCHA_NOT_READY":
            return {"error": result.get("request")}
    return {"error": "TIMEOUT"}


def report_balance():
    """Send balance as a gauge metric."""
    resp = session.get("https://ocr.captchaai.com/res.php", params={
        "key": API_KEY, "action": "getbalance", "json": 1
    })
    data = resp.json()
    if data.get("status") == 1:
        balance = float(data["request"])
        statsd.gauge("captcha.balance", balance)
        return balance
    return None


def report_queue_depth(depth):
    """Report current queue depth."""
    statsd.gauge("captcha.queue.depth", depth)


def report_worker_count(active, total):
    """Report worker health."""
    statsd.gauge("captcha.worker.active", active)
    statsd.gauge("captcha.worker.total", total)

JavaScript – Intégration Datadog

const { StatsD } = require("hot-shots");
const axios = require("axios");

const API_KEY = process.env.CAPTCHAAI_API_KEY;

const dogstatsd = new StatsD({
  host: process.env.DD_AGENT_HOST || "localhost",
  port: parseInt(process.env.DD_DOGSTATSD_PORT || "8125", 10),
  prefix: "captcha.",
  globalTags: [`env:${process.env.NODE_ENV || "development"}`],
});

async function solveCaptchaWithMetrics(sitekey, pageurl, captchaType = "recaptcha_v2") {
  const tags = [`captcha_type:${captchaType}`];
  dogstatsd.increment("solve.count", 1, tags);
  const startTime = Date.now();

  try {
    const result = await solveCaptcha(sitekey, pageurl);
    const elapsed = (Date.now() - startTime) / 1000;

    if (result.solution) {
      dogstatsd.increment("solve.success", 1, tags);
      dogstatsd.histogram("solve.latency", elapsed, tags);
    } else {
      dogstatsd.increment("solve.error", 1, [...tags, `error:${result.error}`]);
    }

    return result;
  } catch (err) {
    dogstatsd.increment("solve.error", 1, [...tags, `error:${err.message}`]);
    throw err;
  }
}

async function solveCaptcha(sitekey, pageurl) {
  const submitResp = await axios.post("https://ocr.captchaai.com/in.php", null, {
    params: {
      key: API_KEY,
      method: "userrecaptcha",
      googlekey: sitekey,
      pageurl: pageurl,
      json: 1,
    },
  });

  if (submitResp.data.status !== 1) {
    return { error: submitResp.data.request };
  }

  const captchaId = submitResp.data.request;
  for (let i = 0; i < 60; i++) {
    await new Promise((r) => setTimeout(r, 5000));
    const pollResp = await axios.get("https://ocr.captchaai.com/res.php", {
      params: { key: API_KEY, action: "get", id: captchaId, json: 1 },
    });
    if (pollResp.data.status === 1) return { solution: pollResp.data.request };
    if (pollResp.data.request !== "CAPCHA_NOT_READY") {
      return { error: pollResp.data.request };
    }
  }
  return { error: "TIMEOUT" };
}

async function reportBalance() {
  try {
    const resp = await axios.get("https://ocr.captchaai.com/res.php", {
      params: { key: API_KEY, action: "getbalance", json: 1 },
    });
    if (resp.data.status === 1) {
      const balance = parseFloat(resp.data.request);
      dogstatsd.gauge("balance", balance);
      return balance;
    }
  } catch (err) {
    console.error("Balance check failed:", err.message);
  }
  return null;
}

// Report balance every minute
setInterval(reportBalance, 60000);

module.exports = { solveCaptchaWithMetrics, reportBalance };

Tableau de bord Datadog JSON

Importez ce modèle JSON dans Datadog pour créer un tableau de bord de surveillance CAPTCHA :

{
  "title": "CaptchaAI Pipeline",
  "widgets": [
    {
      "definition": {
        "type": "timeseries",
        "title": "Solve Rate (Success vs Error)",
        "requests": [
          {"q": "sum:captcha.solve.success{*}.as_count()"},
          {"q": "sum:captcha.solve.error{*}.as_count()"}
        ]
      }
    },
    {
      "definition": {
        "type": "timeseries",
        "title": "Solve Latency (p50, p95, p99)",
        "requests": [
          {"q": "avg:captcha.solve.latency{*}"},
          {"q": "percentile:captcha.solve.latency{*},0.95"},
          {"q": "percentile:captcha.solve.latency{*},0.99"}
        ]
      }
    },
    {
      "definition": {
        "type": "query_value",
        "title": "API Balance",
        "requests": [{"q": "avg:captcha.balance{*}"}]
      }
    },
    {
      "definition": {
        "type": "timeseries",
        "title": "Queue Depth",
        "requests": [{"q": "avg:captcha.queue.depth{*}"}]
      }
    }
  ]
}

Définitions des alertes

Alerte État Gravité
Solde faible captcha.balance < 10 Avertissement
Solde critique captcha.balance < 2 Critique
Taux d'erreur élevé Taux d'erreur > 10 % sur 5 minutes Avertissement
Pic de latence latence p95 > 120 s sur 10 minutes Avertissement
Sauvegarde de file d'attente Profondeur de file d'attente > 100 en croissance pendant 5 minutes Avertissement
Travailleur en panne captcha.worker.active == 0 Critique
# Datadog monitor definition (API create)
- type: metric alert
  name: "CaptchaAI Low Balance"
  query: "avg(last_5m):avg:captcha.balance{*} < 10"
  message: "CaptchaAI balance is low: {{value}}. Top up to avoid solve failures."
  tags:

    - team:scraping
    - service:captcha

Dépannage

Problème Parce que Corriger
Les métriques n'apparaissent pas L'agent DogStatsD ne fonctionne pas Vérifiez DD_AGENT_HOST ; vérifiez docker ps pour le conteneur d'agent
Histogramme de latence vide Aucune solution réussie n'a été suivie Vérifiez que statsd.histogram() est appelé sur le chemin de réussite
Balises manquantes Mauvais format de balise Utilisez le format key:value ; pas d'espaces dans les balises
Métriques en double Plusieurs journalistes en fuite Assurez-vous qu’un seul rapporteur de solde par déploiement

FAQ

Ai-je besoin d’un agent Datadog sur chaque travailleur ?

Exécutez un agent DogStatsD par hôte. Tous les travailleurs de cet hôte envoient des métriques à l'agent local, qui les transmet à la réception de Datadog.

Combien coûtent les métriques personnalisées Datadog ?

Datadog facture des frais par série temporelle de métriques personnalisées. Les 7 métriques ci-dessus avec quelques combinaisons de balises restent généralement dans les limites du niveau gratuit. Vérifiez les tarifs de Datadog pour connaître les coûts exacts.

Puis-je utiliser les traces Datadog APM au lieu de métriques personnalisées ?

Oui. Enveloppez votre fonction de résolution avec ddtrace pour obtenir un traçage automatique. Les métriques personnalisées vous donnent cependant plus de contrôle sur l’agrégation et les alertes.

Articles connexes

Prochaines étapes

Obtenez de l'observabilité dans votre pipeline CAPTCHA -commencez avec une clé API CaptchaAIet connectez-vous à Datadog.

Guides associés :

  • Surveillance Prometheus et Grafana
  • Tableau de bord d'utilisation
  • Gestion des erreurs de rappel
Les commentaires sont désactivés pour cet article.