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 возвращает индикатор ошибки, не вызывая обработчик повторно. Всегда пишите надёжные обработчики: не используйте операции IO, которые могут завершиться сбоем, и не обращайтесь к индексам 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 для сопрограмм

Внутри сопрограммы pcall работает обычно. Чтобы получать трассировки ошибок внутри сопрограмм, оберните тело сопрограммы в xpcall. Обработчик выполняется в контексте стека сопрограммы.

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 включает встроенный редактор кода, поэтому ты пишешь и запускаешь реальный код прямо в браузере и получаешь моментальную обратную связь от AI — локальная установка не требуется.

Все уроки этого курса

  1. Функция error()
  2. Защищённые вызовы с pcall
  3. xpcall и обработчики сообщений
  4. Структурированные объекты ошибок
← Назад к Lua Academy