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
endpcall と 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フィードバックを取得できます。ローカル設定は不要です。
このコースのすべてのレッスン
- error()関数
- pcallによる保護付き呼び出し
- xpcallとメッセージハンドラー
- 構造化エラーオブジェクト