bosun.org@v0.0.0-20210513094433-e25bc3e69a1f/slog/slog.go (about)

     1  // Package slog provides a cross-platform logging interface. It is designed to
     2  // provide a universal logging interface on any operating system. It defaults to
     3  // using the log package of the standard library, but can easily be used with
     4  // other logging backends. Thus, we can use syslog on unicies and the event log
     5  // on windows.
     6  package slog // import "bosun.org/slog"
     7  
     8  import (
     9  	"fmt"
    10  	"log"
    11  	"os"
    12  	"path/filepath"
    13  	"runtime"
    14  	"runtime/debug"
    15  	"strings"
    16  )
    17  
    18  var (
    19  	// LogLineNumber prints the file and line number of the caller.
    20  	LogLineNumber = true
    21  )
    22  
    23  // Logger is the slog logging interface.
    24  type Logger interface {
    25  	Error(v string)
    26  	Info(v string)
    27  	Warning(v string)
    28  	Fatal(v string)
    29  }
    30  
    31  // StdLog logs to a log.Logger.
    32  type StdLog struct {
    33  	Log *log.Logger
    34  }
    35  
    36  // Fatal logs a fatal message and calls os.Exit(1).
    37  func (s *StdLog) Fatal(v string) {
    38  	s.Log.Fatalln("fatal:", rmNl(v))
    39  }
    40  
    41  // Error logs an error message.
    42  func (s *StdLog) Error(v string) {
    43  	s.Log.Println("error:", rmNl(v))
    44  }
    45  
    46  // Info logs an info message.
    47  func (s *StdLog) Info(v string) {
    48  	s.Log.Println("info:", rmNl(v))
    49  }
    50  
    51  // Warning logs a warning message.
    52  func (s *StdLog) Warning(v string) {
    53  	s.Log.Println("warning:", rmNl(v))
    54  }
    55  
    56  func rmNl(v string) string {
    57  	if strings.HasSuffix(v, "\n") {
    58  		v = v[:len(v)-1]
    59  	}
    60  	return v
    61  }
    62  
    63  var logging Logger = &StdLog{Log: log.New(os.Stderr, "", log.LstdFlags)}
    64  
    65  // Set configures l to be the default logger for slog.
    66  func Set(l Logger) {
    67  	logging = l
    68  }
    69  
    70  // Info logs an info message.
    71  func Info(v ...interface{}) {
    72  	output(logging.Info, v...)
    73  }
    74  
    75  // Infof logs an info message.
    76  func Infof(format string, v ...interface{}) {
    77  	outputf(logging.Info, format, v...)
    78  }
    79  
    80  // Infoln logs an info message.
    81  func Infoln(v ...interface{}) {
    82  	outputln(logging.Info, v...)
    83  }
    84  
    85  // Warning logs a warning message.
    86  func Warning(v ...interface{}) {
    87  	output(logging.Warning, v...)
    88  }
    89  
    90  // Warningf logs a warning message.
    91  func Warningf(format string, v ...interface{}) {
    92  	outputf(logging.Warning, format, v...)
    93  }
    94  
    95  // Warningln logs a warning message.
    96  func Warningln(v ...interface{}) {
    97  	outputln(logging.Warning, v...)
    98  }
    99  
   100  // Error logs an error message.
   101  func Error(v ...interface{}) {
   102  	output(logging.Error, v...)
   103  }
   104  
   105  // Errorf logs an error message.
   106  func Errorf(format string, v ...interface{}) {
   107  	outputf(logging.Error, format, v...)
   108  }
   109  
   110  // Errorln logs an error message.
   111  func Errorln(v ...interface{}) {
   112  	outputln(logging.Error, v...)
   113  }
   114  
   115  // Fatal logs a fatal message and calls os.Exit(1).
   116  func Fatal(v ...interface{}) {
   117  	output(logging.Fatal, v...)
   118  	// Call os.Exit here just in case the logging package we are using doesn't.
   119  	os.Exit(1)
   120  }
   121  
   122  // Fatalf logs a fatal message and calls os.Exit(1).
   123  func Fatalf(format string, v ...interface{}) {
   124  	outputf(logging.Fatal, format, v...)
   125  	os.Exit(1)
   126  }
   127  
   128  // Fatalln logs a fatal message and calls os.Exit(1).
   129  func Fatalln(v ...interface{}) {
   130  	outputln(logging.Fatal, v...)
   131  	os.Exit(1)
   132  }
   133  
   134  func out(f func(string), s string) {
   135  	if LogLineNumber {
   136  		if _, filename, line, ok := runtime.Caller(3); ok {
   137  			s = fmt.Sprintf("%s:%d: %v", filepath.Base(filename), line, s)
   138  		}
   139  	}
   140  	f(s)
   141  }
   142  
   143  func output(f func(string), v ...interface{}) {
   144  	out(f, fmt.Sprint(v...))
   145  }
   146  
   147  func outputf(f func(string), format string, v ...interface{}) {
   148  	out(f, fmt.Sprintf(format, v...))
   149  }
   150  
   151  func outputln(f func(string), v ...interface{}) {
   152  	out(f, fmt.Sprintln(v...))
   153  }
   154  
   155  type wrappedError struct {
   156  	error
   157  	line string
   158  }
   159  
   160  func (w wrappedError) Error() string {
   161  	return fmt.Sprintf("%s: %s", w.line, w.error.Error())
   162  }
   163  
   164  //Helper to wrap an error with relevant line information. Wrap an error when it enters "our" code before passing it up the stack.
   165  //This will help narrow down the source of the error.
   166  func Wrap(err error) error {
   167  	if err == nil {
   168  		return nil
   169  	}
   170  	if _, ok := err.(wrappedError); ok {
   171  		return err
   172  	}
   173  	line := ""
   174  	if _, filename, l, ok := runtime.Caller(1); ok {
   175  		line = fmt.Sprintf("%s:%d", filepath.Base(filename), l)
   176  	} else {
   177  		return err
   178  	}
   179  	return wrappedError{err, line}
   180  }
   181  
   182  //Add defer slog.PanicAsFatal() to the top of a goroutine to recover panics and log them using slog.Fatal
   183  func PanicAsFatal() {
   184  	if r := recover(); r != nil {
   185  		s := string(debug.Stack()[:])
   186  		Fatalf("panic: %s\nStackTrace:\n\n%s", r, s)
   187  	}
   188  }