Yapılandırılmış loglama, her CAPTCHA çözümünü — görev kimliği, tür, çözüm süresi ve hata kodu dahil — tek satırlık aranabilir bir JSON kaydına dönüştürür. Böylece bir çözüm başarısız olduğunda düz metin logları grep ile karıştırmak yerine ilgili alana göre filtreler, nedenini saniyeler içinde bulursunuz. Bu rehber, CaptchaAI çözüm akışına Python ve Node.js için üretime hazır JSON loglamayı nasıl ekleyeceğinizi adım adım gösterir.
Fark, özellikle üretimde belli olur: müşteri otomasyonunu teslim tarihiyle yetiştiren bir geliştirici için, gece 03:00'te (Europe/Istanbul) devreye giren bir alarm, düz metin bir yığın yerine task_id ile tek bir isteğe indirilebilen filtrelenmiş bir sorguya dönüşür.
Düz metin loglar neden yetersiz kalır
Düz metin bir satır insan gözü için okunabilir ama makine için işe yaramaz: alana göre filtreleyemez, iki olay arasında ilişki kuramaz, otomatik alarm üretemezsiniz. JSON ise her alanı ayrı bir anahtar yapar.
| Düz metin | Yapılandırılmış JSON |
|---|---|
Captcha solved in 12.3s |
{"event":"captcha_solved","task_id":"abc123","type":"recaptcha_v2","solve_time_ms":12300} |
| Ayrıştırılması zor | Makine tarafından okunabilir |
| Yalnızca Grep araması | Herhangi bir alana göre filtrele |
| Korelasyon yok | task_id; gönderme → sorgulama → enjeksiyon adımlarını birbirine bağlar |
Python: structlog ile JSON loglama
Aşağıdaki yapılandırma her log satırına ISO zaman damgası ve seviye ekleyip çıktıyı doğrudan JSON olarak yazar — ek bir formatlayıcıya gerek kalmadan stdout'a aranabilir loglar dökülür.
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()
Çözüm yaşam döngüsünü loglamak
log.bind() ile bağlam alanlarını (captcha türü, hedef URL, kısaltılmış sitekey) bir kez bağlarsınız; sonraki her satır bu alanları otomatik taşır. Görev kimliği geldiği anda onu da bağlayın — böylece gönderme, sorgulama ve sonuç tek bir task_id üzerinden ilişkilendirilir.
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
Çıktı:
{"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"}
Üç satırın da aynı task_id değerini taşıdığına dikkat edin; tek bir çözümün tüm yaşam döngüsünü bu alanla filtreleyebilirsiniz.
Node.js: pino ile aynı akış
Node.js tarafında pino, düşük ek yükle aynı JSON çıktısını üretir. Kritik nokta, olay adlarını (captcha_submitted, captcha_solved, captcha_solve_failed) Python tarafıyla birebir aynı tutmaktır — iki dildeki servisler tek bir log şemasında toplanır.
const pino = require('pino');
const log = pino({
level: 'info',
timestamp: pino.stdTimeFunctions.isoTime,
});
Çözüm yaşam döngüsünü loglamak
log.child(), structlog'daki log.bind() ile aynı işi görür: bağlam alanlarını türetilmiş bir logger'a bağlar, görev kimliği geldiğinde ikinci bir child ile onu ekler.
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;
}
Log alanları referansı
Her satırda tutarlı bir alan şeması kullanın. Aşağıdaki alanlar, çözüm akışını izlemek ve alarm kurmak için gereken minimum kümedir.
| Alan | Tür | Açıklama |
|---|---|---|
event |
dize | Etkinlik adı: captcha_submitted, captcha_solved, vb. |
task_id |
dize | Korelasyon için CaptchaAI görev kimliği |
captcha_type |
dize | recaptcha_v2, turnstile, image, vb. |
site_url |
dize | Hedef sayfa URL'si |
solve_time_ms |
tamsayı | Gönderimden çözüme kadar geçen toplam süre |
poll_attempts |
tamsayı | Yapılan sorgulama isteği sayısı |
error |
dize | CaptchaAI'den gelen hata kodu |
token_length |
tamsayı | Dönen token'ın uzunluğu |
Filtreleme ve alarm kurma
JSON loglar bir kez oluştuğunda, isterseniz yerelde jq ile, isterseniz merkezî bir toplayıcıda (ELK, Grafana Loki gibi) alana göre sorgulayabilirsiniz. Standart çıktıya JSON yazmak, altyapıdan bağımsız kalmanın en pratik yoludur.
Son bir saatteki başarısız çözümleri bulun
# With jq
cat captcha.log | jq 'select(.level == "error" and .event == "captcha_solve_failed")'
Hata oranı yükselince alarm üretin
Kayan bir pencerede başarı/başarısızlık oranını takip edin; eşik aşıldığında ayrı bir captcha_error_rate_high olayı yazın. Bu olay, alarm sisteminizin tetikleyicisi olur.
# 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),
)
Çözüm sürelerini thread planlamasına bağlayın
solve_time_ms alanının yüzdelik değerlerini (p50, p95) izlemek yalnızca hata ayıklama için değil, kapasite planlaması için de işe yarar. Ortalama çözüm süreniz ve eşzamanlı istek yoğunluğunuz, hangi plana ihtiyaç duyduğunuzu belirler: CaptchaAI thread bazlı faturalandırır, çözüm başına değil. Düşük hacimli bir QA akışı için BASIC ($15/ay, 5 thread) yeterken, yoğunluk arttıkça loglardaki eşzamanlılık verisi sizi ADVANCE ($90/ay, 50 thread) gibi bir plana yönlendirir. Fiyatlar USD üzerinden sabittir; TL kur oynaklığından etkilenmeyen öngörülebilir aylık maliyet, Türkiye'deki geliştiriciler için gerçek bir avantajdır.
Sorun giderme
| Sorun | Sebep | Düzeltme |
|---|---|---|
| Loglar çok ayrıntılı | Her sorgulama girişiminin loglanması | Yalnızca gönderilen, çözülen ve başarısız olayları loglayın |
| Olaylar ilişkilendirilemiyor | Eksik görev kimliği | task_id'yi log.bind() veya log.child() ile erken bağlayın |
| Loglar aranamıyor | Düz metin formatı | structlog veya pino ile JSON'a geçin |
| Loglardaki hassas veriler | Tam API anahtarının loglanması | API anahtarlarını asla loglamayın; sitekey'leri kısaltın |
Son satır önemli: loglarda tam API anahtarı veya kişisel veri tutmak yalnızca güvenlik değil, KVKK açısından da risktir. Sırları maskeleyin, sitekey'leri kısaltın ve logları yetkili QA/veri toplama akışları kapsamında saklayın.
Sık sorulan sorular
structlog mu pino mı kullanmalıyım?
Dil belirleyicidir: Python servislerinde structlog, Node.js servislerinde pino doğal seçenektir. Önemli olan kütüphane değil, iki tarafta da aynı olay adlarını ve alan şemasını kullanmanızdır; böylece loglar tek bir sistemde birleşir.
task_id'yi loglara ne zaman bağlamalıyım?
Görev kimliği gönderim yanıtıyla döner dönmez, sorgulama döngüsüne girmeden önce bağlayın. Erken bağlarsanız gönderme, sorgulama ve sonuç satırlarının hepsi aynı task_id ile filtrelenebilir.
JSON logları merkezî bir sisteme nasıl gönderirim?
Standart çıktıya JSON yazın; ELK, Grafana Loki veya benzeri bir toplayıcı bu akışı olduğu gibi alır. Uygulama içinde ağ çağrısı yapmak yerine stdout'a yazmak hem daha basittir hem de altyapıdan bağımsız kalmanızı sağlar.
Token'ın tamamını loglamak güvenli mi?
Hayır. Token'ın tamamı yerine yalnızca uzunluğunu (token_length) loglayın. Token'lar 500'den fazla karakter olabilir; tamamını yazmak hem gereksiz log hacmi yaratır hem de hassas veri sızıntısı riskini artırır. CAPCHA_NOT_READY gibi sorgulama sırasında beklenen ara durumları da hiç loglamayın; yalnızca nihai sonucu yazın.
CaptchaAI ile gözlemlenebilir CAPTCHA akışları kurun
API anahtarınızı captchaai.com üzerinden alın ve çözüm akışınızın her adımını ilk gününden itibaren JSON loglarıyla izleyin.