blob: 7b1a12379f5f11ab09233e70b4fb42c60cec0d08 [file] [log] [blame]
khenaidoobf6e7bb2018-08-14 22:27:29 -04001/*
2 * Copyright 2018-present Open Networking Foundation
3
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7
8 * http://www.apache.org/licenses/LICENSE-2.0
9
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
khenaidoo7da372d2018-09-21 16:03:09 -040016
Girish Kumarf8d4f8d2020-08-18 11:45:30 +000017// Package log provides a structured Logger interface implemented using zap logger. It provides the following capabilities:
18// 1. Package level logging - a go package can register itself (AddPackage) and have a logger created for that package.
19// 2. Dynamic log level change - for all registered packages (SetAllLogLevel)
20// 3. Dynamic log level change - for a given package (SetPackageLogLevel)
21// 4. Provides a default logger for unregistered packages (however avoid its usage)
22// 5. Allow key-value pairs to be added to a logger(UpdateLogger) or all loggers (UpdateAllLoggers) at run time
23// 6. Add to the log output the location where the log was invoked (filename.functionname.linenumber)
khenaidoo7da372d2018-09-21 16:03:09 -040024//
25// Using package-level logging (recommended approach). In the examples below, log refers to this log package.
khenaidoo7da372d2018-09-21 16:03:09 -040026//
Girish Kumarf8d4f8d2020-08-18 11:45:30 +000027// 1. In the appropriate package, add the following in the init section of the package (usually in a common.go file)
28// The log level can be changed and any number of default fields can be added as well. The log level specifies
29// the lowest log level that will be in the output while the fields will be automatically added to all log printouts.
30// However, as voltha components re-initialize the log level of each registered package to default initial loglevel
31// passed as CLI argument, the log level passed in RegisterPackage call effectively has no effect.
khenaidoo7da372d2018-09-21 16:03:09 -040032//
Girish Kumarf8d4f8d2020-08-18 11:45:30 +000033// var logger log.CLogger
34// func init() {
35// logger, err = log.RegisterPackage(log.JSON, log.ErrorLevel, log.Fields{"key1": "value1"})
36// }
khenaidoo7da372d2018-09-21 16:03:09 -040037//
Girish Kumarf8d4f8d2020-08-18 11:45:30 +000038// 2. In the calling package, use any of the publicly available functions of local package-level logger instance created
39// in previous step. Here is an example to write an Info log with additional fields:
khenaidoo7da372d2018-09-21 16:03:09 -040040//
Girish Kumarf8d4f8d2020-08-18 11:45:30 +000041// logger.Infow("An example", mylog.Fields{"myStringOutput": "output", "myIntOutput": 2})
khenaidoo7da372d2018-09-21 16:03:09 -040042//
Girish Kumarf8d4f8d2020-08-18 11:45:30 +000043// 3. To dynamically change the log level, you can use
44// a) SetLogLevel from inside your package or
45// b) SetPackageLogLevel from anywhere or
46// c) SetAllLogLevel from anywhere.
47//
48// Dynamic Loglevel configuration feature also uses SetPackageLogLevel method based on triggers received due to
49// Changes to configured loglevels
khenaidoo7da372d2018-09-21 16:03:09 -040050
khenaidoocfee5f42018-07-19 22:47:38 -040051package log
52
53import (
khenaidooc6c7bda2020-06-17 17:20:18 -040054 "context"
khenaidoo7da372d2018-09-21 16:03:09 -040055 "errors"
khenaidoo5c11af72018-07-20 17:21:05 -040056 "fmt"
khenaidoob9203542018-09-17 22:56:37 -040057 "path"
khenaidoocfee5f42018-07-19 22:47:38 -040058 "runtime"
59 "strings"
David K. Bainbridgefc8e1812021-04-09 16:09:49 +000060
61 zp "go.uber.org/zap"
62 zc "go.uber.org/zap/zapcore"
khenaidoocfee5f42018-07-19 22:47:38 -040063)
64
Rohan Agrawal7f72f0c2020-01-14 12:05:51 +000065type LogLevel int8
66
khenaidoocfee5f42018-07-19 22:47:38 -040067const (
68 // DebugLevel logs a message at debug level
Rohan Agrawal7f72f0c2020-01-14 12:05:51 +000069 DebugLevel = LogLevel(iota)
khenaidoocfee5f42018-07-19 22:47:38 -040070 // InfoLevel logs a message at info level
71 InfoLevel
72 // WarnLevel logs a message at warning level
73 WarnLevel
74 // ErrorLevel logs a message at error level
75 ErrorLevel
khenaidoocfee5f42018-07-19 22:47:38 -040076 // FatalLevel logs a message, then calls os.Exit(1).
77 FatalLevel
78)
79
80// CONSOLE formats the log for the console, mostly used during development
81const CONSOLE = "console"
khenaidoo5c11af72018-07-20 17:21:05 -040082
khenaidoocfee5f42018-07-19 22:47:38 -040083// JSON formats the log using json format, mostly used by an automated logging system consumption
84const JSON = "json"
85
khenaidooc6c7bda2020-06-17 17:20:18 -040086// Context Aware Logger represents an abstract logging interface. Any logging implementation used
khenaidoocfee5f42018-07-19 22:47:38 -040087// will need to abide by this interface
khenaidooc6c7bda2020-06-17 17:20:18 -040088type CLogger interface {
89 Debug(context.Context, ...interface{})
90 Debugln(context.Context, ...interface{})
91 Debugf(context.Context, string, ...interface{})
92 Debugw(context.Context, string, Fields)
khenaidoocfee5f42018-07-19 22:47:38 -040093
khenaidooc6c7bda2020-06-17 17:20:18 -040094 Info(context.Context, ...interface{})
95 Infoln(context.Context, ...interface{})
96 Infof(context.Context, string, ...interface{})
97 Infow(context.Context, string, Fields)
khenaidoocfee5f42018-07-19 22:47:38 -040098
khenaidooc6c7bda2020-06-17 17:20:18 -040099 Warn(context.Context, ...interface{})
100 Warnln(context.Context, ...interface{})
101 Warnf(context.Context, string, ...interface{})
102 Warnw(context.Context, string, Fields)
khenaidoocfee5f42018-07-19 22:47:38 -0400103
khenaidooc6c7bda2020-06-17 17:20:18 -0400104 Error(context.Context, ...interface{})
105 Errorln(context.Context, ...interface{})
106 Errorf(context.Context, string, ...interface{})
107 Errorw(context.Context, string, Fields)
khenaidoocfee5f42018-07-19 22:47:38 -0400108
khenaidooc6c7bda2020-06-17 17:20:18 -0400109 Fatal(context.Context, ...interface{})
110 Fatalln(context.Context, ...interface{})
111 Fatalf(context.Context, string, ...interface{})
112 Fatalw(context.Context, string, Fields)
khenaidoocfee5f42018-07-19 22:47:38 -0400113
khenaidooc6c7bda2020-06-17 17:20:18 -0400114 With(Fields) CLogger
khenaidoo7da372d2018-09-21 16:03:09 -0400115
116 // The following are added to be able to use this logger as a gRPC LoggerV2 if needed
117 //
khenaidooc6c7bda2020-06-17 17:20:18 -0400118 Warning(context.Context, ...interface{})
119 Warningln(context.Context, ...interface{})
120 Warningf(context.Context, string, ...interface{})
khenaidoo7da372d2018-09-21 16:03:09 -0400121
122 // V reports whether verbosity level l is at least the requested verbose level.
Rohan Agrawal7f72f0c2020-01-14 12:05:51 +0000123 V(l LogLevel) bool
serkant.uluderya2ae470f2020-01-21 11:13:09 -0800124
125 //Returns the log level of this specific logger
Rohan Agrawal7f72f0c2020-01-14 12:05:51 +0000126 GetLogLevel() LogLevel
khenaidoocfee5f42018-07-19 22:47:38 -0400127}
128
khenaidoo7da372d2018-09-21 16:03:09 -0400129// Fields is used as key-value pairs for structured logging
khenaidoocfee5f42018-07-19 22:47:38 -0400130type Fields map[string]interface{}
131
khenaidooc6c7bda2020-06-17 17:20:18 -0400132var defaultLogger *clogger
khenaidoob9203542018-09-17 22:56:37 -0400133var cfg zp.Config
134
khenaidooc6c7bda2020-06-17 17:20:18 -0400135var loggers map[string]*clogger
khenaidoob9203542018-09-17 22:56:37 -0400136var cfgs map[string]zp.Config
khenaidoocfee5f42018-07-19 22:47:38 -0400137
khenaidooc6c7bda2020-06-17 17:20:18 -0400138type clogger struct {
serkant.uluderya2ae470f2020-01-21 11:13:09 -0800139 log *zp.SugaredLogger
140 parent *zp.Logger
141 packageName string
khenaidoocfee5f42018-07-19 22:47:38 -0400142}
143
Rohan Agrawal7f72f0c2020-01-14 12:05:51 +0000144func logLevelToAtomicLevel(l LogLevel) zp.AtomicLevel {
khenaidoocfee5f42018-07-19 22:47:38 -0400145 switch l {
146 case DebugLevel:
147 return zp.NewAtomicLevelAt(zc.DebugLevel)
148 case InfoLevel:
149 return zp.NewAtomicLevelAt(zc.InfoLevel)
150 case WarnLevel:
151 return zp.NewAtomicLevelAt(zc.WarnLevel)
152 case ErrorLevel:
153 return zp.NewAtomicLevelAt(zc.ErrorLevel)
khenaidoocfee5f42018-07-19 22:47:38 -0400154 case FatalLevel:
155 return zp.NewAtomicLevelAt(zc.FatalLevel)
156 }
157 return zp.NewAtomicLevelAt(zc.ErrorLevel)
158}
159
Rohan Agrawal7f72f0c2020-01-14 12:05:51 +0000160func logLevelToLevel(l LogLevel) zc.Level {
khenaidoo7da372d2018-09-21 16:03:09 -0400161 switch l {
162 case DebugLevel:
163 return zc.DebugLevel
164 case InfoLevel:
khenaidoo6f2fbe32019-01-18 16:16:50 -0500165 return zc.InfoLevel
khenaidoo7da372d2018-09-21 16:03:09 -0400166 case WarnLevel:
167 return zc.WarnLevel
168 case ErrorLevel:
169 return zc.ErrorLevel
khenaidoo7da372d2018-09-21 16:03:09 -0400170 case FatalLevel:
171 return zc.FatalLevel
172 }
173 return zc.ErrorLevel
174}
175
Rohan Agrawal7f72f0c2020-01-14 12:05:51 +0000176func levelToLogLevel(l zc.Level) LogLevel {
khenaidoo6f2fbe32019-01-18 16:16:50 -0500177 switch l {
178 case zc.DebugLevel:
179 return DebugLevel
180 case zc.InfoLevel:
181 return InfoLevel
182 case zc.WarnLevel:
183 return WarnLevel
184 case zc.ErrorLevel:
185 return ErrorLevel
serkant.uluderya2ae470f2020-01-21 11:13:09 -0800186 case zc.FatalLevel:
187 return FatalLevel
188 }
189 return ErrorLevel
190}
191
Rohan Agrawal7f72f0c2020-01-14 12:05:51 +0000192func StringToLogLevel(l string) (LogLevel, error) {
193 switch strings.ToUpper(l) {
serkant.uluderya2ae470f2020-01-21 11:13:09 -0800194 case "DEBUG":
Rohan Agrawal7f72f0c2020-01-14 12:05:51 +0000195 return DebugLevel, nil
serkant.uluderya2ae470f2020-01-21 11:13:09 -0800196 case "INFO":
Rohan Agrawal7f72f0c2020-01-14 12:05:51 +0000197 return InfoLevel, nil
serkant.uluderya2ae470f2020-01-21 11:13:09 -0800198 case "WARN":
Rohan Agrawal7f72f0c2020-01-14 12:05:51 +0000199 return WarnLevel, nil
serkant.uluderya2ae470f2020-01-21 11:13:09 -0800200 case "ERROR":
Rohan Agrawal7f72f0c2020-01-14 12:05:51 +0000201 return ErrorLevel, nil
serkant.uluderya2ae470f2020-01-21 11:13:09 -0800202 case "FATAL":
Rohan Agrawal7f72f0c2020-01-14 12:05:51 +0000203 return FatalLevel, nil
khenaidoo6f2fbe32019-01-18 16:16:50 -0500204 }
Rohan Agrawal7f72f0c2020-01-14 12:05:51 +0000205 return 0, errors.New("Given LogLevel is invalid : " + l)
khenaidoo6f2fbe32019-01-18 16:16:50 -0500206}
207
Scott Baker0e78ba22020-02-24 17:58:47 -0800208func LogLevelToString(l LogLevel) (string, error) {
209 switch l {
210 case DebugLevel:
211 return "DEBUG", nil
212 case InfoLevel:
213 return "INFO", nil
214 case WarnLevel:
215 return "WARN", nil
216 case ErrorLevel:
217 return "ERROR", nil
218 case FatalLevel:
219 return "FATAL", nil
220 }
David K. Bainbridgefc8e1812021-04-09 16:09:49 +0000221 return "", fmt.Errorf("Given LogLevel is invalid %d", l)
Scott Baker0e78ba22020-02-24 17:58:47 -0800222}
223
Rohan Agrawal7f72f0c2020-01-14 12:05:51 +0000224func getDefaultConfig(outputType string, level LogLevel, defaultFields Fields) zp.Config {
khenaidoocfee5f42018-07-19 22:47:38 -0400225 return zp.Config{
Rohan Agrawal7f72f0c2020-01-14 12:05:51 +0000226 Level: logLevelToAtomicLevel(level),
khenaidoocfee5f42018-07-19 22:47:38 -0400227 Encoding: outputType,
228 Development: true,
229 OutputPaths: []string{"stdout"},
230 ErrorOutputPaths: []string{"stderr"},
231 InitialFields: defaultFields,
232 EncoderConfig: zc.EncoderConfig{
233 LevelKey: "level",
234 MessageKey: "msg",
235 TimeKey: "ts",
Scott Bakerb9635992020-03-11 21:11:28 -0700236 CallerKey: "caller",
khenaidoocfee5f42018-07-19 22:47:38 -0400237 StacktraceKey: "stacktrace",
238 LineEnding: zc.DefaultLineEnding,
239 EncodeLevel: zc.LowercaseLevelEncoder,
240 EncodeTime: zc.ISO8601TimeEncoder,
241 EncodeDuration: zc.SecondsDurationEncoder,
242 EncodeCaller: zc.ShortCallerEncoder,
243 },
244 }
245}
246
Scott Baker504b4802020-04-17 10:12:20 -0700247func ConstructZapConfig(outputType string, level LogLevel, fields Fields) zp.Config {
248 return getDefaultConfig(outputType, level, fields)
249}
250
khenaidoocfee5f42018-07-19 22:47:38 -0400251// SetLogger needs to be invoked before the logger API can be invoked. This function
252// initialize the default logger (zap's sugaredlogger)
khenaidooc6c7bda2020-06-17 17:20:18 -0400253func SetDefaultLogger(outputType string, level LogLevel, defaultFields Fields) (CLogger, error) {
khenaidoocfee5f42018-07-19 22:47:38 -0400254 // Build a custom config using zap
khenaidood4d922e2018-08-03 22:35:16 -0400255 cfg = getDefaultConfig(outputType, level, defaultFields)
khenaidoocfee5f42018-07-19 22:47:38 -0400256
Scott Bakerb9635992020-03-11 21:11:28 -0700257 l, err := cfg.Build(zp.AddCallerSkip(1))
khenaidoocfee5f42018-07-19 22:47:38 -0400258 if err != nil {
259 return nil, err
260 }
261
khenaidooc6c7bda2020-06-17 17:20:18 -0400262 defaultLogger = &clogger{
khenaidoocfee5f42018-07-19 22:47:38 -0400263 log: l.Sugar(),
264 parent: l,
265 }
266
267 return defaultLogger, nil
268}
269
khenaidoo7da372d2018-09-21 16:03:09 -0400270// AddPackage registers a package to the log map. Each package gets its own logger which allows
271// its config (loglevel) to be changed dynamically without interacting with the other packages.
272// outputType is JSON, level is the lowest level log to output with this logger and defaultFields is a map of
273// key-value pairs to always add to the output.
274// Note: AddPackage also returns a reference to the actual logger. If a calling package uses this reference directly
275//instead of using the publicly available functions in this log package then a number of functionalities will not
276// be available to it, notably log tracing with filename.functionname.linenumber annotation.
277//
khenaidoo6f2fbe32019-01-18 16:16:50 -0500278// pkgNames parameter should be used for testing only as this function detects the caller's package.
khenaidooc6c7bda2020-06-17 17:20:18 -0400279func RegisterPackage(outputType string, level LogLevel, defaultFields Fields, pkgNames ...string) (CLogger, error) {
khenaidoob9203542018-09-17 22:56:37 -0400280 if cfgs == nil {
281 cfgs = make(map[string]zp.Config)
282 }
283 if loggers == nil {
khenaidooc6c7bda2020-06-17 17:20:18 -0400284 loggers = make(map[string]*clogger)
khenaidoob9203542018-09-17 22:56:37 -0400285 }
khenaidoo6f2fbe32019-01-18 16:16:50 -0500286
287 var pkgName string
288 for _, name := range pkgNames {
289 pkgName = name
290 break
291 }
292 if pkgName == "" {
293 pkgName, _, _, _ = getCallerInfo()
294 }
khenaidoob9203542018-09-17 22:56:37 -0400295
khenaidoo7da372d2018-09-21 16:03:09 -0400296 if _, exist := loggers[pkgName]; exist {
297 return loggers[pkgName], nil
khenaidoob9203542018-09-17 22:56:37 -0400298 }
khenaidoo7da372d2018-09-21 16:03:09 -0400299
khenaidoob9203542018-09-17 22:56:37 -0400300 cfgs[pkgName] = getDefaultConfig(outputType, level, defaultFields)
301
Scott Bakerb9635992020-03-11 21:11:28 -0700302 l, err := cfgs[pkgName].Build(zp.AddCallerSkip(1))
khenaidoob9203542018-09-17 22:56:37 -0400303 if err != nil {
khenaidoo7da372d2018-09-21 16:03:09 -0400304 return nil, err
khenaidoob9203542018-09-17 22:56:37 -0400305 }
306
khenaidooc6c7bda2020-06-17 17:20:18 -0400307 loggers[pkgName] = &clogger{
serkant.uluderya2ae470f2020-01-21 11:13:09 -0800308 log: l.Sugar(),
309 parent: l,
310 packageName: pkgName,
khenaidoob9203542018-09-17 22:56:37 -0400311 }
khenaidoo7da372d2018-09-21 16:03:09 -0400312 return loggers[pkgName], nil
khenaidoob9203542018-09-17 22:56:37 -0400313}
314
khenaidoo7da372d2018-09-21 16:03:09 -0400315//UpdateAllLoggers create new loggers for all registered pacakges with the defaultFields.
khenaidoob9203542018-09-17 22:56:37 -0400316func UpdateAllLoggers(defaultFields Fields) error {
317 for pkgName, cfg := range cfgs {
318 for k, v := range defaultFields {
319 if cfg.InitialFields == nil {
320 cfg.InitialFields = make(map[string]interface{})
321 }
322 cfg.InitialFields[k] = v
323 }
Scott Bakerb9635992020-03-11 21:11:28 -0700324 l, err := cfg.Build(zp.AddCallerSkip(1))
khenaidoob9203542018-09-17 22:56:37 -0400325 if err != nil {
326 return err
327 }
328
Scott Bakerb9635992020-03-11 21:11:28 -0700329 // Update the existing zap logger instance
330 loggers[pkgName].log = l.Sugar()
331 loggers[pkgName].parent = l
khenaidoob9203542018-09-17 22:56:37 -0400332 }
333 return nil
334}
335
Scott Baker5f401472019-08-22 08:32:26 -0700336// Return a list of all packages that have individually-configured loggers
337func GetPackageNames() []string {
338 i := 0
339 keys := make([]string, len(loggers))
340 for k := range loggers {
341 keys[i] = k
342 i++
343 }
344 return keys
345}
346
Scott Bakerb9635992020-03-11 21:11:28 -0700347// UpdateLogger updates the logger associated with a caller's package with supplied defaultFields
348func UpdateLogger(defaultFields Fields) error {
khenaidoo7da372d2018-09-21 16:03:09 -0400349 pkgName, _, _, _ := getCallerInfo()
350 if _, exist := loggers[pkgName]; !exist {
Scott Bakerb9635992020-03-11 21:11:28 -0700351 return fmt.Errorf("package-%s-not-registered", pkgName)
khenaidoo7da372d2018-09-21 16:03:09 -0400352 }
khenaidoob9203542018-09-17 22:56:37 -0400353
khenaidoo7da372d2018-09-21 16:03:09 -0400354 // Build a new logger
355 if _, exist := cfgs[pkgName]; !exist {
Scott Bakerb9635992020-03-11 21:11:28 -0700356 return fmt.Errorf("config-%s-not-registered", pkgName)
khenaidoo7da372d2018-09-21 16:03:09 -0400357 }
358
359 cfg := cfgs[pkgName]
360 for k, v := range defaultFields {
361 if cfg.InitialFields == nil {
362 cfg.InitialFields = make(map[string]interface{})
363 }
364 cfg.InitialFields[k] = v
365 }
Scott Bakerb9635992020-03-11 21:11:28 -0700366 l, err := cfg.Build(zp.AddCallerSkip(1))
khenaidoo7da372d2018-09-21 16:03:09 -0400367 if err != nil {
Scott Bakerb9635992020-03-11 21:11:28 -0700368 return err
khenaidoo7da372d2018-09-21 16:03:09 -0400369 }
370
Scott Bakerb9635992020-03-11 21:11:28 -0700371 // Update the existing zap logger instance
372 loggers[pkgName].log = l.Sugar()
373 loggers[pkgName].parent = l
374
375 return nil
khenaidoo7da372d2018-09-21 16:03:09 -0400376}
377
Rohan Agrawal7f72f0c2020-01-14 12:05:51 +0000378func setLevel(cfg zp.Config, level LogLevel) {
khenaidoo7da372d2018-09-21 16:03:09 -0400379 switch level {
380 case DebugLevel:
381 cfg.Level.SetLevel(zc.DebugLevel)
382 case InfoLevel:
383 cfg.Level.SetLevel(zc.InfoLevel)
384 case WarnLevel:
385 cfg.Level.SetLevel(zc.WarnLevel)
386 case ErrorLevel:
387 cfg.Level.SetLevel(zc.ErrorLevel)
khenaidoo7da372d2018-09-21 16:03:09 -0400388 case FatalLevel:
389 cfg.Level.SetLevel(zc.FatalLevel)
390 default:
391 cfg.Level.SetLevel(zc.ErrorLevel)
392 }
khenaidoo6f2fbe32019-01-18 16:16:50 -0500393}
394
395//SetPackageLogLevel dynamically sets the log level of a given package to level. This is typically invoked at an
396// application level during debugging
Rohan Agrawal7f72f0c2020-01-14 12:05:51 +0000397func SetPackageLogLevel(packageName string, level LogLevel) {
khenaidoo6f2fbe32019-01-18 16:16:50 -0500398 // Get proper config
399 if cfg, ok := cfgs[packageName]; ok {
400 setLevel(cfg, level)
401 }
402}
403
404//SetAllLogLevel sets the log level of all registered packages to level
Rohan Agrawal7f72f0c2020-01-14 12:05:51 +0000405func SetAllLogLevel(level LogLevel) {
khenaidoo6f2fbe32019-01-18 16:16:50 -0500406 // Get proper config
407 for _, cfg := range cfgs {
408 setLevel(cfg, level)
409 }
410}
411
412//GetPackageLogLevel returns the current log level of a package.
Rohan Agrawal7f72f0c2020-01-14 12:05:51 +0000413func GetPackageLogLevel(packageName ...string) (LogLevel, error) {
khenaidoo910204f2019-04-08 17:56:40 -0400414 var name string
415 if len(packageName) == 1 {
416 name = packageName[0]
417 } else {
418 name, _, _, _ = getCallerInfo()
419 }
420 if cfg, ok := cfgs[name]; ok {
Rohan Agrawal7f72f0c2020-01-14 12:05:51 +0000421 return levelToLogLevel(cfg.Level.Level()), nil
khenaidoo6f2fbe32019-01-18 16:16:50 -0500422 }
Rohan Agrawal7f72f0c2020-01-14 12:05:51 +0000423 return 0, fmt.Errorf("unknown-package-%s", name)
khenaidoo6f2fbe32019-01-18 16:16:50 -0500424}
425
Scott Baker5f401472019-08-22 08:32:26 -0700426//GetDefaultLogLevel gets the log level used for packages that don't have specific loggers
Rohan Agrawal7f72f0c2020-01-14 12:05:51 +0000427func GetDefaultLogLevel() LogLevel {
428 return levelToLogLevel(cfg.Level.Level())
Scott Baker5f401472019-08-22 08:32:26 -0700429}
430
khenaidoo6f2fbe32019-01-18 16:16:50 -0500431//SetLogLevel sets the log level for the logger corresponding to the caller's package
Rohan Agrawal7f72f0c2020-01-14 12:05:51 +0000432func SetLogLevel(level LogLevel) error {
khenaidoo6f2fbe32019-01-18 16:16:50 -0500433 pkgName, _, _, _ := getCallerInfo()
434 if _, exist := cfgs[pkgName]; !exist {
Rohan Agrawal7f72f0c2020-01-14 12:05:51 +0000435 return fmt.Errorf("unregistered-package-%s", pkgName)
khenaidoo6f2fbe32019-01-18 16:16:50 -0500436 }
437 cfg := cfgs[pkgName]
438 setLevel(cfg, level)
khenaidoo7da372d2018-09-21 16:03:09 -0400439 return nil
440}
441
Scott Baker5f401472019-08-22 08:32:26 -0700442//SetDefaultLogLevel sets the log level used for packages that don't have specific loggers
Rohan Agrawal7f72f0c2020-01-14 12:05:51 +0000443func SetDefaultLogLevel(level LogLevel) {
Scott Baker5f401472019-08-22 08:32:26 -0700444 setLevel(cfg, level)
445}
446
khenaidoocfee5f42018-07-19 22:47:38 -0400447// CleanUp flushed any buffered log entries. Applications should take care to call
448// CleanUp before exiting.
449func CleanUp() error {
khenaidoob9203542018-09-17 22:56:37 -0400450 for _, logger := range loggers {
451 if logger != nil {
452 if logger.parent != nil {
453 if err := logger.parent.Sync(); err != nil {
454 return err
455 }
456 }
457 }
458 }
khenaidoocfee5f42018-07-19 22:47:38 -0400459 if defaultLogger != nil {
460 if defaultLogger.parent != nil {
461 if err := defaultLogger.parent.Sync(); err != nil {
462 return err
463 }
464 }
465 }
466 return nil
467}
468
khenaidoo7da372d2018-09-21 16:03:09 -0400469func getCallerInfo() (string, string, string, int) {
khenaidoo2c6f1672018-09-20 23:14:41 -0400470 // Since the caller of a log function is one stack frame before (in terms of stack higher level) the log.go
471 // filename, then first look for the last log.go filename and then grab the caller info one level higher.
472 maxLevel := 3
khenaidoo7da372d2018-09-21 16:03:09 -0400473 skiplevel := 3 // Level with the most empirical success to see the last log.go stack frame.
khenaidoo2c6f1672018-09-20 23:14:41 -0400474 pc := make([]uintptr, maxLevel)
475 n := runtime.Callers(skiplevel, pc)
khenaidoob9203542018-09-17 22:56:37 -0400476 packageName := ""
khenaidoo2c6f1672018-09-20 23:14:41 -0400477 funcName := ""
478 fileName := ""
479 var line int
480 if n == 0 {
481 return packageName, fileName, funcName, line
482 }
483 frames := runtime.CallersFrames(pc[:n])
484 var frame runtime.Frame
485 var foundFrame runtime.Frame
486 more := true
487 for more {
488 frame, more = frames.Next()
489 _, fileName = path.Split(frame.File)
490 if fileName != "log.go" {
491 foundFrame = frame // First frame after log.go in the frame stack
492 break
493 }
494 }
495 parts := strings.Split(foundFrame.Function, ".")
496 pl := len(parts)
497 if pl >= 2 {
498 funcName = parts[pl-1]
499 if parts[pl-2][0] == '(' {
500 packageName = strings.Join(parts[0:pl-2], ".")
501 } else {
502 packageName = strings.Join(parts[0:pl-1], ".")
503 }
khenaidoocfee5f42018-07-19 22:47:38 -0400504 }
505
khenaidoo2c6f1672018-09-20 23:14:41 -0400506 if strings.HasSuffix(packageName, ".init") {
khenaidoo7da372d2018-09-21 16:03:09 -0400507 packageName = strings.TrimSuffix(packageName, ".init")
khenaidoob9203542018-09-17 22:56:37 -0400508 }
509
510 if strings.HasSuffix(fileName, ".go") {
khenaidoo7da372d2018-09-21 16:03:09 -0400511 fileName = strings.TrimSuffix(fileName, ".go")
khenaidoob9203542018-09-17 22:56:37 -0400512 }
khenaidoo2c6f1672018-09-20 23:14:41 -0400513
514 return packageName, fileName, funcName, foundFrame.Line
khenaidoob9203542018-09-17 22:56:37 -0400515}
516
khenaidoocfee5f42018-07-19 22:47:38 -0400517// With returns a logger initialized with the key-value pairs
khenaidooc6c7bda2020-06-17 17:20:18 -0400518func (l clogger) With(keysAndValues Fields) CLogger {
519 return clogger{log: l.log.With(serializeMap(keysAndValues)...), parent: l.parent}
khenaidoocfee5f42018-07-19 22:47:38 -0400520}
521
522// Debug logs a message at level Debug on the standard logger.
khenaidooc6c7bda2020-06-17 17:20:18 -0400523func (l clogger) Debug(ctx context.Context, args ...interface{}) {
Girish Kumarf8d4f8d2020-08-18 11:45:30 +0000524 l.log.With(GetGlobalLFM().ExtractContextAttributes(ctx)...).Debug(args...)
khenaidoocfee5f42018-07-19 22:47:38 -0400525}
526
527// Debugln logs a message at level Debug on the standard logger with a line feed. Default in any case.
khenaidooc6c7bda2020-06-17 17:20:18 -0400528func (l clogger) Debugln(ctx context.Context, args ...interface{}) {
Girish Kumarf8d4f8d2020-08-18 11:45:30 +0000529 l.log.With(GetGlobalLFM().ExtractContextAttributes(ctx)...).Debug(args...)
khenaidoocfee5f42018-07-19 22:47:38 -0400530}
531
532// Debugw logs a message at level Debug on the standard logger.
khenaidooc6c7bda2020-06-17 17:20:18 -0400533func (l clogger) Debugf(ctx context.Context, format string, args ...interface{}) {
Girish Kumarf8d4f8d2020-08-18 11:45:30 +0000534 l.log.With(GetGlobalLFM().ExtractContextAttributes(ctx)...).Debugf(format, args...)
khenaidoocfee5f42018-07-19 22:47:38 -0400535}
536
537// Debugw logs a message with some additional context. The variadic key-value
538// pairs are treated as they are in With.
khenaidooc6c7bda2020-06-17 17:20:18 -0400539func (l clogger) Debugw(ctx context.Context, msg string, keysAndValues Fields) {
Neha Sharma7d6f3a92020-04-14 15:26:22 +0000540 if l.V(DebugLevel) {
Girish Kumarf8d4f8d2020-08-18 11:45:30 +0000541 l.log.With(GetGlobalLFM().ExtractContextAttributes(ctx)...).Debugw(msg, serializeMap(keysAndValues)...)
Neha Sharma7d6f3a92020-04-14 15:26:22 +0000542 }
khenaidoocfee5f42018-07-19 22:47:38 -0400543}
544
545// Info logs a message at level Info on the standard logger.
khenaidooc6c7bda2020-06-17 17:20:18 -0400546func (l clogger) Info(ctx context.Context, args ...interface{}) {
Girish Kumarf8d4f8d2020-08-18 11:45:30 +0000547 l.log.With(GetGlobalLFM().ExtractContextAttributes(ctx)...).Info(args...)
khenaidoocfee5f42018-07-19 22:47:38 -0400548}
549
550// Infoln logs a message at level Info on the standard logger with a line feed. Default in any case.
khenaidooc6c7bda2020-06-17 17:20:18 -0400551func (l clogger) Infoln(ctx context.Context, args ...interface{}) {
Girish Kumarf8d4f8d2020-08-18 11:45:30 +0000552 l.log.With(GetGlobalLFM().ExtractContextAttributes(ctx)...).Info(args...)
khenaidoocfee5f42018-07-19 22:47:38 -0400553 //msg := fmt.Sprintln(args...)
554 //l.sourced().Info(msg[:len(msg)-1])
555}
556
557// Infof logs a message at level Info on the standard logger.
khenaidooc6c7bda2020-06-17 17:20:18 -0400558func (l clogger) Infof(ctx context.Context, format string, args ...interface{}) {
Girish Kumarf8d4f8d2020-08-18 11:45:30 +0000559 l.log.With(GetGlobalLFM().ExtractContextAttributes(ctx)...).Infof(format, args...)
khenaidoocfee5f42018-07-19 22:47:38 -0400560}
561
562// Infow logs a message with some additional context. The variadic key-value
563// pairs are treated as they are in With.
khenaidooc6c7bda2020-06-17 17:20:18 -0400564func (l clogger) Infow(ctx context.Context, msg string, keysAndValues Fields) {
Neha Sharma7d6f3a92020-04-14 15:26:22 +0000565 if l.V(InfoLevel) {
Girish Kumarf8d4f8d2020-08-18 11:45:30 +0000566 l.log.With(GetGlobalLFM().ExtractContextAttributes(ctx)...).Infow(msg, serializeMap(keysAndValues)...)
Neha Sharma7d6f3a92020-04-14 15:26:22 +0000567 }
khenaidoocfee5f42018-07-19 22:47:38 -0400568}
569
570// Warn logs a message at level Warn on the standard logger.
khenaidooc6c7bda2020-06-17 17:20:18 -0400571func (l clogger) Warn(ctx context.Context, args ...interface{}) {
Girish Kumarf8d4f8d2020-08-18 11:45:30 +0000572 l.log.With(GetGlobalLFM().ExtractContextAttributes(ctx)...).Warn(args...)
khenaidoocfee5f42018-07-19 22:47:38 -0400573}
574
575// Warnln logs a message at level Warn on the standard logger with a line feed. Default in any case.
khenaidooc6c7bda2020-06-17 17:20:18 -0400576func (l clogger) Warnln(ctx context.Context, args ...interface{}) {
Girish Kumarf8d4f8d2020-08-18 11:45:30 +0000577 l.log.With(GetGlobalLFM().ExtractContextAttributes(ctx)...).Warn(args...)
khenaidoocfee5f42018-07-19 22:47:38 -0400578}
579
580// Warnf logs a message at level Warn on the standard logger.
khenaidooc6c7bda2020-06-17 17:20:18 -0400581func (l clogger) Warnf(ctx context.Context, format string, args ...interface{}) {
Girish Kumarf8d4f8d2020-08-18 11:45:30 +0000582 l.log.With(GetGlobalLFM().ExtractContextAttributes(ctx)...).Warnf(format, args...)
khenaidoocfee5f42018-07-19 22:47:38 -0400583}
584
585// Warnw logs a message with some additional context. The variadic key-value
586// pairs are treated as they are in With.
khenaidooc6c7bda2020-06-17 17:20:18 -0400587func (l clogger) Warnw(ctx context.Context, msg string, keysAndValues Fields) {
Neha Sharma7d6f3a92020-04-14 15:26:22 +0000588 if l.V(WarnLevel) {
Girish Kumarf8d4f8d2020-08-18 11:45:30 +0000589 l.log.With(GetGlobalLFM().ExtractContextAttributes(ctx)...).Warnw(msg, serializeMap(keysAndValues)...)
Neha Sharma7d6f3a92020-04-14 15:26:22 +0000590 }
khenaidoocfee5f42018-07-19 22:47:38 -0400591}
592
593// Error logs a message at level Error on the standard logger.
khenaidooc6c7bda2020-06-17 17:20:18 -0400594func (l clogger) Error(ctx context.Context, args ...interface{}) {
Girish Kumarf8d4f8d2020-08-18 11:45:30 +0000595 l.log.With(GetGlobalLFM().ExtractContextAttributes(ctx)...).Error(args...)
khenaidoocfee5f42018-07-19 22:47:38 -0400596}
597
598// Errorln logs a message at level Error on the standard logger with a line feed. Default in any case.
khenaidooc6c7bda2020-06-17 17:20:18 -0400599func (l clogger) Errorln(ctx context.Context, args ...interface{}) {
Girish Kumarf8d4f8d2020-08-18 11:45:30 +0000600 l.log.With(GetGlobalLFM().ExtractContextAttributes(ctx)...).Error(args...)
khenaidoocfee5f42018-07-19 22:47:38 -0400601}
602
603// Errorf logs a message at level Error on the standard logger.
khenaidooc6c7bda2020-06-17 17:20:18 -0400604func (l clogger) Errorf(ctx context.Context, format string, args ...interface{}) {
Girish Kumarf8d4f8d2020-08-18 11:45:30 +0000605 l.log.With(GetGlobalLFM().ExtractContextAttributes(ctx)...).Errorf(format, args...)
khenaidoocfee5f42018-07-19 22:47:38 -0400606}
607
608// Errorw logs a message with some additional context. The variadic key-value
609// pairs are treated as they are in With.
khenaidooc6c7bda2020-06-17 17:20:18 -0400610func (l clogger) Errorw(ctx context.Context, msg string, keysAndValues Fields) {
Neha Sharma7d6f3a92020-04-14 15:26:22 +0000611 if l.V(ErrorLevel) {
Girish Kumarf8d4f8d2020-08-18 11:45:30 +0000612 l.log.With(GetGlobalLFM().ExtractContextAttributes(ctx)...).Errorw(msg, serializeMap(keysAndValues)...)
Neha Sharma7d6f3a92020-04-14 15:26:22 +0000613 }
khenaidoocfee5f42018-07-19 22:47:38 -0400614}
615
616// Fatal logs a message at level Fatal on the standard logger.
khenaidooc6c7bda2020-06-17 17:20:18 -0400617func (l clogger) Fatal(ctx context.Context, args ...interface{}) {
Girish Kumarf8d4f8d2020-08-18 11:45:30 +0000618 l.log.With(GetGlobalLFM().ExtractContextAttributes(ctx)...).Fatal(args...)
khenaidoocfee5f42018-07-19 22:47:38 -0400619}
620
621// Fatalln logs a message at level Fatal on the standard logger with a line feed. Default in any case.
khenaidooc6c7bda2020-06-17 17:20:18 -0400622func (l clogger) Fatalln(ctx context.Context, args ...interface{}) {
Girish Kumarf8d4f8d2020-08-18 11:45:30 +0000623 l.log.With(GetGlobalLFM().ExtractContextAttributes(ctx)...).Fatal(args...)
khenaidoocfee5f42018-07-19 22:47:38 -0400624}
625
626// Fatalf logs a message at level Fatal on the standard logger.
khenaidooc6c7bda2020-06-17 17:20:18 -0400627func (l clogger) Fatalf(ctx context.Context, format string, args ...interface{}) {
Girish Kumarf8d4f8d2020-08-18 11:45:30 +0000628 l.log.With(GetGlobalLFM().ExtractContextAttributes(ctx)...).Fatalf(format, args...)
khenaidoocfee5f42018-07-19 22:47:38 -0400629}
630
631// Fatalw logs a message with some additional context. The variadic key-value
632// pairs are treated as they are in With.
khenaidooc6c7bda2020-06-17 17:20:18 -0400633func (l clogger) Fatalw(ctx context.Context, msg string, keysAndValues Fields) {
Neha Sharma7d6f3a92020-04-14 15:26:22 +0000634 if l.V(FatalLevel) {
Girish Kumarf8d4f8d2020-08-18 11:45:30 +0000635 l.log.With(GetGlobalLFM().ExtractContextAttributes(ctx)...).Fatalw(msg, serializeMap(keysAndValues)...)
Neha Sharma7d6f3a92020-04-14 15:26:22 +0000636 }
khenaidoocfee5f42018-07-19 22:47:38 -0400637}
638
khenaidoo7da372d2018-09-21 16:03:09 -0400639// Warning logs a message at level Warn on the standard logger.
khenaidooc6c7bda2020-06-17 17:20:18 -0400640func (l clogger) Warning(ctx context.Context, args ...interface{}) {
Girish Kumarf8d4f8d2020-08-18 11:45:30 +0000641 l.log.With(GetGlobalLFM().ExtractContextAttributes(ctx)...).Warn(args...)
khenaidoo7da372d2018-09-21 16:03:09 -0400642}
643
644// Warningln logs a message at level Warn on the standard logger with a line feed. Default in any case.
khenaidooc6c7bda2020-06-17 17:20:18 -0400645func (l clogger) Warningln(ctx context.Context, args ...interface{}) {
Girish Kumarf8d4f8d2020-08-18 11:45:30 +0000646 l.log.With(GetGlobalLFM().ExtractContextAttributes(ctx)...).Warn(args...)
khenaidoo7da372d2018-09-21 16:03:09 -0400647}
648
649// Warningf logs a message at level Warn on the standard logger.
khenaidooc6c7bda2020-06-17 17:20:18 -0400650func (l clogger) Warningf(ctx context.Context, format string, args ...interface{}) {
Girish Kumarf8d4f8d2020-08-18 11:45:30 +0000651 l.log.With(GetGlobalLFM().ExtractContextAttributes(ctx)...).Warnf(format, args...)
khenaidoo7da372d2018-09-21 16:03:09 -0400652}
653
654// V reports whether verbosity level l is at least the requested verbose level.
khenaidooc6c7bda2020-06-17 17:20:18 -0400655func (l clogger) V(level LogLevel) bool {
Rohan Agrawal7f72f0c2020-01-14 12:05:51 +0000656 return l.parent.Core().Enabled(logLevelToLevel(level))
khenaidoo7da372d2018-09-21 16:03:09 -0400657}
658
serkant.uluderya2ae470f2020-01-21 11:13:09 -0800659// GetLogLevel returns the current level of the logger
khenaidooc6c7bda2020-06-17 17:20:18 -0400660func (l clogger) GetLogLevel() LogLevel {
Rohan Agrawal7f72f0c2020-01-14 12:05:51 +0000661 return levelToLogLevel(cfgs[l.packageName].Level.Level())
serkant.uluderya2ae470f2020-01-21 11:13:09 -0800662}