高峰期每分钟几千条报错刷爆日志:同一处空指针在 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 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
实战应用:用在哪里 战士的烈火剑法一击熄火后要等八秒冷却,连招断档感强烈。疾风烈火斩在烈火剑法命中后给一次连击窗口:三秒内再…
实战应用:用在哪里 阵营对抗打到中期容易疲软:打赢没有额外好处,输掉也没有代价。阵营声望体系给阵营战装上荣誉刻度:个人与阵营…
实战应用:用在哪里 拿下沙巴克之后呢?城主除了名字挂在城墙上,对城市毫无影响。城主施政玩法让占领变成治理:城主每周获得市政令…
实战应用:用在哪里 主线路光缆被挖断的深夜,全服掉线 20 分钟,玩家以为游戏倒闭。备用线路方案给接入层配第二条出口:主线路…
实战应用:用在哪里 行会官员由帮主任命的老模式弊端明显:任人唯亲、能上不能下。行会选举接口把官员换届做成投票制:每两个月一次…
实战应用:用在哪里 烈火剑法的伤害加成、冷却时间、下一级提升,全靠玩家记忆或去官网查。技能说明悬浮卡片在鼠标悬停技能图标零点…