[FEATURE REQUEST] - Add support for function With to predefine parameters for logger
Logging same multiple parameters in various places, we have to repeat the parameters in multiple places. It would be really helpful if the package supports some function that allows to predefine the parameters and use the logger.
For example
logger := logger.With().Str("attr1", "1").Str("clientID","123")
logger.Info().Msg("This is test")
This would print log with provided parameters
{"attr1": "1", "clientID": "123", "msg": "This is test"}
I through this idioms so long and finally I give up to implement it -- because this API model is memory wasting.
But I developed an alternative method as below
logger := logger
logger.Context = log.NewContext(nil).Str("attr1", "1").Str("clientID","123").Value()
logger.Info().Msg("This is test")
Makes sense
package main
import (
"github.com/oarkflow/log"
)
func main() {
logger := log.NewContext(log.DefaultLogger.Context).Str("attr1", "12").Copy()
logger.Info().Str("foo", "bar").Int("number", 42).Msg("hi, phuslog")
logger.Info().Msgf("foo=%s number=%d error=%+v", "bar", 42, "an error")
}
Any thoughts on such API model?
Here, I guess DefaultLogger.Context will have Level data as well. So I'm not sure how to deal with Level. But this would print all data from DefaultLogger (if present) along with other logs
Came up with following addition
// logger.go
func With(logr *Logger) (e *Entry) {
e = new(Entry)
e.buf = logr.Context
e.logger = logr
return
}
func (e *Entry) Copy() Logger {
logger := Logger{
Context: e.buf,
Writer: e.w,
Level: e.Level,
}
if e.logger != nil {
logger.TimeField = e.logger.TimeField
logger.LogNode = e.logger.LogNode
logger.EnableTracing = e.logger.EnableTracing
logger.TraceIDField = e.logger.TraceIDField
logger.Caller = e.logger.Caller
logger.TimeFormat = e.logger.TimeFormat
logger.TimeLocation = e.logger.TimeLocation
}
return logger
}
Output
package main
import (
"github.com/oarkflow/log"
)
func main() {
log.DefaultLogger.TimeField = "date"
log.DefaultLogger.TimeFormat = "2006-01-02"
logger := log.With(&log.DefaultLogger).Str("attr1", "12").Copy()
logger2 := log.With(&logger).Str("attr2", "1245").Copy()
logger2.Info().Str("foo", "bar").Int("number", 42).Msg("hi, phuslog")
logger2.Error().Msgf("foo=%s number=%d error=%+v", "bar", 42, "an error")
}
// Output:
Yes, it can.
But I can also provide another memory-effective example in real world 😄
- define a request-scope struct and managed by sync.Pool, it contains to explicit
LogContext Contextfield https://github.com/phuslu/liner/blob/master/handler_http.go#L34-L66 - set value to
LogConextwith memory-recycling, theNewContext(ri.LogContext[:0])casting magic https://github.com/phuslu/liner/blob/master/handler_http.go#L100-L116 - call
log.Context(ri.LogContext).explicitly to logging with context https://github.com/phuslu/liner/blob/master/handler_http_web_index.go#L55
As we see, it a bit more troublesome than With idioms, but it's truely 0-allocs and achieves to highest performance.
That's all why I designed/implemented such API mode.