github.com/alibaba/sentinel-golang@v1.0.4/logging/logging.go (about)

     1  // Copyright 1999-2020 Alibaba Group Holding Ltd.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package logging
    16  
    17  import (
    18  	"encoding/json"
    19  	"errors"
    20  	"fmt"
    21  	"log"
    22  	"os"
    23  	"path/filepath"
    24  	"runtime"
    25  	"strings"
    26  	"sync"
    27  	"time"
    28  )
    29  
    30  // Level represents the level of logging.
    31  type Level uint8
    32  
    33  const (
    34  	DebugLevel Level = iota
    35  	InfoLevel
    36  	WarnLevel
    37  	ErrorLevel
    38  )
    39  
    40  const (
    41  	// RecordLogFileName represents the default file name of the record log.
    42  	RecordLogFileName = "sentinel-record.log"
    43  	GlobalCallerDepth = 4
    44  
    45  	defaultLogMsgBufferSize = 256
    46  )
    47  
    48  var (
    49  	DefaultDirName = filepath.Join("logs", "csp")
    50  )
    51  
    52  var (
    53  	globalLogLevel = InfoLevel
    54  	globalLogger   = NewConsoleLogger()
    55  
    56  	FrequentErrorOnce = &sync.Once{}
    57  )
    58  
    59  // GetGlobalLoggerLevel gets the Sentinel log level
    60  func GetGlobalLoggerLevel() Level {
    61  	return globalLogLevel
    62  }
    63  
    64  // ResetGlobalLoggerLevel sets the Sentinel log level
    65  // Note: this function is not thread-safe.
    66  func ResetGlobalLoggerLevel(l Level) {
    67  	globalLogLevel = l
    68  }
    69  
    70  // GetGlobalLogger gets the Sentinel global logger
    71  func GetGlobalLogger() Logger {
    72  	return globalLogger
    73  }
    74  
    75  // ResetGlobalLogger sets the Sentinel global logger
    76  // Note: this function is not thread-safe.
    77  func ResetGlobalLogger(log Logger) error {
    78  	if log == nil {
    79  		return errors.New("nil logger")
    80  	}
    81  	globalLogger = log
    82  	return nil
    83  }
    84  
    85  func NewConsoleLogger() Logger {
    86  	return &DefaultLogger{
    87  		log: log.New(os.Stdout, "", 0),
    88  	}
    89  }
    90  
    91  // outputFile is the full path(absolute path)
    92  func NewSimpleFileLogger(filepath string) (Logger, error) {
    93  	logFile, err := os.OpenFile(filepath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0777)
    94  	if err != nil {
    95  		return nil, err
    96  	}
    97  	return &DefaultLogger{
    98  		log: log.New(logFile, "", 0),
    99  	}, nil
   100  }
   101  
   102  type Logger interface {
   103  	Debug(msg string, keysAndValues ...interface{})
   104  	DebugEnabled() bool
   105  
   106  	// Info logs a non-error message with the given key/value pairs as context.
   107  	//
   108  	// The msg argument should be used to add some constant description to
   109  	// the log line.  The key/value pairs can then be used to add additional
   110  	// variable information.  The key/value pairs should alternate string
   111  	// keys and arbitrary values.
   112  	Info(msg string, keysAndValues ...interface{})
   113  	InfoEnabled() bool
   114  
   115  	Warn(msg string, keysAndValues ...interface{})
   116  	WarnEnabled() bool
   117  
   118  	Error(err error, msg string, keysAndValues ...interface{})
   119  	ErrorEnabled() bool
   120  }
   121  
   122  // sentinel general logger
   123  type DefaultLogger struct {
   124  	log *log.Logger
   125  }
   126  
   127  func caller(depth int) (file string, line int) {
   128  	_, file, line, ok := runtime.Caller(depth)
   129  	if !ok {
   130  		file = "???"
   131  		line = 0
   132  	}
   133  
   134  	// extract
   135  	if osType := runtime.GOOS; osType == "windows" {
   136  		file = strings.ReplaceAll(file, "\\", "/")
   137  	}
   138  	idx := strings.LastIndex(file, "/")
   139  	file = file[idx+1:]
   140  	return
   141  }
   142  
   143  // toSafeJSONString converts to valid JSON string, as the original string may contain '\\', '\n', '\r', '\t' and so on.
   144  func toSafeJSONString(s string) []byte {
   145  	if data, err := json.Marshal(json.RawMessage(s)); err == nil {
   146  		return data
   147  	} else {
   148  		return []byte("\"" + s + "\"")
   149  	}
   150  }
   151  
   152  func AssembleMsg(depth int, logLevel, msg string, err error, keysAndValues ...interface{}) string {
   153  	sb := strings.Builder{}
   154  	sb.Grow(defaultLogMsgBufferSize)
   155  
   156  	file, line := caller(depth)
   157  	timeStr := time.Now().Format("2006-01-02 15:04:05.520")
   158  	caller := fmt.Sprintf("%s:%d", file, line)
   159  	sb.WriteString("{")
   160  
   161  	sb.WriteByte('"')
   162  	sb.WriteString("timestamp")
   163  	sb.WriteByte('"')
   164  	sb.WriteByte(':')
   165  	sb.WriteByte('"')
   166  	sb.WriteString(timeStr)
   167  	sb.WriteByte('"')
   168  	sb.WriteByte(',')
   169  
   170  	sb.WriteByte('"')
   171  	sb.WriteString("caller")
   172  	sb.WriteByte('"')
   173  	sb.WriteByte(':')
   174  	sb.WriteByte('"')
   175  	sb.WriteString(caller)
   176  	sb.WriteByte('"')
   177  	sb.WriteByte(',')
   178  
   179  	sb.WriteByte('"')
   180  	sb.WriteString("logLevel")
   181  	sb.WriteByte('"')
   182  	sb.WriteByte(':')
   183  	sb.WriteByte('"')
   184  	sb.WriteString(logLevel)
   185  	sb.WriteByte('"')
   186  	sb.WriteByte(',')
   187  
   188  	sb.WriteByte('"')
   189  	sb.WriteString("msg")
   190  	sb.WriteByte('"')
   191  	sb.WriteByte(':')
   192  	data := toSafeJSONString(msg)
   193  	sb.Write(data)
   194  
   195  	kvLen := len(keysAndValues)
   196  	if kvLen&1 != 0 {
   197  		sb.WriteByte(',')
   198  		sb.WriteByte('"')
   199  		sb.WriteString("kvs")
   200  		sb.WriteByte('"')
   201  		sb.WriteByte(':')
   202  		s := fmt.Sprintf("%+v", keysAndValues)
   203  		data := toSafeJSONString(s)
   204  		sb.Write(data)
   205  	} else if kvLen != 0 {
   206  		for i := 0; i < kvLen; {
   207  			k := keysAndValues[i]
   208  			v := keysAndValues[i+1]
   209  			kStr, kIsStr := k.(string)
   210  			if !kIsStr {
   211  				kStr = fmt.Sprintf("%+v", k)
   212  			}
   213  			sb.WriteByte(',')
   214  			data := toSafeJSONString(kStr)
   215  			sb.Write(data)
   216  			sb.WriteByte(':')
   217  			switch v.(type) {
   218  			case string:
   219  				data := toSafeJSONString(v.(string))
   220  				sb.Write(data)
   221  			case error:
   222  				data := toSafeJSONString(v.(error).Error())
   223  				sb.Write(data)
   224  			default:
   225  				if vbs, err := json.Marshal(v); err != nil {
   226  					s := fmt.Sprintf("%+v", v)
   227  					data := toSafeJSONString(s)
   228  					sb.Write(data)
   229  				} else {
   230  					sb.Write(vbs)
   231  				}
   232  			}
   233  			i = i + 2
   234  		}
   235  	}
   236  	sb.WriteByte('}')
   237  	if err != nil {
   238  		sb.WriteString("\n")
   239  		sb.WriteString(fmt.Sprintf("%+v", err))
   240  	}
   241  	return sb.String()
   242  }
   243  
   244  func (l *DefaultLogger) Debug(msg string, keysAndValues ...interface{}) {
   245  	if !l.DebugEnabled() {
   246  		return
   247  	}
   248  	l.log.Print(AssembleMsg(GlobalCallerDepth, "DEBUG", msg, nil, keysAndValues...))
   249  }
   250  
   251  func (l *DefaultLogger) DebugEnabled() bool {
   252  	return DebugLevel >= globalLogLevel
   253  }
   254  
   255  func (l *DefaultLogger) Info(msg string, keysAndValues ...interface{}) {
   256  	if !l.InfoEnabled() {
   257  		return
   258  	}
   259  	l.log.Print(AssembleMsg(GlobalCallerDepth, "INFO", msg, nil, keysAndValues...))
   260  }
   261  
   262  func (l *DefaultLogger) InfoEnabled() bool {
   263  	return InfoLevel >= globalLogLevel
   264  }
   265  
   266  func (l *DefaultLogger) Warn(msg string, keysAndValues ...interface{}) {
   267  	if !l.WarnEnabled() {
   268  		return
   269  	}
   270  
   271  	l.log.Print(AssembleMsg(GlobalCallerDepth, "WARNING", msg, nil, keysAndValues...))
   272  }
   273  
   274  func (l *DefaultLogger) WarnEnabled() bool {
   275  	return WarnLevel >= globalLogLevel
   276  }
   277  
   278  func (l *DefaultLogger) Error(err error, msg string, keysAndValues ...interface{}) {
   279  	if !l.ErrorEnabled() {
   280  		return
   281  	}
   282  	l.log.Print(AssembleMsg(GlobalCallerDepth, "ERROR", msg, err, keysAndValues...))
   283  }
   284  
   285  func (l *DefaultLogger) ErrorEnabled() bool {
   286  	return ErrorLevel >= globalLogLevel
   287  }
   288  
   289  func Debug(msg string, keysAndValues ...interface{}) {
   290  	globalLogger.Debug(msg, keysAndValues...)
   291  }
   292  
   293  func DebugEnabled() bool {
   294  	return globalLogger.DebugEnabled()
   295  }
   296  
   297  func Info(msg string, keysAndValues ...interface{}) {
   298  	globalLogger.Info(msg, keysAndValues...)
   299  }
   300  
   301  func InfoEnabled() bool {
   302  	return globalLogger.InfoEnabled()
   303  }
   304  
   305  func Warn(msg string, keysAndValues ...interface{}) {
   306  	globalLogger.Warn(msg, keysAndValues...)
   307  }
   308  
   309  func WarnEnabled() bool {
   310  	return globalLogger.WarnEnabled()
   311  }
   312  
   313  func Error(err error, msg string, keysAndValues ...interface{}) {
   314  	globalLogger.Error(err, msg, keysAndValues...)
   315  }
   316  
   317  func ErrorEnabled() bool {
   318  	return globalLogger.ErrorEnabled()
   319  }