log icon indicating copy to clipboard operation
log copied to clipboard

[FEATURE REQUEST] - Add support for function With to predefine parameters for logger

Open sujit-baniya opened this issue 1 year ago • 4 comments

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"}

sujit-baniya avatar May 11 '24 05:05 sujit-baniya

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")

phuslu avatar May 11 '24 05:05 phuslu

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

sujit-baniya avatar May 11 '24 06:05 sujit-baniya

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:

sujit-baniya avatar May 11 '24 06:05 sujit-baniya

Yes, it can.

But I can also provide another memory-effective example in real world 😄

  1. define a request-scope struct and managed by sync.Pool, it contains to explicit LogContext Context field https://github.com/phuslu/liner/blob/master/handler_http.go#L34-L66
  2. set value to LogConext with memory-recycling, the NewContext(ri.LogContext[:0]) casting magic https://github.com/phuslu/liner/blob/master/handler_http.go#L100-L116
  3. 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.

phuslu avatar May 11 '24 13:05 phuslu