github.com/git-amp/amp-sdk-go@v0.7.5/stdlib/log/logger.go (about)

     1  package log
     2  
     3  import (
     4  	"bytes"
     5  	"flag"
     6  	"fmt"
     7  	"os"
     8  	"os/signal"
     9  	"strings"
    10  	"syscall"
    11  	"time"
    12  
    13  	"github.com/brynbellomy/klog"
    14  )
    15  
    16  // Formatter specifies how each log entry header should be formatted.=
    17  type Formatter interface {
    18  	FormatHeader(severity string, filename string, lineNum int, ioBuf *bytes.Buffer)
    19  }
    20  
    21  // Logger abstracts basic logging functions.
    22  type Logger interface {
    23  	SetLogLabel(inLabel string)
    24  	GetLogLabel() string
    25  	GetLogPrefix() string
    26  	Debug(args ...interface{})
    27  	Debugf(inFormat string, args ...interface{})
    28  	Debugw(inFormat string, fields Fields)
    29  	Success(args ...interface{})
    30  	Successf(inFormat string, args ...interface{})
    31  	Successw(inFormat string, fields Fields)
    32  	LogV(inVerboseLevel int32) bool
    33  	Info(inVerboseLevel int32, args ...interface{})
    34  	Infof(inVerboseLevel int32, inFormat string, args ...interface{})
    35  	Infow(inFormat string, fields Fields)
    36  	Warn(args ...interface{})
    37  	Warnf(inFormat string, args ...interface{})
    38  	Warnw(inFormat string, fields Fields)
    39  	Error(args ...interface{})
    40  	Errorf(inFormat string, args ...interface{})
    41  	Errorw(inFormat string, fields Fields)
    42  	Fatalf(inFormat string, args ...interface{})
    43  }
    44  
    45  func InitFlags(flagset *flag.FlagSet) {
    46  	klog.InitFlags(flagset)
    47  }
    48  
    49  // Uses the stock log formatter with the given settings
    50  func UseStockFormatter(fileNameCharWidth int, useColor bool) {
    51  	UseFormatter(&klog.FmtConstWidth{
    52  		FileNameCharWidth: fileNameCharWidth,
    53  		UseColor:          useColor,
    54  	})
    55  }
    56  
    57  func UseFormatter(inFormatter Formatter) {
    58  	klog.SetFormatter(inFormatter)
    59  }
    60  
    61  func Flush() {
    62  	klog.Flush()
    63  }
    64  
    65  type logger struct {
    66  	hasPrefix bool
    67  	logPrefix string
    68  	logLabel  string
    69  }
    70  
    71  var (
    72  	gLongestLabel int
    73  	gSpacing      = "                                                               "
    74  )
    75  
    76  func (l *logger) Padding() string {
    77  	labelLen := len(l.logLabel)
    78  	if labelLen >= gLongestLabel {
    79  		return " "
    80  	} else {
    81  		return gSpacing[:gLongestLabel-labelLen]
    82  	}
    83  }
    84  
    85  // NewLogger creates and inits a new Logger with the given label.
    86  func NewLogger(label string) Logger {
    87  	l := &logger{}
    88  	if label != "" {
    89  		l.SetLogLabel(label)
    90  	}
    91  	return l
    92  }
    93  
    94  // Fatalf -- see Fatalf (above)
    95  func Fatalf(inFormat string, args ...interface{}) {
    96  	gLogger.Fatalf(inFormat, args...)
    97  }
    98  
    99  var gLogger = logger{}
   100  
   101  // SetLogLabel sets the label prefix for all entries logged.
   102  func (l *logger) SetLogLabel(inLabel string) {
   103  	l.logLabel = inLabel
   104  	l.hasPrefix = len(inLabel) > 0
   105  	if l.hasPrefix {
   106  		l.logPrefix = fmt.Sprintf("[%s] ", inLabel)
   107  
   108  		// Find length of longest line
   109  		{
   110  			longest := gLongestLabel
   111  			max := len(gSpacing) - 1
   112  			N := len(l.logPrefix)
   113  			for pos := 0; pos < N; {
   114  				lineEnd := strings.IndexByte(l.logPrefix[pos:], '\n')
   115  				if lineEnd < 0 {
   116  					pos = N
   117  				}
   118  				lineLen := min(max, 1+lineEnd-pos)
   119  				if lineLen > longest {
   120  					longest = lineLen
   121  					gLongestLabel = longest
   122  				}
   123  				pos += lineEnd + 1
   124  			}
   125  		}
   126  	}
   127  }
   128  
   129  // GetLogLabel returns the label last set via SetLogLabel()
   130  func (l *logger) GetLogLabel() string {
   131  	return l.logLabel
   132  }
   133  
   134  // GetLogPrefix returns the the text that prefixes all log messages for this context.
   135  func (l *logger) GetLogPrefix() string {
   136  	return l.logPrefix
   137  }
   138  
   139  // LogV returns true if logging is currently enabled for log verbose level.
   140  func (l *logger) LogV(inVerboseLevel int32) bool {
   141  	return bool(klog.V(klog.Level(inVerboseLevel)))
   142  }
   143  
   144  func (l *logger) Debug(args ...interface{}) {
   145  	if l.hasPrefix {
   146  		klog.DebugDepth(1, l.logPrefix, l.Padding(), fmt.Sprint(args...))
   147  	} else {
   148  		klog.DebugDepth(1, args...)
   149  	}
   150  }
   151  
   152  func (l *logger) Debugf(inFormat string, args ...interface{}) {
   153  	if l.hasPrefix {
   154  		klog.DebugDepth(1, l.logPrefix, l.Padding(), fmt.Sprintf(inFormat, args...))
   155  	} else {
   156  		klog.DebugDepth(1, fmt.Sprintf(inFormat, args...))
   157  	}
   158  }
   159  
   160  func (l *logger) Debugw(msg string, fields Fields) {
   161  	if l.hasPrefix {
   162  		klog.DebugDepth(1, l.logPrefix, l.Padding(), fmt.Sprintf(msg+" %v", fields))
   163  	} else {
   164  		klog.DebugDepth(1, fmt.Sprintf(msg+" %v", fields))
   165  	}
   166  }
   167  
   168  func (l *logger) Success(args ...interface{}) {
   169  	if l.hasPrefix {
   170  		klog.SuccessDepth(1, l.logPrefix, l.Padding(), fmt.Sprint(args...))
   171  	} else {
   172  		klog.SuccessDepth(1, args...)
   173  	}
   174  }
   175  
   176  func (l *logger) Successf(inFormat string, args ...interface{}) {
   177  	if l.hasPrefix {
   178  		klog.SuccessDepth(1, l.logPrefix, l.Padding(), fmt.Sprintf(inFormat, args...))
   179  	} else {
   180  		klog.SuccessDepth(1, fmt.Sprintf(inFormat, args...))
   181  	}
   182  }
   183  
   184  func (l *logger) Successw(msg string, fields Fields) {
   185  	if l.hasPrefix {
   186  		klog.SuccessDepth(1, l.logPrefix, l.Padding(), fmt.Sprintf(msg+" %v", fields))
   187  	} else {
   188  		klog.SuccessDepth(1, fmt.Sprintf(msg+" %v", fields))
   189  	}
   190  }
   191  
   192  // Info logs to the INFO log.
   193  // Arguments are handled like fmt.Print(); a newline is appended if missing.
   194  //
   195  // Verbose level conventions:
   196  //  0. Enabled during production and field deployment.  Use this for important high-level info.
   197  //  1. Enabled during testing and development. Use for high-level changes in state, mode, or connection.
   198  //  2. Enabled during low-level debugging and troubleshooting.
   199  func (l *logger) Info(inVerboseLevel int32, args ...interface{}) {
   200  	logIt := true
   201  	if inVerboseLevel > 0 {
   202  		logIt = bool(klog.V(klog.Level(inVerboseLevel)))
   203  	}
   204  
   205  	if logIt {
   206  		if l.hasPrefix {
   207  			klog.InfoDepth(1, l.logPrefix, l.Padding(), fmt.Sprint(args...))
   208  		} else {
   209  			klog.InfoDepth(1, args...)
   210  		}
   211  	}
   212  }
   213  
   214  // Infof logs to the INFO log.
   215  // Arguments are handled like fmt.Printf(); a newline is appended if missing.
   216  //
   217  // See comments above for Info() for guidelines for inVerboseLevel.
   218  func (l *logger) Infof(inVerboseLevel int32, inFormat string, args ...interface{}) {
   219  	logIt := true
   220  	if inVerboseLevel > 0 {
   221  		logIt = bool(klog.V(klog.Level(inVerboseLevel)))
   222  	}
   223  
   224  	if logIt {
   225  		if l.hasPrefix {
   226  			klog.InfoDepth(1, l.logPrefix, l.Padding(), fmt.Sprintf(inFormat, args...))
   227  		} else {
   228  			klog.InfoDepth(1, fmt.Sprintf(inFormat, args...))
   229  		}
   230  	}
   231  }
   232  
   233  func (l *logger) Infow(msg string, fields Fields) {
   234  	if l.hasPrefix {
   235  		klog.InfoDepth(1, l.logPrefix, l.Padding(), fmt.Sprintf(msg+" %v", fields))
   236  	} else {
   237  		klog.InfoDepth(1, fmt.Sprintf(msg+" %v", fields))
   238  	}
   239  }
   240  
   241  // Warn logs to the WARNING and INFO logs.
   242  // Arguments are handled like fmt.Print(); a newline is appended if missing.
   243  //
   244  // Warnings are reserved for situations that indicate an inconsistency or an error that
   245  // won't result in a departure of specifications, correctness, or expected behavior.
   246  func (l *logger) Warn(args ...interface{}) {
   247  	if l.hasPrefix {
   248  		klog.WarningDepth(1, l.logPrefix, l.Padding(), fmt.Sprint(args...))
   249  	} else {
   250  		klog.WarningDepth(1, args...)
   251  	}
   252  }
   253  
   254  // Warnf logs to the WARNING and INFO logs.
   255  // Arguments are handled like fmt.Printf(); a newline is appended if missing.
   256  //
   257  // See comments above for Warn() for guidelines on errors vs warnings.
   258  func (l *logger) Warnf(inFormat string, args ...interface{}) {
   259  	if l.hasPrefix {
   260  		klog.WarningDepth(1, l.logPrefix, l.Padding(), fmt.Sprintf(inFormat, args...))
   261  	} else {
   262  		klog.WarningDepth(1, fmt.Sprintf(inFormat, args...))
   263  	}
   264  }
   265  
   266  func (l *logger) Warnw(msg string, fields Fields) {
   267  	if l.hasPrefix {
   268  		klog.WarningDepth(1, l.logPrefix, l.Padding(), fmt.Sprintf(msg+" %v", fields))
   269  	} else {
   270  		klog.WarningDepth(1, fmt.Sprintf(msg+" %v", fields))
   271  	}
   272  }
   273  
   274  // Error logs to the ERROR, WARNING, and INFO logs.
   275  // Arguments are handled like fmt.Print(); a newline is appended if missing.
   276  //
   277  // Errors are reserved for situations that indicate an implementation deficiency, a
   278  // corruption of data or resources, or an issue that if not addressed could spiral into deeper issues.
   279  // Logging an error reflects that correctness or expected behavior is either broken or under threat.
   280  func (l *logger) Error(args ...interface{}) {
   281  	{
   282  		if l.hasPrefix {
   283  			klog.ErrorDepth(1, l.logPrefix, l.Padding(), fmt.Sprint(args...))
   284  		} else {
   285  			klog.ErrorDepth(1, args...)
   286  		}
   287  	}
   288  }
   289  
   290  // Errorf logs to the ERROR, WARNING, and INFO logs.
   291  // Arguments are handled like fmt.Print; a newline is appended if missing.
   292  //
   293  // See comments above for Error() for guidelines on errors vs warnings.
   294  func (l *logger) Errorf(inFormat string, args ...interface{}) {
   295  	{
   296  		if l.hasPrefix {
   297  			klog.ErrorDepth(1, l.logPrefix, l.Padding(), fmt.Sprintf(inFormat, args...))
   298  		} else {
   299  			klog.ErrorDepth(1, fmt.Sprintf(inFormat, args...))
   300  		}
   301  	}
   302  }
   303  
   304  func (l *logger) Errorw(msg string, fields Fields) {
   305  	if l.hasPrefix {
   306  		klog.ErrorDepth(1, l.logPrefix, l.Padding(), fmt.Sprintf(msg+" %v", fields))
   307  	} else {
   308  		klog.ErrorDepth(1, fmt.Sprintf(msg+" %v", fields))
   309  	}
   310  }
   311  
   312  // Fatalf logs to the FATAL, ERROR, WARNING, and INFO logs,
   313  // Arguments are handled like fmt.Printf(); a newline is appended if missing.
   314  func (l *logger) Fatalf(inFormat string, args ...interface{}) {
   315  	{
   316  		if l.hasPrefix {
   317  			klog.FatalDepth(1, l.logPrefix, l.Padding(), fmt.Sprintf(inFormat, args...))
   318  		} else {
   319  			klog.FatalDepth(1, fmt.Sprintf(inFormat, args...))
   320  		}
   321  	}
   322  }
   323  
   324  func AwaitInterrupt() (
   325  	first <-chan struct{},
   326  	repeated <-chan struct{},
   327  ) {
   328  	onFirst := make(chan struct{})
   329  	onRepeated := make(chan struct{})
   330  
   331  	go func() {
   332  		sigInbox := make(chan os.Signal, 1)
   333  
   334  		signal.Notify(sigInbox, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
   335  
   336  		count := 0
   337  		firstTime := int64(0)
   338  
   339  		for sig := range sigInbox {
   340  			count++
   341  			curTime := time.Now().Unix()
   342  
   343  			// Prevent un-terminated ^c character in terminal
   344  			fmt.Println()
   345  
   346  			klog.WarningDepth(1, "Received ", sig.String(), "\n")
   347  
   348  			if onFirst != nil {
   349  				firstTime = curTime
   350  				close(onFirst)
   351  				onFirst = nil
   352  			} else if onRepeated != nil {
   353  				if curTime > firstTime+3 && count >= 3 {
   354  					klog.WarningDepth(1, "Received repeated interrupts\n")
   355  					klog.Flush()
   356  					close(onRepeated)
   357  					onRepeated = nil
   358  				}
   359  			}
   360  		}
   361  	}()
   362  
   363  	klog.InfoDepth(1, "To stop: \x1b[1m^C\x1b[0m  or  \x1b[1mkill -s SIGINT ", os.Getpid(), "\x1b[0m")
   364  	return onFirst, onRepeated
   365  }
   366  
   367  func min(a, b int) int {
   368  	if a < b {
   369  		return a
   370  	}
   371  	return b
   372  }