Khi quy trình CAPTCHA của bạn xử lý hàng nghìn tác vụ, grep sẽ không mở rộng quy mô. ELK Stack (Elasticsearch, Logstash, Kibana) cho phép bạn tìm kiếm, tổng hợp và trực quan hóa nhật ký giải quyết - tìm mẫu lỗi, theo dõi xu hướng độ trễ và chẩn đoán sự cố trong vài giây.
Kiến trúc
[CAPTCHA Workers] → JSON logs → [Filebeat] → [Logstash] → [Elasticsearch]
↓
[Kibana]
Ghi nhật ký có cấu trúc
Python - Đầu ra nhật ký JSON
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 – Ghi nhật ký có cấu trúc
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 };
Cấu hình filebeat
# 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"]
Đường ống đăng nhập
# 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}"
}
}
Mẫu chỉ mục Elaticsearch
{
"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" }
}
}
}
}
Bảng điều khiển Kibana
| bảng điều khiển | Trực quan hóa | Truy vấn |
|---|---|---|
| Giải quyết tỷ lệ thành công | Số liệu | level:info AND message:"Solve success" / tổng cộng |
| Phân tích lỗi | Biểu đồ hình tròn | level:error được nhóm theo error_code |
| Độ trễ theo thời gian | Biểu đồ đường | solve_time trung bình theo thời gian |
| Lỗi theo thời gian | Biểu đồ thanh | Đếm level:error mỗi nhóm 5 phút |
| Giải quyết chậm nhất | Bảng dữ liệu | Top 10 của solve_time giảm dần |
| Hoạt động xếp hàng | Biểu đồ vùng | Tính theo message ("Nhiệm vụ đã gửi" vs "Giải quyết thành công") |
Truy vấn hữu ích
# 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"
Khắc phục sự cố
| Vấn đề | Nguyên nhân | Cách xử lý |
|---|---|---|
| Nhật ký không xuất hiện ở Kibana | Filebeat không gửi nhật ký | Kiểm tra nhật ký Filebeat; xác minh sự trùng khớp của mẫu đường dẫn |
| Lỗi phân tích cú pháp JSON | Các dòng không phải JSON trong tệp nhật ký | Thêm json.keys_under_root vào Filebeat; sửa đầu ra logger |
| Quá nhiều chỉ số | Chỉ số hàng ngày không có ILM | Thiết lập Quản lý vòng đời chỉ mục với khả năng lưu giữ trong 30 ngày |
| Truy vấn chậm | Thiếu ánh xạ từ khóa | Sử dụng loại keyword cho các trường có thể lọc, không phải text |
Câu hỏi thường gặp
Tôi nên giữ lại nhật ký CAPTCHA trong bao lâu?
30 ngày đối với nhật ký hoạt động. 90 ngày nếu bạn cần phân tích xu hướng. Sử dụng Elaticsearch ILM để tự động xóa các chỉ mục cũ.
Tôi có thể sử dụng OpenSearch thay vì Elaticsearch không?
Vâng. OpenSearch tương thích API với Elaticsearch. Plugin đầu ra Logstash, các lựa chọn thay thế Filebeat và Kibana (Bảng điều khiển OpenSearch) hoạt động theo cách tương tự.
Tôi có nên ghi lại văn bản giải pháp CAPTCHA không?
Không. Giải pháp là các mã thông báo sử dụng một lần không có giá trị chẩn đoán. Việc ghi nhật ký chúng sẽ làm tăng thêm chi phí lưu trữ và có thể tạo ra các vấn đề bảo mật. Chỉ ghi nhật ký siêu dữ liệu (ID, loại, độ trễ, trạng thái).
Các bước tiếp theo
Tìm kiếm và phân tích nhật ký CAPTCHA của bạn —lấy khóa API CaptchaAI của bạnvà thiết lập ELK.
Hướng dẫn liên quan:
- Ghi nhật ký có cấu trúc
- Giám sát cơ quan dữ liệu
- Theo dõi từ xa mở