zap icon indicating copy to clipboard operation
zap copied to clipboard

Support custom log levels/general logging.

Open NathanZook opened this issue 5 years ago • 15 comments

My company wants to implement a trace level log. Currently, this is problematic because zap.Logger.check is private.

Digging in, it would seem that a full implementation would extract https://github.com/uber-go/zap/blob/master/logger.go#L273-L282 into a method call. Of course, the natural target of the call is probably the levels themselves, which would be a problem. Making it a method on *zap.Logger would do, I think.

Ideally, we would probably want to change the zapcore.Level constants to variables, and implement a zap.Logger#Log method that takes a zapcore.Level as its second argument.

I know of one major user of Go that implements TRACE1, TRACE2, and TRACE3...

NathanZook avatar Feb 11 '19 21:02 NathanZook

+1 for custom levels. Maybe custom levels not argent right now, but at least adding Trace level would be soooo goood.

iorlas avatar Mar 20 '19 18:03 iorlas

+1 to add trace level

lysu avatar Apr 08 '19 03:04 lysu

Yes, it’s very good if there are more levels.

kolkov avatar May 12 '20 05:05 kolkov

Is anything ever going to be done about this? At a minimum, a trace log level is a must, but support for custom levels would be the ideal solution.

mpfaff avatar May 25 '20 17:05 mpfaff

It's not clear whether this can be done in a fully backwards compatible way (as existing cores and encoders may break). For example, the sampler panics when a negative level is passed in (#713). While the sampler can be fixed because it's part of the zap repository, there may be other implementations that would not support new levels.

We are open to PRs that implement custom levels in a backwards compatible way (ideally with an analysis of existing implementations of zap cores/encoders to ensure that it is not breaking). Unfortunately we can't accept any backwards incompatible changes to zap 1.0

prashantv avatar May 26 '20 19:05 prashantv

Stackdriver has a few more levels on the severe end:

    // Alert means a person must take an action immediately.
    Alert = Severity(logtypepb.LogSeverity_ALERT)
    // Emergency means one or more systems are unusable.
    Emergency = Severity(logtypepb.LogSeverity_EMERGENCY)

https://godoc.org/cloud.google.com/go/logging#Severity

tv42 avatar Oct 09 '20 20:10 tv42

Stackdriver has a few more levels on the severe end:

    // Alert means a person must take an action immediately.
    Alert = Severity(logtypepb.LogSeverity_ALERT)
    // Emergency means one or more systems are unusable.
    Emergency = Severity(logtypepb.LogSeverity_EMERGENCY)

https://godoc.org/cloud.google.com/go/logging#Severity

these are just the syslog levels (https://tools.ietf.org/html/rfc3164#section-4.1.1 (Table 2.))

alxn avatar Oct 09 '20 20:10 alxn

@alxn Good point (I didn't recognize them because of the presentation). However, it doesn't matter whether they're Stackdriver opinions or syslog opinions; they're log destination opinions that zap doesn't share, and they can't currently be added either.

Background: I'm writing an app using zap, sending log to stackdriver (similarly, syslog) via stdio, and just realized while writing the adapter that the models don't match exactly. At least it's a field I can't express in my program with zap, and not the other way around where I would have to lose information.

tv42 avatar Oct 09 '20 21:10 tv42

Zap does have a few other levels (DPanic, Fatal, Panic). They do have different behaviours, but perhaps they can help.

prashantv avatar Oct 09 '20 21:10 prashantv

Since this is still open, I'd like to chime in. My motivation to look into the possibility to use custom log levels with zap was to log (http, gRPC, ...) requests. E.g. zap.Log("request", "GET /users/<some-id>", zap.Int("status", httpStatus)). It would make it very easy to filter by these log lines (or exclude them), extract the error rate and of course you don't have to wonder if a 400 response is InfoLevel, ErrorLevel or ... .

It's basically like keeping request and error log in separate files (remember the times...?). I understand there are ways to work around that (e.g. log every request as info and use fields). But again, querying your logs would definitely be easier with this custom level. I also respect your previous comments about backward compatibility, yet it might be something for a v2?

tmeisel avatar Nov 12 '22 00:11 tmeisel

I extend the trace level like this and it works.

const TraceLevel = zapcore.DebugLevel - 1

func NewDevZapLogger(lvl zapcore.LevelEnabler) *zap.Logger {
	encCfg := zap.NewDevelopmentEncoderConfig()
	encCfg.EncodeLevel = capitalColorLevelEncoder
	zapCore := zapcore.NewCore(zapcore.NewConsoleEncoder(encCfg), zapcore.AddSync(os.Stderr), lvl)
	return zap.New(zapCore)
}

func capitalColorLevelEncoder(level zapcore.Level, enc zapcore.PrimitiveArrayEncoder) {
	if level == TraceLevel {
		enc.AppendString(color.CyanString("TRACE"))
		return
	}
	zapcore.CapitalColorLevelEncoder(level, enc)
}

func Use() {
	zap.ReplaceGlobals(NewDevZapLogger(zap.NewAtomicLevelAt(TraceLevel)))
}

win5do avatar Jan 06 '23 02:01 win5do

@win5do How do you log a message with Trace level? There is no Trace() method, and we can't specify level when log a message.

seaguest avatar Apr 07 '23 13:04 seaguest

@win5do How do you log a message with Trace level? There is no Trace() method, and we can't specify level when log a message.

zap.Logger provide Log API that you can pass log level,or make a wrapper func.

zap.L().Log(TraceLevel, "trace msg")

win5do avatar Apr 09 '23 14:04 win5do

@win5do but it only support string, we need something like Tracef, Tracew, e.g https://github.com/uber-go/zap/pull/933 probably a fork of zap would be better.

seaguest avatar Apr 10 '23 07:04 seaguest

@win5do but it only support string, we need something like Tracef, Tracew, e.g #933 probably a fork of zap would be better.

It's easy to make a wrapper func:

func (s *logger) Tracef(format string, args ...interface{}) {
	s.zapLogger.Log(TraceLevel, fmt.Sprintf(format, args...))
}

Don't forget to handle caller skip.

win5do avatar Apr 11 '23 02:04 win5do