DevOps والتوسع

مراقبة أداء CaptchaAI عبر New Relic APM: التكامل والتنبيهات

لمراقبة مسار حل CAPTCHA على CaptchaAI داخل New Relic تحتاج إلى ثلاثة عناصر: أحداث مخصصة تسجّل كل عملية حل، ولوحة NRQL تُظهر معدّل النجاح وزمن الحل، وسياسات تنبيه تُنذرك قبل أن يشعر المستخدم بأي بطء. يربط هذا الدليل CaptchaAI بـ New Relic APM في Python وNode.js، فترى كل مرحلة — من إرسال الطلب إلى تسليم الرمز — كبيانات قابلة للقياس على لوحتك بدل أن تكتشف الأعطال من شكاوى المستخدمين لاحقًا.

ماذا تراقب في مسار حل CAPTCHA

يمرّ كل طلب بثلاث مراحل: الإرسال، ثم انتظار الحل عبر الاستطلاع الدوري، ثم استخدام الرمز الناتج. راقب في كل مرحلة المؤشر الذي يكشف اعتلالها مبكرًا — زمن الإرسال وأخطاء الـ API عند الإرسال، ومدة الاستطلاع ونسبة انتهاء المهلة أثناء الانتظار، ومعدّل النجاح عند استخدام الرمز:

[Submit Task] → [Wait for Solution] → [Apply Token]
     ↓                  ↓                   ↓
  Submit latency    Poll duration       Token usage
  API errors        Timeout rate        Success rate

أدوات القياس المخصصة في New Relic مع Python

يلتقط الكود التالي كل عملية حل داخل مهمة خلفية واحدة في New Relic. يسجّل نوع CAPTCHA وعنوان الصفحة كسمات مخصصة، ثم يبعث حدثي CaptchaSolveSuccess وCaptchaSolveError مع زمن الحل وعدد مرات الاستطلاع — وهي البيانات التي ستبني عليها لوحاتك وتنبيهاتك لاحقًا. كما تسجّل الدالة report_balance رصيد الحساب كحدث مستقل حتى تراقبه من اللوحة نفسها:

import os
import time
import requests
import newrelic.agent

API_KEY = os.environ["CAPTCHAAI_API_KEY"]
session = requests.Session()


@newrelic.agent.background_task(name="captcha_solve", group="CaptchaAI")
def solve_captcha(sitekey, pageurl, captcha_type="recaptcha_v2"):
    """Solve a CAPTCHA with full New Relic instrumentation."""
    # Add custom attributes for filtering
    newrelic.agent.add_custom_attributes([
        ("captcha_type", captcha_type),
        ("target_url", pageurl),
    ])

    # Submit phase
    submit_result = _submit_task(sitekey, pageurl, captcha_type)
    if "error" in submit_result:
        newrelic.agent.record_custom_event("CaptchaSolveError", {
            "error": submit_result["error"],
            "phase": "submit",
            "captcha_type": captcha_type,
        })
        return submit_result

    # Poll phase
    captcha_id = submit_result["captcha_id"]
    poll_result = _poll_result(captcha_id, captcha_type)

    # Record solve event
    event_data = {
        "captcha_type": captcha_type,
        "captcha_id": captcha_id,
        "success": "solution" in poll_result,
    }
    if "solution" in poll_result:
        event_data["solve_time"] = poll_result.get("elapsed", 0)
        newrelic.agent.record_custom_event("CaptchaSolveSuccess", event_data)
    else:
        event_data["error"] = poll_result.get("error", "unknown")
        newrelic.agent.record_custom_event("CaptchaSolveError", event_data)

    return poll_result


@newrelic.agent.function_trace(name="captcha_submit")
def _submit_task(sitekey, pageurl, captcha_type):
    payload = {
        "key": API_KEY,
        "method": "userrecaptcha",
        "googlekey": sitekey,
        "pageurl": pageurl,
        "json": 1
    }
    resp = session.post("https://ocr.captchaai.com/in.php", data=payload)
    data = resp.json()

    newrelic.agent.add_custom_attributes([
        ("submit_status", data.get("status")),
    ])

    if data.get("status") != 1:
        return {"error": data.get("request")}
    return {"captcha_id": data["request"]}


@newrelic.agent.function_trace(name="captcha_poll")
def _poll_result(captcha_id, captcha_type):
    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 = time.time() - start
            newrelic.agent.add_custom_attributes([
                ("poll_count", poll_count),
                ("solve_time_seconds", round(elapsed, 2)),
            ])
            return {"solution": result["request"], "elapsed": elapsed}

        if result.get("request") != "CAPCHA_NOT_READY":
            return {"error": result.get("request")}

    return {"error": "TIMEOUT"}


def report_balance():
    """Record balance as a custom event."""
    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"])
        newrelic.agent.record_custom_event("CaptchaBalance", {
            "balance": balance,
            "low": balance < 10,
        })
        return balance
    return None

ضبط وكيل New Relic

فعّل الأحداث المخصصة وتتبّع المعاملات في ملف الإعداد حتى تصل بياناتك إلى New Relic. الخيار custom_insights_events.enabled شرط أساسي لظهور أحداث الحل، بينما يحدّد transaction_threshold أي المعاملات تُحفظ تتبّعاتها الكاملة:

# newrelic.ini
[newrelic]
app_name = CaptchaAI Pipeline
license_key = YOUR_NEW_RELIC_LICENSE_KEY
monitor_mode = true
log_level = info
transaction_tracer.enabled = true
transaction_tracer.transaction_threshold = 5.0
custom_insights_events.enabled = true
custom_insights_events.max_samples_stored = 5000

تكامل New Relic مع Node.js

إن كان مسارك يعمل على Node.js، يوفّر المقطع التالي القياس نفسه عبر startBackgroundTransaction وبأسماء الأحداث والسمات ذاتها، ما يبقي لوحاتك موحّدة سواء أرسلت الطلبات من Python أو Node.js. لاحظ حلقة monitorBalance التي تسجّل الرصيد دوريًا كل دقيقة:

const newrelic = require("newrelic");
const axios = require("axios");

const API_KEY = process.env.CAPTCHAAI_API_KEY;

async function solveCaptchaWithNewRelic(sitekey, pageurl, captchaType = "recaptcha_v2") {
  return newrelic.startBackgroundTransaction(
    "CaptchaSolve",
    "CaptchaAI",
    async () => {
      const transaction = newrelic.getTransaction();
      newrelic.addCustomAttributes({
        captchaType,
        targetUrl: pageurl,
      });

      const startTime = Date.now();

      try {
        // Submit
        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) {
          newrelic.recordCustomEvent("CaptchaSolveError", {
            error: submitResp.data.request,
            phase: "submit",
            captchaType,
          });
          transaction.end();
          return { error: submitResp.data.request };
        }

        const captchaId = submitResp.data.request;
        newrelic.addCustomAttributes({ captchaId });

        // Poll
        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 elapsed = (Date.now() - startTime) / 1000;
            newrelic.recordCustomEvent("CaptchaSolveSuccess", {
              captchaType,
              solveTime: elapsed,
              pollCount,
            });
            newrelic.addCustomAttributes({
              solveTime: elapsed,
              pollCount,
            });
            transaction.end();
            return { solution: pollResp.data.request, elapsed };
          }

          if (pollResp.data.request !== "CAPCHA_NOT_READY") {
            newrelic.recordCustomEvent("CaptchaSolveError", {
              error: pollResp.data.request,
              phase: "poll",
              captchaType,
            });
            transaction.end();
            return { error: pollResp.data.request };
          }
        }

        newrelic.recordCustomEvent("CaptchaSolveError", {
          error: "TIMEOUT",
          phase: "poll",
          captchaType,
          pollCount,
        });
        transaction.end();
        return { error: "TIMEOUT" };
      } catch (err) {
        newrelic.noticeError(err);
        transaction.end();
        throw err;
      }
    }
  );
}

// Balance monitoring
async function monitorBalance() {
  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);
      newrelic.recordCustomEvent("CaptchaBalance", { balance });
    }
  } catch (err) {
    newrelic.noticeError(err);
  }
}

setInterval(monitorBalance, 60000);

module.exports = { solveCaptchaWithNewRelic };

لوحات المعلومات واستعلامات NRQL

بعد أن تتدفّق الأحداث إلى New Relic، تحوّلها استعلامات NRQL إلى لوحة تعرض معدّل النجاح، ومتوسط زمن الحل حسب النوع، وتوزيع الأخطاء، وزمن الحل عند النسبة المئوية 95 (P95)، إضافة إلى منحنى الرصيد وعدد المهام في الدقيقة. أنشئ لوحة معلومات New Relic بالاستعلامات التالية:

-- Solve success rate (last hour)
SELECT percentage(count(*), WHERE success = true)
FROM CaptchaSolveSuccess, CaptchaSolveError
SINCE 1 hour ago

-- Average solve time by CAPTCHA type
SELECT average(solveTime)
FROM CaptchaSolveSuccess
FACET captchaType
SINCE 1 hour ago TIMESERIES

-- Error breakdown
SELECT count(*)
FROM CaptchaSolveError
FACET error
SINCE 1 hour ago

-- P95 solve latency
SELECT percentile(solveTime, 95)
FROM CaptchaSolveSuccess
SINCE 1 hour ago TIMESERIES

-- Balance over time
SELECT latest(balance)
FROM CaptchaBalance
SINCE 24 hours ago TIMESERIES 5 minutes

-- Tasks per minute
SELECT rate(count(*), 1 minute)
FROM CaptchaSolveSuccess, CaptchaSolveError
SINCE 1 hour ago TIMESERIES

سياسات التنبيه في New Relic

لا تكفي اللوحة وحدها لأن أحدًا لن يراقبها ليل نهار. حوّل المؤشرات الحرجة إلى تنبيهات تنطلق تلقائيًا عبر البريد أو قناة الفريق. ابدأ بأربع سياسات تغطّي الجودة والسرعة والرصيد وتصاعد الأخطاء:

التنبيه شرط NRQL العتبة
انخفاض معدّل الحل SELECT percentage(count(*), WHERE success = true) أقل من 85% لمدة 5 دقائق
ارتفاع زمن الحل عند P95 SELECT percentile(solveTime, 95) FROM CaptchaSolveSuccess أكثر من 120 ثانية لمدة 10 دقائق
انخفاض الرصيد SELECT latest(balance) FROM CaptchaBalance أقل من 10 دولار
تصاعد الأخطاء SELECT count(*) FROM CaptchaSolveError أكثر من 50 خطأ خلال 5 دقائق

معالجة الأعطال الشائعة

معظم مشكلات هذا التكامل مصدرها إعداد وكيل New Relic لا مسار الحل نفسه. راجع الجدول التالي قبل الغوص في السجلات:

المشكلة السبب المحتمل الحل
الأحداث المخصصة لا تظهر في New Relic الخيار custom_insights_events.enabled معطّل فعّله في ملف newrelic.ini
تتبّعات المعاملات مفقودة عتبة التتبّع مرتفعة جدًا اخفض transaction_threshold إلى 1.0 ثانية
قيم السمات مبتورة القيمة أطول من الحد المسموح أبقِ قيم السمات أقل من 255 حرفًا
لا تصل أي بيانات بعد النشر مفتاح الترخيص خاطئ أو الوكيل لا يبدأ تحقّق عبر newrelic-admin validate-config newrelic.ini

سيناريو تشغيلي: مراقبة الذروة في موسم التخفيضات

تخيّل فريق أتمتة في متجر إلكتروني بالخليج يشغّل مسار مقارنة أسعار يحل reCAPTCHA v2 وCloudflare Turnstile آلاف المرات يوميًا. مع اقتراب موسم الجمعة البيضاء يقفز حجم الطلبات، فيرتفع زمن الحل عند P95 تدريجيًا قبل أن ينهار معدّل النجاح. بدون مراقبة، يكتشف الفريق العطل من طلبات ناقصة في نهاية اليوم.

مع تنبيهات New Relic يتغيّر المشهد: ينطلق تنبيه P95 حين يتجاوز 120 ثانية، فيوسّع المهندس المناوب عدد الـ threads في خطته على CaptchaAI قبل أن يتأثر المستخدم. وفي الوقت نفسه، ينبّه شرط الرصيد الفريق حين يقترب من 10 دولار، فيُشحن الحساب قبل أن يتوقّف المسار في ذروة الحملة. هذه هي الفائدة العملية من ربط CaptchaAI بـ New Relic: قرار مبكر بدل تحقيق متأخر.

أسئلة شائعة

هل يعمل هذا التكامل مع أنواع CAPTCHA غير reCAPTCHA v2؟

نعم. تستخدم الأمثلة recaptcha_v2 كقيمة افتراضية فقط؛ مرّر أي نوع مدعوم عبر معامل captcha_type وستظهر النتائج مصنّفة حسب النوع في لوحة NRQL عبر FACET captchaType. من الأنواع التي تراقبها بالطريقة نفسها:

  • reCAPTCHA v3 لتقييم درجة السلوك دون تفاعل المستخدم.
  • Cloudflare Turnstile كبديل خفيف عن reCAPTCHA.
  • GeeTest v3 في المواقع التي تعتمد تحدّي التمرير.

بهذا تقارن زمن الحل ومعدّل النجاح لكل نوع على حدة من لوحة واحدة.

كيف أراقب الرصيد وأتجنّب توقّف المسار بسبب نفاده؟

تسجّل الدالة report_balance رصيد حسابك كحدث CaptchaBalance، ثم تُنشئ تنبيهًا ينطلق حين ينخفض الرصيد عن 10 دولار. اربط هذا التنبيه بقناة إشعارات فريقك لتشحن الرصيد مبكرًا، فلا تتوقّف عمليات الحل في منتصف حملة نشطة.

ما الفرق بين مراقبة CaptchaAI عبر New Relic وDatadog؟

يعتمد الحلّان على الأحداث المخصصة ونفس مسار الإرسال والاستطلاع، فالكود الأساسي واحد تقريبًا. يتميّز New Relic بلغة استعلام NRQL المرنة واللوحات الجاهزة، بينما تختار بعض الفرق Datadog لأنه متكامل مسبقًا مع بقية بنيتها. القاعدة العملية: ابقَ على أداة الرصد التي يستخدمها فريقك أصلًا.

كم مرة يستطلع الكود النتيجة قبل اعتبار المهمة فاشلة؟

تستطلع الحلقة النتيجة حتى 60 مرة بفاصل 5 ثوانٍ، أي مهلة قصوى تناهز 5 دقائق، ثم تُسجَّل المهمة كخطأ TIMEOUT. اضبط عدد المحاولات وطول الفاصل بما يناسب زمن الحل المرصود لديك لكل نوع من أنواع CAPTCHA.

خطوات تالية

أدلة ذات صلة

التعليقات غير مفعّلة لهذا المقال.