错误引擎 Lua API
概述
错误引擎 Lua API 提供了在 Lua 脚本中创建、管理和传递错误对象的功能。通过 mc_error 模块,Lua 开发者可以创建错误对象、设置错误参数、格式化错误消息、序列化错误以及进行错误转换。
模块加载
local mc_error = require("mc_error")
主要特性
- 多种创建方式:
new(${key} 模板)、new_error(printf 风格)、new_message_error(table 入参) - 格式化消息:错误对象支持
${key}和%s/%d两种占位符,自动渲染err.message - 序列化支持:
err:encode()将错误序列化为 JSON,便于日志记录和跨系统传递 - 错误定义注册表:
load_registries从 JSON 加载错误码定义,convert将错误映射为标准消息 - 增强日志:
print_log自动附加调用栈,print_trace打印错误回溯 - 直接抛错:
raise_error和err:raise()直接抛出 Lua 错误,支持pcall捕获
错误创建函数
mc_error.new
创建新的错误对象。
语法:
mc_error.new(name, format, params)
参数:
name(string) - 错误名称format(string) - 格式字符串params(table, 可选) - 错误参数表
返回值:
err(userdata) - 错误对象
示例:
local mc_error = require("mc_error")
-- 创建简单错误(不带参数)
local err1 = mc_error.new("file_not_found", "文件不存在")
-- 创建带参数的错误
local err2 = mc_error.new("user_error", "用户 ${name} 的年龄是 ${age}", {
name = "张三",
age = 30
})
print(err2.message) -- "用户 张三 的年龄是 30"
mc_error.new_error
创建新的错误对象(兼容接口,支持 printf 风格格式)。
语法:
mc_error.new_error(name, format, ...)
参数:
name(string) - 错误名称format(string) - 格式字符串(支持 printf 风格占位符如 %s, %d)...- 可变参数,用于替换格式字符串中的占位符
返回值:
err(userdata) - 错误对象
示例:
local mc_error = require("mc_error")
-- 使用 printf 风格格式
local err = mc_error.new_error("timeout", "操作在 %dms 后超时", 5000)
print(err.message) -- "操作在 5000ms 后超时"
-- 使用多个参数
local err2 = mc_error.new_error("user_info", "用户 %s 的年龄是 %d", "张三", 30)
print(err2.message) -- "用户 张三 的年龄是 30"
mc_error.new_message_error
从 table 创建错误对象。
语法:
mc_error.new_message_error(table)
参数:
table(table) - 包含错误信息的表,支持以下字段:name(string) - 错误名称message或format(string) - 错误格式(message 优先)params或args(table) - 错误参数(params 优先)- 其他字段 - 保存到 uservalue
返回值:
err(userdata) - 错误对象
示例:
local mc_error = require("mc_error")
local err = mc_error.new_message_error({
name = "config_error",
format = "配置文件 ${file} 加载失败",
params = {
file = "/etc/myapp/config"
},
custom_field = "自定义数据" -- 保存到 uservalue
})
print(err.message) -- "配置文件 /etc/myapp/config 加载失败"
print(err.custom_field) -- "自定义数据"
错误对象方法
err:tostring
将错误对象转换为字符串。
语法:
err:tostring()
返回值:
str(string) - 错误字符串
示例:
local mc_error = require("mc_error")
local err = mc_error.new("error", "操作失败")
print(err:tostring()) -- "error: 操作失败"
err:args
获取错误参数表。
语法:
err:args()
返回值:
params(table) - 参数表
示例:
local mc_error = require("mc_error")
local err = mc_error.new("error", "${greeting}, ${name}", {
greeting = "你好",
name = "世界"
})
local params = err:args()
for k, v in pairs(params) do
print(k, v)
end
err:traceback
获取调用栈信息。
语法:
err:traceback()
返回值:
stack(string) - 调用栈字符串
示例:
local mc_error = require("mc_error")
local err = mc_error.new("error", "发生错误")
err:traceback()
local stack = err:traceback()
print(stack)
err:post_process
参数后处理,将参数名映射为位置索引。
语法:
err:post_process(param_struct)
参数:
param_struct(table) - 参数结构定义,数组格式,每个元素是一个包含name字段的表
示例:
local mc_error = require("mc_error")
local err = mc_error.new("error", "%username: %age", {
username = "张三",
age = 30
})
local param_struct = {
{name = "username"},
{name = "age"}
}
err:post_process(param_struct)
-- 参数将被转换为位置索引
err:raise
抛出 Lua 错误。
语法:
err:raise()
异常: 总是抛出 Lua 错误
示例:
local mc_error = require("mc_error")
local err = mc_error.new("error", "操作失败")
if invalid_input then
err:raise() -- 抛出错误
end
-- 下面的代码不会执行
print("这行不会输出")
err:encode
将错误对象序列化为 JSON 字符串。
语法:
err:encode()
返回值:
json(string) - JSON 字符串
示例:
local mc_error = require("mc_error")
local err = mc_error.new("error", "发生错误", {
code = 100,
detail = "详细信息"
})
local json = err:encode()
print(json)
错误对象属性
err.name
错误名称。
类型: string
示例:
local mc_error = require("mc_error")
local err = mc_error.new("file_error", "文件错误")
print(err.name) -- "file_error"
-- 修改名称
err.name = "new_error"
print(err.name) -- "new_error"
err.message
格式化后的错误消息(只读)。
类型: string
示例:
local mc_error = require("mc_error")
local err = mc_error.new("error", "用户 ${name} 的年龄是 ${age}", {
name = "张三",
age = 30
})
print(err.message) -- "用户 张三 的年龄是 30"
-- message 是只读的,不能直接修改
err.message = "新消息" -- 无效
err.params
错误参数表(通过创建时设置,或在 uservalue 中存储额外数据)。
类型: table
注意:
- 通过
mc_error.new()的第三个参数设置的参数会存储在内部的 params 中 - 通过
err.xxx = yyy设置的属性会存储在 uservalue 中
示例:
local mc_error = require("mc_error")
-- 方式1:创建时设置参数
local err = mc_error.new("error", "${greeting}, ${name}", {
greeting = "你好",
name = "世界"
})
-- 访问参数
for k, v in pairs(err:args()) do
print(k, v)
end
-- 方式2:通过 uservalue 设置额外属性
err.custom_field = "自定义数据"
print(err.custom_field) -- "自定义数据"
错误转换函数
mc_error.load_registries
从文件加载错误定义。
语法:
mc_error.load_registries(base_path, custom_path)
参数:
base_path(string) - 基础错误定义文件路径(JSON)custom_path(string) - 自定义错误定义文件路径(JSON,可选)
返回值: 无
示例:
local mc_error = require("mc_error")
-- 只加载基础定义
mc_error.load_registries("/etc/errors/base.json")
-- 加载基础定义和自定义定义
mc_error.load_registries(
"/etc/errors/base.json",
"/etc/errors/custom.json"
)
mc_error.load_registries_from_string
从字符串加载错误定义。
语法:
mc_error.load_registries_from_string(base_json, custom_json)
参数:
base_json(string) - 基础错误定义 JSON 字符串custom_json(string) - 自定义错误定义 JSON 字符串(可选)
返回值: 无
示例:
local mc_error = require("mc_error")
local base_json = [[{
"errors": {
"file_not_found": {
"format": "文件 ${path} 不存在"
}
}
}]]
mc_error.load_registries_from_string(base_json)
mc_error.convert
转换错误为标准消息格式。
语法:
mc_error.convert(err)
参数:
err(userdata) - 错误对象
返回值:
result(table) - 转换后的消息表,包含以下字段:message_id(string) - 消息IDmessage_name(string) - 消息名称message(string) - 转换后的消息severity(string) - 严重级别registry_prefix(string) - 注册表前缀registry_version(string) - 注册表版本
示例:
local mc_error = require("mc_error")
-- 先加载错误定义
local base_json = [[{
"errors": {
"file_not_found": {
"message_id": "FILE001",
"message_name": "File Not Found",
"message": "File ${path} not found",
"severity": "error"
}
}
}]]
mc_error.load_registries_from_string(base_json)
-- 创建错误并转换
local err = mc_error.new("file_not_found", "文件 ${path} 不存在", {
path = "/etc/config"
})
local result = mc_error.convert(err)
print(result.message) -- "File /etc/config not found"
print(result.message_id) -- "FILE001"
print(result.severity) -- "error"
mc_error.convert_to_dict
转换错误为 dict 格式。
语法:
mc_error.convert_to_dict(err)
参数:
err(userdata) - 错误对象
返回值:
dict(table) - dict 表
示例:
local mc_error = require("mc_error")
local err = mc_error.new("error", "发生错误", {
code = 100
})
local dict = mc_error.convert_to_dict(err)
-- dict 包含转换后的错误信息
工具函数
mc_error.raise_error
直接抛出错误。
语法:
mc_error.raise_error(name, message, ...)
参数:
name(string) - 错误名称message(string) - 错误消息(支持 printf 风格格式)...- 可变参数,用于替换格式字符串中的占位符
异常: 总是抛出 Lua 错误
示例:
local mc_error = require("mc_error")
-- 简单用法
mc_error.raise_error("error", "操作失败")
-- 带参数
mc_error.raise_error("validation_error", "字段 %s 验证失败", "username")
-- 使用多个参数
mc_error.raise_error("timeout", "操作在 %dms 后超时,重试 %d 次", 5000, 3)
mc_error.print_log
增强的日志打印,自动包含调用栈信息。
语法:
mc_error.print_log(severity, format, ...)
参数:
severity(string) - 日志级别("debug", "info", "warning", "error", "fatal")format(string) - 格式字符串(支持 printf 风格)...- 可变参数
示例:
local mc_error = require("mc_error")
mc_error.print_log("info", "用户 %s 登录成功", "admin")
mc_error.print_log("error", "操作失败: %s", "无效参数")
mc_error.print_log("warning", "配置文件 %s 不存在", "/etc/config")
mc_error.print_trace
打印错误调用栈跟踪。
语法:
mc_error.print_trace(backtrace_level, err_data)
参数:
backtrace_level(number) - 回溯级别(可选)err_data(userdata/table) - 错误对象或错误数据
示例:
local mc_error = require("mc_error")
local err = mc_error.new("error", "发生错误", {
code = 100
})
mc_error.print_trace(1, err)
完整示例
基本错误处理
local mc_error = require("mc_error")
-- 创建错误对象
local err = mc_error.new("file_not_found", "文件 ${path} 不存在", {
path = "/etc/config.conf"
})
-- 访问错误信息
print("错误名称: " .. err.name)
print("错误消息: " .. err.message)
-- 序列化错误
local json = err:encode()
print("JSON: " .. json)
错误链和异常
local mc_error = require("mc_error")
-- 检查并抛出错误
local function load_config(path)
if path == nil then
local err = mc_error.new("invalid_arg", "路径不能为空")
err:raise()
end
-- 模拟文件操作失败
local file = io.open(path, "r")
if not file then
local err = mc_error.new("file_error", "无法打开文件 ${path}", {
path = path
})
err:raise()
end
return file
end
-- 捕获和处理错误
local ok, err = pcall(load_config, nil)
if not ok then
print("捕获错误: " .. err.name)
print("错误消息: " .. err.message)
end
使用不同的参数设置方式
local mc_error = require("mc_error")
-- 方式1:创建时设置参数
local err1 = mc_error.new("user_error", "用户 ${name} 的年龄是 ${age}", {
name = "张三",
age = 30
})
print(err1.message) -- "用户 张三 的年龄是 30"
-- 方式2:使用 new_error(printf 风格)
local err2 = mc_error.new_error("timeout", "操作在 %dms 后超时", 5000)
print(err2.message) -- "操作在 5000ms 后超时"
-- 方式3:从 table 创建
local err3 = mc_error.new_message_error({
name = "config_error",
format = "配置文件 ${file} 加载失败",
params = {
file = "/etc/myapp/config"
}
})
print(err3.message) -- "配置文件 /etc/myapp/config 加载失败"
错误转换
local mc_error = require("mc_error")
-- 加载错误定义
local base_json = [[{
"errors": {
"file_not_found": {
"message_id": "FILE001",
"message_name": "File Not Found",
"message": "File ${path} not found",
"severity": "error"
}
}
}]]
mc_error.load_registries_from_string(base_json)
-- 创建错误并转换
local err = mc_error.new("file_not_found", "文件 ${path} 不存在", {
path = "/etc/config"
})
local result = mc_error.convert(err)
print(result.message) -- "File /etc/config not found"
print(result.message_id) -- "FILE001"
print(result.severity) -- "error"
错误日志
local mc_error = require("mc_error")
-- 使用增强日志
mc_error.print_log("info", "应用程序启动")
mc_error.print_log("debug", "加载配置文件: %s", "/etc/config")
-- 错误日志
local function process_request(url)
if not url then
mc_error.print_log("error", "URL 不能为空")
return nil
end
mc_error.print_log("info", "处理请求: %s", url)
return true
end
process_request("http://example.com")
## 最佳实践
### ✅ 使用错误注册表实现错误码统一管理
将错误定义集中到 JSON 文件中,通过 `load_registries` 加载,保证错误消息在多处引用时一致:
```lua
-- ✅ 集中管理错误定义
mc_error.load_registries("/etc/errors/base.json")
local err = mc_error.new("file_not_found", "文件 ${path} 不存在", {path = path})
local result = mc_error.convert(err)
-- result.message_id / message / severity 均来自注册表
✅ 用 pcall 优雅捕获错误并提取字段
err:raise() 抛出的错误对象可以通过 pcall 捕获并访问其 name、message 属性:
-- ✅ pcall 捕获后访问错误字段
local ok, err = pcall(do_dangerous_work)
if not ok then
log:error("操作失败: ${msg} (${name})", "msg", err.message, "name", err.name)
end
❌ 不要忽略 new_error 和 new 的占位符差异
new_error 使用 printf 风格(%s, %d),new 使用 ${key} 模板,混用会导致格式异常:
-- ❌ 混用占位符
local err = mc_error.new_error("err", "用户 ${name} 不存在", "admin")
-- ${name} 不会被替换,因为 new_error 不支持 ${key}
-- ✅ 保持一致
local err = mc_error.new_error("err", "用户 %s 不存在", "admin")
-- 或
local err = mc_error.new("err", "用户 ${name} 不存在", {name = "admin"})
❌ 避免在循环中反复创建同名错误对象
每次 mc_error.new 都会创建新的 userdata,高频调用会增加 GC 压力:
-- ❌ 循环内反复创建
for _, item in ipairs(items) do
if not validate(item) then
mc_error.raise_error("invalid", "无效项: %s", item.id)
end
end
-- ✅ 复用格式字符串,或用 print_log 记录后继续
for _, item in ipairs(items) do
if not validate(item) then
mc_error.print_log("warning", "跳过无效项: %s", item.id)
end
end
相关文档
- 错误引擎设计说明 - C++ 错误引擎模块设计
- 日志 Lua API - 结构化日志接口
- 参数校验 Lua API - 参数校验库
- Lua API 索引