glogcenter 是一个轻量的集中式日志中心(Go 写的,部署简单,开箱即用),我在项目里用它收日志,整体体验不错。不过真实用起来,先后踩到了两个问题:一个是内存持续增长,一个是集群模式下用户管理失效。两次都是翻源码定位到根因,各提了一个 PR——#92 和 #93,现在都已合并。这篇文章把两次排查的过程写下来。

问题一:内存持续增长,最后定位到 2 行代码

现象

长期运行时内存单调增长、不回落。怀疑方向很快收敛到 goroutine 泄漏(这个案例也关联了项目的 #70)。

读源码:泄漏藏在「关闭→重开」的生命周期里

glogcenter 的日志仓有一个闲置自动关闭机制(autoCloseWhenMaxIdle,默认闲置 300 秒):日志仓闲置后自动关闭,再次写入时重新打开、新建一套存储结构。问题在于——旧存储的消费协程 readyGo 从不退出。每次「关闭→重开」周期净泄漏一个 goroutine,连同 64 槽 channel 和整个存储结构体的引用一起挂在那里。按日分仓 + 断续写入是很常见的场景,每个闲置间隙就累积一个,长期运行自然持续增长。

根因:Go 里 break 退不出 for

真正的原因是一个经典的 Go 语义坑。readyGo 是一个 for-select 循环,里面有两处在收到 nil(Close 的退出信号)后写了 break——但在 Go 语义里,select 中的 break 只退出 select,不会退出外层的 for;而整个函数没有任何 return。于是每次 Close 之后,消费协程必然存活,结局分两种:

  1. 闲置关闭(常态):关闭时索引已建完,协程停驻在内层 <-s.storeChan 上永久阻塞——通道永远不会被关闭(nil 只在 closing=true 之后到达,判定分支不可达);
  2. 关闭时仍有未建索引的日志:建索引因存储已关闭持续报错,协程热循环空转,还刷错误日志。

生产日志里有实锤:关闭LogDataStorage 之后,仍会出现一条 空闲等待接收日志——那就是泄漏的协程在关闭后重新停驻。

修复与验证

修复本身小到惊人:两处 break 改成 return,一共 2 行。热路径行为和「关闭后按需自动重开」的语义完全不变。

改之前先写测试复现:新增一个 goroutine 计数法测试,分两阶段——忙时立即关闭(覆盖 select 分支退出路径)、闲置自动关闭(覆盖内层阻塞接收这条生产路径)。修复前测试稳定失败(delta = +1,泄漏复现),修复后两个阶段都是 delta = +0。go vet 干净,-race 无新竞争。

→ PR #92(已合并)

问题二:集群模式下,用户管理静默失效

现象

开启集群模式(GLC_CLUSTER_MODE=true)后,一组「灵异现象」:

关键发现:载荷全打到了「日志转发」接口

排查发现,用户管理、改密、删除、登录会话这些节点间转发的载荷,实际全部被投递到了日志转发接口 /glc/v1/log/transferAdd——载荷与日志模型字段毫无重叠、Text 为空,于是被静默丢弃,接口还返回 Ok。

根因:参数从没被用过

源码里最耐人寻味的一点:TransferGlc(uri, jsonlog) 的 uri 参数从未被使用——函数体里硬编码转发到 /v1/log/transferAdd。5 个调用方传入了 4 种 uri,5 个 transfer 控制器和路由全部就绪,唯独函数体没用 uri。日志转发「恰好」因为硬编码值等于 LogTransferAdd 而看起来一切正常,把这个缺陷掩盖得严严实实。

顺着读下去还发现两个连带 bug:

  1. UserDelController 的转发用错了常量——复制粘贴错误,删用户载荷会打到 transferLogin 上,替被删用户在各节点建立会话;
  2. 登录转发控制器缺登录守卫:会话缓存仅在开启登录时初始化,登录关闭的节点被命中会因 nil 缓存 panic(登录控制器本身有守卫,转发版漏了)。

修复与验证

修复共 3 处 5 行:函数体改用 uri 参数(恢复全部 4 类传播)、删除转发常量改正、转发版补上与登录控制器一致的登录守卫。测试同样先行:三阶段回归测试,修复前都以正确原因失败,修复后全过,包括「删除用户后,伪集群节点实际收到 /glc/v1/sysuser/transferDel」这类端到端断言。

→ PR #93(已合并)

两次排查攒下的心得

写在最后

给自己的建议(也送给遇到类似问题的你):遇到开源组件的异常,别急着绕过去——现象 → 最小复现 → 读源码 → 定位根因,这条路走通一次,下一次就快了。两个 PR 的完整描述都在:

项目地址:github.com/gotoeasy/glogcenter