package logx import ( "context" "fmt" "log/slog" "mayfly-go/pkg/utils/runtimex" "runtime" "strings" ) var ( config *Config ) func GetConfig() *Config { if config == nil { return &Config{ Level: "info", Type: "text", AddSource: false, } } return config } func Init(logConf Config) { config = &logConf var handler slog.Handler if logConf.Type == "text" { handler = NewTextHandler(config) } else { handler = NewJsonHandler(config) } slog.SetDefault(slog.New(handler)) } func Print(msg string, args ...any) { Log(context.Background(), slog.LevelInfo, msg, args...) } func Debug(msg string, args ...any) { Log(context.Background(), slog.LevelDebug, msg, args...) } func Debugf(format string, args ...any) { Log(context.Background(), slog.LevelDebug, fmt.Sprintf(format, args...)) } func DebugWithFields(msg string, mapFields map[string]any) { Log(context.Background(), slog.LevelDebug, msg, map2Attrs(mapFields)...) } // debug记录,并将堆栈信息添加至msg里,默认记录10个堆栈信息 func DebugTrace(msg string, err error) { Log(context.Background(), slog.LevelDebug, fmt.Sprintf(msg+"%s\n%s", err.Error(), runtimex.StatckStr(2, 10))) } func Info(msg string, args ...any) { Log(context.Background(), slog.LevelInfo, msg, args...) } func InfoContext(ctx context.Context, msg string, args ...any) { Log(ctx, slog.LevelInfo, msg, args...) } func Infof(format string, args ...any) { Log(context.Background(), slog.LevelInfo, fmt.Sprintf(format, args...)) } func InfoWithFields(msg string, mapFields map[string]any) { Log(context.Background(), slog.LevelInfo, msg, map2Attrs(mapFields)...) } func Warn(msg string, args ...any) { Log(context.Background(), slog.LevelWarn, msg, args...) } func Warnf(format string, args ...any) { Log(context.Background(), slog.LevelWarn, fmt.Sprintf(format, args...)) } func WarnWithFields(msg string, mapFields map[string]any) { Log(context.Background(), slog.LevelWarn, msg, map2Attrs(mapFields)...) } func Error(msg string, args ...any) { Log(context.Background(), slog.LevelError, msg, args...) } func Errorf(format string, args ...any) { Log(context.Background(), slog.LevelError, fmt.Sprintf(format, args...)) } // 错误记录,并将堆栈信息添加至msg里,默认记录10个堆栈信息 func ErrorTrace(msg string, err error) { Log(context.Background(), slog.LevelError, fmt.Sprintf(msg+"%s\n%s", err.Error(), runtimex.StatckStr(2, 10))) } func ErrorWithFields(msg string, mapFields map[string]any) { Log(context.Background(), slog.LevelError, msg, map2Attrs(mapFields)...) } func Panic(msg string, args ...any) { Log(context.Background(), slog.LevelError, msg, args...) panic(msg) } func Panicf(format string, args ...any) { msg := fmt.Sprintf(format, args...) Log(context.Background(), slog.LevelError, fmt.Sprintf(format, args...)) panic(msg) } func Log(ctx context.Context, level slog.Level, msg string, args ...any) { slog.Log(ctx, level, msg, appendCommonAttr(ctx, level, args)...) } // 获取日志公共属性 func getCommonAttr(ctx context.Context, level slog.Level) []any { commonAttrs := make([]any, 0) // 如果系统配置添加方法信息或者为错误级别时则 记录方法信息及行号 if config.AddSource || level == slog.LevelError { // skip [runtime.Callers, getCommonAttr, appendCommonAttr, logx.Log, logx.Info|Debug|Warn|Error..] var pcs [1]uintptr runtime.Callers(5, pcs[:]) fs := runtime.CallersFrames(pcs[:]) f, _ := fs.Next() source := &Source{ Function: f.Function, Fileline: fmt.Sprintf("%s:%d", runtimex.ParseFrameFile(f.File), f.Line), } commonAttrs = append(commonAttrs, slog.SourceKey, source) } return commonAttrs } func appendCommonAttr(ctx context.Context, level slog.Level, args []any) []any { commonAttrs := getCommonAttr(ctx, level) if len(commonAttrs) > 0 { args = append(commonAttrs, args...) } return args } // map类型转为attr func map2Attrs(mapArg map[string]any) []any { atts := make([]any, 0) for k, v := range mapArg { atts = append(atts, slog.Any(k, v)) } return atts } type Source struct { // Function is the package path-qualified function name containing the // source line. If non-empty, this string uniquely identifies a single // function in the program. This may be the empty string if not known. Function string `json:"function"` // File and Line are the file name and line number (1-based) of the source // line. These may be the empty string and zero, respectively, if not known. Fileline string `json:"fileline"` } func (s Source) String() string { // 查找最后一个/的位置, 如mayfly-go/pkg/starter.runWebServer lastIndex := strings.LastIndex(s.Function, "/") // 获取最后一段,即starter.runWebServer funcName := s.Function[lastIndex+1:] return fmt.Sprintf("%s#%s", s.Fileline, funcName) }