Tutorials

CAPTCHA İşlemleri için Yapılandırılmış Günlük Kaydı

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.


İlgili kılavuzlar

Bu makale için yorumlar devre dışı bırakılmıştır.