126 lines
3.1 KiB
Go
126 lines
3.1 KiB
Go
/*
|
|
Copyright IBM Corp. All Rights Reserved.
|
|
|
|
SPDX-License-Identifier: Apache-2.0
|
|
*/
|
|
|
|
package flogging
|
|
|
|
import (
|
|
"go.uber.org/zap/zapcore"
|
|
)
|
|
|
|
type Encoding int8
|
|
|
|
const (
|
|
CONSOLE = iota
|
|
JSON
|
|
LOGFMT
|
|
)
|
|
|
|
// EncodingSelector is used to determine whether log records are
|
|
// encoded as JSON or in human readable CONSOLE or LOGFMT formats.
|
|
type EncodingSelector interface {
|
|
Encoding() Encoding
|
|
}
|
|
|
|
// Core is a custom implementation of a zapcore.Core. It's a terrible hack that
|
|
// only exists to work around the intersection of state associated with
|
|
// encoders, implementation hiding in zapcore, and implicit, ad-hoc logger
|
|
// initialization within fabric.
|
|
//
|
|
// In addition to encoding log entries and fields to a buffer, zap Encoder
|
|
// implementations also need to maintain field state. When zapcore.Core.With is
|
|
// used, the associated encoder is cloned and the fields are added to the
|
|
// encoder. This means that encoder instances cannot be shared across cores.
|
|
//
|
|
// In terms of implementation hiding, it's difficult for our FormatEncoder to
|
|
// cleanly wrap the JSON and console implementations from zap as all methods
|
|
// from the zapcore.ObjectEncoder would need to be implemented to delegate to
|
|
// the correct backend.
|
|
//
|
|
// This implementation works by associating multiple encoders with a core. When
|
|
// fields are added to the core, the fields are added to all of the encoder
|
|
// implementations. The core also references the logging configuration to
|
|
// determine the proper encoding to use, the writer to delegate to, and the
|
|
// enabled levels.
|
|
type Core struct {
|
|
zapcore.LevelEnabler
|
|
Levels *LoggerLevels
|
|
Encoders map[Encoding]zapcore.Encoder
|
|
Selector EncodingSelector
|
|
Output zapcore.WriteSyncer
|
|
Observer Observer
|
|
}
|
|
|
|
//go:generate counterfeiter -o mock/observer.go -fake-name Observer . Observer
|
|
|
|
type Observer interface {
|
|
Check(e zapcore.Entry, ce *zapcore.CheckedEntry)
|
|
WriteEntry(e zapcore.Entry, fields []zapcore.Field)
|
|
}
|
|
|
|
func (c *Core) With(fields []zapcore.Field) zapcore.Core {
|
|
clones := map[Encoding]zapcore.Encoder{}
|
|
for name, enc := range c.Encoders {
|
|
clone := enc.Clone()
|
|
addFields(clone, fields)
|
|
clones[name] = clone
|
|
}
|
|
|
|
return &Core{
|
|
LevelEnabler: c.LevelEnabler,
|
|
Levels: c.Levels,
|
|
Encoders: clones,
|
|
Selector: c.Selector,
|
|
Output: c.Output,
|
|
Observer: c.Observer,
|
|
}
|
|
}
|
|
|
|
func (c *Core) Check(e zapcore.Entry, ce *zapcore.CheckedEntry) *zapcore.CheckedEntry {
|
|
if c.Observer != nil {
|
|
c.Observer.Check(e, ce)
|
|
}
|
|
|
|
if c.Enabled(e.Level) && c.Levels.Level(e.LoggerName).Enabled(e.Level) {
|
|
return ce.AddCore(e, c)
|
|
}
|
|
return ce
|
|
}
|
|
|
|
func (c *Core) Write(e zapcore.Entry, fields []zapcore.Field) error {
|
|
encoding := c.Selector.Encoding()
|
|
enc := c.Encoders[encoding]
|
|
|
|
buf, err := enc.EncodeEntry(e, fields)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
_, err = c.Output.Write(buf.Bytes())
|
|
buf.Free()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
|
|
if e.Level >= zapcore.PanicLevel {
|
|
c.Sync()
|
|
}
|
|
|
|
if c.Observer != nil {
|
|
c.Observer.WriteEntry(e, fields)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *Core) Sync() error {
|
|
return c.Output.Sync()
|
|
}
|
|
|
|
func addFields(enc zapcore.ObjectEncoder, fields []zapcore.Field) {
|
|
for i := range fields {
|
|
fields[i].AddTo(enc)
|
|
}
|
|
}
|