feat: support logx.WithFields (#2128)

master
Kevin Wan 2 years ago committed by GitHub
parent 6e50c87dca
commit 24787a946b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -11,65 +11,65 @@ import (
// WithContext sets ctx to log, for keeping tracing information. // WithContext sets ctx to log, for keeping tracing information.
func WithContext(ctx context.Context) Logger { func WithContext(ctx context.Context) Logger {
return &traceLogger{ return &contextLogger{
ctx: ctx, ctx: ctx,
} }
} }
type traceLogger struct { type contextLogger struct {
logEntry logEntry
ctx context.Context ctx context.Context
} }
func (l *traceLogger) Error(v ...interface{}) { func (l *contextLogger) Error(v ...interface{}) {
l.err(fmt.Sprint(v...)) l.err(fmt.Sprint(v...))
} }
func (l *traceLogger) Errorf(format string, v ...interface{}) { func (l *contextLogger) Errorf(format string, v ...interface{}) {
l.err(fmt.Sprintf(format, v...)) l.err(fmt.Sprintf(format, v...))
} }
func (l *traceLogger) Errorv(v interface{}) { func (l *contextLogger) Errorv(v interface{}) {
l.err(fmt.Sprint(v)) l.err(fmt.Sprint(v))
} }
func (l *traceLogger) Errorw(msg string, fields ...LogField) { func (l *contextLogger) Errorw(msg string, fields ...LogField) {
l.err(msg, fields...) l.err(msg, fields...)
} }
func (l *traceLogger) Info(v ...interface{}) { func (l *contextLogger) Info(v ...interface{}) {
l.info(fmt.Sprint(v...)) l.info(fmt.Sprint(v...))
} }
func (l *traceLogger) Infof(format string, v ...interface{}) { func (l *contextLogger) Infof(format string, v ...interface{}) {
l.info(fmt.Sprintf(format, v...)) l.info(fmt.Sprintf(format, v...))
} }
func (l *traceLogger) Infov(v interface{}) { func (l *contextLogger) Infov(v interface{}) {
l.info(v) l.info(v)
} }
func (l *traceLogger) Infow(msg string, fields ...LogField) { func (l *contextLogger) Infow(msg string, fields ...LogField) {
l.info(msg, fields...) l.info(msg, fields...)
} }
func (l *traceLogger) Slow(v ...interface{}) { func (l *contextLogger) Slow(v ...interface{}) {
l.slow(fmt.Sprint(v...)) l.slow(fmt.Sprint(v...))
} }
func (l *traceLogger) Slowf(format string, v ...interface{}) { func (l *contextLogger) Slowf(format string, v ...interface{}) {
l.slow(fmt.Sprintf(format, v...)) l.slow(fmt.Sprintf(format, v...))
} }
func (l *traceLogger) Slowv(v interface{}) { func (l *contextLogger) Slowv(v interface{}) {
l.slow(v) l.slow(v)
} }
func (l *traceLogger) Sloww(msg string, fields ...LogField) { func (l *contextLogger) Sloww(msg string, fields ...LogField) {
l.slow(msg, fields...) l.slow(msg, fields...)
} }
func (l *traceLogger) WithContext(ctx context.Context) Logger { func (l *contextLogger) WithContext(ctx context.Context) Logger {
if ctx == nil { if ctx == nil {
return l return l
} }
@ -78,40 +78,49 @@ func (l *traceLogger) WithContext(ctx context.Context) Logger {
return l return l
} }
func (l *traceLogger) WithDuration(duration time.Duration) Logger { func (l *contextLogger) WithDuration(duration time.Duration) Logger {
l.Duration = timex.ReprOfDuration(duration) l.Duration = timex.ReprOfDuration(duration)
return l return l
} }
func (l *traceLogger) buildFields(fields ...LogField) []LogField { func (l *contextLogger) buildFields(fields ...LogField) []LogField {
if len(l.Duration) > 0 { if len(l.Duration) > 0 {
fields = append(fields, Field(durationKey, l.Duration)) fields = append(fields, Field(durationKey, l.Duration))
} }
traceID := traceIdFromContext(l.ctx) traceID := traceIdFromContext(l.ctx)
if len(traceID) > 0 { if len(traceID) > 0 {
fields = append(fields, Field(traceKey, traceID)) fields = append(fields, Field(traceKey, traceID))
} }
spanID := spanIdFromContext(l.ctx) spanID := spanIdFromContext(l.ctx)
if len(spanID) > 0 { if len(spanID) > 0 {
fields = append(fields, Field(spanKey, spanID)) fields = append(fields, Field(spanKey, spanID))
} }
val := l.ctx.Value(fieldsContextKey)
if val != nil {
if arr, ok := val.([]LogField); ok {
fields = append(fields, arr...)
}
}
return fields return fields
} }
func (l *traceLogger) err(v interface{}, fields ...LogField) { func (l *contextLogger) err(v interface{}, fields ...LogField) {
if shallLog(ErrorLevel) { if shallLog(ErrorLevel) {
getWriter().Error(v, l.buildFields(fields...)...) getWriter().Error(v, l.buildFields(fields...)...)
} }
} }
func (l *traceLogger) info(v interface{}, fields ...LogField) { func (l *contextLogger) info(v interface{}, fields ...LogField) {
if shallLog(InfoLevel) { if shallLog(InfoLevel) {
getWriter().Info(v, l.buildFields(fields...)...) getWriter().Info(v, l.buildFields(fields...)...)
} }
} }
func (l *traceLogger) slow(v interface{}, fields ...LogField) { func (l *contextLogger) slow(v interface{}, fields ...LogField) {
if shallLog(ErrorLevel) { if shallLog(ErrorLevel) {
getWriter().Slow(v, l.buildFields(fields...)...) getWriter().Slow(v, l.buildFields(fields...)...)
} }

@ -192,6 +192,25 @@ func TestTraceWithoutContext(t *testing.T) {
validate(t, w.String(), false, false) validate(t, w.String(), false, false)
} }
func TestLogWithFields(t *testing.T) {
w := new(mockWriter)
old := writer.Swap(w)
writer.lock.RLock()
defer func() {
writer.lock.RUnlock()
writer.Store(old)
}()
ctx := WithFields(context.Background(), Field("foo", "bar"))
l := WithContext(ctx)
SetLevel(InfoLevel)
l.Info(testlog)
var val mockValue
assert.Nil(t, json.Unmarshal([]byte(w.String()), &val))
assert.Equal(t, "bar", val.Foo)
}
func validate(t *testing.T, body string, expectedTrace, expectedSpan bool) { func validate(t *testing.T, body string, expectedTrace, expectedSpan bool) {
var val mockValue var val mockValue
dec := json.NewDecoder(strings.NewReader(body)) dec := json.NewDecoder(strings.NewReader(body))
@ -217,4 +236,5 @@ func validate(t *testing.T, body string, expectedTrace, expectedSpan bool) {
type mockValue struct { type mockValue struct {
Trace string `json:"trace"` Trace string `json:"trace"`
Span string `json:"span"` Span string `json:"span"`
Foo string `json:"foo"`
} }

@ -66,7 +66,7 @@ func (l *durationLogger) Sloww(msg string, fields ...LogField) {
} }
func (l *durationLogger) WithContext(ctx context.Context) Logger { func (l *durationLogger) WithContext(ctx context.Context) Logger {
return &traceLogger{ return &contextLogger{
ctx: ctx, ctx: ctx,
logEntry: logEntry{ logEntry: logEntry{
Duration: l.Duration, Duration: l.Duration,

@ -0,0 +1,18 @@
package logx
import "context"
var fieldsContextKey contextKey
type contextKey struct{}
// WithFields returns a new context with the given fields.
func WithFields(ctx context.Context, fields ...LogField) context.Context {
if val := ctx.Value(fieldsContextKey); val != nil {
if arr, ok := val.([]LogField); ok {
return context.WithValue(ctx, fieldsContextKey, append(arr, fields...))
}
}
return context.WithValue(ctx, fieldsContextKey, fields)
}

@ -0,0 +1,35 @@
package logx
import (
"context"
"testing"
"github.com/stretchr/testify/assert"
)
func TestWithFields(t *testing.T) {
ctx := WithFields(context.Background(), Field("a", 1), Field("b", 2))
vals := ctx.Value(fieldsContextKey)
assert.NotNil(t, vals)
fields, ok := vals.([]LogField)
assert.True(t, ok)
assert.EqualValues(t, []LogField{Field("a", 1), Field("b", 2)}, fields)
}
func TestWithFieldsAppend(t *testing.T) {
var dummyKey struct{}
ctx := context.WithValue(context.Background(), dummyKey, "dummy")
ctx = WithFields(ctx, Field("a", 1), Field("b", 2))
ctx = WithFields(ctx, Field("c", 3), Field("d", 4))
vals := ctx.Value(fieldsContextKey)
assert.NotNil(t, vals)
fields, ok := vals.([]LogField)
assert.True(t, ok)
assert.Equal(t, "dummy", ctx.Value(dummyKey))
assert.EqualValues(t, []LogField{
Field("a", 1),
Field("b", 2),
Field("c", 3),
Field("d", 4),
}, fields)
}
Loading…
Cancel
Save