github.com/nycdavid/zeus@v0.0.0-20201208104106-9ba439429e03/go/shinylog/shinylog.go (about)

     1  package shinylog
     2  
     3  import (
     4  	"fmt"
     5  	"io"
     6  	"log"
     7  	"os"
     8  	"strings"
     9  	"sync"
    10  	"syscall"
    11  )
    12  
    13  type ShinyLogger struct {
    14  	mu                  sync.Mutex
    15  	errorLogger         log.Logger
    16  	locationErrorLogger log.Logger
    17  	suppressOutput      bool
    18  	disableColor        bool
    19  }
    20  
    21  type loggerOptions struct {
    22  	isError, printNewline, includeLocation bool
    23  }
    24  
    25  var errorOptions loggerOptions
    26  var stdoutOptions loggerOptions
    27  var stderrOptions loggerOptions
    28  
    29  func init() {
    30  	errorOptions = loggerOptions{isError: true, printNewline: true, includeLocation: true}
    31  	stdoutOptions = loggerOptions{isError: false, printNewline: true, includeLocation: false}
    32  	stderrOptions = loggerOptions{isError: false, printNewline: true, includeLocation: false}
    33  }
    34  
    35  func NewShinyLogger(out, err interface {
    36  	io.Writer
    37  }) *ShinyLogger {
    38  	errorLogger := log.New(err, "", 0)
    39  	locationErrorLogger := log.New(err, "", log.Lshortfile)
    40  	var mu sync.Mutex
    41  	return &ShinyLogger{mu, *errorLogger, *locationErrorLogger, false, false}
    42  }
    43  
    44  func NewTraceLogger(out interface {
    45  	io.Writer
    46  }) *log.Logger {
    47  	return log.New(out, "", log.Ldate|log.Ltime|log.Lmicroseconds)
    48  }
    49  
    50  const (
    51  	red         = "\x1b[31m"
    52  	green       = "\x1b[32m"
    53  	brightgreen = "\x1b[1;32m"
    54  	yellow      = "\x1b[33m"
    55  	blue        = "\x1b[34m"
    56  	magenta     = "\x1b[35m"
    57  	reset       = "\x1b[0m"
    58  )
    59  
    60  var dlm sync.RWMutex
    61  var defaultLogger *ShinyLogger = NewShinyLogger(os.Stdout, os.Stderr)
    62  var traceLogger *log.Logger = nil
    63  
    64  func DefaultLogger() *ShinyLogger {
    65  	dlm.RLock()
    66  	defer dlm.RUnlock()
    67  	return defaultLogger
    68  }
    69  
    70  func SetDefaultLogger(sl *ShinyLogger) {
    71  	dlm.Lock()
    72  	defaultLogger = sl
    73  	dlm.Unlock()
    74  }
    75  
    76  func TraceLogger() *log.Logger {
    77  	dlm.RLock()
    78  	defer dlm.RUnlock()
    79  	return traceLogger
    80  }
    81  
    82  func SetTraceLogger(sl *log.Logger) {
    83  	dlm.Lock()
    84  	traceLogger = sl
    85  	dlm.Unlock()
    86  }
    87  
    88  func Suppress()                           { DefaultLogger().Suppress() }
    89  func DisableColor()                       { DefaultLogger().DisableColor() }
    90  func Colorized(msg string) (printed bool) { return DefaultLogger().Colorized(msg) }
    91  func Error(err error) bool                { return DefaultLogger().Error(err) }
    92  func FatalError(err error)                { DefaultLogger().FatalError(err) }
    93  func FatalErrorString(msg string)         { DefaultLogger().FatalErrorString(msg) }
    94  func ErrorString(msg string) bool         { return DefaultLogger().ErrorString(msg) }
    95  func StdErrorString(msg string) bool      { return DefaultLogger().StdErrorString(msg) }
    96  func Red(msg string) bool                 { return DefaultLogger().Red(msg) }
    97  func Green(msg string) bool               { return DefaultLogger().Green(msg) }
    98  func Brightgreen(msg string) bool         { return DefaultLogger().Brightgreen(msg) }
    99  func Yellow(msg string) bool              { return DefaultLogger().Yellow(msg) }
   100  func Blue(msg string) bool                { return DefaultLogger().Blue(msg) }
   101  func Magenta(msg string) bool             { return DefaultLogger().Magenta(msg) }
   102  
   103  func TraceEnabled() bool {
   104  	return TraceLogger() != nil
   105  }
   106  
   107  func Trace(format string, v ...interface{}) bool {
   108  	if TraceEnabled() {
   109  		TraceLogger().Printf(format, v...)
   110  		return true
   111  	}
   112  	return false
   113  }
   114  
   115  func (l *ShinyLogger) Suppress() {
   116  	l.mu.Lock()
   117  	defer l.mu.Unlock()
   118  	l.suppressOutput = true
   119  }
   120  
   121  func (l *ShinyLogger) DisableColor() {
   122  	l.disableColor = true
   123  }
   124  
   125  func (l *ShinyLogger) Colorized(msg string) (printed bool) {
   126  	return l.colorized(3, msg, stdoutOptions)
   127  }
   128  
   129  func (l *ShinyLogger) ColorizedSansNl(msg string) (printed bool) {
   130  	return l.colorized(3, msg, loggerOptions{isError: false, printNewline: false, includeLocation: false})
   131  }
   132  
   133  // If we send SIGTERM rather than explicitly exiting,
   134  // the signal can be handled and the master can clean up.
   135  // This is a workaround for Go not having `atexit` :(.
   136  func terminate() {
   137  	proc, _ := os.FindProcess(os.Getpid())
   138  	proc.Signal(syscall.SIGTERM)
   139  }
   140  
   141  func (l *ShinyLogger) FatalErrorString(msg string) {
   142  	l.colorized(3, "{red}"+msg, errorOptions)
   143  	terminate()
   144  }
   145  
   146  func (l *ShinyLogger) FatalError(err error) {
   147  	l.colorized(3, "{red}"+err.Error(), errorOptions)
   148  	terminate()
   149  }
   150  
   151  func (l *ShinyLogger) Error(err error) bool {
   152  	return l.colorized(3, "{red}"+err.Error(), errorOptions)
   153  }
   154  
   155  func (l *ShinyLogger) ErrorString(msg string) bool {
   156  	return l.colorized(3, "{red}"+msg, errorOptions)
   157  }
   158  
   159  func (l *ShinyLogger) StdErrorString(msg string) bool {
   160  	return l.colorized(3, "{red}"+msg, stderrOptions)
   161  }
   162  
   163  func (l *ShinyLogger) Red(msg string) bool {
   164  	return l.colorized(3, "{red}"+msg, stdoutOptions)
   165  }
   166  
   167  func (l *ShinyLogger) Green(msg string) bool {
   168  	return l.colorized(3, "{green}"+msg, stdoutOptions)
   169  }
   170  
   171  func (l *ShinyLogger) Brightgreen(msg string) bool {
   172  	return l.colorized(3, "{brightgreen}"+msg, stdoutOptions)
   173  }
   174  
   175  func (l *ShinyLogger) Yellow(msg string) bool {
   176  	return l.colorized(3, "{yellow}"+msg, stdoutOptions)
   177  }
   178  
   179  func (l *ShinyLogger) Blue(msg string) bool {
   180  	return l.colorized(3, "{blue}"+msg, stdoutOptions)
   181  }
   182  
   183  func (l *ShinyLogger) Magenta(msg string) bool {
   184  	return l.colorized(3, "{magenta}"+msg, stdoutOptions)
   185  }
   186  
   187  func (l *ShinyLogger) formatColors(msg string) string {
   188  	if l.disableColor {
   189  		msg = strings.Replace(msg, "{red}", "", -1)
   190  		msg = strings.Replace(msg, "{green}", "", -1)
   191  		msg = strings.Replace(msg, "{brightgreen}", "", -1)
   192  		msg = strings.Replace(msg, "{yellow}", "", -1)
   193  		msg = strings.Replace(msg, "{blue}", "", -1)
   194  		msg = strings.Replace(msg, "{magenta}", "", -1)
   195  		msg = strings.Replace(msg, "{reset}", "", -1)
   196  	} else {
   197  		msg = strings.Replace(msg, "{red}", red, -1)
   198  		msg = strings.Replace(msg, "{green}", green, -1)
   199  		msg = strings.Replace(msg, "{brightgreen}", brightgreen, -1)
   200  		msg = strings.Replace(msg, "{yellow}", yellow, -1)
   201  		msg = strings.Replace(msg, "{blue}", blue, -1)
   202  		msg = strings.Replace(msg, "{magenta}", magenta, -1)
   203  		msg = strings.Replace(msg, "{reset}", reset, -1)
   204  	}
   205  	return msg
   206  }
   207  
   208  func (l *ShinyLogger) colorized(callDepth int, msg string, options loggerOptions) bool {
   209  	l.mu.Lock()
   210  	defer l.mu.Unlock()
   211  
   212  	if !l.suppressOutput {
   213  		msg = l.formatColors(msg)
   214  
   215  		if l == DefaultLogger() {
   216  			callDepth += 1 // this was called through a proxy method
   217  		}
   218  		if options.isError {
   219  			if options.includeLocation {
   220  				l.locationErrorLogger.Output(callDepth, msg+reset)
   221  			} else {
   222  				l.errorLogger.Output(callDepth, msg+reset)
   223  			}
   224  		} else {
   225  			if options.printNewline {
   226  				fmt.Println(msg + reset)
   227  			} else {
   228  				fmt.Print(msg + reset)
   229  			}
   230  		}
   231  	}
   232  	return !l.suppressOutput
   233  }