diff --git a/api/apilogging/logger.go b/api/apilogging/logger.go new file mode 100644 index 0000000000..98ab7e1256 --- /dev/null +++ b/api/apilogging/logger.go @@ -0,0 +1,52 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package apilogging + +//Logger - Standard logger interface +type Logger interface { + Fatal(v ...interface{}) + + Fatalf(format string, v ...interface{}) + + Fatalln(v ...interface{}) + + Panic(v ...interface{}) + + Panicf(format string, v ...interface{}) + + Panicln(v ...interface{}) + + Print(v ...interface{}) + + Printf(format string, v ...interface{}) + + Println(v ...interface{}) + + Debug(args ...interface{}) + + Debugf(format string, args ...interface{}) + + Debugln(args ...interface{}) + + Info(args ...interface{}) + + Infof(format string, args ...interface{}) + + Infoln(args ...interface{}) + + Warn(args ...interface{}) + + Warnf(format string, args ...interface{}) + + Warnln(args ...interface{}) + + Error(args ...interface{}) + + Errorf(format string, args ...interface{}) + + Errorln(args ...interface{}) +} diff --git a/pkg/logging/level.go b/pkg/logging/level.go new file mode 100644 index 0000000000..018e3f05b9 --- /dev/null +++ b/pkg/logging/level.go @@ -0,0 +1,94 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package logging + +import ( + "errors" + "strings" + "sync" +) + +// ErrInvalidLogLevel is used when an invalid log level has been used. +var ErrInvalidLogLevel = errors.New("logger: invalid log level") + +// Level defines all available log levels for log messages. +type Level int + +// Log levels. +const ( + CRITICAL Level = iota + ERROR + WARNING + INFO + DEBUG +) + +var levelNames = []string{ + "CRITICAL", + "ERROR", + "WARNING", + "INFO", + "DEBUG", +} + +// String returns the string representation of a logging level. +func (p Level) String() string { + return levelNames[p] +} + +// LogLevel returns the log level from a string representation. +func LogLevel(level string) (Level, error) { + for i, name := range levelNames { + if strings.EqualFold(name, level) { + return Level(i), nil + } + } + return ERROR, ErrInvalidLogLevel +} + +// Leveled interface is the interface required to be able to add leveled +// logging. +type Leveled interface { + GetLevel(string) Level + SetLevel(Level, string) + IsEnabledFor(Level, string) bool +} + +type moduleLeveled struct { + sync.RWMutex + levels map[string]Level +} + +// GetLevel returns the log level for the given module. +func (l *moduleLeveled) GetLevel(module string) Level { + l.RLock() + defer l.RUnlock() + level, exists := l.levels[module] + if exists == false { + level, exists = l.levels[""] + // no configuration exists, default to info + if exists == false { + level = INFO + } + } + return level +} + +// SetLevel sets the log level for the given module. +func (l *moduleLeveled) SetLevel(level Level, module string) { + l.Lock() + defer l.Unlock() + if l.levels == nil { + l.levels = make(map[string]Level) + } + l.levels[module] = level +} + +// IsEnabledFor will return true if logging is enabled for the given module. +func (l *moduleLeveled) IsEnabledFor(level Level, module string) bool { + return level <= l.GetLevel(module) +} diff --git a/pkg/logging/level_test.go b/pkg/logging/level_test.go new file mode 100644 index 0000000000..ff3054a4e6 --- /dev/null +++ b/pkg/logging/level_test.go @@ -0,0 +1,95 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ +package logging + +import ( + "testing" +) + +func TestLogLevels(t *testing.T) { + + mlevel := moduleLeveled{} + + mlevel.SetLevel(INFO, "module-xyz-info") + mlevel.SetLevel(DEBUG, "module-xyz-debug") + mlevel.SetLevel(ERROR, "module-xyz-error") + mlevel.SetLevel(WARNING, "module-xyz-warning") + + //Run info level checks + verifyTrue(t, mlevel.IsEnabledFor(INFO, "module-xyz-info")) + verifyFalse(t, mlevel.IsEnabledFor(DEBUG, "module-xyz-info")) + verifyTrue(t, mlevel.IsEnabledFor(ERROR, "module-xyz-info")) + verifyTrue(t, mlevel.IsEnabledFor(WARNING, "module-xyz-info")) + + //Run debug level checks + verifyTrue(t, mlevel.IsEnabledFor(INFO, "module-xyz-debug")) + verifyTrue(t, mlevel.IsEnabledFor(DEBUG, "module-xyz-debug")) + verifyTrue(t, mlevel.IsEnabledFor(ERROR, "module-xyz-debug")) + verifyTrue(t, mlevel.IsEnabledFor(WARNING, "module-xyz-debug")) + + //Run info level checks + verifyFalse(t, mlevel.IsEnabledFor(INFO, "module-xyz-error")) + verifyFalse(t, mlevel.IsEnabledFor(DEBUG, "module-xyz-error")) + verifyTrue(t, mlevel.IsEnabledFor(ERROR, "module-xyz-error")) + verifyFalse(t, mlevel.IsEnabledFor(WARNING, "module-xyz-error")) + + //Run info level checks + verifyFalse(t, mlevel.IsEnabledFor(INFO, "module-xyz-warning")) + verifyFalse(t, mlevel.IsEnabledFor(DEBUG, "module-xyz-warning")) + verifyTrue(t, mlevel.IsEnabledFor(ERROR, "module-xyz-warning")) + verifyTrue(t, mlevel.IsEnabledFor(WARNING, "module-xyz-warning")) + + //Run default log level check --> which is info currently + verifyTrue(t, mlevel.IsEnabledFor(INFO, "module-xyz-random-module")) + verifyFalse(t, mlevel.IsEnabledFor(DEBUG, "module-xyz-random-module")) + verifyTrue(t, mlevel.IsEnabledFor(ERROR, "module-xyz-random-module")) + verifyTrue(t, mlevel.IsEnabledFor(WARNING, "module-xyz-random-module")) + +} + +func TestGetLogLevels(t *testing.T) { + + level, err := LogLevel("info") + verifyLogLevel(t, INFO, level, err, true) + + level, err = LogLevel("iNfO") + verifyLogLevel(t, INFO, level, err, true) + + level, err = LogLevel("debug") + verifyLogLevel(t, DEBUG, level, err, true) + + level, err = LogLevel("DeBuG") + verifyLogLevel(t, DEBUG, level, err, true) + + level, err = LogLevel("warning") + verifyLogLevel(t, WARNING, level, err, true) + + level, err = LogLevel("WarNIng") + verifyLogLevel(t, WARNING, level, err, true) + + level, err = LogLevel("error") + verifyLogLevel(t, ERROR, level, err, true) + + level, err = LogLevel("eRRoR") + verifyLogLevel(t, ERROR, level, err, true) + + level, err = LogLevel("outofthebox") + verifyLogLevel(t, -1, level, err, false) + + level, err = LogLevel("") + verifyLogLevel(t, -1, level, err, false) +} + +func verifyLogLevel(t *testing.T, expectedLevel Level, currentlevel Level, err error, success bool) { + if success { + verifyEmpty(t, err, "not supposed to get error for this scenario") + } else { + verifyNotEmpty(t, err, "supposed to get error for this scenario, but got error : %v", err) + return + } + + verifyTrue(t, currentlevel == expectedLevel, "unexpected log level : expected '%s', but got '%s'", expectedLevel, currentlevel) +} diff --git a/pkg/logging/logger.go b/pkg/logging/logger.go new file mode 100644 index 0000000000..ad8d9bd70c --- /dev/null +++ b/pkg/logging/logger.go @@ -0,0 +1,376 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ + +package logging + +import ( + "fmt" + "log" + "os" + "path/filepath" + "runtime" + + "sync" + + "github.com/hyperledger/fabric-sdk-go/api/apilogging" +) + +var mutex = &sync.Mutex{} + +//Logger basic implementation of api.Logger interface +type Logger struct { + logger apilogging.Logger + module string +} + +var moduleLevels = moduleLeveled{} + +var customLogger apilogging.Logger + +const ( + logLevelFormatter = "UTC - %s -> %s " + logPrefixFormatter = " [%s] " +) + +// GetLogger creates and returns a Logger object based on the module name. +func GetLogger(module string) (*Logger, error) { + return &Logger{logger: getDefaultLogger(module), module: module}, nil +} + +// NewLogger is like GetLogger but panics if the logger can't be created. +func NewLogger(module string) *Logger { + logger, err := GetLogger(module) + if err != nil { + panic("logger: " + module + ": " + err.Error()) + } + return logger +} + +//SetCustomLogger sets new custom logger which takes over logging operations already created and +//new logger which are going to be created. Care should be taken while using this method. +//It is recommended to add Custom loggers before making any loggings. +func SetCustomLogger(newCustomLogger apilogging.Logger) { + mutex.Lock() + customLogger = newCustomLogger + mutex.Unlock() +} + +func SetLevel(level Level, module string) { + moduleLevels.SetLevel(level, module) +} + +func GetLevel(module string) Level { + return moduleLevels.GetLevel(module) +} + +func IsEnabledFor(level Level, module string) bool { + return moduleLevels.IsEnabledFor(level, module) +} + +func (l *Logger) Fatal(args ...interface{}) { + l.getCurrentLogger().Fatal(args...) +} + +func (l *Logger) Fatalf(format string, args ...interface{}) { + l.getCurrentLogger().Fatalf(format, args...) +} + +func (l *Logger) Fatalln(args ...interface{}) { + l.getCurrentLogger().Fatalln(args...) +} + +func (l *Logger) Panic(args ...interface{}) { + l.getCurrentLogger().Panic(args...) +} + +func (l *Logger) Panicf(format string, args ...interface{}) { + l.getCurrentLogger().Panicf(format, args...) +} + +func (l *Logger) Panicln(args ...interface{}) { + l.getCurrentLogger().Panicln(args...) +} + +func (l *Logger) Print(args ...interface{}) { + l.getCurrentLogger().Print(args...) +} + +func (l *Logger) Printf(format string, args ...interface{}) { + l.getCurrentLogger().Printf(format, args...) +} + +func (l *Logger) Println(args ...interface{}) { + l.getCurrentLogger().Println(args...) +} + +func (l *Logger) Debug(args ...interface{}) { + if IsEnabledFor(DEBUG, l.module) { + l.getCurrentLogger().Debug(args...) + } +} + +func (l *Logger) Debugf(format string, args ...interface{}) { + if IsEnabledFor(DEBUG, l.module) { + l.getCurrentLogger().Debugf(format, args...) + } +} + +func (l *Logger) Debugln(args ...interface{}) { + if IsEnabledFor(DEBUG, l.module) { + l.getCurrentLogger().Debugln(args...) + } +} + +func (l *Logger) Info(args ...interface{}) { + if IsEnabledFor(INFO, l.module) { + l.getCurrentLogger().Info(args...) + } +} + +func (l *Logger) Infof(format string, args ...interface{}) { + if IsEnabledFor(INFO, l.module) { + l.getCurrentLogger().Infof(format, args...) + } +} + +func (l *Logger) Infoln(args ...interface{}) { + if IsEnabledFor(INFO, l.module) { + l.getCurrentLogger().Infoln(args...) + } +} + +func (l *Logger) Warn(args ...interface{}) { + if IsEnabledFor(WARNING, l.module) { + l.getCurrentLogger().Warn(args...) + } +} + +func (l *Logger) Warnf(format string, args ...interface{}) { + if IsEnabledFor(WARNING, l.module) { + l.getCurrentLogger().Warnf(format, args...) + } +} + +func (l *Logger) Warnln(args ...interface{}) { + if IsEnabledFor(WARNING, l.module) { + l.getCurrentLogger().Warnln(args...) + } +} + +func (l *Logger) Error(args ...interface{}) { + if IsEnabledFor(ERROR, l.module) { + l.getCurrentLogger().Error(args...) + } +} + +func (l *Logger) Errorf(format string, args ...interface{}) { + if IsEnabledFor(ERROR, l.module) { + l.getCurrentLogger().Errorf(format, args...) + } +} + +func (l *Logger) Errorln(args ...interface{}) { + if IsEnabledFor(ERROR, l.module) { + l.getCurrentLogger().Errorln(args...) + } +} + +func (l *Logger) getCurrentLogger() apilogging.Logger { + if customLogger != nil { + return customLogger + } + return l.logger +} + +/* + Default logger Implementation +*/ + +func getDefaultLogger(module string) apilogging.Logger { + newLogger := log.New(os.Stdout, fmt.Sprintf(logPrefixFormatter, module), log.Ldate|log.Ltime|log.LUTC) + return &DefaultLogger{defaultLogger: newLogger} +} + +type formatted func(string, ...interface{}) string +type simple func(...interface{}) string + +type DefaultLogger struct { + defaultLogger *log.Logger +} + +// Fatal is CRITICAL log followed by a call to os.Exit(1). +func (l *DefaultLogger) Fatal(args ...interface{}) { + + l.log(CRITICAL, args...) + l.defaultLogger.Fatal(args...) +} + +// Fatalf is CRITICAL log formatted followed by a call to os.Exit(1). +func (l *DefaultLogger) Fatalf(format string, args ...interface{}) { + l.logf(CRITICAL, format, args...) + l.defaultLogger.Fatalf(format, args...) +} + +// Fatalln is CRITICAL log ln followed by a call to os.Exit(1). +func (l *DefaultLogger) Fatalln(args ...interface{}) { + l.logln(CRITICAL, args...) + l.defaultLogger.Fatalln(args...) +} + +// Panic is CRITICAL log followed by a call to panic() +func (l *DefaultLogger) Panic(args ...interface{}) { + l.log(CRITICAL, args...) + l.defaultLogger.Panic(args...) +} + +// Panicf is CRITICAL log formatted followed by a call to panic() +func (l *DefaultLogger) Panicf(format string, args ...interface{}) { + l.logf(CRITICAL, format, args...) + l.defaultLogger.Panicf(format, args...) +} + +// Panicln is CRITICAL log ln followed by a call to panic() +func (l *DefaultLogger) Panicln(args ...interface{}) { + l.logln(CRITICAL, args...) + l.defaultLogger.Panicln(args...) +} + +// Print calls l.Output to print to the logger. +// Arguments are handled in the manner of fmt.Print. +func (l *DefaultLogger) Print(args ...interface{}) { + l.defaultLogger.Print(args...) +} + +// Printf calls l.Output to print to the logger. +// Arguments are handled in the manner of fmt.Printf. +func (l *DefaultLogger) Printf(format string, args ...interface{}) { + l.defaultLogger.Printf(format, args...) +} + +// Println calls l.Output to print to the logger. +// Arguments are handled in the manner of fmt.Println. +func (l *DefaultLogger) Println(args ...interface{}) { + l.defaultLogger.Println(args...) +} + +// Debug calls l.Output to print to the logger. +// Arguments are handled in the manner of fmt.Print. +func (l *DefaultLogger) Debug(args ...interface{}) { + l.log(DEBUG, args...) +} + +// Debugf calls l.Output to print to the logger. +// Arguments are handled in the manner of fmt.Printf. +func (l *DefaultLogger) Debugf(format string, args ...interface{}) { + l.logf(DEBUG, format, args...) +} + +// Debugln calls l.Output to print to the logger. +// Arguments are handled in the manner of fmt.Println. +func (l *DefaultLogger) Debugln(args ...interface{}) { + l.logln(DEBUG, args...) +} + +// Info calls l.Output to print to the logger. +// Arguments are handled in the manner of fmt.Print. +func (l *DefaultLogger) Info(args ...interface{}) { + l.log(INFO, args...) +} + +// Infof calls l.Output to print to the logger. +// Arguments are handled in the manner of fmt.Printf. +func (l *DefaultLogger) Infof(format string, args ...interface{}) { + l.logf(INFO, format, args...) +} + +// Infoln calls l.Output to print to the logger. +// Arguments are handled in the manner of fmt.Println. +func (l *DefaultLogger) Infoln(args ...interface{}) { + l.logln(INFO, args...) +} + +// Warn calls l.Output to print to the logger. +// Arguments are handled in the manner of fmt.Print. +func (l *DefaultLogger) Warn(args ...interface{}) { + l.log(WARNING, args...) +} + +// Warnf calls l.Output to print to the logger. +// Arguments are handled in the manner of fmt.Printf. +func (l *DefaultLogger) Warnf(format string, args ...interface{}) { + l.logf(WARNING, format, args...) +} + +// Warnln calls l.Output to print to the logger. +// Arguments are handled in the manner of fmt.Println. +func (l *DefaultLogger) Warnln(args ...interface{}) { + l.logln(WARNING, args...) +} + +// Error calls l.Output to print to the logger. +// Arguments are handled in the manner of fmt.Print. +func (l *DefaultLogger) Error(args ...interface{}) { + l.log(ERROR, args...) +} + +// Errorf calls l.Output to print to the logger. +// Arguments are handled in the manner of fmt.Printf. +func (l *DefaultLogger) Errorf(format string, args ...interface{}) { + l.logf(ERROR, format, args...) +} + +// Errorln calls l.Output to print to the logger. +// Arguments are handled in the manner of fmt.Println. +func (l *DefaultLogger) Errorln(args ...interface{}) { + l.logln(ERROR, args...) +} + +func (l *DefaultLogger) logf(level Level, format string, args ...interface{}) { + //Format prefix to show function name and log level and to indicate that timezone used is UTC + customPrefix := fmt.Sprintf(logLevelFormatter, l.getCaller(), level) + l.defaultLogger.Output(2, customPrefix+fmt.Sprintf(format, args...)) +} + +func (l *DefaultLogger) log(level Level, args ...interface{}) { + + //Format prefix to show function name and log level and to indicate that timezone used is UTC + customPrefix := fmt.Sprintf(logLevelFormatter, l.getCaller(), level) + l.defaultLogger.Output(2, customPrefix+fmt.Sprint(args...)) +} + +func (l *DefaultLogger) logln(level Level, args ...interface{}) { + //Format prefix to show function name and log level and to indicate that timezone used is UTC + customPrefix := fmt.Sprintf(logLevelFormatter, l.getCaller(), level) + l.defaultLogger.Output(2, customPrefix+fmt.Sprintln(args...)) +} + +//func (l *DefaultLogger) log(level Level, formatter simple, formatterf formatted, format string, args ...interface{}) { +// //Format prefix to show function name and log level and to indicate that timezone used is UTC +// customPrefix := fmt.Sprintf(logLevelFormatter, l.getCaller(), level) +// if formatter != nil { +// customPrefix = customPrefix + formatter(args...) +// } else if formatterf != nil { +// customPrefix = customPrefix + formatterf(format, args...) +// } +// l.defaultLogger.Output(2, customPrefix) +//} + +// getCaller utility to find caller function used to mention in log lines +func (l *DefaultLogger) getCaller() string { + fpcs := make([]uintptr, 1) + // skip 3 levels to get to the caller of whoever called getCaller() + n := runtime.Callers(4, fpcs) + if n == 0 { + return "n/a" + } + + fun := runtime.FuncForPC(fpcs[0] - 1) + if fun == nil { + return "n/a" + } + _, funName := filepath.Split(fun.Name()) + return funName +} diff --git a/pkg/logging/logger_test.go b/pkg/logging/logger_test.go new file mode 100644 index 0000000000..2558fef37c --- /dev/null +++ b/pkg/logging/logger_test.go @@ -0,0 +1,281 @@ +/* +Copyright SecureKey Technologies Inc. All Rights Reserved. + +SPDX-License-Identifier: Apache-2.0 +*/ +package logging + +import ( + "bytes" + "fmt" + "log" + "regexp" + "testing" +) + +const ( + basicLevelOutputExpectedRegex = "\\[%s\\] .* UTC - logging.* -> %s brown fox jumps over the lazy dog" + printLevelOutputExpectedRegex = "\\[%s\\] .* brown fox jumps over the lazy dog" + customLevelOutputExpectedRegex = "\\[%s\\] .* CUSTOM LOG OUTPUT" + moduleName = "module-xyz" +) + +type fn func(...interface{}) +type fnf func(string, ...interface{}) + +func TestDefaultLogging(t *testing.T) { + + SetCustomLogger(nil) + logger := NewLogger(moduleName) + + //change the output to buffer + var buf bytes.Buffer + logger.logger.(*DefaultLogger).defaultLogger.SetOutput(&buf) + + //No level set for this module so log level should be info + verifyTrue(t, INFO == GetLevel(moduleName), " default log level is INFO") + + //Test logger.print outputs + verifyBasicLogging(t, -1, logger.Print, nil, &buf, false) + verifyBasicLogging(t, -1, logger.Println, nil, &buf, false) + verifyBasicLogging(t, -1, nil, logger.Printf, &buf, false) + + //Test logger.info outputs + verifyBasicLogging(t, INFO, logger.Info, nil, &buf, false) + verifyBasicLogging(t, INFO, logger.Infoln, nil, &buf, false) + verifyBasicLogging(t, INFO, nil, logger.Infof, &buf, false) + + //Test logger.warn outputs + verifyBasicLogging(t, WARNING, logger.Warn, nil, &buf, false) + verifyBasicLogging(t, WARNING, logger.Warnln, nil, &buf, false) + verifyBasicLogging(t, WARNING, nil, logger.Warnf, &buf, false) + + //Test logger.error outputs + verifyBasicLogging(t, ERROR, logger.Error, nil, &buf, false) + verifyBasicLogging(t, ERROR, logger.Errorln, nil, &buf, false) + verifyBasicLogging(t, ERROR, nil, logger.Errorf, &buf, false) + + /* + SINCE DEBUG LOG IS NOT YET ENABLED, LOG OUTPUT SHOULD BE EMPTY + */ + //Test logger.debug outputs when DEBUG level is not enabled + logger.Debug("brown fox jumps over the lazy dog") + logger.Debugln("brown fox jumps over the lazy dog") + logger.Debugf("brown %s jumps over the lazy %s", "fox", "dog") + + verifyEmpty(t, buf.String(), "debug log isn't supposed to show up for info level") + + //Now change the log level to DEBUG + SetLevel(DEBUG, moduleName) + + //Test logger.debug outputs + verifyBasicLogging(t, DEBUG, logger.Debug, nil, &buf, false) + verifyBasicLogging(t, DEBUG, logger.Debugln, nil, &buf, false) + verifyBasicLogging(t, DEBUG, nil, logger.Debugf, &buf, false) + +} + +func TestLevelledLoggingForCustomLogger(t *testing.T) { + + //prepare custom logger for which output is bytes buffer + var buf bytes.Buffer + customLogger := log.New(&buf, fmt.Sprintf(logPrefixFormatter, moduleName), log.Ldate|log.Ltime|log.LUTC) + + //Create new logger + logger := NewLogger(moduleName) + //Now add custom logger + SetCustomLogger(&SampleCustomLogger{customLogger: customLogger}) + + //Test logger.print outputs + verifyBasicLogging(t, INFO, logger.Print, nil, &buf, true) + verifyBasicLogging(t, INFO, logger.Println, nil, &buf, true) + verifyBasicLogging(t, INFO, nil, logger.Printf, &buf, true) + + //Test logger.info outputs + verifyBasicLogging(t, INFO, logger.Info, nil, &buf, true) + verifyBasicLogging(t, INFO, logger.Infoln, nil, &buf, true) + verifyBasicLogging(t, INFO, nil, logger.Infof, &buf, true) + + //Test logger.warn outputs + verifyBasicLogging(t, WARNING, logger.Warn, nil, &buf, true) + verifyBasicLogging(t, WARNING, logger.Warnln, nil, &buf, true) + verifyBasicLogging(t, WARNING, nil, logger.Warnf, &buf, true) + + //In middle of test, get new logger, it should still stick to custom logger + logger = NewLogger(moduleName) + + //Test logger.error outputs + verifyBasicLogging(t, ERROR, logger.Error, nil, &buf, true) + verifyBasicLogging(t, ERROR, logger.Errorln, nil, &buf, true) + verifyBasicLogging(t, ERROR, nil, logger.Errorf, &buf, true) + + //Test logger.debug outputs + verifyBasicLogging(t, DEBUG, logger.Debug, nil, &buf, true) + verifyBasicLogging(t, DEBUG, logger.Debugln, nil, &buf, true) + verifyBasicLogging(t, DEBUG, nil, logger.Debugf, &buf, true) + + ////Test logger.fatal outputs - this custom logger doesn't cause os exit code 1 + verifyBasicLogging(t, CRITICAL, logger.Fatal, nil, &buf, true) + verifyBasicLogging(t, CRITICAL, logger.Fatalln, nil, &buf, true) + verifyBasicLogging(t, CRITICAL, nil, logger.Fatalf, &buf, true) + + //Test logger.panic outputs - this custom logger doesn't cause panic + verifyBasicLogging(t, CRITICAL, logger.Panic, nil, &buf, true) + verifyBasicLogging(t, CRITICAL, logger.Panicln, nil, &buf, true) + verifyBasicLogging(t, CRITICAL, nil, logger.Panicf, &buf, true) +} + +func TestDefaultLoggingPanic(t *testing.T) { + + //Reset custom logger, need default one + SetCustomLogger(nil) + logger := NewLogger(moduleName) + + verifyCriticalLoggings(t, CRITICAL, logger.Panic, nil, logger) + verifyCriticalLoggings(t, CRITICAL, logger.Panicln, nil, logger) + verifyCriticalLoggings(t, CRITICAL, nil, logger.Panicf, logger) + +} + +//verifyCriticalLoggings utility func which does job calling and verifying CRITICAL log level functions - PANIC +func verifyCriticalLoggings(t *testing.T, level Level, loggerFunc fn, loggerFuncf fnf, logger *Logger) { + + //change the output to buffer + var buf bytes.Buffer + logger.logger.(*DefaultLogger).defaultLogger.SetOutput(&buf) + + //Handling panic as well as checking log output + defer func() { + if r := recover(); r == nil { + t.Errorf("%v was supposed to panic", loggerFunc) + } + regex := fmt.Sprintf(basicLevelOutputExpectedRegex, moduleName, levelNames[level]) + match, err := regexp.MatchString(regex, buf.String()) + verifyEmpty(t, err, "error while matching regex with logoutput wasnt expected") + verifyTrue(t, match, "CRITICAL logger isn't producing output as expected, \n logoutput:%s\n regex: %s", buf.String(), regex) + + }() + + //Call logger func + if loggerFunc != nil { + loggerFunc("brown fox jumps over the lazy dog") + } else if loggerFuncf != nil { + loggerFuncf("brown %s jumps over the lazy %s", "fox", "dog") + } +} + +//verifyBasicLogging utility func which does job calling and verifying basic log level functions - DEBUG, INFO, ERROR, WARNING +func verifyBasicLogging(t *testing.T, level Level, loggerFunc fn, loggerFuncf fnf, buf *bytes.Buffer, verifyCustom bool) { + + //Call logger func + if loggerFunc != nil { + loggerFunc("brown fox jumps over the lazy dog") + } else if loggerFuncf != nil { + loggerFuncf("brown %s jumps over the lazy %s", "fox", "dog") + } + + //check output + regex := "" + levelName := "print" + + if verifyCustom { + levelName = levelNames[level] + regex = fmt.Sprintf(customLevelOutputExpectedRegex, moduleName) + } else if level > 0 && !verifyCustom { + levelName = levelNames[level] + regex = fmt.Sprintf(basicLevelOutputExpectedRegex, moduleName, levelName) + } else { + regex = fmt.Sprintf(printLevelOutputExpectedRegex, moduleName) + } + + match, err := regexp.MatchString(regex, buf.String()) + + verifyEmpty(t, err, "error while matching regex with logoutput wasnt expected") + verifyTrue(t, match, "%s logger isn't producing output as expected, \n logoutput:%s\n regex: %s", levelName, buf.String(), regex) + + //Reset output buffer, for next use + buf.Reset() +} + +func verifyTrue(t *testing.T, input bool, msgAndArgs ...interface{}) { + if !input { + failTest(t, msgAndArgs) + } +} + +func verifyFalse(t *testing.T, input bool, msgAndArgs ...interface{}) { + if input { + failTest(t, msgAndArgs) + } +} + +func verifyEmpty(t *testing.T, in interface{}, msgAndArgs ...interface{}) { + if in == nil { + return + } else if in == "" { + return + } + failTest(t, msgAndArgs...) +} + +func verifyNotEmpty(t *testing.T, in interface{}, msgAndArgs ...interface{}) { + if in != nil { + return + } else if in != "" { + return + } + failTest(t, msgAndArgs...) +} + +func failTest(t *testing.T, msgAndArgs ...interface{}) { + if len(msgAndArgs) == 1 { + t.Fatal(msgAndArgs[0]) + } + if len(msgAndArgs) > 1 { + t.Fatalf(msgAndArgs[0].(string), msgAndArgs[1:]...) + } +} + +/* + Test custom logger +*/ + +type SampleCustomLogger struct { + customLogger *log.Logger +} + +func (l *SampleCustomLogger) Fatal(v ...interface{}) { l.customLogger.Print("CUSTOM LOG OUTPUT") } +func (l *SampleCustomLogger) Fatalf(format string, v ...interface{}) { + l.customLogger.Print("CUSTOM LOG OUTPUT") +} +func (l *SampleCustomLogger) Fatalln(v ...interface{}) { l.customLogger.Print("CUSTOM LOG OUTPUT") } +func (l *SampleCustomLogger) Panic(v ...interface{}) { l.customLogger.Print("CUSTOM LOG OUTPUT") } +func (l *SampleCustomLogger) Panicf(format string, v ...interface{}) { + l.customLogger.Print("CUSTOM LOG OUTPUT") +} +func (l *SampleCustomLogger) Panicln(v ...interface{}) { l.customLogger.Print("CUSTOM LOG OUTPUT") } +func (l *SampleCustomLogger) Print(v ...interface{}) { l.customLogger.Print("CUSTOM LOG OUTPUT") } +func (l *SampleCustomLogger) Printf(format string, v ...interface{}) { + l.customLogger.Print("CUSTOM LOG OUTPUT") +} +func (l *SampleCustomLogger) Println(v ...interface{}) { l.customLogger.Print("CUSTOM LOG OUTPUT") } +func (l *SampleCustomLogger) Debug(args ...interface{}) { l.customLogger.Print("CUSTOM LOG OUTPUT") } +func (l *SampleCustomLogger) Debugf(format string, args ...interface{}) { + l.customLogger.Print("CUSTOM LOG OUTPUT") +} +func (l *SampleCustomLogger) Debugln(args ...interface{}) { l.customLogger.Print("CUSTOM LOG OUTPUT") } +func (l *SampleCustomLogger) Info(args ...interface{}) { l.customLogger.Print("CUSTOM LOG OUTPUT") } +func (l *SampleCustomLogger) Infof(format string, args ...interface{}) { + l.customLogger.Print("CUSTOM LOG OUTPUT") +} +func (l *SampleCustomLogger) Infoln(args ...interface{}) { l.customLogger.Print("CUSTOM LOG OUTPUT") } +func (l *SampleCustomLogger) Warn(args ...interface{}) { l.customLogger.Print("CUSTOM LOG OUTPUT") } +func (l *SampleCustomLogger) Warnf(format string, args ...interface{}) { + l.customLogger.Print("CUSTOM LOG OUTPUT") +} +func (l *SampleCustomLogger) Warnln(args ...interface{}) { l.customLogger.Print("CUSTOM LOG OUTPUT") } +func (l *SampleCustomLogger) Error(args ...interface{}) { l.customLogger.Print("CUSTOM LOG OUTPUT") } +func (l *SampleCustomLogger) Errorf(format string, args ...interface{}) { + l.customLogger.Print("CUSTOM LOG OUTPUT") +} +func (l *SampleCustomLogger) Errorln(args ...interface{}) { l.customLogger.Print("CUSTOM LOG OUTPUT") }