blob: c16f20f914f44e346712e1858db55c9a2ead6f85 [file] [log] [blame]
// Licensed to the Apache Software Foundation (ASF) under one or more
// contributor license agreements. See the NOTICE file distributed with
// this work for additional information regarding copyright ownership.
// The ASF licenses this file to You under the Apache License, Version 2.0
// (the "License"); you may not use this file except in compliance with
// the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package log
import (
"fmt"
"os"
"strconv"
"time"
"github.com/apache/incubator-eventmesh/eventmesh-server-go/log/rollwriter"
"go.uber.org/zap"
"go.uber.org/zap/zapcore"
)
var defaultConfig = []OutputConfig{
{
Writer: "console",
Level: "debug",
Formatter: "console",
},
}
// Some ZapCore constants.
const (
ConsoleZapCore = "console"
FileZapCore = "file"
)
// Levels is the map from string to zapcore.Level.
var Levels = map[string]zapcore.Level{
"": zapcore.DebugLevel,
"debug": zapcore.DebugLevel,
"info": zapcore.InfoLevel,
"warn": zapcore.WarnLevel,
"error": zapcore.ErrorLevel,
"fatal": zapcore.FatalLevel,
}
var levelToZapLevel = map[Level]zapcore.Level{
LevelTrace: zapcore.DebugLevel,
LevelDebug: zapcore.DebugLevel,
LevelInfo: zapcore.InfoLevel,
LevelWarn: zapcore.WarnLevel,
LevelError: zapcore.ErrorLevel,
LevelFatal: zapcore.FatalLevel,
}
var zapLevelToLevel = map[zapcore.Level]Level{
zapcore.DebugLevel: LevelDebug,
zapcore.InfoLevel: LevelInfo,
zapcore.WarnLevel: LevelWarn,
zapcore.ErrorLevel: LevelError,
zapcore.FatalLevel: LevelFatal,
}
// NewZapLog creates a workflow default Logger from zap whose caller skip is set to 2.
func NewZapLog(c Config) Logger {
return NewZapLogWithCallerSkip(c, 2)
}
// NewZapLogWithCallerSkip creates a workflow default Logger from zap.
func NewZapLogWithCallerSkip(c Config, callerSkip int) Logger {
var (
cores []zapcore.Core
levels []zap.AtomicLevel
)
for _, o := range c {
writer := GetWriter(o.Writer)
if writer == nil {
panic("log: writer core: " + o.Writer + " no registered")
}
decoder := &Decoder{OutputConfig: &o}
if err := writer.Setup(o.Writer, decoder); err != nil {
panic("log: writer core: " + o.Writer + " setup fail: " + err.Error())
}
cores = append(cores, decoder.Core)
levels = append(levels, decoder.ZapLevel)
}
return &zapLog{
levels: levels,
logger: zap.New(
zapcore.NewTee(cores...),
zap.AddCallerSkip(callerSkip),
zap.AddCaller(),
),
}
}
func newEncoder(c *OutputConfig) zapcore.Encoder {
encoderCfg := zapcore.EncoderConfig{
TimeKey: GetLogEncoderKey("T", c.FormatConfig.TimeKey),
LevelKey: GetLogEncoderKey("L", c.FormatConfig.LevelKey),
NameKey: GetLogEncoderKey("N", c.FormatConfig.NameKey),
CallerKey: GetLogEncoderKey("C", c.FormatConfig.CallerKey),
FunctionKey: GetLogEncoderKey(zapcore.OmitKey, c.FormatConfig.FunctionKey),
MessageKey: GetLogEncoderKey("M", c.FormatConfig.MessageKey),
StacktraceKey: GetLogEncoderKey("S", c.FormatConfig.StacktraceKey),
LineEnding: zapcore.DefaultLineEnding,
EncodeLevel: zapcore.CapitalLevelEncoder,
EncodeTime: NewTimeEncoder(c.FormatConfig.TimeFmt),
EncodeDuration: zapcore.StringDurationEncoder,
EncodeCaller: zapcore.ShortCallerEncoder,
}
switch c.Formatter {
case "console":
return zapcore.NewConsoleEncoder(encoderCfg)
case "json":
return zapcore.NewJSONEncoder(encoderCfg)
default:
return zapcore.NewConsoleEncoder(encoderCfg)
}
}
// GetLogEncoderKey gets user defined log output name, uses defKey if empty.
func GetLogEncoderKey(defKey, key string) string {
if key == "" {
return defKey
}
return key
}
func newConsoleCore(c *OutputConfig) (zapcore.Core, zap.AtomicLevel) {
lvl := zap.NewAtomicLevelAt(Levels[c.Level])
return zapcore.NewCore(
newEncoder(c),
zapcore.Lock(os.Stdout),
lvl), lvl
}
func newFileCore(c *OutputConfig) (zapcore.Core, zap.AtomicLevel, error) {
opts := []rollwriter.Option{
rollwriter.WithMaxAge(c.WriteConfig.MaxAge),
rollwriter.WithMaxBackups(c.WriteConfig.MaxBackups),
rollwriter.WithCompress(c.WriteConfig.Compress),
rollwriter.WithMaxSize(c.WriteConfig.MaxSize),
}
// roll by time.
if c.WriteConfig.RollType != RollBySize {
opts = append(opts, rollwriter.WithRotationTime(c.WriteConfig.TimeUnit.Format()))
}
writer, err := rollwriter.NewRollWriter(c.WriteConfig.Filename, opts...)
if err != nil {
return nil, zap.AtomicLevel{}, err
}
// write mod.
var ws zapcore.WriteSyncer
if c.WriteConfig.WriteMode == WriteSync {
ws = zapcore.AddSync(writer)
} else {
dropLog := c.WriteConfig.WriteMode == WriteFast
ws = rollwriter.NewAsyncRollWriter(writer,
rollwriter.WithDropLog(dropLog),
)
}
// log level.
lvl := zap.NewAtomicLevelAt(Levels[c.Level])
return zapcore.NewCore(
newEncoder(c),
ws, lvl,
), lvl, nil
}
// NewTimeEncoder creates a time format encoder.
func NewTimeEncoder(format string) zapcore.TimeEncoder {
switch format {
case "":
return func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendByteString(DefaultTimeFormat(t))
}
case "seconds":
return zapcore.EpochTimeEncoder
case "milliseconds":
return zapcore.EpochMillisTimeEncoder
case "nanoseconds":
return zapcore.EpochNanosTimeEncoder
default:
return func(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
enc.AppendString(CustomTimeFormat(t, format))
}
}
}
// CustomTimeFormat customize time format.
func CustomTimeFormat(t time.Time, format string) string {
return t.Format(format)
}
// DefaultTimeFormat returns the default time format.
func DefaultTimeFormat(t time.Time) []byte {
t = t.Local()
year, month, day := t.Date()
hour, minute, second := t.Clock()
micros := t.Nanosecond() / 1000
buf := make([]byte, 23)
buf[0] = byte((year/1000)%10) + '0'
buf[1] = byte((year/100)%10) + '0'
buf[2] = byte((year/10)%10) + '0'
buf[3] = byte(year%10) + '0'
buf[4] = '-'
buf[5] = byte((month)/10) + '0'
buf[6] = byte((month)%10) + '0'
buf[7] = '-'
buf[8] = byte((day)/10) + '0'
buf[9] = byte((day)%10) + '0'
buf[10] = ' '
buf[11] = byte((hour)/10) + '0'
buf[12] = byte((hour)%10) + '0'
buf[13] = ':'
buf[14] = byte((minute)/10) + '0'
buf[15] = byte((minute)%10) + '0'
buf[16] = ':'
buf[17] = byte((second)/10) + '0'
buf[18] = byte((second)%10) + '0'
buf[19] = '.'
buf[20] = byte((micros/100000)%10) + '0'
buf[21] = byte((micros/10000)%10) + '0'
buf[22] = byte((micros/1000)%10) + '0'
return buf
}
// ZapLogWrapper delegates zapLogger which was introduced in this
// By ZapLogWrapper proxy, we can add a layer to the debug series function calls, so that the caller
// information can be set correctly.
type ZapLogWrapper struct {
l *zapLog
}
// GetLogger returns interval zapLog.
func (z *ZapLogWrapper) GetLogger() Logger {
return z.l
}
// Trace logs to TRACE log. Arguments are handled in the manner of fmt.Print.
func (z *ZapLogWrapper) Trace(args ...interface{}) {
z.l.Trace(args...)
}
// Tracef logs to TRACE log. Arguments are handled in the manner of fmt.Printf.
func (z *ZapLogWrapper) Tracef(format string, args ...interface{}) {
z.l.Tracef(format, args...)
}
// Debug logs to DEBUG log. Arguments are handled in the manner of fmt.Print.
func (z *ZapLogWrapper) Debug(args ...interface{}) {
z.l.Debug(args...)
}
// Debugf logs to DEBUG log. Arguments are handled in the manner of fmt.Printf.
func (z *ZapLogWrapper) Debugf(format string, args ...interface{}) {
z.l.Debugf(format, args...)
}
// Info logs to INFO log. Arguments are handled in the manner of fmt.Print.
func (z *ZapLogWrapper) Info(args ...interface{}) {
z.l.Info(args...)
}
// Infof logs to INFO log. Arguments are handled in the manner of fmt.Printf.
func (z *ZapLogWrapper) Infof(format string, args ...interface{}) {
z.l.Infof(format, args...)
}
// Warn logs to WARNING log. Arguments are handled in the manner of fmt.Print.
func (z *ZapLogWrapper) Warn(args ...interface{}) {
z.l.Warn(args...)
}
// Warnf logs to WARNING log. Arguments are handled in the manner of fmt.Printf.
func (z *ZapLogWrapper) Warnf(format string, args ...interface{}) {
z.l.Warnf(format, args...)
}
// Error logs to ERROR log. Arguments are handled in the manner of fmt.Print.
func (z *ZapLogWrapper) Error(args ...interface{}) {
z.l.Error(args...)
}
// Errorf logs to ERROR log. Arguments are handled in the manner of fmt.Printf.
func (z *ZapLogWrapper) Errorf(format string, args ...interface{}) {
z.l.Errorf(format, args...)
}
// Fatal logs to FATAL log. Arguments are handled in the manner of fmt.Print.
func (z *ZapLogWrapper) Fatal(args ...interface{}) {
z.l.Fatal(args...)
}
// Fatalf logs to FATAL log. Arguments are handled in the manner of fmt.Printf.
func (z *ZapLogWrapper) Fatalf(format string, args ...interface{}) {
z.l.Fatalf(format, args...)
}
// Sync calls the zap logger's Sync method, and flushes any buffered log entries.
// Applications should take care to call Sync before exiting.
func (z *ZapLogWrapper) Sync() error {
return z.l.Sync()
}
// SetLevel set output log level.
func (z *ZapLogWrapper) SetLevel(output string, level Level) {
z.l.SetLevel(output, level)
}
// GetLevel gets output log level.
func (z *ZapLogWrapper) GetLevel(output string) Level {
return z.l.GetLevel(output)
}
// WithFields set some user defined data to logs, such as uid, imei, etc.
// Use this function at the beginning of each request. The returned new Logger should be used to
// print logs.
// Fields must be paired.
// Deprecated: use With instead.
func (z *ZapLogWrapper) WithFields(fields ...string) Logger {
return z.l.WithFields(fields...)
}
// With add user defined fields to Logger. Fields support multiple values.
func (z *ZapLogWrapper) With(fields ...Field) Logger {
return z.l.With(fields...)
}
// zapLog is a Logger implementation based on zaplogger.
type zapLog struct {
levels []zap.AtomicLevel
logger *zap.Logger
}
// WithFields set some user defined data to logs, such as uid, imei, etc.
// Use this function at the beginning of each request. The returned new Logger should be used to
// print logs.
// Fields must be paired.
// Deprecated: use With instead.
func (l *zapLog) WithFields(fields ...string) Logger {
zapFields := make([]zap.Field, len(fields)/2)
for i := range zapFields {
zapFields[i] = zap.String(fields[2*i], fields[2*i+1])
}
// By ZapLogWrapper proxy, we can add a layer to the debug series function calls, so that the
// caller information can be set correctly.
return &ZapLogWrapper{
l: &zapLog{
levels: l.levels,
logger: l.logger.With(zapFields...)}}
}
// With add user defined fields to Logger. Fields support multiple values.
func (l *zapLog) With(fields ...Field) Logger {
zapFields := make([]zap.Field, len(fields))
for i := range fields {
zapFields[i] = zap.Any(fields[i].Key, fields[i].Value)
}
// By ZapLogWrapper proxy, we can add a layer to the debug series function calls, so that the
// caller information can be set correctly.
return &ZapLogWrapper{
l: &zapLog{
levels: l.levels,
logger: l.logger.With(zapFields...)}}
}
func getLogMsg(args ...interface{}) string {
msg := fmt.Sprint(args...)
return msg
}
func getLogMsgf(format string, args ...interface{}) string {
msg := fmt.Sprintf(format, args...)
return msg
}
// Trace logs to TRACE log. Arguments are handled in the manner of fmt.Print.
func (l *zapLog) Trace(args ...interface{}) {
if l.logger.Core().Enabled(zapcore.DebugLevel) {
l.logger.Debug(getLogMsg(args...))
}
}
// Tracef logs to TRACE log. Arguments are handled in the manner of fmt.Printf.
func (l *zapLog) Tracef(format string, args ...interface{}) {
if l.logger.Core().Enabled(zapcore.DebugLevel) {
l.logger.Debug(getLogMsgf(format, args...))
}
}
// Debug logs to DEBUG log. Arguments are handled in the manner of fmt.Print.
func (l *zapLog) Debug(args ...interface{}) {
if l.logger.Core().Enabled(zapcore.DebugLevel) {
l.logger.Debug(getLogMsg(args...))
}
}
// Debugf logs to DEBUG log. Arguments are handled in the manner of fmt.Printf.
func (l *zapLog) Debugf(format string, args ...interface{}) {
if l.logger.Core().Enabled(zapcore.DebugLevel) {
l.logger.Debug(getLogMsgf(format, args...))
}
}
// Info logs to INFO log. Arguments are handled in the manner of fmt.Print.
func (l *zapLog) Info(args ...interface{}) {
if l.logger.Core().Enabled(zapcore.InfoLevel) {
l.logger.Info(getLogMsg(args...))
}
}
// Infof logs to INFO log. Arguments are handled in the manner of fmt.Printf.
func (l *zapLog) Infof(format string, args ...interface{}) {
if l.logger.Core().Enabled(zapcore.InfoLevel) {
l.logger.Info(getLogMsgf(format, args...))
}
}
// Warn logs to WARNING log. Arguments are handled in the manner of fmt.Print.
func (l *zapLog) Warn(args ...interface{}) {
if l.logger.Core().Enabled(zapcore.WarnLevel) {
l.logger.Warn(getLogMsg(args...))
}
}
// Warnf logs to WARNING log. Arguments are handled in the manner of fmt.Printf.
func (l *zapLog) Warnf(format string, args ...interface{}) {
if l.logger.Core().Enabled(zapcore.WarnLevel) {
l.logger.Warn(getLogMsgf(format, args...))
}
}
// Error logs to ERROR log. Arguments are handled in the manner of fmt.Print.
func (l *zapLog) Error(args ...interface{}) {
if l.logger.Core().Enabled(zapcore.ErrorLevel) {
l.logger.Error(getLogMsg(args...))
}
}
// Errorf logs to ERROR log. Arguments are handled in the manner of fmt.Printf.
func (l *zapLog) Errorf(format string, args ...interface{}) {
if l.logger.Core().Enabled(zapcore.ErrorLevel) {
l.logger.Error(getLogMsgf(format, args...))
}
}
// Fatal logs to FATAL log. Arguments are handled in the manner of fmt.Print.
func (l *zapLog) Fatal(args ...interface{}) {
if l.logger.Core().Enabled(zapcore.FatalLevel) {
l.logger.Fatal(getLogMsg(args...))
}
}
// Fatalf logs to FATAL log. Arguments are handled in the manner of fmt.Printf.
func (l *zapLog) Fatalf(format string, args ...interface{}) {
if l.logger.Core().Enabled(zapcore.FatalLevel) {
l.logger.Fatal(getLogMsgf(format, args...))
}
}
// Sync calls the zap logger's Sync method, and flushes any buffered log entries.
// Applications should take care to call Sync before exiting.
func (l *zapLog) Sync() error {
return l.logger.Sync()
}
// SetLevel sets output log level.
func (l *zapLog) SetLevel(output string, level Level) {
i, e := strconv.Atoi(output)
if e != nil {
return
}
if i < 0 || i >= len(l.levels) {
return
}
l.levels[i].SetLevel(levelToZapLevel[level])
}
// GetLevel gets output log level.
func (l *zapLog) GetLevel(output string) Level {
i, e := strconv.Atoi(output)
if e != nil {
return LevelDebug
}
if i < 0 || i >= len(l.levels) {
return LevelDebug
}
return zapLevelToLevel[l.levels[i].Level()]
}