Lorsque votre organisation résout quotidiennement des milliers de CAPTCHA, vous avez besoin d’un enregistrement de chaque demande. Les journaux d'audit répondent à des questions telles que : Qui a déclenché cette résolution ? C'était pour quel site ? Combien ça a coûté ? Quand est-ce arrivé? Ce guide montre comment mettre en œuvre une journalisation d'audit complète pourCaptchaAIopérations.
Quoi enregistrer
Chaque résolution CAPTCHA doit enregistrer :
| Champ | Objectif | Exemple |
|---|---|---|
timestamp |
Quand la demande a été faite | 2026-04-04T14:30:00Z |
request_id |
Identifiant unique pour cette résolution | uuid4() |
captcha_type |
Méthode CAPTCHA utilisée | userrecaptcha |
target_site |
URL de la page en cours de résolution | https://example.com/login |
task_id |
ID de tâche CaptchaAI | 73829451 |
status |
Résultat | solved, failed, timeout |
solve_time_ms |
Temps entre la soumission et le résultat | 18432 |
error_code |
Erreur en cas d'échec | ERROR_CAPTCHA_UNSOLVABLE |
initiator |
Qui ou quoi a déclenché la résolution | scraper-job-42 |
cost |
Coût estimé | 0.003 |
Ne consignez pas : les clés API, les jetons CAPTCHA (ils sont temporaires) ou les informations personnelles identifiables provenant des sites cibles.
Implémentation 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",
)
Sortie du journal d'audit (format 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}
Implémentation 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;
}
}
Interrogation des journaux d'audit
Résumé quotidien
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")
Rétention et stockage
| Volume | Taille du journal quotidien | Stockage mensuel | Recommandation |
|---|---|---|---|
| 100 solutions/day | ~30 Ko | ~1 Mo | Fichier local |
| 1 000 solutions/day | ~300 Ko | ~10 Mo | Fichier local + rotation |
| 10 000 solutions/day | ~3 Mo | ~100 Mo | Expédier à l'agrégateur de journaux |
| 100 000 solutions/day | ~30 Mo | ~1 Go | Journalisation centralisée (ELK, Datadog) |
Dépannage
| Problème | Parce que | Corriger |
|---|---|---|
| Le fichier journal devient trop volumineux | Aucune rotation configurée | Utilisez RotatingFileHandler ou logrotate |
| Dossiers d'audit manquants | Exception avant la journalisation | Connectez-vous au bloc finally |
| Écritures lentes à volume élevé | Fichier synchrone I/O | Utiliser des écritures de fichiers asynchrones ou un tampon |
| Horodatages incohérents | Dérive de l'horloge système | Utilisez NTP ; se connecter en UTC |
FAQ
Dois-je enregistrer le jeton CAPTCHA dans la piste d'audit ?
Non. Les jetons sont temporaires (expirent dans 60 à 300 secondes) et n'ont aucune valeur d'audit. Leur journalisation augmente le stockage sans avantage.
Puis-je utiliser les journaux d’audit pour le rapprochement des factures ?
Oui. Comparez les totaux de vos journaux d'audit avec le tableau de bord d'utilisation de CaptchaAI pour vérifier l'exactitude de la facturation.
Quelle durée de conservation dois-je définir ?
90 jours est la norme pour les journaux d’audit opérationnels. Pour une journalisation axée sur la conformité, vérifiez les exigences de votre secteur (SOC 2, GDPR, HIPAA).
Articles connexes
- Suivi de résolution de Captcha sans serveur Dynamodb
Prochaines étapes
Ajoutez de la responsabilité à chaque résolution de CAPTCHA –récupérez votre clé API CaptchaAI.
Guides associés :
- Surveillance du tableau de bord d'utilisation
- Journalisation structurée
- Vérification du solde et recharge automatique