DevOps & Scaling

Monitoramento CaptchaAI com New Relic: Integração APM

Uma tarefa de CAPTCHA que costuma levar 15 segundos hoje está levando 55 — e sem instrumentação, descobrir se o culpado é o worker de scraping travado ou o checkout de um cliente parado vira uma sessão inteira de grep em log. Ligue a CaptchaAI ao New Relic APM e cada chamada a in.php/res.php passa a ser uma transação rastreável: latência de envio, tempo de polling, taxa de sucesso e saldo da conta aparecem no mesmo painel onde você já acompanha o resto da aplicação.

Neste guia você encontra:

  • O que instrumentar em cada fase do pipeline (envio, espera, aplicação do token)
  • Instrumentação completa em Python e em JavaScript/Node.js
  • Consultas NRQL prontas para montar o painel
  • Alertas de taxa de resolução, latência e saldo
  • Erros comuns de configuração e como corrigi-los

Quais métricas do pipeline CaptchaAI acompanhar no New Relic

O pipeline de resolução tem três fases bem distintas, e cada uma pede um tipo de métrica diferente:

  • Envio — latência da chamada a in.php e erros de submissão (chave inválida, sitekey errada)
  • Espera — duração do polling em res.php e taxa de timeout
  • Aplicação do token — se o token chegou a ser usado no formulário e taxa de sucesso final
[Submit Task] → [Wait for Solution] → [Apply Token]
     ↓                  ↓                   ↓
  Submit latency    Poll duration       Token usage
  API errors        Timeout rate        Success rate

As seções a seguir mostram como capturar essas métricas com atributos e eventos personalizados — primeiro em Python, depois em Node.js.

Um detalhe que muda o resultado na prática: se os workers rodam fora da região do seu tráfego, o RTT extra de cada chamada de polling se soma no painel de latência. Para tráfego majoritariamente brasileiro, medir a partir de uma região como sa-east-1 (São Paulo) dá um baseline mais realista do que medir a partir dos EUA ou da Europa. E se algum atributo personalizado guardar dado que identifique uma pessoa — IP ou e-mail capturado durante um scraping, por exemplo — trate essa telemetria com o mesmo cuidado que a LGPD exige de qualquer outro dado pessoal: não logue esses campos como atributo do New Relic.

Instrumentação em Python: eventos personalizados do CaptchaAI no New Relic

O decorador @newrelic.agent.background_task transforma cada chamada a solve_captcha em uma transação em segundo plano rastreada pelo agente. Dentro dela, os eventos CaptchaSolveSuccess e CaptchaSolveError guardam o que o APM sozinho não sabe: o tipo de CAPTCHA, o tempo de resolução e a causa exata da falha.

import os
import time
import requests
import newrelic.agent

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


@newrelic.agent.background_task(name="captcha_solve", group="CaptchaAI")
def solve_captcha(sitekey, pageurl, captcha_type="recaptcha_v2"):
    """Solve a CAPTCHA with full New Relic instrumentation."""
    # Add custom attributes for filtering
    newrelic.agent.add_custom_attributes([
        ("captcha_type", captcha_type),
        ("target_url", pageurl),
    ])

    # Submit phase
    submit_result = _submit_task(sitekey, pageurl, captcha_type)
    if "error" in submit_result:
        newrelic.agent.record_custom_event("CaptchaSolveError", {
            "error": submit_result["error"],
            "phase": "submit",
            "captcha_type": captcha_type,
        })
        return submit_result

    # Poll phase
    captcha_id = submit_result["captcha_id"]
    poll_result = _poll_result(captcha_id, captcha_type)

    # Record solve event
    event_data = {
        "captcha_type": captcha_type,
        "captcha_id": captcha_id,
        "success": "solution" in poll_result,
    }
    if "solution" in poll_result:
        event_data["solve_time"] = poll_result.get("elapsed", 0)
        newrelic.agent.record_custom_event("CaptchaSolveSuccess", event_data)
    else:
        event_data["error"] = poll_result.get("error", "unknown")
        newrelic.agent.record_custom_event("CaptchaSolveError", event_data)

    return poll_result


@newrelic.agent.function_trace(name="captcha_submit")
def _submit_task(sitekey, pageurl, captcha_type):
    payload = {
        "key": API_KEY,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": pageurl,
        "json": 1
    }
    resp = session.post("https://ocr.captchaai.com/in.php", data=payload)
    data = resp.json()

    newrelic.agent.add_custom_attributes([
        ("submit_status", data.get("status")),
    ])

    if data.get("status") != 1:
        return {"error": data.get("request")}
    return {"captcha_id": data["request"]}


@newrelic.agent.function_trace(name="captcha_poll")
def _poll_result(captcha_id, captcha_type):
    start = time.time()
    poll_count = 0

    for _ in range(60):
        time.sleep(5)
        poll_count += 1
        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
            newrelic.agent.add_custom_attributes([
                ("poll_count", poll_count),
                ("solve_time_seconds", round(elapsed, 2)),
            ])
            return {"solution": result["request"], "elapsed": elapsed}

        if result.get("request") != "CAPCHA_NOT_READY":
            return {"error": result.get("request")}

    return {"error": "TIMEOUT"}


def report_balance():
    """Record balance as a custom event."""
    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"])
        newrelic.agent.record_custom_event("CaptchaBalance", {
            "balance": balance,
            "low": balance < 10,
        })
        return balance
    return None

newrelic.ini: dois ajustes para não perder dados em produção

Dois pontos custam caro se ficarem no padrão de fábrica: custom_insights_events.enabled precisa estar true para os eventos acima aparecerem no NRQL, e o transaction_threshold de 5 segundos costuma ser alto demais — a fase de polling sozinha já ultrapassa isso na maioria dos tipos de CAPTCHA.

# newrelic.ini
[newrelic]
app_name = CaptchaAI Pipeline
license_key = YOUR_NEW_RELIC_LICENSE_KEY
monitor_mode = true
log_level = info
transaction_tracer.enabled = true
transaction_tracer.transaction_threshold = 5.0
custom_insights_events.enabled = true
custom_insights_events.max_samples_stored = 5000

JavaScript: enviando eventos do CaptchaAI para o New Relic

A versão em Node.js segue a mesma lógica — envio, polling, evento de sucesso ou erro — só que dentro de uma startBackgroundTransaction e usando axios no lugar do requests do Python.

const newrelic = require("newrelic");
const axios = require("axios");

const API_KEY = process.env.CAPTCHAAI_API_KEY;

async function solveCaptchaWithNewRelic(sitekey, pageurl, captchaType = "recaptcha_v2") {
  return newrelic.startBackgroundTransaction(
    "CaptchaSolve",
    "CaptchaAI",
    async () => {
      const transaction = newrelic.getTransaction();
      newrelic.addCustomAttributes({
        captchaType,
        targetUrl: pageurl,
      });

      const startTime = Date.now();

      try {
        // Submit
        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) {
          newrelic.recordCustomEvent("CaptchaSolveError", {
            error: submitResp.data.request,
            phase: "submit",
            captchaType,
          });
          transaction.end();
          return { error: submitResp.data.request };
        }

        const captchaId = submitResp.data.request;
        newrelic.addCustomAttributes({ captchaId });

        // Poll
        let pollCount = 0;
        for (let i = 0; i < 60; i++) {
          await new Promise((r) => setTimeout(r, 5000));
          pollCount++;

          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) {
            const elapsed = (Date.now() - startTime) / 1000;
            newrelic.recordCustomEvent("CaptchaSolveSuccess", {
              captchaType,
              solveTime: elapsed,
              pollCount,
            });
            newrelic.addCustomAttributes({
              solveTime: elapsed,
              pollCount,
            });
            transaction.end();
            return { solution: pollResp.data.request, elapsed };
          }

          if (pollResp.data.request !== "CAPCHA_NOT_READY") {
            newrelic.recordCustomEvent("CaptchaSolveError", {
              error: pollResp.data.request,
              phase: "poll",
              captchaType,
            });
            transaction.end();
            return { error: pollResp.data.request };
          }
        }

        newrelic.recordCustomEvent("CaptchaSolveError", {
          error: "TIMEOUT",
          phase: "poll",
          captchaType,
          pollCount,
        });
        transaction.end();
        return { error: "TIMEOUT" };
      } catch (err) {
        newrelic.noticeError(err);
        transaction.end();
        throw err;
      }
    }
  );
}

// Balance monitoring
async function monitorBalance() {
  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);
      newrelic.recordCustomEvent("CaptchaBalance", { balance });
    }
  } catch (err) {
    newrelic.noticeError(err);
  }
}

setInterval(monitorBalance, 60000);

module.exports = { solveCaptchaWithNewRelic };

Consultas NRQL para o painel de monitoramento da CaptchaAI

Com os eventos personalizados chegando, estas seis consultas cobrem o essencial:

  1. Taxa de sucesso na última hora
  2. Tempo médio de resolução por tipo de CAPTCHA
  3. Erros mais frequentes, agrupados por causa
  4. Latência P95 ao longo do tempo
  5. Saldo da conta nas últimas 24 horas
  6. Volume de tarefas por minuto
-- Solve success rate (last hour)
SELECT percentage(count(*), WHERE success = true)
FROM CaptchaSolveSuccess, CaptchaSolveError
SINCE 1 hour ago

-- Average solve time by CAPTCHA type
SELECT average(solveTime)
FROM CaptchaSolveSuccess
FACET captchaType
SINCE 1 hour ago TIMESERIES

-- Error breakdown
SELECT count(*)
FROM CaptchaSolveError
FACET error
SINCE 1 hour ago

-- P95 solve latency
SELECT percentile(solveTime, 95)
FROM CaptchaSolveSuccess
SINCE 1 hour ago TIMESERIES

-- Balance over time
SELECT latest(balance)
FROM CaptchaBalance
SINCE 24 hours ago TIMESERIES 5 minutes

-- Tasks per minute
SELECT rate(count(*), 1 minute)
FROM CaptchaSolveSuccess, CaptchaSolveError
SINCE 1 hour ago TIMESERIES

Alertas de taxa de resolução, latência e saldo

Estas quatro condições, montadas a partir das mesmas consultas NRQL, cobrem os sintomas que normalmente chegam primeiro como chamado de suporte:

  • Queda na taxa de resolução
  • Latência acima do aceitável no P95
  • Saldo da conta perto de zerar
  • Pico repentino de erros
Alerta Condição NRQL Limite
Taxa de resolução baixa SELECT percentage(count(*), WHERE success = true) <85% por 5 min
Latência alta SELECT percentile(solveTime, 95) FROM CaptchaSolveSuccess > 120s por 10 min
Saldo baixo SELECT latest(balance) FROM CaptchaBalance <US$ 10
Pico de erros SELECT count(*) FROM CaptchaSolveError > 50 em 5 minutos

Erros comuns na integração CaptchaAI + New Relic

Problema Causa Correção
Eventos personalizados não aparecem custom_insights_events.enabled está como falso Habilite em newrelic.ini
Rastreamentos de transação ausentes Limite (threshold) muito alto Reduza transaction_threshold para 1,0s
Atributos truncados Valor muito longo Mantenha os valores dos atributos com menos de 255 caracteres
Nenhum dado após o deploy Chave de licença errada ou agente não inicia Verifique com newrelic-admin validate-config newrelic.ini

Perguntas frequentes

Esse monitoramento funciona para os tipos em beta (CaptchaFox, Friendly Captcha, Lemin)?

Sim — a instrumentação não depende do tipo de CAPTCHA, então basta registrar o captcha_type correto nos atributos e eventos personalizados. O que muda é o que dá para comparar:

  • Tipos GA (reCAPTCHA v2/v3, Turnstile, GeeTest v3, imagem/OCR, grid, BLS) — taxa de sucesso e tempo de resolução já têm uma referência pública.
  • Tipos em beta (CaptchaFox, Friendly Captcha, Lemin) — ainda sem taxa pública; use os próprios eventos do seu painel como linha de base.
  • hCaptcha e FunCaptcha continuam fora do catálogo da CaptchaAI, então não há o que monitorar ali.

É seguro registrar esses eventos sem expor a chave de API nos logs do New Relic?

Sim, desde que você nunca inclua CAPTCHAAI_API_KEY como atributo personalizado — os exemplos deste guia só enviam captcha_type, captcha_id e tempos de resolução. Para uma camada extra, use attributes.exclude no newrelic.ini para bloquear qualquer atributo que comece com key ou token.

Qual a diferença entre o APM automático e os eventos personalizados que criamos aqui?

O APM instrumenta sozinho as chamadas HTTP, então já mostra latência e erros de rede sem nenhum código extra. Só que ele não sabe o que é um CAPTCHA — não distingue um erro de rede de um ERROR_WRONG_USER_KEY, por exemplo. É para isso que servem os eventos personalizados: eles carregam o contexto de negócio (tipo de CAPTCHA, tempo de resolução, causa da falha) que o APM sozinho não captura.

A instrumentação do New Relic deixa a resolução do CAPTCHA mais lenta?

Não de forma perceptível. O agente adiciona microssegundos de overhead por chamada instrumentada, e o tempo de resolução de um CAPTCHA (5 a 120 segundos, dependendo do tipo) torna essa diferença impossível de medir na prática.

Artigos relacionados

Próximos passos

Tenha visibilidade completa do seu pipeline de CAPTCHA — comece com uma chave de API da CaptchaAI e conecte o New Relic em poucos minutos.

Veja também:

Os comentários estão desativados para este artigo.