0Pricing
Lua Academy · レッスン

xpcallとメッセージハンドラー

カスタムハンドラーを指定したxpcallで、詳細なエラートレースバックを取得します。

「xpcallとメッセージハンドラー」はCoddyKit上の無料Lua Academyレッスンです。 これはレッスン3/4です。 下記で完全なレッスンを無料で読むことができます。その後、ブラウザ内の組み込みコードエディタと24時間対応のAIチューターでハンズオン演習できます。 これはLua Academy学習パスの一部であり、ウェブとCoddyKitアプリ全体で進捗が同期されます。 Lua Academyコースには全4レッスンが含まれています。

xpcall の構文

xpcall(f, handler, ...) は、保護モードで引数 ... を付けて f を呼び出し、エラーが発生すると handler(errorObject) を呼び出します。ハンドラーの戻り値が xpcall の2番目の戻り値になります。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)

エラーオブジェクトの拡張

ハンドラーは、単純な文字列エラーを詳細なオブジェクトに変換したり、詳細なオブジェクトにさらに情報を追加したりできます。これにより、低レベルのコードは単純なエラーを発生させ、ハンドラーがコンテキスト(リクエスト ID、ユーザーセッション、環境情報)を追加できます。

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 の2番目の戻り値になります。ハンドラーが nil を返すと、xpcall の2番目の戻り値も 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

理解度チェック

pcall に対する xpcall の主な利点は何ですか?

まとめ: xpcall

まとめ:

  • xpcall(f, handler, ...) — スタックが保持されている間にハンドラーが実行されます
  • 完全なトレースバックにはハンドラーとして debug.traceback を使用します
  • ハンドラー自体がエラーを発生させないよう、単純に保ちます
  • ハンドラーでエラーにコンテキストを追加します
  • メインループやサーバーハンドラーを xpcall でラップします
  • ハンドラーの戻り値が xpcall の2番目の戻り値になります

よくある質問

「xpcallとメッセージハンドラー」レッスンは無料ですか?

はい。「xpcallとメッセージハンドラー」の完全なテキストはこのウェブで無料で読めます。インタラクティブに演習し(組み込みコードエディタと24時間対応のAIチューター)、Lua Academyコースの残りをアンロックするには、CoddyKit PROにアップグレードしてください。 Lua Academyコースには全4レッスンが含まれています。

「xpcallとメッセージハンドラー」で何を学びますか?

カスタムハンドラーを指定したxpcallで、詳細なエラートレースバックを取得します。 ブラウザで直接実行するハンズオンコードでLua Academyを演習し、24時間対応のAIチューターがレッスンを進める中での質問に答えます。

Lua Academyを始めるのに経験は必要ですか?

事前経験は必要ありません。CoddyKitのLua Academyは初級者から上級者向けに構成されているため、ここから始めるか最初から始めて、自分のペースで進むことができます。 これはレッスン3/4です。

「xpcallとメッセージハンドラー」レッスンにはどのくらい時間がかかりますか?

ほとんどのCoddyKitレッスンは約5~10分かかります。各レッスンはコンパクトでインタラクティブなので、着実に進歩し、ウェブとアプリ全体で正確に前回の場所から再開できます。

このLua Academyレッスンでコードを書いて実行できますか?

はい。すべてのLua Academyレッスンに組み込みコードエディタが含まれているため、ブラウザでリアルコードを書いて実行し、即座のAIフィードバックを取得できます。ローカル設定は不要です。

このコースのすべてのレッスン

  1. error()関数
  2. pcallによる保護付き呼び出し
  3. xpcallとメッセージハンドラー
  4. 構造化エラーオブジェクト
← Lua Academyに戻る