github.com/vmware/transport-go@v1.3.4/plank/utils/logger.go (about)

     1  // Copyright 2019-2021 VMware, Inc.
     2  // SPDX-License-Identifier: BSD-2-Clause
     3  
     4  package utils
     5  
     6  import (
     7  	"github.com/sirupsen/logrus"
     8  	"io"
     9  	"os"
    10  	"path"
    11  )
    12  
    13  var Log *PlankLogger
    14  
    15  type LogConfig struct {
    16  	AccessLog     string           `json:"access_log"`
    17  	ErrorLog      string           `json:"error_log"`
    18  	OutputLog     string           `json:"output_log"`
    19  	FormatOptions *LogFormatOption `json:"format_options"`
    20  	Root          string           `json:"root"`
    21  	accessLogFp   io.Writer        `json:"-"`
    22  	errorLogFp    io.Writer        `json:"-"`
    23  	outputLogFp   io.Writer        `json:"-"`
    24  }
    25  
    26  // LogFormatOption is merely a wrapper of logrus.TextFormatter because TextFormatter does not allow serializing
    27  // its public members of the struct
    28  type LogFormatOption struct {
    29  	// Set to true to bypass checking for a TTY before outputting colors.
    30  	ForceColors bool `json:"force_colors"`
    31  
    32  	// Force disabling colors.
    33  	DisableColors bool `json:"disable_colors"`
    34  
    35  	// Force quoting of all values
    36  	ForceQuote bool `json:"force_quote"`
    37  
    38  	// DisableQuote disables quoting for all values.
    39  	// DisableQuote will have a lower priority than ForceQuote.
    40  	// If both of them are set to true, quote will be forced on all values.
    41  	DisableQuote bool `json:"disable_quote"`
    42  
    43  	// Override coloring based on CLICOLOR and CLICOLOR_FORCE. - https://bixense.com/clicolors/
    44  	EnvironmentOverrideColors bool `json:"environment_override_colors"`
    45  
    46  	// Disable timestamp logging. useful when output is redirected to logging
    47  	// system that already adds timestamps.
    48  	DisableTimestamp bool `json:"disable_timestamp"`
    49  
    50  	// Enable logging the full timestamp when a TTY is attached instead of just
    51  	// the time passed since beginning of execution.
    52  	FullTimestamp bool `json:"full_timestamp"`
    53  
    54  	// TimestampFormat to use for display when a full timestamp is printed
    55  	TimestampFormat string `json:"timestamp_format"`
    56  
    57  	// The fields are sorted by default for a consistent output. For applications
    58  	// that log extremely frequently and don't use the JSON formatter this may not
    59  	// be desired.
    60  	DisableSorting bool `json:"disable_sorting"`
    61  
    62  	// Disables the truncation of the level text to 4 characters.
    63  	DisableLevelTruncation bool `json:"disable_level_truncation"`
    64  
    65  	// PadLevelText Adds padding the level text so that all the levels output at the same length
    66  	// PadLevelText is a superset of the DisableLevelTruncation option
    67  	PadLevelText bool `json:"pad_level_text"`
    68  
    69  	// QuoteEmptyFields will wrap empty fields in quotes if true
    70  	QuoteEmptyFields bool `json:"quote_empty_fields"`
    71  
    72  	// Whether the logger's out is to a terminal
    73  	isTerminal bool `json:"is_terminal"`
    74  
    75  	// FieldMap allows users to customize the names of keys for default fields.
    76  	// As an example:
    77  	// formatter := &TextFormatter{
    78  	//     FieldMap: FieldMap{
    79  	//         FieldKeyTime:  "@timestamp",
    80  	//         FieldKeyLevel: "@level",
    81  	//         FieldKeyMsg:   "@message"}}
    82  	FieldMap logrus.FieldMap
    83  }
    84  
    85  func (lc *LogConfig) PrepareLogFiles() error {
    86  	var fp io.Writer
    87  	var err error
    88  	if fp, err = lc.prepareLogFilePointer(lc.AccessLog); err != nil {
    89  		return err
    90  	}
    91  	lc.accessLogFp = fp
    92  
    93  	if fp, err = lc.prepareLogFilePointer(lc.ErrorLog); err != nil {
    94  		return err
    95  	}
    96  	lc.errorLogFp = fp
    97  
    98  	if fp, err = lc.prepareLogFilePointer(lc.OutputLog); err != nil {
    99  		return err
   100  	}
   101  	lc.outputLogFp = fp
   102  
   103  	return nil
   104  }
   105  
   106  func (lc *LogConfig) GetAccessLogFilePointer() io.Writer {
   107  	return lc.accessLogFp
   108  }
   109  
   110  func (lc *LogConfig) GetErrorLogFilePointer() io.Writer {
   111  	return lc.errorLogFp
   112  }
   113  
   114  func (lc *LogConfig) GetPlatformLogFilePointer() io.Writer {
   115  	return lc.outputLogFp
   116  }
   117  
   118  func (lc *LogConfig) prepareLogFilePointer(target string) (fp io.Writer, err error) {
   119  	if target == "stdout" {
   120  		fp = os.Stdout
   121  	} else if target == "stderr" {
   122  		fp = os.Stderr
   123  	} else if target == "null" {
   124  		fp = &noopWriter{}
   125  	} else {
   126  		logFilePath := JoinBasePathIfRelativeRegularFilePath(lc.Root, target)
   127  		fp, err = GetNewLogFilePointer(logFilePath)
   128  	}
   129  	return
   130  }
   131  
   132  type PlankLogger struct {
   133  	*logrus.Logger
   134  }
   135  
   136  func (l *PlankLogger) setCommonFields() *logrus.Entry {
   137  	fr := getFrame(2)
   138  	pkgName := path.Base(path.Dir(fr.File))
   139  	fileName := path.Base(fr.File)
   140  	return l.WithFields(logrus.Fields{
   141  		"goroutine": GetGoRoutineID(),
   142  		"package":   pkgName,
   143  		"fileName":  fileName,
   144  	})
   145  }
   146  
   147  func (l *PlankLogger) Trace(args ...interface{}) {
   148  	l.setCommonFields().Trace(args...)
   149  }
   150  
   151  func (l *PlankLogger) Traceln(args ...interface{}) {
   152  	l.setCommonFields().Traceln(args...)
   153  }
   154  
   155  func (l *PlankLogger) Tracef(format string, args ...interface{}) {
   156  	l.setCommonFields().Tracef(format, args...)
   157  }
   158  
   159  func (l *PlankLogger) Debug(args ...interface{}) {
   160  	l.setCommonFields().Debug(args...)
   161  }
   162  
   163  func (l *PlankLogger) Debugln(args ...interface{}) {
   164  	l.setCommonFields().Debugln(args...)
   165  }
   166  
   167  func (l *PlankLogger) Debugf(format string, args ...interface{}) {
   168  	l.setCommonFields().Debugf(format, args...)
   169  }
   170  
   171  func (l *PlankLogger) Info(args ...interface{}) {
   172  	l.setCommonFields().Info(args...)
   173  }
   174  
   175  func (l *PlankLogger) Infoln(args ...interface{}) {
   176  	l.setCommonFields().Infoln(args...)
   177  }
   178  
   179  func (l *PlankLogger) Infof(format string, args ...interface{}) {
   180  	l.setCommonFields().Infof(format, args...)
   181  }
   182  
   183  func (l *PlankLogger) Warn(args ...interface{}) {
   184  	l.setCommonFields().Warn(args...)
   185  }
   186  
   187  func (l *PlankLogger) Warnln(args ...interface{}) {
   188  	l.setCommonFields().Warnln(args...)
   189  }
   190  
   191  func (l *PlankLogger) Warnf(format string, args ...interface{}) {
   192  	l.setCommonFields().Warnf(format, args...)
   193  }
   194  
   195  func (l *PlankLogger) Error(args ...interface{}) {
   196  	l.setCommonFields().Error(args...)
   197  }
   198  
   199  func (l *PlankLogger) Errorln(args ...interface{}) {
   200  	l.setCommonFields().Errorln(args...)
   201  }
   202  
   203  func (l *PlankLogger) Errorf(format string, args ...interface{}) {
   204  	l.setCommonFields().Errorf(format, args...)
   205  }
   206  
   207  func (l *PlankLogger) Panic(args ...interface{}) {
   208  	l.setCommonFields().Panic(args...)
   209  }
   210  
   211  func (l *PlankLogger) Panicln(args ...interface{}) {
   212  	l.setCommonFields().Panicln(args...)
   213  }
   214  
   215  func (l *PlankLogger) Panicf(format string, args ...interface{}) {
   216  	l.setCommonFields().Panicf(format, args...)
   217  }
   218  
   219  // noopWriter does absolutely nothing and return immediately. for references
   220  // to those who wonder why this is here then in the first place, this is so
   221  // this no-op writer instance can be passed as an access logger to not bother
   222  // logging HTTP access logs which may not be necessary for some applications
   223  // that want as lowest IO bottlenecks as possible.
   224  type noopWriter struct{}
   225  
   226  func (noopWriter *noopWriter) Write(p []byte) (n int, err error) {
   227  	return 0, nil
   228  }
   229  
   230  func init() {
   231  	Log = &PlankLogger{logrus.New()}
   232  }
   233  
   234  // CreateTextFormatterFromFormatOptions takes *server.LogFormatOption and returns
   235  // the pointer to a new logrus.TextFormatter instance.
   236  func CreateTextFormatterFromFormatOptions(opts *LogFormatOption) *logrus.TextFormatter {
   237  	return &logrus.TextFormatter{
   238  		ForceColors:               opts.ForceColors,
   239  		DisableColors:             opts.DisableColors,
   240  		ForceQuote:                opts.ForceQuote,
   241  		DisableQuote:              opts.DisableQuote,
   242  		EnvironmentOverrideColors: opts.EnvironmentOverrideColors,
   243  		DisableTimestamp:          opts.DisableTimestamp,
   244  		FullTimestamp:             opts.FullTimestamp,
   245  		TimestampFormat:           opts.TimestampFormat,
   246  		DisableSorting:            opts.DisableSorting,
   247  		DisableLevelTruncation:    opts.DisableLevelTruncation,
   248  		PadLevelText:              opts.PadLevelText,
   249  		QuoteEmptyFields:          opts.QuoteEmptyFields,
   250  		FieldMap:                  opts.FieldMap,
   251  	}
   252  }
   253  
   254  // GetNewLogFilePointer returns the pointer to a new os.File instance given the file name
   255  func GetNewLogFilePointer(file string) (*os.File, error) {
   256  	return os.OpenFile(file, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0600)
   257  }