对比范围:f82d014d6e6343695e634ff05d7001126b26c99a → 7733b85c798af788723bd77b8ca90bdf3ffd49a5
分支:v1.0.29
变更规模:1 个文件 / 1 hunk(+12 行,-0 行)
| 文件 | 变更内容 |
|---|---|
internal/scheduler/scheduler.go | scanTasks() 新增「任务扫描心跳」日志与耗时统计 |
本版本为一次观测性(可观测/日志)补丁,唯一改动是给调度器任务扫描循环 scanTasks() 增加一行"存活心跳"日志,用于区分「服务没在跑」与「没有到期任务」两种场景。
func (d *Dispatcher) scanTasks() {
+ scanStart := timeutil.Now()
tasks, err := d.repo.GetPendingTasks(d.cfg.GetConcurrentTasks())
if err != nil {
logrus.WithError(err).Error("查询待执行任务失败")
return
}
+ // 巡检心跳:每轮扫描都留一行(排查时能区分"服务没在跑"与"没有到期任务";
+ // 此前只在启动/任务执行时打日志,平台里超过 1 小时没数据会被误判成采集中断)
+ defer func() {
+ logrus.WithFields(logrus.Fields{
+ "pending": len(tasks),
+ "concurrent": d.cfg.GetConcurrentTasks(),
+ "elapsed_ms": timeutil.Now().Sub(scanStart).Milliseconds(),
+ "service": "dzs-ai-content",
+ "scan_round": "scheduler",
+ }).Info("任务扫描完成(无到期任务时也记录,作为存活心跳)")
+ }()
for _, task := range tasks {
scanStart 用于统计本轮扫描耗时;在 defer 中被引用,无未使用变量编译问题。defer 心跳:每轮扫描结束(含无到期任务)打印一条结构化日志,字段 pending / concurrent / elapsed_ms / service / scan_round。timeutil 已存在:文件头已导入 github.com/dzs/dzs-ai-content/pkg/timeutil,无需新增 import。tasks 为函数内局部切片,d.cfg.GetConcurrentTasks() 内部有 RLock 保护,无数据竞争。数据来源:
codescan.issues_diff(base → head)
| 严重度 | 类型 | 位置 | 描述 |
|---|---|---|---|
| Low | Dependency | internal/scheduler/scheduler.go:657 | 'scanTasks' 扇入 1 / 扇出 22(合计 23),超过异常阈值 |
判读:该新增问题正是本次改动带来的度量副作用——在 scanTasks() 内新增了 timeutil.Now()(2 次)、logrus.WithFields、Milliseconds()、GetConcurrentTasks() 等调用,使函数的扇出计数超过阈值而被判为「扇入扇出异常」。它属于结构度量类告警,并非功能缺陷,本次改动为纯日志增强,无实际风险。
尽管改动本身低风险,仍有两处值得留意:
scanTasks() 由 taskScanLoop 以 TASK_SCAN_INTERVAL_SECONDS 周期驱动,默认 10 秒(见 internal/config/config.go:TaskScanInterval: getEnvInt("TASK_SCAN_INTERVAL_SECONDS", 10))。Info 级。pending > 0 时打印、或改用更低的日志级别/独立的低频指标(OTel metric)。如维持现状,需确认日志采集方的采样/保留策略可承受该频率。defer 注册位置导致错误路径无心跳defer 注册在 GetPendingTasks 的错误分支之后:当查询待执行任务失败(return)时,心跳日志不会打印(该路径已有 Error 日志)。defer 上移至函数入口处。scanTasks 心跳),无业务逻辑变更,无功能缺陷、无安全问题。