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]

Reply via email to