تبدأ مراقبة CaptchaAI في Datadog من أربع إشارات تحسم استقرار خط حل الكابتشا: معدل النجاح، وزمن الحل عند النسبة المئوية p95، وتوزيع الأخطاء، ورصيد الحساب. حين ترسل هذه الإشارات إلى Datadog عبر وكيل DogStatsD، تنتقل من اكتشاف العطل بعد توقّف الأتمتة إلى تنبيه يصلك قبل أن يتأثر أي طلب فعلي. يوضّح هذا الدليل ما تُرسله من مقاييس، وكيفية دمجها في Python وNode.js، ثم بناء لوحة تحكم وضبط تنبيهات تعمل في الإنتاج.
ما المقاييس التي يجب رصدها في خط حل الكابتشا؟
حدّد أولًا المقاييس السبعة التي تكشف صحة الأتمتة. لكل مقياس نوعه في Datadog: العدّاد للأحداث التراكمية، والرسم البياني للتوزيع، والمقياس اللحظي للحالة الآنية.
| المقياس | النوع | لماذا يهمّ؟ |
|---|---|---|
captcha.solve.count |
عدّاد | إجمالي المهام المُرسلة |
captcha.solve.success |
عدّاد | الحلول الناجحة |
captcha.solve.error |
عدّاد | الحلول الفاشلة (مصنّفة حسب نوع الخطأ) |
captcha.solve.latency |
رسم بياني | الزمن من الإرسال حتى الحل |
captcha.queue.depth |
مقياس لحظي | المهام المعلّقة في قائمة الانتظار |
captcha.balance |
مقياس لحظي | رصيد الحساب المتبقي |
captcha.worker.active |
مقياس لحظي | عمليات العمّال النشطة |
وسم كل مقياس بنوع الكابتشا (captcha_type) يتيح لك لاحقًا فصل أداء reCAPTCHA v2 و Cloudflare Turnstile و GeeTest v3 في اللوحة نفسها.
سيناريو من السوق: فريق مراقبة أسعار خليجي تحت الذروة
خذ فريق أتمتة في متجر إلكتروني خليجي على خطة ADVANCE (90$ شهريًا، 50 خيط معالجة). في ذروة الجمعة البيضاء تمتلئ الخيوط الخمسون فتنمو captcha.queue.depth ويقفز زمن الحل عند p95؛ ومع تنبيه على هذين المقياسين يصل الإنذار خلال دقائق فيوسّع الفريق العمّال أو يرفع خطته قبل انكسار خط البيانات.
ربط CaptchaAI بـ Datadog عبر DogStatsD في Python
الطريقة الأنظف في Python مُزخرِف (decorator) يلفّ دالة الحل، فيقيس زمن كل محاولة ويصنّفها بين نجاح وخطأ، ويرسل الرصيد من res.php:
import os
import time
import functools
import requests
from datadog import initialize, statsd
# Initialize Datadog
initialize(
statsd_host=os.environ.get("DD_AGENT_HOST", "localhost"),
statsd_port=int(os.environ.get("DD_DOGSTATSD_PORT", "8125"))
)
API_KEY = os.environ["CAPTCHAAI_API_KEY"]
session = requests.Session()
def track_captcha_metrics(captcha_type="recaptcha_v2"):
"""Decorator to track solve metrics."""
def decorator(func):
@functools.wraps(func)
def wrapper(*args, **kwargs):
tags = [f"captcha_type:{captcha_type}"]
statsd.increment("captcha.solve.count", tags=tags)
start = time.time()
try:
result = func(*args, **kwargs)
elapsed = time.time() - start
if "solution" in result:
statsd.increment("captcha.solve.success", tags=tags)
statsd.histogram("captcha.solve.latency", elapsed, tags=tags)
else:
error = result.get("error", "unknown")
statsd.increment(
"captcha.solve.error",
tags=tags + [f"error:{error}"]
)
return result
except Exception as e:
statsd.increment(
"captcha.solve.error",
tags=tags + [f"error:{type(e).__name__}"]
)
raise
return wrapper
return decorator
@track_captcha_metrics(captcha_type="recaptcha_v2")
def solve_recaptcha(sitekey, pageurl):
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:
return {"error": data.get("request")}
captcha_id = data["request"]
for _ in range(60):
time.sleep(5)
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:
return {"solution": result["request"]}
if result.get("request") != "CAPCHA_NOT_READY":
return {"error": result.get("request")}
return {"error": "TIMEOUT"}
def report_balance():
"""Send balance as a gauge metric."""
resp = session.get("https://ocr.captchaai.com/res.php", params={
"key": API_KEY, "action": "getbalance", "json": 1
})
data = resp.json()
if data.get("status") == 1:
balance = float(data["request"])
statsd.gauge("captcha.balance", balance)
return balance
return None
def report_queue_depth(depth):
"""Report current queue depth."""
statsd.gauge("captcha.queue.depth", depth)
def report_worker_count(active, total):
"""Report worker health."""
statsd.gauge("captcha.worker.active", active)
statsd.gauge("captcha.worker.total", total)
إرسال المقاييس من Node.js إلى Datadog
في Node.js توفّر مكتبة hot-shots عميل StatsD متوافقًا مع DogStatsD. يوحّد prefix بادئة المقاييس، ويسم globalTags البيئة تلقائيًا لفصل الإنتاج عن التطوير:
const { StatsD } = require("hot-shots");
const axios = require("axios");
const API_KEY = process.env.CAPTCHAAI_API_KEY;
const dogstatsd = new StatsD({
host: process.env.DD_AGENT_HOST || "localhost",
port: parseInt(process.env.DD_DOGSTATSD_PORT || "8125", 10),
prefix: "captcha.",
globalTags: [`env:${process.env.NODE_ENV || "development"}`],
});
async function solveCaptchaWithMetrics(sitekey, pageurl, captchaType = "recaptcha_v2") {
const tags = [`captcha_type:${captchaType}`];
dogstatsd.increment("solve.count", 1, tags);
const startTime = Date.now();
try {
const result = await solveCaptcha(sitekey, pageurl);
const elapsed = (Date.now() - startTime) / 1000;
if (result.solution) {
dogstatsd.increment("solve.success", 1, tags);
dogstatsd.histogram("solve.latency", elapsed, tags);
} else {
dogstatsd.increment("solve.error", 1, [...tags, `error:${result.error}`]);
}
return result;
} catch (err) {
dogstatsd.increment("solve.error", 1, [...tags, `error:${err.message}`]);
throw err;
}
}
async function solveCaptcha(sitekey, pageurl) {
const submitResp = await axios.post("https://ocr.captchaai.com/in.php", null, {
params: {
key: API_KEY,
method: "userrecaptcha",
googlekey: sitekey,
pageurl: pageurl,
json: 1,
},
});
if (submitResp.data.status !== 1) {
return { error: submitResp.data.request };
}
const captchaId = submitResp.data.request;
for (let i = 0; i < 60; i++) {
await new Promise((r) => setTimeout(r, 5000));
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) return { solution: pollResp.data.request };
if (pollResp.data.request !== "CAPCHA_NOT_READY") {
return { error: pollResp.data.request };
}
}
return { error: "TIMEOUT" };
}
async function reportBalance() {
try {
const resp = await axios.get("https://ocr.captchaai.com/res.php", {
params: { key: API_KEY, action: "getbalance", json: 1 },
});
if (resp.data.status === 1) {
const balance = parseFloat(resp.data.request);
dogstatsd.gauge("balance", balance);
return balance;
}
} catch (err) {
console.error("Balance check failed:", err.message);
}
return null;
}
// Report balance every minute
setInterval(reportBalance, 60000);
module.exports = { solveCaptchaWithMetrics, reportBalance };
بناء لوحة مراقبة CaptchaAI في Datadog
بعد تدفّق المقاييس، تحتاج لوحة تجمع الصورة في نظرة واحدة: النجاح مقابل الخطأ، وزمن الحل عند p50 و p95 و p99، والرصيد، وعمق الطابور. استورد قالب JSON التالي في Datadog:
{
"title": "CaptchaAI Pipeline",
"widgets": [
{
"definition": {
"type": "timeseries",
"title": "Solve Rate (Success vs Error)",
"requests": [
{"q": "sum:captcha.solve.success{*}.as_count()"},
{"q": "sum:captcha.solve.error{*}.as_count()"}
]
}
},
{
"definition": {
"type": "timeseries",
"title": "Solve Latency (p50, p95, p99)",
"requests": [
{"q": "avg:captcha.solve.latency{*}"},
{"q": "percentile:captcha.solve.latency{*},0.95"},
{"q": "percentile:captcha.solve.latency{*},0.99"}
]
}
},
{
"definition": {
"type": "query_value",
"title": "API Balance",
"requests": [{"q": "avg:captcha.balance{*}"}]
}
},
{
"definition": {
"type": "timeseries",
"title": "Queue Depth",
"requests": [{"q": "avg:captcha.queue.depth{*}"}]
}
}
]
}
ضبط التنبيهات قبل توقّف خط الأنابيب
اللوحة تُظهر الحالة، لكن التنبيهات ما يوقظك في الوقت المناسب. اضبط عتبات واقعية: تحذير مبكر، وإنذار حرِج عند توقّف الخدمة. الجدول التالي مجموعة أولية جيدة:
| التنبيه | الشرط | الشدّة |
|---|---|---|
| رصيد منخفض | captcha.balance < 10 |
تحذير |
| رصيد حرج | captcha.balance < 2 |
حرِج |
| ارتفاع معدل الأخطاء | معدل الخطأ > 10% خلال 5 دقائق | تحذير |
| قفزة في زمن الحل | p95 > 120 ثانية خلال 10 دقائق | تحذير |
| تراكم قائمة الانتظار | عمق الطابور > 100 وينمو خلال 5 دقائق | تحذير |
| توقّف العمّال | captcha.worker.active == 0 |
حرِج |
أنشئ أي تنبيه عبر واجهة Datadog البرمجية. المثال التالي يعرّف مراقب انخفاض الرصيد بصيغة YAML:
# Datadog monitor definition (API create)
- type: metric alert
name: "CaptchaAI Low Balance"
query: "avg(last_5m):avg:captcha.balance{*} < 10"
message: "CaptchaAI balance is low: {{value}}. Top up to avoid solve failures."
tags:
- team:scraping
- service:captcha
معالجة الأعطال الشائعة
معظم مشكلات المراقبة سببها وكيل DogStatsD أو صيغة الوسوم، لا خدمة الحل. راجع الجدول التالي عند غياب أي مقياس:
| المشكلة | السبب المحتمل | الإجراء |
|---|---|---|
| المقاييس لا تظهر في Datadog | وكيل DogStatsD لا يعمل أو المنفذ خاطئ | تحقّق من DD_AGENT_HOST وDD_DOGSTATSD_PORT وتأكّد من تشغيل حاوية الوكيل عبر docker ps |
| الرسم البياني لزمن الحل فارغ | لا حلول ناجحة يجري تتبّعها | تأكّد من استدعاء statsd.histogram() على مسار النجاح فقط |
| الوسوم مفقودة أو غير قابلة للتصفية | صيغة وسم خاطئة | استخدم صيغة key:value دون مسافات داخل الوسم |
| مقاييس مكرّرة أو أرقام مضخّمة | أكثر من مُبلِّغ يعمل على الرصيد | شغّل مُبلِّغ رصيد واحدًا فقط لكل عملية نشر |
أسئلة شائعة
كيف أفصل مقاييس كل نوع كابتشا داخل Datadog؟
مرّر captcha_type عند وسم كل مقياس، ثم صفِّ اللوحة حسبه لتقارن زمن حل reCAPTCHA v2 مقابل Cloudflare Turnstile مقابل GeeTest v3 دون خلط الأرقام.
ما القيمة المناسبة لعتبة تنبيه الرصيد؟
اضبطها على معدل استهلاكك لا برقم ثابت. القاعدة أن يمنحك حدّ التحذير مهلة يوم عمل كامل لإعادة الشحن قبل بلوغ الحدّ الحرِج.
متى يُطلق تنبيه زمن الحل عند p95؟
راقب خطك الأساسي لأسبوع، ثم اضبط العتبة أعلى من ذروتك الطبيعية بهامش معقول. عتبة 120 ثانية بداية جيدة، واخفضها إن كان زمن حلك المعتاد أقصر.
هل أحتاج وكيل Datadog على كل خادم عامل؟
لا؛ شغّل وكيل DogStatsD واحدًا لكل مضيف (host)، وترسل جميع العمّال عليه مقاييسها إلى الوكيل المحلي الذي يمرّرها إلى Datadog.
الخطوات التالية
- البدء السريع مع CaptchaAI: حلّ أول كابتشا في 5 دقائق
- حلّ reCAPTCHA v2 عبر الـ API خطوة بخطوة
- حلّ Cloudflare Turnstile عبر الـ API
- حلّ GeeTest v3 عبر الـ API