0Pricing
Lua Academy · درس

‏xpcall ومعالجات الرسائل

استخدم xpcall مع معالج مخصص للحصول على تتبعات تفصيلية للأخطاء

‏xpcall ومعالجات الرسائل درس مجاني في Lua Academy على CoddyKit. هذا هو الدرس 3 من أصل 4. يمكنك قراءة الدرس كاملاً أدناه مجاناً — ثم تمرن عليه مباشرة في المتصفح باستخدام محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7. هذا الدرس جزء من مسار التعلم في Lua Academy، وتقدمك يتزامن عبر الويب وتطبيق CoddyKit. تتضمن دورة Lua Academy 4 دروس في المجموع.

صيغة xpcall

يستدعي xpcall(f, handler, ...) الدالة f بالوسائط ... في وضع محمي، ويستدعي عند حدوث خطأ handler(errorObject). تصبح قيمة الإرجاع من المعالج قيمة الإرجاع الثانية لـ xpcall. بخلاف pcall، يشغّل xpcall المعالج بينما يكون المكدس سليمًا.

local function handler(err)
  return "HANDLED: " .. tostring(err)
end

local ok, msg = xpcall(
  function() error("something bad") end,
  handler
)

print(ok)   -- false
print(msg)  -- HANDLED: ...: something bad

استخدام debug.traceback معالجًا

أكثر معالجات xpcall شيوعًا هو debug.traceback. مرّره مباشرةً بوصفه المعالج — فهو ينسّق الخطأ مع تتبّع كامل لمكدس الاستدعاءات، وهو أمر بالغ الأهمية لتصحيح أخطاء الإنتاج.

local function level3() error("deep error") end
local function level2() level3() end
local function level1() level2() end

local ok, err = xpcall(level1, debug.traceback)

if not ok then
  -- err contains the full stack trace
  print(err)
end

معالج مخصّص مع السياق

يمكن لمعالج مخصّص إضافة سياق، مثل الطابع الزمني، أو تسجيل الخطأ في ملف، أو إشعار أنظمة المراقبة، ثم إعادة الخطأ المنسّق. هذا هو النمط القياسي لمعالجة أخطاء الإنتاج في تطبيقات Lua.

local function errorHandler(err)
  local trace = debug.traceback(err, 2)
  local ts = os.date("%H:%M:%S")
  local report = string.format("[%s] ERROR\n%s", ts, trace)
  -- Could log to file here
  io.stderr:write(report .. "\n")
  return report
end

local ok, msg = xpcall(
  function()
    local t = nil
    return t.field   -- error!
  end,
  errorHandler
)
print("ok:", ok)

لا يمكن للمعالج إصدار خطأ

إذا أصدر معالج الرسالة نفسه خطأً، تعيد Lua مؤشر خطأ من دون استدعاء المعالج مرة أخرى. اكتب المعالجات دائمًا بحيث تكون شديدة المتانة — من دون عمليات إدخال وإخراج قد تفشل، ومن دون فهرسة لقيم nil.

local function safeHandler(err)
  -- Keep handler simple and safe
  local ok, trace = pcall(debug.traceback, err, 2)
  if ok then return trace
  else return tostring(err) .. " (traceback failed)"
  end
end

local ok, msg = xpcall(
  function() error({complex="error table"}) end,
  safeHandler
)
print(ok, type(msg))

استخدام xpcall للحلقة الرئيسية

في البرامج طويلة التشغيل (مثل الخوادم وحلقات الألعاب)، غلّف الدالة الرئيسية في xpcall لالتقاط أي أخطاء غير معالجة وتسجيلها من دون تعطل البرنامج. ويمكن للحلقة الرئيسية بعد ذلك أن تقرر ما إذا كانت ستُعاد أو ستخرج.

local function mainApp()
  -- simulate work
  for i = 1, 3 do
    print("Tick", i)
    if i == 2 then error("transient error") end
  end
end

local function handler(e)
  return debug.traceback("App error: "..tostring(e), 2)
end

local ok, err = xpcall(mainApp, handler)
if not ok then
  print("Application crashed:\n" .. err)
end

الإبلاغ المنظّم عن الأخطاء

اجمع بين xpcall وكائنات الأخطاء المنظّمة ومعالج غني لإنتاج تقارير أخطاء مفصّلة وقابلة للتنفيذ لأغراض تصحيح الأخطاء أو لوحات المراقبة.

local function handler(err)
  local info = {
    error   = tostring(err),
    time    = os.date("!%Y-%m-%dT%H:%M:%SZ"),
    trace   = debug.traceback(nil, 2),
  }
  return info
end

local ok, report = xpcall(
  function() error({code=500, msg="internal error"}) end,
  handler
)

if not ok then
  print("Time:", report.time)
  print("Error:", report.error)
  -- print("Trace:", report.trace)
end

استخدام xpcall مع Coroutines

يعمل pcall بصورة طبيعية داخل coroutine. وللحصول على تتبعات الأخطاء من الأخطاء الواقعة داخل coroutines، غلّف جسم coroutine في xpcall. يعمل المعالج ضمن سياق مكدس coroutine.

local function co_body()
  error("error inside coroutine")
end

local co = coroutine.create(function()
  local ok, err = xpcall(co_body, debug.traceback)
  if not ok then
    print("Caught in coroutine:", err:match("([^\n]+)"))
  end
end)

coroutine.resume(co)

إثراء كائن الخطأ

يمكن للمعالج تحويل خطأ نصي عادي إلى كائن غني، أو إثراء كائن غني بمزيد من المعلومات. يتيح ذلك للشيفرة منخفضة المستوى إصدار أخطاء بسيطة، بينما يضيف المعالج السياق (معرّف الطلب، وجلسة المستخدم، ومعلومات البيئة).

local requestID = "req-123"

local function handler(err)
  if type(err) == "string" then
    return {message=err, requestID=requestID, level="error"}
  end
  err.requestID = requestID
  return err
end

local ok, result = xpcall(
  function() error("database timeout") end,
  handler
)

if not ok then
  print(result.message, result.requestID)
  -- database timeout  req-123
end

مقارنة pcall وxpcall

استخدم pcall عندما: تحتاج إلى قيمة الخطأ فقط، أو يكون الخطأ متوقعًا وتتم معالجته داخل السطر، أو تكون البساطة مهمة. استخدم xpcall عندما: تحتاج إلى تتبعات المكدس، أو تكون عند حدّ علوي، أو تريد إضافة سياق إلى جميع الأخطاء.

-- pcall: simple, no overhead
local ok, err = pcall(function()
  return 1/0   -- no error in Lua! returns inf
end)
print(ok, err)   -- true  inf

-- xpcall: adds traceback
local ok2, err2 = xpcall(
  function() error("real error") end,
  debug.traceback
)
print(ok2)       -- false
print(err2:sub(1,40))  -- first line of traceback

قيمة إرجاع المعالج

أيًا كانت القيمة التي يعيدها المعالج، تصبح القيمة الثانية التي يعيدها xpcall. إذا أعاد المعالج nil، فستكون قيمة الإرجاع الثانية لـ xpcall هي nil. ويُعد المعالج الذي يعيد كائن الخطأ الأصلي مع معلومات إضافية النهج الأكثر مرونة.

local function enrichedHandler(err)
  return {
    original = err,
    traceback = debug.traceback(nil, 2),
    timestamp = os.time(),
  }
end

local ok, report = xpcall(
  function() error("oops") end,
  enrichedHandler
)

if not ok then
  print(type(report))          -- table
  print(report.original)       -- ...: oops
  print(report.timestamp > 0)  -- true
end

تحقق سريع

ما الميزة الأساسية التي يوفرها xpcall مقارنةً بـ pcall؟

مراجعة: xpcall

الملخص:

  • xpcall(f, handler, ...) — يعمل المعالج بينما يكون المكدس سليمًا
  • استخدم debug.traceback معالجًا للحصول على تتبعات كاملة
  • يجب ألا يصدر المعالج خطأً — أبقه بسيطًا
  • أثرِ الأخطاء بالسياق داخل المعالج
  • غلّف الحلقات الرئيسية أو معالجات الخادم في xpcall
  • تصبح قيمة إرجاع المعالج قيمة الإرجاع الثانية لـ xpcall

الأسئلة الشائعة

هل درس «‏xpcall ومعالجات الرسائل» مجاني؟

نعم — نص درس «‏xpcall ومعالجات الرسائل» كامل متاح مجاناً هنا على الويب. لتمرينه بشكل تفاعلي (محرر أكواد مدمج ومدرس ذكاء اصطناعي متاح 24/7) وفتح باقي دورة Lua Academy، انتقل إلى CoddyKit PRO. تتضمن دورة Lua Academy 4 دروس في المجموع.

ماذا ستتعلم في «‏xpcall ومعالجات الرسائل»؟

استخدم xpcall مع معالج مخصص للحصول على تتبعات تفصيلية للأخطاء تتمرن على Lua Academy مع أكواد عملية تشغلها مباشرة في المتصفح، ومدرس ذكاء اصطناعي متاح 24/7 يجيب على أسئلتك أثناء عملك.

هل أحتاج إلى خبرة سابقة لأبدأ Lua Academy؟

لا تُشترط خبرة سابقة. Lua Academy على CoddyKit منظم للمبتدئين حتى المتقدمين، لذا يمكنك البدء من هنا أو من البداية والتقدم بسرعتك الخاصة. هذا هو الدرس 3 من أصل 4.

كم من الوقت يستغرق درس «‏xpcall ومعالجات الرسائل»؟

معظم دروس CoddyKit تستغرق حوالي 5–10 دقائق. كل منها موجز وتفاعلي، لذا تحرز تقدماً مستمراً وتستأنف من حيث توقفت عبر الويب والتطبيق.

هل يمكنني كتابة وتشغيل أكواد في درس Lua Academy هذا؟

نعم. كل درس في Lua Academy يتضمن محرر أكواد مدمج، لذا تكتب وتشغل أكواداً حقيقية مباشرة في متصفحك وتحصل على تعليقات فورية من الذكاء الاصطناعي — بدون إعداد محلي.

جميع الدروس في هذه الدورة

  1. الدالة error()
  2. الاستدعاءات المحمية باستخدام pcall
  3. ‏xpcall ومعالجات الرسائل
  4. كائنات الأخطاء المهيكلة
← العودة إلى Lua Academy