github.com/jxskiss/gopkg@v0.17.3/zlog/global.go (about)

     1  package zlog
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"runtime"
     7  
     8  	"go.uber.org/zap"
     9  )
    10  
    11  var (
    12  	gL, gL_1 *zap.Logger
    13  	gS, gS_1 *zap.SugaredLogger
    14  	gP       *Properties
    15  )
    16  
    17  func init() {
    18  	ReplaceGlobals(mustNewGlobalLogger(&Config{}))
    19  }
    20  
    21  // Properties records some information about the global config.
    22  type Properties struct {
    23  	cfg   GlobalConfig
    24  	level atomicLevel
    25  }
    26  
    27  func (p *Properties) setup() func() {
    28  	if p.cfg.MethodNameKey == "" {
    29  		p.cfg.MethodNameKey = defaultMethodNameKey
    30  	}
    31  	var resetStdLog = func() {}
    32  	if p.cfg.RedirectStdLog {
    33  		resetStdLog = zap.RedirectStdLog(L())
    34  	}
    35  	oldDisableTrace := disableTrace
    36  	var resetDisableTrace = func() {
    37  		disableTrace = oldDisableTrace
    38  	}
    39  	disableTrace = p.cfg.DisableTrace
    40  	return func() {
    41  		resetDisableTrace()
    42  		resetStdLog()
    43  	}
    44  }
    45  
    46  // GetLevel gets the logging level of the logger.
    47  func (p *Properties) GetLevel() Level { return p.level.Level() }
    48  
    49  // SetLevel modifies the logging level of the logger.
    50  func (p *Properties) SetLevel(lvl Level) { p.level.SetLevel(lvl) }
    51  
    52  // SetupGlobals setups the global loggers in this package and zap library.
    53  // By default, global loggers are set with default configuration with info
    54  // level and json format, you may use this function to change the default
    55  // loggers.
    56  //
    57  // See Config and GlobalConfig for available configurations.
    58  //
    59  // It should be called at program startup, library code shall not touch
    60  // this function.
    61  func SetupGlobals(cfg *Config, opts ...zap.Option) {
    62  	ReplaceGlobals(mustNewGlobalLogger(cfg, opts...))
    63  }
    64  
    65  func mustNewGlobalLogger(cfg *Config, opts ...zap.Option) (*zap.Logger, *Properties) {
    66  	logger, props, err := New(cfg, opts...)
    67  	if err != nil {
    68  		panic(fmt.Sprintf("invalid config to initialize logger: %v", err))
    69  	}
    70  	return logger, props
    71  }
    72  
    73  // ReplaceGlobals replaces the global Logger and SugaredLogger, and returns a
    74  // function to restore the original values.
    75  //
    76  // It should be called at program startup, library code shall not touch
    77  // this function.
    78  func ReplaceGlobals(logger *zap.Logger, props *Properties) func() {
    79  	oldL, oldP := gL, gP
    80  
    81  	gL = logger
    82  	gS = logger.Sugar()
    83  	gP = props
    84  
    85  	gL_1 = logger.WithOptions(zap.AddCallerSkip(1))
    86  	gS_1 = gL_1.Sugar()
    87  
    88  	resetProps := props.setup()
    89  	zap.ReplaceGlobals(logger)
    90  
    91  	return func() {
    92  		resetProps()
    93  		ReplaceGlobals(oldL, oldP)
    94  	}
    95  }
    96  
    97  // SetDevelopment sets the global logger in development mode, and redirects
    98  // output from the standard log library's package-global logger to the
    99  // global logger in this package.
   100  //
   101  // It should only be called at program startup, when you run in development
   102  // mode, for production mode, please check SetupGlobals and ReplaceGlobals.
   103  func SetDevelopment() {
   104  	cfg := &Config{}
   105  	cfg.Development = true
   106  	cfg.RedirectStdLog = true
   107  	ReplaceGlobals(mustNewGlobalLogger(cfg))
   108  }
   109  
   110  // GetLevel gets the global logging level.
   111  func GetLevel() Level { return gP.GetLevel() }
   112  
   113  // SetLevel modifies the global logging level on the fly.
   114  // It's safe for concurrent use.
   115  func SetLevel(lvl Level) { gP.SetLevel(lvl) }
   116  
   117  // L returns the global Logger, which can be reconfigured with
   118  // SetupGlobals and ReplaceGlobals.
   119  func L() *zap.Logger { return gL }
   120  
   121  // S returns the global SugaredLogger, which can be reconfigured with
   122  // SetupGlobals and ReplaceGlobals.
   123  func S() *zap.SugaredLogger { return gS }
   124  
   125  // Sync flushes any buffered log entries.
   126  func Sync() error {
   127  	if err := L().Sync(); err != nil {
   128  		return err
   129  	}
   130  	if err := S().Sync(); err != nil {
   131  		return err
   132  	}
   133  	if err := _l().Sync(); err != nil {
   134  		return err
   135  	}
   136  	if err := _s().Sync(); err != nil {
   137  		return err
   138  	}
   139  	return nil
   140  }
   141  
   142  // -------- global logging functions -------- //
   143  
   144  func _l() *zap.Logger        { return gL_1 }
   145  func _s() *zap.SugaredLogger { return gS_1 }
   146  
   147  func Debug(msg string, fields ...zap.Field)  { _l().Debug(msg, fields...) }
   148  func Info(msg string, fields ...zap.Field)   { _l().Info(msg, fields...) }
   149  func Warn(msg string, fields ...zap.Field)   { _l().Warn(msg, fields...) }
   150  func Error(msg string, fields ...zap.Field)  { _l().Error(msg, fields...) }
   151  func DPanic(msg string, fields ...zap.Field) { _l().DPanic(msg, fields...) }
   152  func Panic(msg string, fields ...zap.Field)  { _l().Panic(msg, fields...) }
   153  func Fatal(msg string, fields ...zap.Field)  { _l().Fatal(msg, fields...) }
   154  
   155  func Debugf(format string, args ...interface{})  { _s().Debugf(format, args...) }
   156  func Infof(format string, args ...interface{})   { _s().Infof(format, args...) }
   157  func Warnf(format string, args ...interface{})   { _s().Warnf(format, args...) }
   158  func Errorf(format string, args ...interface{})  { _s().Errorf(format, args...) }
   159  func DPanicf(format string, args ...interface{}) { _s().DPanicf(format, args...) }
   160  func Panicf(format string, args ...interface{})  { _s().Panicf(format, args...) }
   161  func Fatalf(format string, args ...interface{})  { _s().Fatalf(format, args...) }
   162  
   163  // Print uses fmt.Sprint to log a message at InfoLevel if it's enabled.
   164  //
   165  // It has same signature with log.Print, which helps to migrate from the
   166  // standard library to this package.
   167  func Print(args ...interface{}) { _l().Info(fmt.Sprint(args...)) }
   168  
   169  // Printf logs a message at InfoLevel if it's enabled.
   170  //
   171  // It has same signature with log.Printf, which helps to migrate from the
   172  // standard library to this package.
   173  func Printf(format string, args ...interface{}) { _s().Infof(format, args...) }
   174  
   175  // -------- utility functions -------- //
   176  
   177  // With creates a child logger and adds structured context to it.
   178  // Fields added to the child don't affect the parent, and vice versa.
   179  func With(fields ...zap.Field) *zap.Logger {
   180  	return L().With(fields...)
   181  }
   182  
   183  // WithCtx creates a child logger and customizes its behavior using context
   184  // data (e.g. adding fields, dynamically change logging level, etc.)
   185  //
   186  // If the ctx is created by WithBuilder, it carries a Builder instance,
   187  // this function uses that Builder to build the logger, else it calls
   188  // GlobalConfig.CtxFunc to get CtxResult from ctx. In case that
   189  // GlobalConfig.CtxFunc is not configured globally, it logs an error
   190  // message at DPANIC level.
   191  //
   192  // Also see GlobalConfig.CtxFunc, CtxArgs and CtxResult for more details.
   193  func WithCtx(ctx context.Context, extra ...zap.Field) *zap.Logger {
   194  	if ctx == nil {
   195  		return L().With(extra...)
   196  	}
   197  	if builder := getCtxBuilder(ctx); builder != nil {
   198  		return builder.With(extra...).Build()
   199  	}
   200  	ctxFunc := gP.cfg.CtxFunc
   201  	if ctxFunc == nil {
   202  		L().DPanic("calling WithCtx without CtxFunc configured")
   203  		return L().With(extra...)
   204  	}
   205  	ctxResult := ctxFunc(ctx, CtxArgs{})
   206  	return B(nil).withCtxResult(ctxResult).With(extra...).Build()
   207  }
   208  
   209  // WithMethod creates a child logger and adds the caller's method name
   210  // to the logger.
   211  // It also adds the given extra fields to the logger.
   212  func WithMethod(extra ...zap.Field) *zap.Logger {
   213  	methodName, _, _, ok := getCaller(1)
   214  	if !ok {
   215  		return L().With(extra...)
   216  	}
   217  	methodNameKey := gP.cfg.MethodNameKey
   218  	if len(extra) == 0 {
   219  		return L().With(zap.String(methodNameKey, methodName))
   220  	}
   221  	fields := append([]zap.Field{zap.String(methodNameKey, methodName)}, extra...)
   222  	return L().With(fields...)
   223  }
   224  
   225  // Named creates a child logger and adds a new name segment to the logger's
   226  // name. By default, loggers are unnamed.
   227  // It also adds the given extra fields to the logger.
   228  func Named(name string, extra ...zap.Field) *zap.Logger {
   229  	return L().Named(name).With(extra...)
   230  }
   231  
   232  func getCaller(skip int) (name, file string, line int, ok bool) {
   233  	pc, file, line, ok := runtime.Caller(skip + 1)
   234  	if !ok {
   235  		return
   236  	}
   237  	name = runtime.FuncForPC(pc).Name()
   238  	for i := len(name) - 1; i >= 0; i-- {
   239  		if name[i] == '/' {
   240  			name = name[i+1:]
   241  			break
   242  		}
   243  	}
   244  	pathSepCnt := 0
   245  	for i := len(file) - 1; i >= 0; i-- {
   246  		if file[i] == '/' {
   247  			pathSepCnt++
   248  			if pathSepCnt == 2 {
   249  				file = file[i+1:]
   250  				break
   251  			}
   252  		}
   253  	}
   254  	return
   255  }