github.com/GuanceCloud/cliutils@v1.1.21/logger/root.go (about)

     1  // Unless explicitly stated otherwise all files in this repository are licensed
     2  // under the MIT License.
     3  // This product includes software developed at Guance Cloud (https://www.guance.com/).
     4  // Copyright 2021-present Guance, Inc.
     5  
     6  // Package logger wrapped zap as a basic logging implement.
     7  package logger
     8  
     9  import (
    10  	"fmt"
    11  	"log"
    12  	"net/url"
    13  	"os"
    14  	"path/filepath"
    15  	"strings"
    16  
    17  	"github.com/robfig/cron/v3"
    18  	"go.uber.org/zap"
    19  	"gopkg.in/natefinch/lumberjack.v2"
    20  )
    21  
    22  var (
    23  	root          *zap.Logger
    24  	defaultOption = &Option{
    25  		Level: DEBUG,
    26  		Flags: OPT_DEFAULT,
    27  	}
    28  
    29  	SchemeTCP = "tcp"
    30  	SchemeUDP = "udp"
    31  )
    32  
    33  const (
    34  	NameKeyMod   = "mod"
    35  	NameKeyMsg   = "msg"
    36  	NameKeyLevel = "lev"
    37  	NameKeyTime  = "ts"
    38  	NameKeyPos   = "pos"
    39  )
    40  
    41  func doSetGlobalRootLogger(fpath, level string, options int) error {
    42  	if fpath == "" {
    43  		return fmt.Errorf("fpath should not empty")
    44  	}
    45  
    46  	mtx.Lock()
    47  	defer mtx.Unlock()
    48  
    49  	if root != nil {
    50  		return nil
    51  	}
    52  
    53  	var err error
    54  	root, err = newRootLogger(fpath, level, options)
    55  	if err != nil {
    56  		return err
    57  	}
    58  
    59  	return nil
    60  }
    61  
    62  // SetGlobalRootLogger deprecated, use InitRoot() instead.
    63  func SetGlobalRootLogger(fpath, level string, options int) error {
    64  	return doSetGlobalRootLogger(fpath, level, options)
    65  }
    66  
    67  // InitRoot used to setup global root logger, include
    68  //   - log level
    69  //   - log path
    70  //   - set to disk file(with or without rotate)
    71  //   - set to some remtoe TCP/UDP server
    72  //   - a bounch of other OPT_XXXs
    73  func InitRoot(opt *Option) error {
    74  	if opt == nil {
    75  		opt = defaultOption
    76  	}
    77  
    78  	switch opt.Level {
    79  	case DEBUG, INFO, WARN, ERROR, PANIC, FATAL, DPANIC:
    80  	case "": // 默认使用 DEBUG
    81  		opt.Level = DEBUG
    82  
    83  	default:
    84  		return fmt.Errorf("invalid log level `%s'", opt.Level)
    85  	}
    86  
    87  	if opt.Flags == 0 {
    88  		opt.Flags = OPT_DEFAULT
    89  	}
    90  
    91  	if opt.Path != "" && (opt.Flags&OPT_STDOUT != 0) {
    92  		return fmt.Errorf("set stdout logging with log path '%s', flag:%b", opt.Path, opt.Flags)
    93  	}
    94  
    95  	switch opt.Path {
    96  	case "":
    97  		if v, ok := os.LookupEnv("LOGGER_PATH"); ok {
    98  			opt.Path = v
    99  			return setRootLoggerFromEnv(opt)
   100  		}
   101  
   102  		return doSetStdoutLogger(opt)
   103  
   104  	default:
   105  		return doSetGlobalRootLogger(opt.Path, opt.Level, opt.Flags)
   106  	}
   107  }
   108  
   109  func newRootLogger(fpath, level string, options int) (*zap.Logger, error) {
   110  	if fpath == "" {
   111  		return newNormalRootLogger(fpath, level, options)
   112  	}
   113  
   114  	u, err := url.Parse(fpath)
   115  	if err != nil {
   116  		return nil, err
   117  	}
   118  
   119  	switch strings.ToLower(u.Scheme) {
   120  	case SchemeTCP, SchemeUDP: // logs sending to some remote TCP/UDP server
   121  		return newCustomizeRootLogger(level,
   122  			options,
   123  			&remoteEndpoint{protocol: u.Scheme, host: u.Host})
   124  
   125  	default: // they must be some disk path file
   126  		if _, err := os.Stat(fpath); err != nil { // create file if not exists
   127  			if err := os.MkdirAll(filepath.Dir(fpath), 0o600); err != nil {
   128  				return nil, fmt.Errorf("MkdirAll(%s): %w", fpath, err)
   129  			}
   130  
   131  			// create empty log file
   132  			if err := os.WriteFile(fpath, nil, 0o600); err != nil {
   133  				return nil, fmt.Errorf("WriteFile(%s): %w", fpath, err)
   134  			}
   135  		}
   136  	}
   137  
   138  	// auto-rotate disk logging file
   139  	if options&OPT_ROTATE != 0 &&
   140  		options&OPT_STDOUT == 0 && // can't rotate stdout
   141  		fpath != os.DevNull { // can't rotate(rename) /dev/null
   142  		return newCustomizeRootLogger(level, options, &lumberjack.Logger{
   143  			Filename:   fpath,
   144  			MaxSize:    MaxSize,
   145  			MaxBackups: MaxBackups,
   146  			MaxAge:     MaxAge,
   147  		})
   148  	}
   149  
   150  	return newNormalRootLogger(fpath, level, options)
   151  }
   152  
   153  // InitCustomizeRoot used to setup global root logger, include
   154  //   - log path
   155  //   - log maxsize
   156  //   - log compress
   157  
   158  func InitCustomizeRoot(opt *Option) (*zap.Logger, error) {
   159  	mtx.Lock()
   160  	defer mtx.Unlock()
   161  
   162  	lumberLog := &lumberjack.Logger{
   163  		Filename: opt.Path,
   164  		MaxSize:  opt.MaxSize,
   165  		Compress: opt.Compress,
   166  	}
   167  
   168  	c := cron.New(cron.WithSeconds())
   169  	if _, err := c.AddFunc("50 59 * * * *",
   170  		func() {
   171  			if err := lumberLog.Rotate(); err != nil {
   172  				log.Printf("lumberLog.Rotate: %s, ignored", err.Error())
   173  			}
   174  		}); err != nil {
   175  		return nil, err
   176  	}
   177  	c.Start()
   178  
   179  	return newOnlyMessageRootLogger(lumberLog)
   180  }