DevOps & Skalierung

CaptchaAI-Überwachung mit Datadog: Metriken und Alerts

Um Ihre CaptchaAI-Pipeline in Datadog zu überwachen, senden Sie vier Kennzahlen als eigene Metriken: Lösungsrate, Latenz, Fehlerrate und Guthaben. Aus diesen Metriken bauen Sie Dashboards und definieren Alerts, die anschlagen, bevor die Pipeline steht. Eine CAPTCHA-Pipeline fällt selten mit einem lauten Knall aus – sie wird langsam schlechter: Die Latenz steigt, die Erfolgsquote sinkt, und irgendwann ist das Guthaben leer, ohne dass jemand rechtzeitig hinsieht. Dieser Leitfaden zeigt die Instrumentierung in Python und Node.js, eine fertige Dashboard-Vorlage und die passenden Schwellenwert-Alerts.

Voraussetzungen

Bevor Metriken in Datadog auflaufen, brauchen Sie drei Dinge:

  • Einen laufenden Datadog-Agent mit aktiviertem DogStatsD (UDP-Port 8125).
  • Einen CaptchaAI-API-Schlüssel, hinterlegt als Umgebungsvariable CAPTCHAAI_API_KEY.
  • Worker in Python oder Node.js, in die Sie die Instrumentierung einsetzen.

Steht diese Basis, senden die folgenden Beispiele ihre Werte an den lokalen Agent, der sie an die Datadog-Intake weiterreicht.

Die wichtigsten CaptchaAI-Metriken

Diese sieben Metriken decken die gesamte Pipeline ab – von der übermittelten Aufgabe bis zum verbleibenden Guthaben. Der Datadog-Metriktyp (Counter, Gauge, Histogram) bestimmt, wie Datadog den Wert aggregiert.

Metrik Typ Wozu sie dient
captcha.solve.count Counter Gesamtzahl der übermittelten Aufgaben
captcha.solve.success Counter Erfolgreich gelöste CAPTCHAs
captcha.solve.error Counter Fehlgeschlagene Lösungen, aufgeschlüsselt nach Fehlertyp
captcha.solve.latency Histogram Dauer von der Übermittlung bis zur Lösung
captcha.queue.depth Gauge Offene Aufgaben in der Warteschlange
captcha.balance Gauge Verbleibendes API-Guthaben
captcha.worker.active Gauge Aktive Worker-Prozesse

Ein typisches Szenario aus der Praxis: Ein Scraping-Team betreibt seine Worker auf mehreren Hetzner-Servern und startet den Guthaben-Check als Cron-Job in GitLab CI. Fällt die Erfolgsquote nachts unter den üblichen Bereich oder staut sich die Warteschlange, meldet Datadog das an den Bereitschaftsdienst, lange bevor am Morgen die ersten Aufträge auflaufen.

Metriken aus Python senden (DogStatsD)

Ein Decorator um Ihre Lösungsfunktion erledigt die Instrumentierung an einer einzigen Stelle: Er zählt jede Übermittlung, misst die Latenz erfolgreicher Lösungen und schlüsselt Fehler nach Typ auf. Das Guthaben senden Sie separat als Gauge.

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)

Der Tag captcha_type trennt reCAPTCHA v2, Cloudflare Turnstile und Bild-CAPTCHAs in denselben Metriken – so werten Sie später jeden Typ einzeln aus, ohne zusätzliche Metriknamen zu pflegen.

Metriken aus Node.js senden (hot-shots)

Für Node.js-Worker übernimmt die Bibliothek hot-shots denselben Job. Das prefix setzt den gemeinsamen Namensraum captcha., die globalen Tags kennzeichnen die Umgebung. Die Wrapper-Funktion zählt, misst und meldet Fehler analog zur Python-Variante.

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 };

Dashboard per JSON-Vorlage importieren

Sobald Metriken einlaufen, brauchen Sie eine Ansicht. Importieren Sie diese JSON-Vorlage in Datadog, um in einem Schritt ein CAPTCHA-Dashboard mit Lösungsrate, Latenzperzentilen, Guthaben und Warteschlangentiefe anzulegen:

{
  "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{*}"}]
      }
    }
  ]
}

Alerts definieren

Ein Dashboard zeigt Probleme erst, wenn jemand hinschaut. Alerts holen Sie aktiv ab. Die folgenden sechs Regeln decken die häufigsten Ausfallmuster ab – vom leeren Guthaben bis zum ausgefallenen Worker.

Alert Bedingung Schweregrad
Niedriges Guthaben captcha.balance < 10 Warnung
Kritisches Guthaben captcha.balance < 2 Kritisch
Hohe Fehlerquote Fehlerquote > 10 % über 5 Minuten Warnung
Latenzspitze p95-Latenz > 120 Sekunden über 10 Minuten Warnung
Warteschlangen-Rückstau Warteschlangentiefe > 100, 5 Minuten steigend Warnung
Worker ausgefallen captcha.worker.active == 0 Kritisch

Den Guthaben-Alert legen Sie direkt über die Datadog-Monitor-API an:

# 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

Fehlersuche

Die meisten Anlaufprobleme liegen an der Verbindung zum Datadog-Agent oder an falschen Tags – nicht an CaptchaAI selbst.

Problem Ursache Lösung
Metriken erscheinen nicht in Datadog API-Key fehlt oder statsd-Port nicht erreichbar Datadog-Agent-Status prüfen, UDP-Port 8125 freigeben
Latenz-Alerts erzeugen zu viele Fehlalarme Schwellenwert zu niedrig für die normale Varianz P95-Baseline über einige Tage messen, Schwellenwert entsprechend anheben
Dashboard zeigt keine Daten Tag-Filter oder Metrikname falsch Namen und Tags im Datadog-Metrics-Explorer prüfen
Alert löst nicht aus Auswertungsfenster zu kurz Fenster auf mindestens 5 Minuten setzen, rollup-Methode kontrollieren

Häufige Fragen

Welche CaptchaAI-Metriken sollte ich zuerst überwachen?

Guthaben, Erfolgsquote und p95-Latenz. Diese drei zeigen am schnellsten, ob die Pipeline gesund ist: Ein leeres Guthaben stoppt jede Lösung, eine fallende Erfolgsquote deutet auf falsche Sitekeys oder ein Problem beim Anbieter hin, und steigende Latenz kündigt Engpässe an, bevor die Warteschlange überläuft.

Kann ich verschiedene CAPTCHA-Typen in Datadog getrennt auswerten?

Ja. Der Tag captcha_type im Beispielcode trennt reCAPTCHA v2, Cloudflare Turnstile und Bild-CAPTCHAs innerhalb derselben Metriken. In Dashboards und Alerts filtern oder gruppieren Sie dann per captcha_type:..., ohne zusätzliche Metriknamen anlegen zu müssen.

Wie vermeide ich Fehlalarme bei Latenz-Alerts?

Messen Sie zuerst eine Baseline. Beobachten Sie die p95-Latenz über einige Tage, setzen Sie den Schwellenwert deutlich darüber und werten Sie über ein Fenster von mindestens 5 Minuten aus – so löst ein einzelner Ausreißer keinen Alarm aus.

Warnt mich Datadog rechtzeitig vor leerem Guthaben?

Ja, über einen Schwellenwert-Alert auf captcha.balance. Ein zweistufiges Setup – Warnung unter 10, kritisch unter 2 – gibt Ihnen genug Vorlauf zum Aufladen, bevor die ersten Lösungen fehlschlagen.


Weiterführende Anleitungen

Kommentare sind für diesen Artikel deaktiviert.