rs / zerolog

Zero Allocation JSON Logger
MIT License
10.61k stars 572 forks source link

Support for custom log levels #686

Open dadrus opened 1 month ago

dadrus commented 1 month ago

I'm using zerolog to log both regular events, and access log events using two separately configured loggers. I would like to be able to filter these events in the resulted logs easily and thinking about something like a custom log level, e.g. ACCESS. I'm wondering if introducing an option to define custom log levels, like e.g. possible with slog is something you would consider as a valuable extension. Having a new log level named ACCESS or similar for the abovesaid purpose would be fully sufficient as well.

jamestelfer commented 1 month ago

This isn't the solution that you're after, but a strategy I've used is to add a type field to the logger; i.e. logger.Str("type", "access"). I can then filter the logs based on the JSON fields.

It is possible to use log levels lower than trace or higher than error, but this can interact poorly with the configured logging level (which acts as an integer threshold). I don't know of a way for the same integer level to have two names.

There is a global zerolog.LevelFieldMarshalFunc that you might find useful when writing the JSON logs using a non-standard level.

(Not a maintainer, just a bystander.)

dadrus commented 1 month ago

I'm indeed using an approach similar to the one you've mentioned right now - setting a specific field. IMO it is not the optimal one.

jamestelfer commented 1 month ago

I'm abusing zerolog to provide an auditing capability. I don't think this is recommended; for me it's just a pragmatic first step.

I'm using a level value well above Disabled (7), which AFAICT means that explicitly disabling the log won't touch the audit logger (or at least it's OK in my very constrained scenario).

This means there's a couple of tradeoffs in this approach:

Given that the level can be local to the logger, it might work for you.

var (
    // auditLevel is the log level at which audit logs are written.
    auditLevel = zerolog.Level(20)
)

// write with the audit level
logger.WithLevel(auditLevel).Str("key", val).Msg("") // no added MSG

func zerologConfiguration() {
    // configure the console writer
    zerolog.FormattedLevels[auditLevel] = "AUD"

    // format the audit level as "audit", falling back to the default
    marshal := zerolog.LevelFieldMarshalFunc
    zerolog.LevelFieldMarshalFunc = func(l zerolog.Level) string {
        if l == auditLevel {
            return "audit"
        }
        return marshal(l)
    }
}