Tutoriais

Log estruturado para operações CAPTCHA

Quando uma solução CAPTCHA falha às 3 da manhã, logs de texto simples como "Error solving captcha" não ajudam. Logs JSON estruturados com IDs de tarefas, tipos de captcha, tempos de resolução e códigos de erro permitem filtrar, pesquisar e alertar exatamente sobre o que deu errado.


Por que registro estruturado

Texto simples JSON estruturado
Captcha solved in 12.3s {"event":"captcha_solved","task_id":"abc123","type":"recaptcha_v2","solve_time_ms":12300}
Difícil de analisar Legível por máquina
Pesquisa somente Grep Filtrar por qualquer campo
Sem correlação Links de ID de tarefa enviar → enquete → injetar

Python: estruturalog

import structlog
import time

structlog.configure(
    processors=[
        structlog.processors.TimeStamper(fmt="iso"),
        structlog.processors.add_log_level,
        structlog.processors.JSONRenderer(),
    ],
    logger_factory=structlog.PrintLoggerFactory(),
)

log = structlog.get_logger()

Registrando o ciclo de vida da resolução

import requests

API_KEY = "YOUR_API_KEY"


def solve_captcha(captcha_type, sitekey, page_url, proxy=None):
    solve_log = log.bind(
        captcha_type=captcha_type,
        site_url=page_url,
        sitekey=sitekey[:12] + "...",
    )

    # Submit
    start = time.time()
    solve_log.info("captcha_submit_start")

    resp = requests.post("https://ocr.captchaai.com/in.php", data={
        "key": API_KEY,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": page_url,
        "json": "1",
    }).json()

    if resp["status"] != 1:
        solve_log.error("captcha_submit_failed", error=resp["request"])
        return None

    task_id = resp["request"]
    submit_ms = int((time.time() - start) * 1000)
    solve_log = solve_log.bind(task_id=task_id)
    solve_log.info("captcha_submitted", submit_ms=submit_ms)

    # Poll
    for attempt in range(24):
        time.sleep(5)
        result = requests.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY, "action": "get", "id": task_id, "json": "1"
        }).json()

        if result["status"] == 1:
            solve_ms = int((time.time() - start) * 1000)
            solve_log.info(
                "captcha_solved",
                solve_time_ms=solve_ms,
                poll_attempts=attempt + 1,
                token_length=len(result["request"]),
            )
            return result["request"]

        if result["request"] != "CAPCHA_NOT_READY":
            solve_log.error(
                "captcha_solve_failed",
                error=result["request"],
                poll_attempts=attempt + 1,
            )
            return None

    solve_log.warning("captcha_solve_timeout", poll_attempts=24)
    return None

Saída:

{"event":"captcha_submit_start","captcha_type":"recaptcha_v2","site_url":"https://example.com","sitekey":"6Le-wvkSAAAA...","timestamp":"2025-07-15T10:30:00Z","level":"info"}
{"event":"captcha_submitted","task_id":"71845302","submit_ms":245,"timestamp":"2025-07-15T10:30:00Z","level":"info"}
{"event":"captcha_solved","task_id":"71845302","solve_time_ms":18230,"poll_attempts":4,"token_length":580,"timestamp":"2025-07-15T10:30:18Z","level":"info"}

Node.js: pino

const pino = require('pino');

const log = pino({
  level: 'info',
  timestamp: pino.stdTimeFunctions.isoTime,
});

Registrando o ciclo de vida da resolução

const axios = require('axios');

const API_KEY = 'YOUR_API_KEY';

async function solveCaptcha(captchaType, sitekey, pageUrl) {
  const taskLog = log.child({
    captchaType,
    siteUrl: pageUrl,
    sitekey: sitekey.substring(0, 12) + '...',
  });

  const start = Date.now();
  taskLog.info('captcha_submit_start');

  const submit = await axios.post('https://ocr.captchaai.com/in.php', null, {
    params: {
      key: API_KEY, method: 'userrecaptcha',
      googlekey: sitekey, pageurl: pageUrl, json: 1,
    },
  });

  if (submit.data.status !== 1) {
    taskLog.error({ error: submit.data.request }, 'captcha_submit_failed');
    return null;
  }

  const taskId = submit.data.request;
  const boundLog = taskLog.child({ taskId });
  boundLog.info({ submitMs: Date.now() - start }, 'captcha_submitted');

  for (let attempt = 1; attempt <= 24; attempt++) {
    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: taskId, json: 1 },
    });

    if (poll.data.status === 1) {
      boundLog.info({
        solveTimeMs: Date.now() - start,
        pollAttempts: attempt,
        tokenLength: poll.data.request.length,
      }, 'captcha_solved');
      return poll.data.request;
    }

    if (poll.data.request !== 'CAPCHA_NOT_READY') {
      boundLog.error({ error: poll.data.request, pollAttempts: attempt }, 'captcha_solve_failed');
      return null;
    }
  }

  boundLog.warn({ pollAttempts: 24 }, 'captcha_solve_timeout');
  return null;
}

Referência de campos de log

Campo Tipo Descrição
event corda Nome do evento: captcha_submitted, captcha_solved, etc.
task_id corda ID da tarefa CaptchaAI para correlação
captcha_type corda recaptcha_v2, turnstile, image, etc.
site_url corda URL da página de destino
solve_time_ms inteiro Tempo total desde o envio até a resolução
poll_attempts inteiro Número de solicitações de enquete feitas
error corda Código de erro de CaptchaAI
token_length inteiro Comprimento do token retornado

Filtragem e alerta

Encontre todas as falhas na última hora

# With jq
cat captcha.log | jq 'select(.level == "error" and .event == "captcha_solve_failed")'

Alerta sobre alta taxa de falhas

# Count errors vs successes in a rolling window
from collections import deque

class ErrorRateMonitor:
    def __init__(self, window_size=100, threshold=0.2):
        self.results = deque(maxlen=window_size)
        self.threshold = threshold

    def record(self, success):
        self.results.append(success)
        if len(self.results) >= 50:
            error_rate = 1 - sum(self.results) / len(self.results)
            if error_rate > self.threshold:
                log.warning(
                    "captcha_error_rate_high",
                    error_rate=round(error_rate, 3),
                    window=len(self.results),
                )

Solução de problemas

Problema Causa Correção
Registros muito detalhados Registrando todas as tentativas de pesquisa Registrar apenas eventos de envio, resolvidos e com falha
Não é possível correlacionar eventos ID da tarefa ausente Vincule task_id antecipadamente com log.bind() ou log.child()
Registros não pesquisáveis Formato de texto simples Mude para JSON com structlog ou pino
Dados confidenciais em logs Registrando chave de API completa Nunca registre chaves de API; truncar chaves de site

Perguntas frequentes

Devo registrar o token CAPTCHA?

Registre o comprimento do token, não o token completo. Os tokens podem ter mais de 500 caracteres e adicionar volume de log desnecessário.

Qual nível de log para CAPCHA_NOT_READY?

Não registre nada - isso é esperado durante a votação. Registre apenas o resultado final.


Crie fluxos de trabalho CAPTCHA observáveis com CaptchaAI

Obtenha sua chave API emcaptchaai.com.


Guias relacionados

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