blob: c59d9a71f24317d7c1f68d29aada4529905d23ef [file] [log] [blame]
// 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
// +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
}
}