DevOps ve Ölçeklendirme

CAPTCHA Çözme Günlük Analizi için ELK Yığını

CAPTCHA işlem hattınız binlerce görevi işlediğinde grep ölçeklenmez. ELK Stack (Elasticsearch, Logstash, Kibana), çözüm günlüklerini aramanıza, toplamanıza ve görselleştirmenize, hata modellerini bulmanıza, gecikme eğilimlerini izlemenize ve sorunları saniyeler içinde teşhis etmenize olanak tanır.

Mimarlık

[CAPTCHA Workers] → JSON logs → [Filebeat] → [Logstash] → [Elasticsearch]
                                                                ↓
                                                           [Kibana]

Yapılandırılmış Günlük Kaydı

Python – JSON Günlük Çıktısı

import os
import json
import time
import logging
import sys
import requests

API_KEY = os.environ["CAPTCHAAI_API_KEY"]


class JSONFormatter(logging.Formatter):
    def format(self, record):
        log_entry = {
            "timestamp": self.formatTime(record),
            "level": record.levelname,
            "logger": record.name,
            "message": record.getMessage(),
        }
        # Add extra fields
        if hasattr(record, "captcha_id"):
            log_entry["captcha_id"] = record.captcha_id
        if hasattr(record, "captcha_type"):
            log_entry["captcha_type"] = record.captcha_type
        if hasattr(record, "solve_time"):
            log_entry["solve_time"] = record.solve_time
        if hasattr(record, "error_code"):
            log_entry["error_code"] = record.error_code
        if hasattr(record, "target_url"):
            log_entry["target_url"] = record.target_url
        if hasattr(record, "poll_count"):
            log_entry["poll_count"] = record.poll_count
        return json.dumps(log_entry)


# Configure logger
logger = logging.getLogger("captchaai")
logger.setLevel(logging.INFO)
handler = logging.StreamHandler(sys.stdout)
handler.setFormatter(JSONFormatter())
logger.addHandler(handler)

session = requests.Session()


def solve_captcha(sitekey, pageurl, captcha_type="recaptcha_v2"):
    extra = {"captcha_type": captcha_type, "target_url": pageurl}

    # Submit
    resp = session.post("https://ocr.captchaai.com/in.php", data={
        "key": API_KEY,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": pageurl,
        "json": 1
    })
    data = resp.json()

    if data.get("status") != 1:
        logger.error("Submit failed", extra={
            **extra, "error_code": data.get("request")
        })
        return {"error": data.get("request")}

    captcha_id = data["request"]
    extra["captcha_id"] = captcha_id
    logger.info("Task submitted", extra=extra)

    # Poll
    start = time.time()
    poll_count = 0
    for _ in range(60):
        time.sleep(5)
        poll_count += 1
        result = session.get("https://ocr.captchaai.com/res.php", params={
            "key": API_KEY, "action": "get", "id": captcha_id, "json": 1
        }).json()

        if result.get("status") == 1:
            elapsed = round(time.time() - start, 2)
            logger.info("Solve success", extra={
                **extra,
                "solve_time": elapsed,
                "poll_count": poll_count
            })
            return {"solution": result["request"]}

        if result.get("request") != "CAPCHA_NOT_READY":
            logger.error("Solve failed", extra={
                **extra,
                "error_code": result.get("request"),
                "poll_count": poll_count
            })
            return {"error": result.get("request")}

    logger.error("Solve timeout", extra={
        **extra,
        "error_code": "TIMEOUT",
        "poll_count": poll_count
    })
    return {"error": "TIMEOUT"}

JavaScript – Yapılandırılmış Günlük Kaydı

const axios = require("axios");

const API_KEY = process.env.CAPTCHAAI_API_KEY;

function log(level, message, fields = {}) {
  const entry = {
    timestamp: new Date().toISOString(),
    level,
    message,
    service: "captcha-worker",
    ...fields,
  };
  console.log(JSON.stringify(entry));
}

async function solveCaptcha(sitekey, pageurl, captchaType = "recaptcha_v2") {
  const fields = { captchaType, targetUrl: pageurl };

  const submitResp = await axios.post("https://ocr.captchaai.com/in.php", null, {
    params: {
      key: API_KEY, method: "userrecaptcha",
      googlekey: sitekey, pageurl, json: 1,
    },
  });

  if (submitResp.data.status !== 1) {
    log("error", "Submit failed", { ...fields, errorCode: submitResp.data.request });
    return { error: submitResp.data.request };
  }

  const captchaId = submitResp.data.request;
  fields.captchaId = captchaId;
  log("info", "Task submitted", fields);

  const startTime = Date.now();
  let pollCount = 0;

  for (let i = 0; i < 60; i++) {
    await new Promise((r) => setTimeout(r, 5000));
    pollCount++;

    const pollResp = await axios.get("https://ocr.captchaai.com/res.php", {
      params: { key: API_KEY, action: "get", id: captchaId, json: 1 },
    });

    if (pollResp.data.status === 1) {
      const solveTime = ((Date.now() - startTime) / 1000).toFixed(2);
      log("info", "Solve success", { ...fields, solveTime: parseFloat(solveTime), pollCount });
      return { solution: pollResp.data.request };
    }

    if (pollResp.data.request !== "CAPCHA_NOT_READY") {
      log("error", "Solve failed", { ...fields, errorCode: pollResp.data.request, pollCount });
      return { error: pollResp.data.request };
    }
  }

  log("error", "Solve timeout", { ...fields, errorCode: "TIMEOUT", pollCount });
  return { error: "TIMEOUT" };
}

module.exports = { solveCaptcha };

Dosya Atışı Yapılandırması

# filebeat.yml
filebeat.inputs:

  - type: log
    paths:

      - /var/log/captcha-worker/*.log
    json:
      keys_under_root: true
      add_error_key: true
      message_key: message

output.logstash:
  hosts: ["logstash:5044"]

Logstash Boru Hattı

# logstash-captcha.conf
input {
  beats {
    port => 5044
  }
}

filter {
  # Parse JSON logs
  json {
    source => "message"
    target => "captcha"
  }

  # Add computed fields
  if [captcha][solve_time] {
    mutate {
      add_field => {
        "solve_time_bucket" => "fast"
      }
    }
    if [captcha][solve_time] > 30 {
      mutate { update => { "solve_time_bucket" => "medium" } }
    }
    if [captcha][solve_time] > 90 {
      mutate { update => { "solve_time_bucket" => "slow" } }
    }
  }

  # Extract date
  date {
    match => ["[captcha][timestamp]", "ISO8601"]
    target => "@timestamp"
  }
}

output {
  elasticsearch {
    hosts => ["elasticsearch:9200"]
    index => "captcha-logs-%{+YYYY.MM.dd}"
  }
}

Elasticsearch Dizin Şablonu

{
  "index_patterns": ["captcha-logs-*"],
  "template": {
    "settings": {
      "number_of_shards": 1,
      "number_of_replicas": 0
    },
    "mappings": {
      "properties": {
        "captcha_type": { "type": "keyword" },
        "captcha_id": { "type": "keyword" },
        "error_code": { "type": "keyword" },
        "solve_time": { "type": "float" },
        "poll_count": { "type": "integer" },
        "target_url": { "type": "keyword" },
        "level": { "type": "keyword" },
        "message": { "type": "text" }
      }
    }
  }
}

Kibana Kontrol Panelleri

Paneli Görselleştirme Sorgu
Başarı oranını çöz Metrik level:info AND message:"Solve success" / toplam
Hata dökümü Pasta grafiği level:error, error_code'ye göre gruplandırılmıştır
Zaman içindeki gecikme Çizgi grafiği Zaman içindeki ortalama solve_time
Zaman içindeki hatalar Çubuk grafik 5 dakikalık kova başına level:error'yi sayın
En yavaş çözümler Veri tablosu solve_time'ye göre azalan şekilde ilk 10
Kuyruk etkinliği Alan grafiği message'ye göre say ("Görev gönderildi" ve "Başarıyı çöz")

Yararlı Sorgular

# All errors in the last hour
level:error AND @timestamp:[now-1h TO now]

# Timeout errors for reCAPTCHA
error_code:TIMEOUT AND captcha_type:recaptcha_v2

# Slow solves (> 60 seconds)
solve_time:>60

# Errors for a specific target URL
level:error AND target_url:"example.com"

# Specific CAPTCHA ID investigation
captcha_id:"73519847"

Sorun giderme

Sorun Sebep Düzeltme
Günlükler Kibana'da görünmüyor Filebeat günlükleri göndermiyor Filebeat günlüklerini kontrol edin; yol modeli eşleşmelerini doğrulayın
JSON ayrıştırma hataları Günlük dosyasındaki JSON olmayan satırlar json.keys_under_root'yi Filebeat'e ekleyin; günlükçü çıkışını düzelt
Çok fazla endeks ILM'siz günlük endeks 30 günlük saklamayla Index Lifecycle Management'ı kurun
Yavaş sorgular Eksik anahtar kelime eşlemesi Filtrelenebilir alanlar için text değil keyword türünü kullanın

SSS

CAPTCHA günlüklerini ne kadar süre saklamalıyım?

Operasyonel günlükler için 30 gün. Trend analizine ihtiyacınız varsa 90 gün. Eski endeksleri otomatik olarak silmek için Elasticsearch ILM'yi kullanın.

Elasticsearch yerine OpenSearch'ü kullanabilir miyim?

Evet. OpenSearch, Elasticsearch ile API uyumludur. Logstash çıktı eklentisi, Filebeat ve Kibana alternatifleri (OpenSearch Kontrol Panelleri) aynı şekilde çalışır.

CAPTCHA çözüm metnini kaydetmeli miyim?

Hayır. Çözümler, tanı değeri olmayan tek kullanımlık tokenlardır. Bunları günlüğe kaydetmek depolama maliyetini artırır ve güvenlik sorunları yaratabilir. Yalnızca meta verileri günlüğe kaydedin (kimlik, tür, gecikme, durum).

Sonraki Adımlar

CAPTCHA günlüklerinizi arayın ve analiz edin —CaptchaAI API anahtarınızı alınve ELK'yi kurduk.

İlgili kılavuzlar:

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