github.com/masterhung0112/hk_server/v5@v5.0.0-20220302090640-ec71aef15e1c/shared/mlog/logr.go (about) 1 // Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved. 2 // See LICENSE.txt for license information. 3 4 package mlog 5 6 import ( 7 "encoding/json" 8 "fmt" 9 "io" 10 "os" 11 12 "github.com/hashicorp/go-multierror" 13 "github.com/mattermost/logr" 14 logrFmt "github.com/mattermost/logr/format" 15 "github.com/mattermost/logr/target" 16 "go.uber.org/zap/zapcore" 17 ) 18 19 const ( 20 DefaultMaxTargetQueue = 1000 21 DefaultSysLogPort = 514 22 ) 23 24 type LogLevel struct { 25 ID logr.LevelID 26 Name string 27 Stacktrace bool 28 } 29 30 type LogTarget struct { 31 Type string // one of "console", "file", "tcp", "syslog", "none". 32 Format string // one of "json", "plain" 33 Levels []LogLevel 34 Options json.RawMessage 35 MaxQueueSize int 36 } 37 38 type LogTargetCfg map[string]*LogTarget 39 type LogrCleanup func() error 40 41 func newLogr() *logr.Logger { 42 lgr := &logr.Logr{} 43 lgr.OnExit = func(int) {} 44 lgr.OnPanic = func(interface{}) {} 45 lgr.OnLoggerError = onLoggerError 46 lgr.OnQueueFull = onQueueFull 47 lgr.OnTargetQueueFull = onTargetQueueFull 48 49 logger := lgr.NewLogger() 50 return &logger 51 } 52 53 func logrAddTargets(logger *logr.Logger, targets LogTargetCfg) error { 54 lgr := logger.Logr() 55 var errs error 56 for name, t := range targets { 57 target, err := NewLogrTarget(name, t) 58 if err != nil { 59 errs = multierror.Append(err) 60 continue 61 } 62 if target != nil { 63 target.SetName(name) 64 lgr.AddTarget(target) 65 } 66 } 67 return errs 68 } 69 70 // NewLogrTarget creates a `logr.Target` based on a target config. 71 // Can be used when parsing custom config files, or when programmatically adding 72 // built-in targets. Use `mlog.AddTarget` to add custom targets. 73 func NewLogrTarget(name string, t *LogTarget) (logr.Target, error) { 74 formatter, err := newFormatter(name, t.Format) 75 if err != nil { 76 return nil, err 77 } 78 filter := newFilter(t.Levels) 79 80 if t.MaxQueueSize == 0 { 81 t.MaxQueueSize = DefaultMaxTargetQueue 82 } 83 84 switch t.Type { 85 case "console": 86 return newConsoleTarget(name, t, filter, formatter) 87 case "file": 88 return newFileTarget(name, t, filter, formatter) 89 case "syslog": 90 return newSyslogTarget(name, t, filter, formatter) 91 case "tcp": 92 return newTCPTarget(name, t, filter, formatter) 93 case "none": 94 return nil, nil 95 } 96 return nil, fmt.Errorf("invalid type '%s' for target %s", t.Type, name) 97 } 98 99 func newFilter(levels []LogLevel) logr.Filter { 100 filter := &logr.CustomFilter{} 101 for _, lvl := range levels { 102 filter.Add(logr.Level(lvl)) 103 } 104 return filter 105 } 106 107 func newFormatter(name string, format string) (logr.Formatter, error) { 108 switch format { 109 case "json", "": 110 return &logrFmt.JSON{}, nil 111 case "plain": 112 return &logrFmt.Plain{Delim: " | "}, nil 113 default: 114 return nil, fmt.Errorf("invalid format '%s' for target %s", format, name) 115 } 116 } 117 118 func newConsoleTarget(name string, t *LogTarget, filter logr.Filter, formatter logr.Formatter) (logr.Target, error) { 119 type consoleOptions struct { 120 Out string `json:"Out"` 121 } 122 options := &consoleOptions{} 123 if err := json.Unmarshal(t.Options, options); err != nil { 124 return nil, err 125 } 126 127 var w io.Writer 128 switch options.Out { 129 case "stdout", "": 130 w = os.Stdout 131 case "stderr": 132 w = os.Stderr 133 default: 134 return nil, fmt.Errorf("invalid out '%s' for target %s", options.Out, name) 135 } 136 137 newTarget := target.NewWriterTarget(filter, formatter, w, t.MaxQueueSize) 138 return newTarget, nil 139 } 140 141 func newFileTarget(name string, t *LogTarget, filter logr.Filter, formatter logr.Formatter) (logr.Target, error) { 142 type fileOptions struct { 143 Filename string `json:"Filename"` 144 MaxSize int `json:"MaxSizeMB"` 145 MaxAge int `json:"MaxAgeDays"` 146 MaxBackups int `json:"MaxBackups"` 147 Compress bool `json:"Compress"` 148 } 149 options := &fileOptions{} 150 if err := json.Unmarshal(t.Options, options); err != nil { 151 return nil, err 152 } 153 return newFileTargetWithOpts(name, t, target.FileOptions(*options), filter, formatter) 154 } 155 156 func newFileTargetWithOpts(name string, t *LogTarget, opts target.FileOptions, filter logr.Filter, formatter logr.Formatter) (logr.Target, error) { 157 if opts.Filename == "" { 158 return nil, fmt.Errorf("missing 'Filename' option for target %s", name) 159 } 160 if err := checkFileWritable(opts.Filename); err != nil { 161 return nil, fmt.Errorf("error writing to 'Filename' for target %s: %w", name, err) 162 } 163 164 newTarget := target.NewFileTarget(filter, formatter, opts, t.MaxQueueSize) 165 return newTarget, nil 166 } 167 168 func newSyslogTarget(name string, t *LogTarget, filter logr.Filter, formatter logr.Formatter) (logr.Target, error) { 169 options := &SyslogParams{} 170 if err := json.Unmarshal(t.Options, options); err != nil { 171 return nil, err 172 } 173 174 if options.IP == "" { 175 return nil, fmt.Errorf("missing 'IP' option for target %s", name) 176 } 177 if options.Port == 0 { 178 options.Port = DefaultSysLogPort 179 } 180 return NewSyslogTarget(filter, formatter, options, t.MaxQueueSize) 181 } 182 183 func newTCPTarget(name string, t *LogTarget, filter logr.Filter, formatter logr.Formatter) (logr.Target, error) { 184 options := &TcpParams{} 185 if err := json.Unmarshal(t.Options, options); err != nil { 186 return nil, err 187 } 188 189 if options.IP == "" { 190 return nil, fmt.Errorf("missing 'IP' option for target %s", name) 191 } 192 if options.Port == 0 { 193 return nil, fmt.Errorf("missing 'Port' option for target %s", name) 194 } 195 return NewTcpTarget(filter, formatter, options, t.MaxQueueSize) 196 } 197 198 func checkFileWritable(filename string) error { 199 // try opening/creating the file for writing 200 file, err := os.OpenFile(filename, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600) 201 if err != nil { 202 return err 203 } 204 file.Close() 205 return nil 206 } 207 208 func isLevelEnabled(logger *logr.Logger, level logr.Level) bool { 209 if logger == nil || logger.Logr() == nil { 210 return false 211 } 212 213 status := logger.Logr().IsLevelEnabled(level) 214 return status.Enabled 215 } 216 217 // zapToLogr converts Zap fields to Logr fields. 218 // This will not be needed once Logr is used for all logging. 219 func zapToLogr(zapFields []Field) logr.Fields { 220 encoder := zapcore.NewMapObjectEncoder() 221 for _, zapField := range zapFields { 222 zapField.AddTo(encoder) 223 } 224 return logr.Fields(encoder.Fields) 225 } 226 227 // mlogLevelToLogrLevel converts a mlog logger level to 228 // an array of discrete Logr levels. 229 func mlogLevelToLogrLevels(level string) []LogLevel { 230 levels := make([]LogLevel, 0) 231 levels = append(levels, LvlError, LvlPanic, LvlFatal, LvlStdLog) 232 233 switch level { 234 case LevelDebug: 235 levels = append(levels, LvlDebug) 236 fallthrough 237 case LevelInfo: 238 levels = append(levels, LvlInfo) 239 fallthrough 240 case LevelWarn: 241 levels = append(levels, LvlWarn) 242 } 243 return levels 244 }