高峰期每分钟几千条报错刷爆日志:同一处空指针在 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 实战帖,语法、参数与示例一篇讲透。进入文章地图 · 查看全部 →
学员常见误区 Lua函数可返回多个值,学员用固定变量数接收时如果变量少于返回值,多余返回值被静默丢弃;如果变量多于返回值,多…
设计初衷 行会建筑的死穴是一次全解锁:会员没有逐步建设的过程感。梯度设计让每栋建筑都有前置条件和资源门槛。 数值模型 建筑分…
设计初衷 婚姻系统的属性加成是社交玩法的经济锚点:加成太弱没人结婚,太强则"为了属性被迫结婚"扭曲了社交本质。婚姻边界的设计…
设计初衷 宝箱类玩法的信任危机都源于同一句话:"概率是不是骗人的。"期望公示把概率从事后争议变成事前契约:奖池概率表全量公示…
设计初衷 流拍物(拍卖未成交的退回物品)堆积在卖家背包里成为死资产:低价值物流拍后无人问津,高价值物流拍后卖家不愿降价重拍。…
业务场景 沙巴克战功榜每周结算,玩家提交战功前不知道"再打多少能进前 10、前 10 的奖励是什么"。名次预览:输入自己的战…