Ketika solve CAPTCHA gagal pada jam 3 pagi, log teks biasa seperti "Error solving captcha" tidak membantu. Log JSON terstruktur dengan ID tugas, jenis captcha, waktu solve, dan kode kesalahan memungkinkan Anda memfilter, mencari, dan memperingatkan apa yang salah.
Mengapa logging terstruktur
| Teks biasa | JSON terstruktur |
|---|---|
Captcha solved in 12.3s |
{"event":"captcha_solved","task_id":"abc123","type":"recaptcha_v2","solve_time_ms":12300} |
| Sulit untuk diurai | Dapat dibaca mesin |
| Pencarian khusus Grep | Filter berdasarkan bidang apa pun |
| Tidak ada korelasi | Tautan ID tugas mengirimkan -> jajak pendapat -> menyuntikkan |
Python: structlog
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()
Mencatat siklus hidup solve
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
Keluaran:
{"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,
});
Mencatat siklus hidup solve
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;
}
Referensi bidang log
| Bidang | Ketik | Deskripsi |
|---|---|---|
event |
tali | Nama acara: captcha_submitted, captcha_solved, dll. |
task_id |
tali | ID tugas CaptchaAI untuk korelasi |
captcha_type |
tali | recaptcha_v2, turnstile, image, dll. |
site_url |
tali | URL halaman target |
solve_time_ms |
bilangan bulat | Total waktu dari pengiriman hingga solve |
poll_attempts |
bilangan bulat | Jumlah request jajak pendapat yang dibuat |
error |
tali | Kode kesalahan dari CaptchaAI |
token_length |
bilangan bulat | Panjang token yang dikembalikan |
Memfilter dan memperingatkan
Temukan semua kegagalan dalam satu jam terakhir
# With jq
cat captcha.log | jq 'select(.level == "error" and .event == "captcha_solve_failed")'
Peringatan tentang tingkat kegagalan yang tinggi
# 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),
)
Pemecahan Masalah
| Masalah | Penyebab | Solusi |
|---|---|---|
| Log terlalu bertele-tele | Mencatat setiap upaya jajak pendapat | Log hanya mengirimkan, menyelesaikan, dan peristiwa yang gagal |
| Tidak dapat menghubungkan peristiwa | ID tugas tidak ada | Ikat task_id lebih awal dengan log.bind() atau log.child() |
| Log tidak dapat dicari | Format teks biasa | Beralih ke JSON dengan structlog atau pino |
| Data sensitif dalam log | Mencatat kunci API lengkap | Jangan pernah mencatat kunci API; potong sitekey |
Pertanyaan Umum
Haruskah saya mencatat token CAPTCHA?
Catat panjang token, bukan token lengkap. Token dapat berisi 500+ karakter dan menambahkan volume log yang tidak diperlukan.
Level log mana untuk CAPCHA_NOT_READY?
Jangan mencatatnya sama sekali – hal ini diperkirakan terjadi pada saat pemungutan suara. Hanya catat hasil akhirnya.
Bangun alur kerja CAPTCHA yang dapat diamati dengan CaptchaAI
Dapatkan kunci API Anda dicaptchaai.com.