| // Copyright 2021 The Go Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style |
| // license that can be found in the LICENSE file. |
| |
| //go:build !disable_events |
| |
| // zap provides an implementation of zapcore.Core for events. |
| // To use globally: |
| // |
| // zap.ReplaceGlobals(zap.New(NewCore(exporter))) |
| // |
| // If you call elogging.SetExporter, then you can pass nil |
| // for the exporter above and it will use the global one. |
| package zap |
| |
| import ( |
| "context" |
| "fmt" |
| "math" |
| "reflect" |
| "time" |
| |
| "go.uber.org/zap" |
| "go.uber.org/zap/zapcore" |
| "golang.org/x/exp/event" |
| "golang.org/x/exp/event/severity" |
| ) |
| |
| type core struct { |
| ev *event.Event // cloned but never delivered |
| labels []event.Label |
| } |
| |
| var _ zapcore.Core = (*core)(nil) |
| |
| func NewCore(ctx context.Context) zapcore.Core { |
| return &core{ev: event.New(ctx, event.LogKind)} |
| } |
| |
| func (c *core) Enabled(level zapcore.Level) bool { |
| return true |
| } |
| |
| func (c *core) With(fields []zapcore.Field) zapcore.Core { |
| c2 := *c |
| if len(fields) > 0 { |
| c2.labels = make([]event.Label, len(c.labels), len(c.labels)+len(fields)) |
| copy(c2.labels, c.labels) |
| for _, f := range fields { |
| c2.labels = append(c2.labels, newLabel(f)) |
| } |
| } |
| return &c2 |
| } |
| |
| func (c *core) Write(e zapcore.Entry, fs []zapcore.Field) error { |
| if c.ev == nil { |
| return nil |
| } |
| ev := c.ev.Clone() |
| if ev == nil { |
| return nil |
| } |
| ev.At = e.Time |
| // TODO: Add labels more efficiently: compare cap(ev.Labels) to the total number to add, |
| // and allocate a []Label once. |
| ev.Labels = append(ev.Labels, c.labels...) |
| ev.Labels = append(ev.Labels, convertLevel(e.Level).Label()) |
| ev.Labels = append(ev.Labels, event.String("name", e.LoggerName)) |
| if e.Stack != "" { |
| ev.Labels = append(ev.Labels, event.String("stack", e.Stack)) |
| } |
| if e.Caller.Defined { |
| ev.Labels = append(ev.Labels, event.String("caller", e.Caller.String())) |
| } |
| for _, f := range fs { |
| ev.Labels = append(ev.Labels, newLabel(f)) |
| } |
| ev.Labels = append(ev.Labels, event.String("msg", e.Message)) |
| ev.Deliver() |
| return nil |
| } |
| |
| func (c *core) Check(e zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry { |
| return ce.AddCore(e, c) |
| } |
| |
| func (c *core) Sync() error { return nil } |
| |
| func newLabel(f zap.Field) event.Label { |
| switch f.Type { |
| case zapcore.ArrayMarshalerType, zapcore.ObjectMarshalerType, zapcore.BinaryType, zapcore.ByteStringType, |
| zapcore.Complex128Type, zapcore.Complex64Type, zapcore.TimeFullType, zapcore.ReflectType, |
| zapcore.ErrorType: |
| return event.Value(f.Key, f.Interface) |
| case zapcore.DurationType: |
| return event.Duration(f.Key, time.Duration(f.Integer)) |
| case zapcore.Float64Type: |
| return event.Float64(f.Key, math.Float64frombits(uint64(f.Integer))) |
| case zapcore.Float32Type: |
| return event.Float64(f.Key, float64(math.Float32frombits(uint32(f.Integer)))) |
| case zapcore.BoolType: |
| b := false |
| if f.Integer != 0 { |
| b = true |
| } |
| return event.Bool(f.Key, b) |
| case zapcore.Int64Type: |
| return event.Int64(f.Key, f.Integer) |
| case zapcore.Int32Type: |
| return event.Int64(f.Key, f.Integer) |
| |
| //, zapcore.Int16Type, zapcore.Int8Type, |
| // zapcore.Uint64Type, zapcore.Uint32Type, zapcore.Uint16Type, zapcore.Uint8Type, zapcore.UintptrType: |
| // return (f.Key,uint64(f.Integer)) |
| case zapcore.StringType: |
| return event.String(f.Key, f.String) |
| case zapcore.TimeType: |
| t := time.Unix(0, f.Integer) |
| if f.Interface != nil { |
| t = t.In(f.Interface.(*time.Location)) |
| } |
| return event.Value(f.Key, t) |
| case zapcore.StringerType: |
| return event.String(f.Key, stringerToString(f.Interface)) |
| case zapcore.NamespaceType: |
| // TODO: ??? |
| return event.Label{} |
| case zapcore.SkipType: |
| // TODO: avoid creating a label at all in this case. |
| return event.Label{} |
| default: |
| panic(fmt.Sprintf("unknown field type: %v", f)) |
| } |
| } |
| |
| // Adapter from encodeStringer in go.uber.org/zap/zapcore/field.go. |
| func stringerToString(stringer interface{}) (s string) { |
| // Try to capture panics (from nil references or otherwise) when calling |
| // the String() method, similar to https://golang.org/src/fmt/print.go#L540 |
| defer func() { |
| if err := recover(); err != nil { |
| // If it's a nil pointer, just say "<nil>". The likeliest causes are a |
| // Stringer that fails to guard against nil or a nil pointer for a |
| // value receiver, and in either case, "<nil>" is a nice result. |
| if v := reflect.ValueOf(stringer); v.Kind() == reflect.Ptr && v.IsNil() { |
| s = "<nil>" |
| return |
| } |
| s = fmt.Sprintf("PANIC=%v", err) |
| } |
| }() |
| |
| return stringer.(fmt.Stringer).String() |
| } |
| |
| func convertLevel(level zapcore.Level) severity.Level { |
| switch level { |
| case zapcore.DebugLevel: |
| return severity.Debug |
| case zapcore.InfoLevel: |
| return severity.Info |
| case zapcore.WarnLevel: |
| return severity.Warning |
| case zapcore.ErrorLevel: |
| return severity.Error |
| case zapcore.DPanicLevel: |
| return severity.Fatal - 1 |
| case zapcore.PanicLevel: |
| return severity.Fatal + 1 |
| case zapcore.FatalLevel: |
| return severity.Fatal |
| default: |
| return severity.Trace |
| } |
| } |