You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Go语言log/slog自定义默认Handler:满足需求并解决死锁问题

解决slog自定义Handler死锁并满足需求的方案

死锁原因分析

你遇到的死锁问题,根源在于自定义Handler直接嵌套了slog.Default()返回的默认Handler。默认Handler内部依赖log包的全局互斥锁,当你调用slog.SetDefault将自定义Handler的Logger设为默认后,日志输出时会出现锁的循环等待:自定义Handler的Handle方法调用底层默认Handler的Handle,而默认Handler又会尝试获取已被当前调用路径持有的全局锁,最终导致所有goroutine阻塞,触发"all goroutines are asleep"错误。

满足需求的简便方案

要同时实现三个需求,正确的做法是直接创建与默认Handler配置一致的底层Handler,而非嵌套slog.Default()返回的Handler。默认Handler本质是输出到os.Stderr的TextHandler,且使用默认配置(级别为Info、无额外属性等)。

修正后的代码

package main

import (
	"context"
	"log/slog"
	"os"
)

type CustomHandler struct {
	slog.Handler
}

func NewCustomHandler() *CustomHandler {
	// 创建与默认Handler配置一致的TextHandler
	defaultHandler := slog.NewTextHandler(os.Stderr, nil)
	return &CustomHandler{Handler: defaultHandler}
}

func (m *CustomHandler) Handle(ctx context.Context, record slog.Record) error {
	// 自定义修改日志消息
	record.Message = "[do something] " + record.Message
	return m.Handler.Handle(ctx, record)
}

func main() {
	customLogger := slog.New(NewCustomHandler())
	// 设置为默认Logger
	slog.SetDefault(customLogger)

	slog.Info("Hello") // 正常输出:time=... level=INFO msg="[do something] Hello"
}

需求匹配说明

  • 使用默认Handler输出格式:通过slog.NewTextHandler(os.Stderr, nil)创建的Handler,与slog.Default()的默认Handler配置完全一致,输出格式相同。
  • 自定义修改日志消息:在Handle方法中直接修改record.Message,实现消息前缀添加等自定义逻辑。
  • 设置为默认处理器:通过slog.SetDefault将自定义Handler对应的Logger设为全局默认,后续调用slog.Info等方法时会自动使用自定义Handler。

额外说明

如果需要匹配默认Handler的动态配置(比如根据环境变量SLOG_FORMAT切换JSON/Text格式),可以先通过slog.Default().Handler()获取底层Handler的类型,再创建对应类型的Handler。但大多数场景下,直接使用TextHandler即可满足默认格式需求。

内容的提问来源于stack exchange,提问作者ofstudio

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.25 22:48:40