// This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. package logging import ( "io" "log" "strings" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) // LogWriter is a wrapper around zap.Logger that implements io.Writer interface. type LogWriter struct { dest *zap.Logger level zapcore.Level } // NewWriter creates new log zap log writer. func NewWriter(l *zap.Logger, level zapcore.Level) io.Writer { return &LogWriter{ dest: l, level: level, } } // Write implements io.Writer interface. func (lw *LogWriter) Write(line []byte) (int, error) { checked := lw.dest.Check(lw.level, strings.TrimSpace(string(line))) if checked == nil { return 0, nil } checked.Write() return len(line), nil } // logWrapper wraps around standard logger. type logWrapper struct { log *log.Logger } // Write implements io.Writer interface. func (lw *logWrapper) Write(line []byte) (int, error) { if lw.log == nil { log.Print(string(line)) } else { lw.log.Print(string(line)) } return len(line), nil } // StdWriter creates a sync writer that writes all logs to the std logger. var StdWriter = &logWrapper{nil} // LogDestination defines logging destination Config. type LogDestination struct { // Level log level. level zapcore.LevelEnabler writer io.Writer config zapcore.EncoderConfig } // EncoderOption defines a log destination encoder config setter. type EncoderOption func(config *zapcore.EncoderConfig) // WithoutTimestamp disables timestamp. func WithoutTimestamp() EncoderOption { return func(config *zapcore.EncoderConfig) { config.EncodeTime = nil } } // WithoutLogLevels disable log level. func WithoutLogLevels() EncoderOption { return func(config *zapcore.EncoderConfig) { config.EncodeLevel = nil } } // WithColoredLevels enables log level colored output. func WithColoredLevels() EncoderOption { return func(config *zapcore.EncoderConfig) { config.EncodeLevel = zapcore.CapitalColorLevelEncoder } } // NewLogDestination creates new log destination. func NewLogDestination(writer io.Writer, logLevel zapcore.LevelEnabler, options ...EncoderOption) *LogDestination { config := zap.NewDevelopmentEncoderConfig() config.ConsoleSeparator = " " config.StacktraceKey = "error" for _, option := range options { option(&config) } return &LogDestination{ level: logLevel, config: config, writer: writer, } } // Wrap is a simple helper to wrap io.Writer with default arguments. func Wrap(writer io.Writer) *zap.Logger { return ZapLogger( NewLogDestination(writer, zapcore.DebugLevel), ) } // ZapLogger creates new default Zap Logger. func ZapLogger(dests ...*LogDestination) *zap.Logger { if len(dests) == 0 { panic("at least one writer must be defined") } cores := []zapcore.Core{} for _, dest := range dests { consoleEncoder := zapcore.NewConsoleEncoder(dest.config) cores = append(cores, zapcore.NewCore(consoleEncoder, zapcore.AddSync(dest.writer), dest.level)) } core := zapcore.NewTee(cores...) logger := zap.New(core) return logger } // Component helper for creating zap.Field. func Component(name string) zapcore.Field { return zap.String("component", name) }