Cuando tu organización resuelve miles de CAPTCHA diariamente, necesitas un registro de cada solicitud. Los registros de auditoría responden preguntas como: ¿Quién desencadenó esta resolución? ¿Para qué sitio fue? ¿Cuánto costó? ¿Cuándo ocurrió? Esta guía muestra cómo implementar un registro de auditoría integral para operaciones de CaptchaAI.
Qué registrar
Cada solución CAPTCHA debe registrar:
| campo | Propósito | Ejemplo |
|---|---|---|
timestamp |
Cuando se hizo la solicitud | 2026-04-04T14:30:00Z |
request_id |
Identificador único para esta solución. | uuid4() |
captcha_type |
Método CAPTCHA utilizado | userrecaptcha |
target_site |
La URL de la página se está resolviendo | https://staging.example.com/qa-login |
task_id |
ID de tarea CaptchaAI | 73829451 |
status |
Resultado | solved, failed, timeout |
solve_time_ms |
Tiempo desde el envío hasta el resultado | 18432 |
error_code |
Error si falla | ERROR_CAPTCHA_UNSOLVABLE |
initiator |
¿Quién o qué desencadenó la solución? | scraper-job-42 |
cost |
Costo estimado | 0.003 |
No registre: claves API, tokens CAPTCHA (son temporales) o información de identificación personal de los sitios de destino.
Implementación de Python
# audit_solver.py
import os
import uuid
import time
import json
import logging
from datetime import datetime, timezone
import requests
API_KEY = os.environ.get("CAPTCHAAI_KEY", "YOUR_API_KEY")
# Configure audit logger — separate from application logs
audit_logger = logging.getLogger("captcha_audit")
audit_logger.setLevel(logging.INFO)
# File handler with rotation
from logging.handlers import RotatingFileHandler
handler = RotatingFileHandler(
"captcha_audit.jsonl",
maxBytes=50_000_000, # 50 MB per file
backupCount=10,
)
handler.setFormatter(logging.Formatter("%(message)s"))
audit_logger.addHandler(handler)
def log_audit(record):
"""Write a structured audit record."""
audit_logger.info(json.dumps(record, default=str))
def solve_with_audit(sitekey, pageurl, captcha_type="userrecaptcha",
initiator="unknown"):
"""Solve a CAPTCHA with full audit logging."""
request_id = str(uuid.uuid4())
start = time.time()
audit_record = {
"request_id": request_id,
"timestamp": datetime.now(timezone.utc).isoformat(),
"captcha_type": captcha_type,
"target_site": pageurl,
"initiator": initiator,
"status": "submitted",
}
session = requests.Session()
try:
# Submit
resp = session.get("https://ocr.captchaai.com/in.php", params={
"key": API_KEY,
"method": captcha_type,
"googlekey": sitekey,
"pageurl": pageurl,
"json": "1",
})
result = resp.json()
if result.get("status") != 1:
audit_record.update({
"status": "submit_failed",
"error_code": result.get("request"),
"solve_time_ms": int((time.time() - start) * 1000),
})
log_audit(audit_record)
return None
task_id = result["request"]
audit_record["task_id"] = task_id
# Poll
time.sleep(15)
for _ in range(25):
poll = session.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "get",
"id": task_id, "json": "1",
})
poll_result = poll.json()
if poll_result.get("status") == 1:
solve_time = int((time.time() - start) * 1000)
audit_record.update({
"status": "solved",
"solve_time_ms": solve_time,
"cost_estimate": 0.003, # Adjust per your rate
})
log_audit(audit_record)
return poll_result["request"]
if poll_result.get("request") != "CAPCHA_NOT_READY":
audit_record.update({
"status": "failed",
"error_code": poll_result.get("request"),
"solve_time_ms": int((time.time() - start) * 1000),
})
log_audit(audit_record)
return None
time.sleep(5)
audit_record.update({
"status": "timeout",
"solve_time_ms": int((time.time() - start) * 1000),
})
log_audit(audit_record)
return None
except Exception as e:
audit_record.update({
"status": "error",
"error_code": str(e)[:200],
"solve_time_ms": int((time.time() - start) * 1000),
})
log_audit(audit_record)
raise
# Usage
token = solve_with_audit(
sitekey="6Le-wvkSAAAAAPBMRTvw0Q4Muexq9bi0DJwx_mJ-",
pageurl="https://www.google.com/recaptcha/api2/demo",
initiator="price-scraper-v2",
)
Salida del registro de auditoría (formato JSONL)
{"request_id":"a1b2c3d4-...","timestamp":"2026-04-04T14:30:00+00:00","captcha_type":"userrecaptcha","target_site":"https://www.google.com/recaptcha/api2/demo","initiator":"price-scraper-v2","status":"solved","task_id":"73829451","solve_time_ms":18432,"cost_estimate":0.003}
Implementación de JavaScript
// audit_solver.js
const fs = require('fs');
const { v4: uuidv4 } = require('uuid');
const axios = require('axios');
const API_KEY = process.env.CAPTCHAAI_KEY || 'YOUR_API_KEY';
const AUDIT_FILE = 'captcha_audit.jsonl';
function logAudit(record) {
fs.appendFileSync(AUDIT_FILE, JSON.stringify(record) + '\n');
}
async function solveWithAudit(sitekey, pageurl, initiator = 'unknown') {
const requestId = uuidv4();
const start = Date.now();
const record = {
request_id: requestId,
timestamp: new Date().toISOString(),
captcha_type: 'userrecaptcha',
target_site: pageurl,
initiator,
status: 'submitted',
};
try {
const submit = await axios.get('https://ocr.captchaai.com/in.php', {
params: {
key: API_KEY, method: 'userrecaptcha',
googlekey: sitekey, pageurl, json: '1',
},
});
if (submit.data.status !== 1) {
record.status = 'submit_failed';
record.error_code = submit.data.request;
record.solve_time_ms = Date.now() - start;
logAudit(record);
return null;
}
record.task_id = submit.data.request;
await new Promise(r => setTimeout(r, 15000));
for (let i = 0; i < 25; i++) {
const poll = await axios.get('https://ocr.captchaai.com/res.php', {
params: { key: API_KEY, action: 'get', id: submit.data.request, json: '1' },
});
if (poll.data.status === 1) {
record.status = 'solved';
record.solve_time_ms = Date.now() - start;
record.cost_estimate = 0.003;
logAudit(record);
return poll.data.request;
}
if (poll.data.request !== 'CAPCHA_NOT_READY') {
record.status = 'failed';
record.error_code = poll.data.request;
record.solve_time_ms = Date.now() - start;
logAudit(record);
return null;
}
await new Promise(r => setTimeout(r, 5000));
}
record.status = 'timeout';
record.solve_time_ms = Date.now() - start;
logAudit(record);
return null;
} catch (e) {
record.status = 'error';
record.error_code = e.message.slice(0, 200);
record.solve_time_ms = Date.now() - start;
logAudit(record);
throw e;
}
}
Consulta de registros de auditoría
Resumen diario
import json
from collections import Counter
from datetime import date
def daily_summary(log_file, target_date=None):
"""Generate a daily summary from audit logs."""
target = target_date or date.today().isoformat()
statuses = Counter()
total_cost = 0
solve_times = []
with open(log_file) as f:
for line in f:
record = json.loads(line)
if record["timestamp"].startswith(target):
statuses[record["status"]] += 1
total_cost += record.get("cost_estimate", 0)
if record.get("solve_time_ms"):
solve_times.append(record["solve_time_ms"])
print(f"Date: {target}")
print(f"Total requests: {sum(statuses.values())}")
print(f"Statuses: {dict(statuses)}")
print(f"Estimated cost: ${total_cost:.2f}")
if solve_times:
print(f"Median solve time: {sorted(solve_times)[len(solve_times)//2]}ms")
daily_summary("captcha_audit.jsonl")
Retención y almacenamiento
| Volumen | Tamaño del registro diario | Almacenamiento mensual | Recomendación |
|---|---|---|---|
| 100 resuelve/day | ~30 KB | ~1MB | archivo local |
| 1.000 soluciones/day | ~300 KB | ~10MB | Archivo local + rotación |
| 10.000 soluciones/day | ~3MB | ~100MB | Enviar al agregador de registros |
| 100.000 soluciones/day | ~30MB | ~1GB | Registro centralizado (ELK, Datadog) |
Solución de problemas
| Problema | causa | Solución |
|---|---|---|
| El archivo de registro crece demasiado | No hay rotación configurada | Utilice RotatingFileHandler o logrotate |
| Faltan registros de auditoría | Excepción antes de iniciar sesión | Iniciar sesión en el bloque finally |
| Escrituras lentas a alto volumen | Archivo sincrónico I/O | Utilice escrituras de archivos asíncronos o búfer |
| Marcas de tiempo inconsistentes | Desviación del reloj del sistema | Utilice NTP; iniciar sesiónUTC |
Preguntas frecuentes
¿Debo registrar el token CAPTCHA en la pista de auditoría?
No. Los tokens son temporales (caducan en 60 a 300 segundos) y no tienen valor de auditoría. Registrarlos aumenta el almacenamiento sin ningún beneficio.
¿Puedo utilizar registros de auditoría para la conciliación de facturación?
Sí. Compare los totales de su registro de auditoría con el panel de uso de CaptchaAI para verificar la precisión de la facturación.
¿Qué período de retención debo establecer?
90 días es el estándar para los registros de auditoría operativa. Para el registro basado en el cumplimiento, consulte los requisitos de su industria (SOC 2, GDPR, HIPAA).
Artículos relacionados
- Seguimiento de resolución de Captcha sin servidor de Dynamodb
Añade responsabilidad a cada resolución CAPTCHA: obtén tu API key de CaptchaAI.
Guías relacionadas:
- Panel de monitoreo de uso
- Registro estructurado de operaciones CAPTCHA