This is an automated email from the ASF dual-hosted git repository. maxyang pushed a commit to branch main in repository https://gitbox.apache.org/repos/asf/cloudberry-go-libs.git
commit ac345b75e8510fab3bbe0160ec6486d699c74a6d Author: Alexander Denissov <[email protected]> AuthorDate: Fri May 10 13:53:27 2024 -0700 Added separate shell prefix and colorization to gplog (#88) PXF uses gplog library to output to the console and log messages to the file, however we have the following requirements that are implemented by this PR. All existing functionality is intact, so the changes should be additive only. Have separate prefixes for console and log file output. We do not want to have users see the "header" with timestamps, pids, etc as it pollutes the important message payloads. The prefixes are now separated and I included the default function that would only append logging level for WARNING, ERROR, and CRITICAL message levels. We have colorized output to the console, so this functionality also has been added as a logger flag. When enabled, the colorization will apply to ERROR and CRITICAL messages (red) , WARNING messages (yellow) and INFO messages logged with new Success() method only (green). --- .gitignore | 3 + gplog/gplog.go | 198 +++++++++++++++++++++++++-------- gplog/gplog_test.go | 297 +++++++++++++++++++++++++++++++++++++++++++++++++ operating/operating.go | 4 + 4 files changed, 455 insertions(+), 47 deletions(-) diff --git a/.gitignore b/.gitignore index b7a59fe..151b748 100644 --- a/.gitignore +++ b/.gitignore @@ -24,4 +24,7 @@ _testmain.go *.test *.prof +# IntelliJ project metadata +.idea/ + vendor diff --git a/gplog/gplog.go b/gplog/gplog.go index 5663409..456a1b8 100644 --- a/gplog/gplog.go +++ b/gplog/gplog.go @@ -9,6 +9,7 @@ import ( "io" "log" "os" + "regexp" "strings" "sync" @@ -59,6 +60,19 @@ const ( LOGDEBUG ) +// ESCAPE - ASCII escape character to start color character sequences +const ESCAPE = "\x1b" + +type Color int + +// color codes in the order of their escape character sequences +const ( + NONE Color = iota + RED + GREEN + YELLOW +) + /* * Leveled logging output functions using the above log levels are implemented * below. Info(), Verbose(), and Debug() print messages when the log level is @@ -87,14 +101,16 @@ type LogFileNameFunc func(string, string) string type ExitFunc func() type GpLogger struct { - logStdout *log.Logger - logStderr *log.Logger - logFile *log.Logger - logFileName string - shellVerbosity int - fileVerbosity int - header string - logPrefixFunc LogPrefixFunc + logStdout *log.Logger + logStderr *log.Logger + logFile *log.Logger + logFileName string + shellVerbosity int + fileVerbosity int + header string + logPrefixFunc LogPrefixFunc + shellLogPrefixFunc LogPrefixFunc + colorize bool } /* @@ -152,14 +168,16 @@ func NewLogger(stdout io.Writer, stderr io.Writer, logFile io.Writer, logFileNam fileVerbosity = logFileVerbosity[0] } return &GpLogger{ - logStdout: log.New(stdout, "", 0), - logStderr: log.New(stderr, "", 0), - logFile: log.New(logFile, "", 0), - logFileName: logFileName, - shellVerbosity: shellVerbosity, - fileVerbosity: fileVerbosity, - header: GetHeader(program), - logPrefixFunc: nil, + logStdout: log.New(stdout, "", 0), + logStderr: log.New(stderr, "", 0), + logFile: log.New(logFile, "", 0), + logFileName: logFileName, + shellVerbosity: shellVerbosity, + fileVerbosity: fileVerbosity, + header: GetHeader(program), + logPrefixFunc: nil, + shellLogPrefixFunc: nil, + colorize: false, } } @@ -177,6 +195,30 @@ func SetLogPrefixFunc(logPrefixFunc func(string) string) { logger.logPrefixFunc = logPrefixFunc } +// SetShellLogPrefixFunc registers a function that returns a prefix for messages that get printed to the shell console +func SetShellLogPrefixFunc(logPrefixFunc func(string) string) { + logger.shellLogPrefixFunc = logPrefixFunc +} + +// SetColorize sets the flag defining whether to colorize the output to the shell console. +// The output to log files is never colorized, even if the flag is set to true. +// Shell console output colorization depends on the message levels: +// red - for CRITICAL (non-panic) and ERROR levels +// yellow - for WARNING levels +// green - for INFO levels produced via Success function call only +// no color - for all other levels +func SetColorize(shouldColorize bool) { + logger.colorize = shouldColorize +} + +// GetColorize returns whether the colorization of shell console output has been enabled +func GetColorize() bool { + if logger == nil { + return false + } + return logger.colorize +} + func SetLogFileNameFunc(fileNameFunc func(string, string) string) { logFileNameFunc = fileNameFunc } @@ -190,6 +232,21 @@ func defaultLogPrefixFunc(level string) string { return fmt.Sprintf("%s %s", logTimestamp, fmt.Sprintf(logger.header, level)) } +// levelsToPrefix is a regex for determining if the message level will be shown on console +// by the DefaultShortLogPrefixFunc function +var levelsToPrefix = regexp.MustCompile(`WARNING|ERROR|CRITICAL`) + +// DefaultShortLogPrefixFunc returns a short prefix for messages typically sent to the shell console +// It does not include the timestamp and other system information that would be found in a log file +// and only displays the message logging level for WARNING, ERROR, and CRITICAL levels. This function must be +// explicitly set by the users of the logger by calling SetShellLogPrefixFunc(gplog.DefaultShortLogPrefixFunc) +func DefaultShortLogPrefixFunc(level string) string { + if levelsToPrefix.MatchString(level) { + return fmt.Sprintf("%s: ", level) + } + return "" +} + func GetLogPrefix(level string) string { if logger.logPrefixFunc != nil { return logger.logPrefixFunc(level) @@ -197,6 +254,18 @@ func GetLogPrefix(level string) string { return defaultLogPrefixFunc(level) } +// GetShellLogPrefix returns a prefix to prepend to the message before sending it to the shell console +// Use this mechanism if it is desired to have different prefixes for messages sent to the console and to the log file. +// A caller must first set a custom function that will provide a custom prefix by calling SetShellLogPrefixFunc. +// If the custom function has not been provided, this function returns a prefix produced by the GetLogPrefix function, +// so that the prefixes for the shell console and the log file will be the same. +func GetShellLogPrefix(level string) string { + if logger.shellLogPrefixFunc != nil { + return logger.shellLogPrefixFunc(level) + } + return GetLogPrefix(level) +} + func GetLogFilePath() string { return logger.logFileName } @@ -232,14 +301,26 @@ func SetErrorCode(code int) { func Info(s string, v ...interface{}) { logMutex.Lock() defer logMutex.Unlock() - if logger.fileVerbosity >= LOGINFO || logger.shellVerbosity >= LOGINFO { + if logger.fileVerbosity >= LOGINFO { message := GetLogPrefix("INFO") + fmt.Sprintf(s, v...) - if logger.fileVerbosity >= LOGINFO { - _ = logger.logFile.Output(1, message) - } - if logger.shellVerbosity >= LOGINFO { - _ = logger.logStdout.Output(1, message) - } + _ = logger.logFile.Output(1, message) + } + if logger.shellVerbosity >= LOGINFO { + message := GetShellLogPrefix("INFO") + fmt.Sprintf(s, v...) + _ = logger.logStdout.Output(1, message) + } +} + +func Success(s string, v ...interface{}) { + logMutex.Lock() + defer logMutex.Unlock() + if logger.fileVerbosity >= LOGINFO { + message := GetLogPrefix("INFO") + fmt.Sprintf(s, v...) + _ = logger.logFile.Output(1, message) + } + if logger.shellVerbosity >= LOGINFO { + message := GetShellLogPrefix("INFO") + fmt.Sprintf(s, v...) + _ = logger.logStdout.Output(1, Colorize(GREEN, message)) } } @@ -248,7 +329,8 @@ func Warn(s string, v ...interface{}) { defer logMutex.Unlock() message := GetLogPrefix("WARNING") + fmt.Sprintf(s, v...) _ = logger.logFile.Output(1, message) - _ = logger.logStdout.Output(1, message) + message = GetShellLogPrefix("WARNING") + fmt.Sprintf(s, v...) + _ = logger.logStdout.Output(1, Colorize(YELLOW, message)) } /* @@ -265,7 +347,7 @@ func Progress(s string, v ...interface{}) { _ = logger.logFile.Output(1, message) } if logger.shellVerbosity >= LOGVERBOSE { - message = GetLogPrefix("DEBUG") + fmt.Sprintf(s, v...) + message = GetShellLogPrefix("DEBUG") + fmt.Sprintf(s, v...) _ = logger.logStdout.Output(1, message) } } @@ -273,45 +355,44 @@ func Progress(s string, v ...interface{}) { func Verbose(s string, v ...interface{}) { logMutex.Lock() defer logMutex.Unlock() - if logger.fileVerbosity >= LOGVERBOSE || logger.shellVerbosity >= LOGVERBOSE { + if logger.fileVerbosity >= LOGVERBOSE { message := GetLogPrefix("DEBUG") + fmt.Sprintf(s, v...) - if logger.fileVerbosity >= LOGVERBOSE { - _ = logger.logFile.Output(1, message) - } - if logger.shellVerbosity >= LOGVERBOSE { - _ = logger.logStdout.Output(1, message) - } + _ = logger.logFile.Output(1, message) + } + if logger.shellVerbosity >= LOGVERBOSE { + message := GetShellLogPrefix("DEBUG") + fmt.Sprintf(s, v...) + _ = logger.logStdout.Output(1, message) } } func Debug(s string, v ...interface{}) { logMutex.Lock() defer logMutex.Unlock() - if logger.fileVerbosity >= LOGDEBUG || logger.shellVerbosity >= LOGDEBUG { + if logger.fileVerbosity >= LOGDEBUG { message := GetLogPrefix("DEBUG") + fmt.Sprintf(s, v...) - if logger.fileVerbosity >= LOGDEBUG { - _ = logger.logFile.Output(1, message) - } - if logger.shellVerbosity >= LOGDEBUG { - _ = logger.logStdout.Output(1, message) - } + _ = logger.logFile.Output(1, message) + } + if logger.shellVerbosity >= LOGDEBUG { + message := GetShellLogPrefix("DEBUG") + fmt.Sprintf(s, v...) + _ = logger.logStdout.Output(1, message) } } func Error(s string, v ...interface{}) { logMutex.Lock() defer logMutex.Unlock() - message := GetLogPrefix("ERROR") + fmt.Sprintf(s, v...) errorCode = 1 + message := GetLogPrefix("ERROR") + fmt.Sprintf(s, v...) _ = logger.logFile.Output(1, message) - _ = logger.logStderr.Output(1, message) + message = GetShellLogPrefix("ERROR") + fmt.Sprintf(s, v...) + _ = logger.logStderr.Output(1, Colorize(RED, message)) } func Fatal(err error, s string, v ...interface{}) { logMutex.Lock() defer logMutex.Unlock() - message := GetLogPrefix("CRITICAL") errorCode = 2 + message := "" stackTraceStr := "" if err != nil { message += fmt.Sprintf("%v", err) @@ -321,11 +402,15 @@ func Fatal(err error, s string, v ...interface{}) { } } message += strings.TrimSpace(fmt.Sprintf(s, v...)) - _ = logger.logFile.Output(1, message+stackTraceStr) + fullMessage := GetLogPrefix("CRITICAL") + message + _ = logger.logFile.Output(1, fullMessage+stackTraceStr) + fullMessage = GetShellLogPrefix("CRITICAL") + message + // messages for panic are not colorized to allow any recover logic to inspect the actual fullMessage + // if the fullMessage needs to be output to the shell console, the caller should colorize it explicitly, if desired if logger.shellVerbosity >= LOGVERBOSE { - abort(message + stackTraceStr) + abort(fullMessage + stackTraceStr) } else { - abort(message) + abort(fullMessage) } } @@ -342,10 +427,11 @@ func FatalOnError(err error, output ...string) { func FatalWithoutPanic(s string, v ...interface{}) { logMutex.Lock() defer logMutex.Unlock() - message := GetLogPrefix("CRITICAL") + fmt.Sprintf(s, v...) errorCode = 2 + message := GetLogPrefix("CRITICAL") + fmt.Sprintf(s, v...) _ = logger.logFile.Output(1, message) - _ = logger.logStderr.Output(1, message) + message = GetShellLogPrefix("CRITICAL") + fmt.Sprintf(s, v...) + _ = logger.logStderr.Output(1, Colorize(RED, message)) exitFunc() } @@ -405,3 +491,21 @@ func createLogDirectory(dirname string) { func defaultExit() { os.Exit(1) } + +// color returns special characters that should be prepended to a string to make it of a particular color on the console +func color(c Color) string { + if c == NONE { + return fmt.Sprintf("%s[%dm", ESCAPE, c) + } + return fmt.Sprintf("%s[3%dm", ESCAPE, c) +} + +// Colorize wraps a string with special characters so that the string has a provided color when output to the console +// colorization happens only if the logger flag `colorize` is set to true. The function is exported to allow +// colorization outside the logging methods, such as when recovering from a `panic` when Fatal messages are logged. +func Colorize(c Color, text string) string { + if logger.colorize { + return color(c) + text + color(NONE) + } + return text +} diff --git a/gplog/gplog_test.go b/gplog/gplog_test.go index 084a95e..d736a07 100644 --- a/gplog/gplog_test.go +++ b/gplog/gplog_test.go @@ -152,6 +152,44 @@ var _ = Describe("logger/log tests", func() { gplog.SetLogPrefixFunc(nil) }) }) + Describe("GetShellLogPrefix", func() { + It("returns a prefix for the current time", func() { + expectedFormat := "20170101:01:01:01 testProgram:testUser:testHost:000000-[%s]:-" + Expect(gplog.GetShellLogPrefix("DEBUG")).To(Equal(fmt.Sprintf(expectedFormat, "DEBUG"))) + Expect(gplog.GetShellLogPrefix("INFO")).To(Equal(fmt.Sprintf(expectedFormat, "INFO"))) + Expect(gplog.GetShellLogPrefix("WARNING")).To(Equal(fmt.Sprintf(expectedFormat, "WARNING"))) + Expect(gplog.GetShellLogPrefix("ERROR")).To(Equal(fmt.Sprintf(expectedFormat, "ERROR"))) + Expect(gplog.GetShellLogPrefix("CRITICAL")).To(Equal(fmt.Sprintf(expectedFormat, "CRITICAL"))) + Expect(gplog.GetShellLogPrefix("FOO")).To(Equal(fmt.Sprintf(expectedFormat, "FOO"))) + Expect(gplog.GetShellLogPrefix("")).To(Equal(fmt.Sprintf(expectedFormat, ""))) + }) + It("returns a default short log prefix", func() { + gplog.SetShellLogPrefixFunc(gplog.DefaultShortLogPrefixFunc) + Expect(gplog.GetShellLogPrefix("DEBUG")).To(Equal("")) + Expect(gplog.GetShellLogPrefix("INFO")).To(Equal("")) + Expect(gplog.GetShellLogPrefix("WARNING")).To(Equal("WARNING: ")) + Expect(gplog.GetShellLogPrefix("ERROR")).To(Equal("ERROR: ")) + Expect(gplog.GetShellLogPrefix("CRITICAL")).To(Equal("CRITICAL: ")) + Expect(gplog.GetShellLogPrefix("FOO")).To(Equal("")) + Expect(gplog.GetShellLogPrefix("")).To(Equal("")) + gplog.SetShellLogPrefixFunc(nil) + }) + It("returns a custom log prefix", func() { + myPrefixFunc := func(level string) string { + logTimestamp := operating.System.Now().Format("20060102:15:04:05") + return fmt.Sprintf("%s-custom-%s", logTimestamp, level) + } + gplog.SetShellLogPrefixFunc(myPrefixFunc) + Expect(gplog.GetShellLogPrefix("DEBUG")).To(Equal("20170101:01:01:01-custom-DEBUG")) + Expect(gplog.GetShellLogPrefix("INFO")).To(Equal("20170101:01:01:01-custom-INFO")) + Expect(gplog.GetShellLogPrefix("WARNING")).To(Equal("20170101:01:01:01-custom-WARNING")) + Expect(gplog.GetShellLogPrefix("ERROR")).To(Equal("20170101:01:01:01-custom-ERROR")) + Expect(gplog.GetShellLogPrefix("CRITICAL")).To(Equal("20170101:01:01:01-custom-CRITICAL")) + Expect(gplog.GetShellLogPrefix("FOO")).To(Equal("20170101:01:01:01-custom-FOO")) + Expect(gplog.GetShellLogPrefix("")).To(Equal("20170101:01:01:01-custom-")) + gplog.SetShellLogPrefixFunc(nil) + }) + }) Describe("Output function tests", func() { patternExpected := "20170101:01:01:01 testProgram:testUser:testHost:000000-[%s]:-" infoExpected := fmt.Sprintf(patternExpected, "INFO") @@ -189,6 +227,15 @@ var _ = Describe("logger/log tests", func() { testhelper.ExpectRegexp(logfile, infoExpected+expectedMessage) }) }) + Context("Success", func() { + It("prints to the log file", func() { + expectedMessage := "error info" + gplog.Success(expectedMessage) + testhelper.NotExpectRegexp(stdout, infoExpected+expectedMessage) + testhelper.NotExpectRegexp(stderr, infoExpected+expectedMessage) + testhelper.ExpectRegexp(logfile, infoExpected+expectedMessage) + }) + }) Context("Warn", func() { It("prints to stdout and the log file", func() { expectedMessage := "error warn" @@ -262,6 +309,15 @@ var _ = Describe("logger/log tests", func() { testhelper.ExpectRegexp(logfile, infoExpected+expectedMessage) }) }) + Context("Success", func() { + It("prints to stdout and the log file", func() { + expectedMessage := "info info" + gplog.Success(expectedMessage) + testhelper.ExpectRegexp(stdout, infoExpected+expectedMessage) + testhelper.NotExpectRegexp(stderr, infoExpected+expectedMessage) + testhelper.ExpectRegexp(logfile, infoExpected+expectedMessage) + }) + }) Context("Warn", func() { It("prints to stdout and the log file", func() { expectedMessage := "info warn" @@ -335,6 +391,15 @@ var _ = Describe("logger/log tests", func() { testhelper.ExpectRegexp(logfile, infoExpected+expectedMessage) }) }) + Context("Success", func() { + It("prints to stdout and the log file", func() { + expectedMessage := "verbose info" + gplog.Success(expectedMessage) + testhelper.ExpectRegexp(stdout, infoExpected+expectedMessage) + testhelper.NotExpectRegexp(stderr, infoExpected+expectedMessage) + testhelper.ExpectRegexp(logfile, infoExpected+expectedMessage) + }) + }) Context("Warn", func() { It("prints to stdout and the log file", func() { expectedMessage := "verbose warn" @@ -409,6 +474,15 @@ var _ = Describe("logger/log tests", func() { testhelper.ExpectRegexp(logfile, infoExpected+expectedMessage) }) }) + Context("Success", func() { + It("prints to stdout and the log file", func() { + expectedMessage := "debug info" + gplog.Success(expectedMessage) + testhelper.ExpectRegexp(stdout, infoExpected+expectedMessage) + testhelper.NotExpectRegexp(stderr, infoExpected+expectedMessage) + testhelper.ExpectRegexp(logfile, infoExpected+expectedMessage) + }) + }) Context("Warn", func() { It("prints to stdout and the log file", func() { expectedMessage := "debug warn" @@ -485,6 +559,15 @@ var _ = Describe("logger/log tests", func() { testhelper.NotExpectRegexp(logfile, infoExpected+expectedMessage) }) }) + Context("Success", func() { + It("prints to stdout", func() { + expectedMessage := "logfile error info" + gplog.Success(expectedMessage) + testhelper.ExpectRegexp(stdout, infoExpected+expectedMessage) + testhelper.NotExpectRegexp(stderr, infoExpected+expectedMessage) + testhelper.NotExpectRegexp(logfile, infoExpected+expectedMessage) + }) + }) Context("Warn", func() { It("prints to stdout and the log file", func() { expectedMessage := "logfile error warn" @@ -571,6 +654,15 @@ var _ = Describe("logger/log tests", func() { testhelper.ExpectRegexp(logfile, infoExpected+expectedMessage) }) }) + Context("Success", func() { + It("prints to stdout and the log file", func() { + expectedMessage := "logfile info info" + gplog.Success(expectedMessage) + testhelper.ExpectRegexp(stdout, infoExpected+expectedMessage) + testhelper.NotExpectRegexp(stderr, infoExpected+expectedMessage) + testhelper.ExpectRegexp(logfile, infoExpected+expectedMessage) + }) + }) Context("Warn", func() { It("prints to stdout and the log file", func() { expectedMessage := "logfile info warn" @@ -639,5 +731,210 @@ var _ = Describe("logger/log tests", func() { }) }) }) + Describe("Custom Shell Prefix used - shell verbosity set to Debug", func() { + BeforeEach(func() { + gplog.SetVerbosity(gplog.LOGDEBUG) + gplog.SetLogFileVerbosity(gplog.LOGDEBUG) + gplog.SetShellLogPrefixFunc(gplog.DefaultShortLogPrefixFunc) + }) + AfterEach(func() { + gplog.SetShellLogPrefixFunc(nil) + }) + + Context("Initialization", func() { + It("returns colorization info", func() { + Expect(gplog.GetColorize()).To(BeFalse()) + }) + }) + Context("Info", func() { + It("prints to stdout and the log file", func() { + expectedMessage := "debug info" + gplog.Info(expectedMessage) + testhelper.ExpectRegexp(stdout, expectedMessage) + testhelper.NotExpectRegexp(stderr, expectedMessage) + testhelper.NotExpectRegexp(stderr, infoExpected+expectedMessage) + testhelper.ExpectRegexp(logfile, infoExpected+expectedMessage) + }) + }) + Context("Success", func() { + It("prints to stdout and the log file", func() { + expectedMessage := "debug info" + gplog.Success(expectedMessage) + testhelper.ExpectRegexp(stdout, expectedMessage) + testhelper.NotExpectRegexp(stderr, expectedMessage) + testhelper.NotExpectRegexp(stderr, infoExpected+expectedMessage) + testhelper.ExpectRegexp(logfile, infoExpected+expectedMessage) + }) + }) + Context("Warn", func() { + It("prints to stdout and the log file", func() { + expectedMessage := "debug warn" + gplog.Warn(expectedMessage) + testhelper.ExpectRegexp(stdout, "WARNING: "+expectedMessage) + testhelper.NotExpectRegexp(stderr, "WARNING: "+expectedMessage) + testhelper.NotExpectRegexp(stderr, warnExpected+expectedMessage) + testhelper.ExpectRegexp(logfile, warnExpected+expectedMessage) + }) + }) + Context("Progress", func() { + It("prints to stdout and the log file", func() { + expectedMessage := "debug progress" + gplog.Progress(expectedMessage) + testhelper.ExpectRegexp(stdout, expectedMessage) + testhelper.NotExpectRegexp(stderr, expectedMessage) + testhelper.NotExpectRegexp(stderr, verboseExpected+expectedMessage) + testhelper.ExpectRegexp(logfile, infoExpected+expectedMessage) + }) + }) + Context("Verbose", func() { + It("prints to stdout and the log file", func() { + expectedMessage := "debug verbose" + gplog.Verbose(expectedMessage) + testhelper.ExpectRegexp(stdout, expectedMessage) + testhelper.NotExpectRegexp(stderr, expectedMessage) + testhelper.NotExpectRegexp(stderr, verboseExpected+expectedMessage) + testhelper.ExpectRegexp(logfile, verboseExpected+expectedMessage) + }) + }) + Context("Debug", func() { + It("prints to stdout and the log file", func() { + expectedMessage := "debug debug" + gplog.Debug(expectedMessage) + testhelper.ExpectRegexp(stdout, expectedMessage) + testhelper.NotExpectRegexp(stderr, expectedMessage) + testhelper.NotExpectRegexp(stderr, debugExpected+expectedMessage) + testhelper.ExpectRegexp(logfile, debugExpected+expectedMessage) + }) + }) + Context("Error", func() { + It("prints to stderr and the log file", func() { + expectedMessage := "debug error" + gplog.Error(expectedMessage) + testhelper.NotExpectRegexp(stdout, "ERROR: "+expectedMessage) + testhelper.NotExpectRegexp(stdout, errorExpected+expectedMessage) + testhelper.ExpectRegexp(stderr, "ERROR: "+expectedMessage) + testhelper.ExpectRegexp(logfile, errorExpected+expectedMessage) + }) + }) + Context("Fatal", func() { + It("prints to the log file, then panics", func() { + expectedMessage := "debug fatal" + defer func() { + testhelper.NotExpectRegexp(stdout, "CRITICAL: "+expectedMessage) + testhelper.NotExpectRegexp(stdout, fatalExpected+expectedMessage) + testhelper.NotExpectRegexp(stderr, "CRITICAL: "+expectedMessage) + testhelper.NotExpectRegexp(stderr, fatalExpected+expectedMessage) + testhelper.ExpectRegexp(logfile, fatalExpected+expectedMessage) + }() + defer testhelper.ShouldPanicWithMessage("CRITICAL: " + expectedMessage) + gplog.Fatal(errors.New(expectedMessage), "") + }) + }) + }) + Describe("Custom Shell Prefix used with colorization - shell verbosity set to Debug", func() { + BeforeEach(func() { + gplog.SetVerbosity(gplog.LOGDEBUG) + gplog.SetLogFileVerbosity(gplog.LOGDEBUG) + gplog.SetShellLogPrefixFunc(gplog.DefaultShortLogPrefixFunc) + gplog.SetColorize(true) + }) + AfterEach(func() { + gplog.SetShellLogPrefixFunc(nil) + gplog.SetColorize(false) + }) + + Context("Initialization", func() { + It("returns colorization info", func() { + Expect(gplog.GetColorize()).To(BeTrue()) + }) + }) + Context("Info", func() { + It("prints to stdout and the log file", func() { + expectedMessage := "debug info" + gplog.Info(expectedMessage) + testhelper.ExpectRegexp(stdout, expectedMessage) + testhelper.NotExpectRegexp(stderr, expectedMessage) + testhelper.NotExpectRegexp(stderr, infoExpected+expectedMessage) + testhelper.ExpectRegexp(logfile, infoExpected+expectedMessage) + }) + }) + Context("Success", func() { + It("prints to stdout and the log file", func() { + expectedMessage := "debug success" + expectedConsoleMessage := fmt.Sprintf("%[1]s[32mdebug success%[1]s[0m", "\x1b") + gplog.Success(expectedMessage) + testhelper.ExpectRegexp(stdout, expectedConsoleMessage) + testhelper.NotExpectRegexp(stderr, "INFO: "+expectedMessage) + testhelper.NotExpectRegexp(stderr, infoExpected+expectedMessage) + testhelper.ExpectRegexp(logfile, infoExpected+expectedMessage) + }) + }) + Context("Warn", func() { + It("prints to stdout and the log file", func() { + expectedMessage := "debug warn" + expectedConsoleMessage := fmt.Sprintf("%[1]s[33mWARNING: debug warn%[1]s[0m", "\x1b") + gplog.Warn(expectedMessage) + testhelper.ExpectRegexp(stdout, expectedConsoleMessage) + testhelper.NotExpectRegexp(stderr, "WARNING: "+expectedMessage) + testhelper.NotExpectRegexp(stderr, warnExpected+expectedMessage) + testhelper.ExpectRegexp(logfile, warnExpected+expectedMessage) + }) + }) + Context("Progress", func() { + It("prints to stdout and the log file", func() { + expectedMessage := "debug progress" + gplog.Progress(expectedMessage) + testhelper.ExpectRegexp(stdout, expectedMessage) + testhelper.NotExpectRegexp(stderr, expectedMessage) + testhelper.NotExpectRegexp(stderr, verboseExpected+expectedMessage) + testhelper.ExpectRegexp(logfile, infoExpected+expectedMessage) + }) + }) + Context("Verbose", func() { + It("prints to stdout and the log file", func() { + expectedMessage := "debug verbose" + gplog.Verbose(expectedMessage) + testhelper.ExpectRegexp(stdout, expectedMessage) + testhelper.NotExpectRegexp(stderr, expectedMessage) + testhelper.NotExpectRegexp(stderr, verboseExpected+expectedMessage) + testhelper.ExpectRegexp(logfile, verboseExpected+expectedMessage) + }) + }) + Context("Debug", func() { + It("prints to stdout and the log file", func() { + expectedMessage := "debug debug" + gplog.Debug(expectedMessage) + testhelper.ExpectRegexp(stdout, expectedMessage) + testhelper.NotExpectRegexp(stderr, expectedMessage) + testhelper.NotExpectRegexp(stderr, debugExpected+expectedMessage) + testhelper.ExpectRegexp(logfile, debugExpected+expectedMessage) + }) + }) + Context("Error", func() { + It("prints to stderr and the log file", func() { + expectedMessage := "debug error" + expectedConsoleMessage := fmt.Sprintf("%[1]s[31mERROR: debug error%[1]s[0m", "\x1b") + gplog.Error(expectedMessage) + testhelper.NotExpectRegexp(stdout, "ERROR: "+expectedMessage) + testhelper.NotExpectRegexp(stdout, errorExpected+expectedMessage) + testhelper.ExpectRegexp(stderr, expectedConsoleMessage) + testhelper.ExpectRegexp(logfile, errorExpected+expectedMessage) + }) + }) + Context("Fatal", func() { + It("prints to the log file, then panics", func() { + expectedMessage := "debug fatal" + defer func() { + testhelper.NotExpectRegexp(stdout, "CRITICAL: "+expectedMessage) + testhelper.NotExpectRegexp(stdout, fatalExpected+expectedMessage) + testhelper.NotExpectRegexp(stderr, "CRITICAL: "+expectedMessage) + testhelper.NotExpectRegexp(stderr, fatalExpected+expectedMessage) + testhelper.ExpectRegexp(logfile, fatalExpected+expectedMessage) + }() + defer testhelper.ShouldPanicWithMessage("CRITICAL: " + expectedMessage) + gplog.Fatal(errors.New(expectedMessage), "") + }) + }) + }) }) }) diff --git a/operating/operating.go b/operating/operating.go index 4ad7e5f..ec64e3f 100644 --- a/operating/operating.go +++ b/operating/operating.go @@ -57,11 +57,13 @@ func OpenFileWrite(name string, flag int, perm os.FileMode) (io.WriteCloser, err type SystemFunctions struct { Chmod func(name string, mode os.FileMode) error CurrentUser func() (*user.User, error) + Exit func(code int) Getenv func(key string) string Getpid func() int Glob func(pattern string) (matches []string, err error) Hostname func() (string, error) IsNotExist func(err error) bool + LookupEnv func(key string) (string, bool) MkdirAll func(path string, perm os.FileMode) error Now func() time.Time OpenFileRead func(name string, flag int, perm os.FileMode) (ReadCloserAt, error) @@ -80,12 +82,14 @@ func InitializeSystemFunctions() *SystemFunctions { return &SystemFunctions{ Chmod: os.Chmod, CurrentUser: user.Current, + Exit: os.Exit, Getenv: os.Getenv, Getpid: os.Getpid, Glob: filepath.Glob, Hostname: os.Hostname, IsNotExist: os.IsNotExist, MkdirAll: os.MkdirAll, + LookupEnv: os.LookupEnv, Now: time.Now, OpenFileRead: OpenFileRead, OpenFileWrite: OpenFileWrite, --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
