github.com/iotexproject/iotex-core@v1.14.1-rc1/pkg/log/log.go (about)

     1  // Copyright (c) 2019 IoTeX Foundation
     2  // This source code is provided 'as is' and no warranties are given as to title or non-infringement, merchantability
     3  // or fitness for purpose and, to the extent permitted by law, all liability for your use of the code is disclaimed.
     4  // This source code is governed by Apache License 2.0 that can be found in the LICENSE file.
     5  
     6  package log
     7  
     8  import (
     9  	"log"
    10  	"net/http"
    11  	"os"
    12  	"sync"
    13  
    14  	"go.elastic.co/ecszap"
    15  	"go.uber.org/zap"
    16  	"go.uber.org/zap/zapcore"
    17  
    18  	"github.com/pkg/errors"
    19  )
    20  
    21  // GlobalConfig defines the global logger configurations.
    22  type GlobalConfig struct {
    23  	Zap                *zap.Config  `json:"zap" yaml:"zap"`
    24  	Trace              *TraceConfig `json:"trace" yaml:"trace"`
    25  	StderrRedirectFile *string      `json:"stderrRedirectFile" yaml:"stderrRedirectFile"`
    26  	RedirectStdLog     bool         `json:"stdLogRedirect" yaml:"stdLogRedirect"`
    27  	EcsIntegration     bool         `json:"ecsIntegration" yaml:"ecsIntegration"`
    28  }
    29  
    30  var (
    31  	_globalCfg        GlobalConfig
    32  	_logMu            sync.RWMutex
    33  	_logServeMux      = http.NewServeMux()
    34  	_subLoggers       map[string]*zap.Logger
    35  	_globalLoggerName = "global"
    36  )
    37  
    38  func init() {
    39  	zapCfg := zap.NewDevelopmentConfig()
    40  	zapCfg.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
    41  	zapCfg.Level.SetLevel(zap.InfoLevel)
    42  	l, err := zapCfg.Build()
    43  	if err != nil {
    44  		log.Println("Failed to init zap global logger, no zap log will be shown till zap is properly initialized: ", err)
    45  		return
    46  	}
    47  	_logMu.Lock()
    48  	_globalCfg.Zap = &zapCfg
    49  	_subLoggers = make(map[string]*zap.Logger)
    50  	_logMu.Unlock()
    51  	zap.ReplaceGlobals(l)
    52  }
    53  
    54  // L wraps zap.L().
    55  func L() *zap.Logger { return zap.L() }
    56  
    57  // S wraps zap.S().
    58  func S() *zap.SugaredLogger { return zap.S() }
    59  
    60  // Logger returns logger of the given name
    61  func Logger(name string) *zap.Logger {
    62  	logger, ok := _subLoggers[name]
    63  	if !ok {
    64  		return L()
    65  	}
    66  	return logger
    67  }
    68  
    69  // InitLoggers initializes the global logger and other sub loggers.
    70  func InitLoggers(globalCfg GlobalConfig, subCfgs map[string]GlobalConfig, opts ...zap.Option) error {
    71  	if _, exists := subCfgs[_globalLoggerName]; exists {
    72  		return errors.New("'" + _globalLoggerName + "' is a reserved name for global logger")
    73  	}
    74  	subCfgs[_globalLoggerName] = globalCfg
    75  	for name, cfg := range subCfgs {
    76  		if _, exists := _subLoggers[name]; exists {
    77  			return errors.Errorf("duplicate sub logger name: %s", name)
    78  		}
    79  		if cfg.Zap == nil {
    80  			zapCfg := zap.NewProductionConfig()
    81  			cfg.Zap = &zapCfg
    82  		} else {
    83  			cfg.Zap.EncoderConfig = zap.NewProductionEncoderConfig()
    84  		}
    85  		if globalCfg.EcsIntegration {
    86  			cfg.Zap.EncoderConfig = ecszap.ECSCompatibleEncoderConfig(cfg.Zap.EncoderConfig)
    87  		}
    88  
    89  		var cores []zapcore.Core
    90  		if cfg.StderrRedirectFile != nil {
    91  			stderrF, err := os.OpenFile(*cfg.StderrRedirectFile, os.O_WRONLY|os.O_CREATE|os.O_SYNC|os.O_APPEND, 0600)
    92  			if err != nil {
    93  				return err
    94  			}
    95  
    96  			cores = append(cores, zapcore.NewCore(
    97  				zapcore.NewJSONEncoder(cfg.Zap.EncoderConfig),
    98  				zapcore.AddSync(stderrF),
    99  				cfg.Zap.Level))
   100  		}
   101  		switch cfg.Zap.Encoding {
   102  		case "console":
   103  			consoleCfg := zap.NewDevelopmentConfig()
   104  			consoleCfg.EncoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder
   105  			cores = append(cores, zapcore.NewCore(
   106  				zapcore.NewConsoleEncoder(consoleCfg.EncoderConfig),
   107  				zapcore.AddSync(os.Stdout),
   108  				cfg.Zap.Level))
   109  		case "json":
   110  			cfg.Zap.EncoderConfig.EncodeTime = zapcore.ISO8601TimeEncoder
   111  			cores = append(cores, zapcore.NewCore(
   112  				zapcore.NewJSONEncoder(cfg.Zap.EncoderConfig),
   113  				zapcore.AddSync(os.Stdout),
   114  				cfg.Zap.Level))
   115  		default:
   116  			return errors.Errorf("unknown encoding: %s", cfg.Zap.Encoding)
   117  		}
   118  
   119  		core := zapcore.NewTee(cores...)
   120  		logger := zap.New(core, opts...)
   121  
   122  		_logMu.Lock()
   123  		if name == _globalLoggerName {
   124  			_globalCfg = cfg
   125  			if cfg.RedirectStdLog {
   126  				zap.RedirectStdLog(logger)
   127  			}
   128  			zap.ReplaceGlobals(logger)
   129  			if err := initTraceLogger(logger, cfg.Trace); err != nil {
   130  				return err
   131  			}
   132  		} else {
   133  			_subLoggers[name] = logger
   134  		}
   135  		_logServeMux.HandleFunc("/"+name, cfg.Zap.Level.ServeHTTP)
   136  		_logMu.Unlock()
   137  	}
   138  
   139  	return nil
   140  }
   141  
   142  // RegisterLevelConfigMux registers log's level config http mux.
   143  func RegisterLevelConfigMux(root *http.ServeMux) {
   144  	_logMu.Lock()
   145  	root.Handle("/logging/", http.StripPrefix("/logging", _logServeMux))
   146  	_logMu.Unlock()
   147  }