高峰期每分钟几千条报错刷爆日志:同一处空指针在 800 个玩家身上各炸一次,八千条日志说的是一件事。错误聚合按堆栈指纹合并计数——八千条收敛成一行"指纹 A:8000 次",修复的优先级一目了然。
---错误入桶:堆栈前三帧做指纹
function ErrAgg.capture(err, trace)
local frames = {}
for f in trace:gmatch("([%w_%.]+):%d+:") do
frames[#frames + 1] = f
if #frames >= 3 then break end
end
local fp = table.concat(frames, "|")
local bucket = ErrAgg.buckets[fp]
if not bucket then
bucket = { count = 0, first = err, at = os.time(), sample = trace }
ErrAgg.buckets[fp] = bucket
end
bucket.count = bucket.count + 1
if bucket.count == 100 then
ErrAgg.alert(fp, bucket)
end
end
指纹取堆栈前三帧的函数名拼接——行号会漂移,函数名的稳定性高得多。同一指纹只存首条完整堆栈作样本,后续只累加计数,日志的体积从每条全量堆栈降到一行计数。同指纹累计 100 次触发告警,低频的一次性错误不打扰。
---全局定时器 48 号:每分钟输出 TopN
setontimerex(48, 60)
function ErrAgg.report()
local list = {}
for fp, b in pairs(ErrAgg.buckets) do
list[#list + 1] = { fp = fp, count = b.count, at = b.at }
end
table.sort(list, function(a, b) return a.count > b.count end)
for i = 1, math.min(10, #list) do
Ops.log(("Top%d %s x%d"):format(i, list[i].fp, list[i].count))
end
ErrAgg.buckets = {}
end
每分钟输出计数前十的指纹,输出后清零重计——报表看的是流量而不是存量,持续报错的指纹每分钟都在榜首,修好即从榜上消失。指纹的响应面按 distinct 玩家数加权:一人反复触发与百人各触发一次的优先级天差地别。修复后的版本号标记让错误的消失可验证:新版本里旧指纹的计数归零即确认修复。
聚合系统的自检:指纹的碰撞率(不同错误同指纹)抽样人工核对,碰撞超过 5% 调整帧数;告警的到达率监控。
指纹曾经含行号,热更新一行代码的偏移让同一错误分裂成三个指纹,计数被稀释告警失效,函数名指纹修复。桶曾经不限量,异常风暴一夜造出 4 万个桶撑爆内存,桶数上限加淘汰。样本堆栈曾经取的最新一条,截断的堆栈丢了关键上下文,样本固定取首条完整堆栈。
TopN 报表进运维群日报,错误的趋势与版本发布时间轴叠放对照。指纹到处置的映射表:高频指纹标注负责人与工单号,错误的归属一目了然。采样的全量保留首条样本堆栈,修复的线索永远在现场。
本文由 996 技术组基于 996 引擎官方知识库与浮生梦老师课程体系整理。团队长期从事传奇类引擎 Lua 后端逻辑、客户端界面与商业版本交付,内容以官方知识库与真实项目为出处,按版本持续修订。
全站技术干货持续更新:996 引擎 / Lua 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
实战应用:用在哪里 全服 400 多个 NPC 脚本在开服瞬间集中加载,引擎进程启动耗时 47 秒,值班期间重启就是 47 …
实战应用:用在哪里 行会跨图集合靠世界喊话,从比奇赶到沙巴克动辄三分钟,集合效率低。行会驿站给每个行会一个专属传送网络:驿站…
实战应用:用在哪里 跨服战场周日结算夜出了双事故:30 名玩家积分凭空翻倍,随后 12 名玩家积分变成负数。两条线索指向同一…
实战应用:用在哪里 烈火剑法点到一半想换逐日剑法,没有遗忘功能只能养小号,新手加点点废的投诉长期霸榜。技能遗忘与重修框架管三…
实战应用:用在哪里 战斗日志全量滚动,烈火剑法暴击、治疗、拾取一场下来上千条,列表越长滑动越卡。改造分两件事:内存里只留最近…
实战应用:用在哪里 夏季活动缺一个全服参与的轻竞速,选定龙舟:五人一队从白日门河道上游划向盟重码头,河道布设障碍与顺流带,全…