lmittmann / tint

🌈 slog.Handler that writes tinted (colorized) logs
https://pkg.go.dev/github.com/lmittmann/tint
MIT License
725 stars 38 forks source link

More log levels please #36

Closed suntong closed 1 year ago

suntong commented 1 year ago

Please consider adding more log levels by default, as I used to have more than one debug levels, just like we can use multiple -v for ssh to get different levels of debug info.

I'm proposing:

const (
    LevelTrace  = slog.Level(-8)
    LevelDbg3   = slog.Level(-7)
    LevelDbg2   = slog.Level(-6)
    LevelDbg1   = slog.Level(-5)
    LevelNotice = slog.Level(2)
    LevelFatal  = slog.Level(12)
)

var LevelNames = map[slog.Leveler]string{
    LevelTrace:  "TRC",
    LevelDbg3:   "D-3",
    LevelDbg2:   "D-2",
    LevelDbg1:   "D-1",
    LevelNotice: "NTC",
    LevelFatal:  "FTL",
}

. . .

        opts := &slog.HandlerOptions{
            Level: LevelTrace,
            ReplaceAttr: func(groups []string, a slog.Attr) slog.Attr {
                if a.Key == slog.LevelKey {
                    level := a.Value.Any().(slog.Level)
                    levelLabel, exists := LevelNames[level]
                    if !exists {
                        levelLabel = level.String()
                    }

                    a.Value = slog.StringValue(levelLabel)
                }

                return a
            },
        }

        logger := slog.New(slog.NewTextHandler(os.Stdout, opts))

        ctx := context.Background()
        logger.Log(ctx, LevelDbg1, "Hidden debug message level1")
        logger.Log(ctx, LevelDbg2, "Hidden debug message level2")
        logger.Log(ctx, LevelDbg3, "Hidden debug message level3")
        logger.Log(ctx, LevelTrace, "Trace message")
        logger.Log(ctx, LevelNotice, "Notice message")
        logger.Log(ctx, LevelFatal, "Fatal level")

Which would give us:

time=... level=D-1 msg="Hidden debug message level1"
time=... level=D-2 msg="Hidden debug message level2"
time=... level=D-3 msg="Hidden debug message level3"
time=... level=TRC msg="Trace message"
time=... level=NTC msg="Notice message"
time=... level=FTL msg="Fatal level"

NB, I tried to put the above into tint, but got:

panic: interface conversion: interface {} is int64, not slog.Level

at my logger.Log(ctx, LevelDbg1 line.

Ref: https://betterstack.com/community/guides/logging/logging-in-go/#creating-custom-log-levels

lmittmann commented 1 year ago

tint and slog support custom levels. See e.g. https://github.com/lmittmann/tint/blob/main/handler_test.go#L246-L251 or the slog doc. If you want tint to write custom level labels beyond e.g. INF+1 you can use Options.ReplaceAttr.

suntong commented 1 year ago

OK, @lmittmann, I'm trying to do my custom level labels with the code in OP, but,

                if a.Key == slog.LevelKey {
                    level := a.Value.Any().(slog.Level)

the above code works with slog.HandlerOptions but fails with tint's tint.Options.ReplaceAttr:

panic: interface conversion: interface {} is int64, not slog.Level

Would you show me how exactly to make use of tint.Options.ReplaceAttr and a.Value please?

lmittmann commented 1 year ago

The panic indicates that you are asserting a wrong type for the level. But you are right that there should not be a difference in the type of the level passed to ReplaceAttr between tint and slog.