github.com/rudderlabs/rudder-go-kit@v0.30.0/logger/logger.go (about)

     1  // Package logger
     2  /*
     3  Logger Interface Use instance of logger instead of exported functions
     4  
     5  usage example
     6  
     7     import (
     8  	   "errors"
     9  	   "github.com/rudderlabs/rudder-go-kit/config"
    10  	   "github.com/rudderlabs/rudder-go-kit/logger"
    11     )
    12  
    13     var c = config.New()
    14     var loggerFactory = logger.NewFactory(c)
    15     var log logger.Logger = loggerFactory.NewLogger()
    16     ...
    17     log.Error(...)
    18  
    19  or if you want to use the default logger factory (not advised):
    20  
    21     var log logger.Logger = logger.NewLogger()
    22     ...
    23     log.Error(...)
    24  
    25  */
    26  //go:generate mockgen -destination=mock_logger/mock_logger.go -package mock_logger github.com/rudderlabs/rudder-go-kit/logger Logger
    27  package logger
    28  
    29  import (
    30  	"bytes"
    31  	"io"
    32  	"net/http"
    33  	"runtime"
    34  	"strings"
    35  
    36  	"go.uber.org/zap"
    37  )
    38  
    39  /*
    40  Using levels(like Debug, Info etc.) in logging is a way to categorize logs based on their importance.
    41  The idea is to have the option of running the application in different logging levels based on
    42  how verbose we want the logging to be.
    43  For example, using Debug level of logging, logs everything, and it might slow the application, so we run application
    44  in DEBUG level for local development or when we want to look through the entire flow of events in detail.
    45  We use 4 logging levels here Debug, Info, Warn and Error.
    46  */
    47  
    48  type Logger interface {
    49  	// IsDebugLevel Returns true is debug lvl is enabled
    50  	IsDebugLevel() bool
    51  
    52  	// Debug level logging. Most verbose logging level.
    53  	Debug(args ...any)
    54  
    55  	// Debugf does debug level logging similar to fmt.Printf. Most verbose logging level
    56  	Debugf(format string, args ...any)
    57  
    58  	// Debugw does debug level structured logging. Most verbose logging level
    59  	Debugw(msg string, keysAndValues ...any)
    60  
    61  	// Debugn does debug level non-sugared structured logging. Most verbose logging level
    62  	Debugn(msg string, fields ...Field)
    63  
    64  	// Info level logging. Use this to log the state of the application.
    65  	// Don't use Logger.Info in the flow of individual events. Use Logger.Debug instead.
    66  	Info(args ...any)
    67  
    68  	// Infof does info level logging similar to fmt.Printf. Use this to log the state of the application.
    69  	// Don't use Logger.Info in the flow of individual events. Use Logger.Debug instead.
    70  	Infof(format string, args ...any)
    71  
    72  	// Infow does info level structured logging. Use this to log the state of the application.
    73  	// Don't use Logger.Info in the flow of individual events. Use Logger.Debug instead.
    74  	Infow(msg string, keysAndValues ...any)
    75  
    76  	// Infon does info level non-sugared structured logging. Use this to log the state of the application.
    77  	// Don't use Logger.Info in the flow of individual events. Use Logger.Debug instead.
    78  	Infon(msg string, fields ...Field)
    79  
    80  	// Warn level logging. Use this to log warnings
    81  	Warn(args ...any)
    82  
    83  	// Warnf does warn level logging similar to fmt.Printf. Use this to log warnings
    84  	Warnf(format string, args ...any)
    85  
    86  	// Warnw does warn level structured logging. Use this to log warnings
    87  	Warnw(msg string, keysAndValues ...any)
    88  
    89  	// Warnn does warn level non-sugared structured logging. Use this to log warnings
    90  	Warnn(msg string, fields ...Field)
    91  
    92  	// Error level logging. Use this to log errors which don't immediately halt the application.
    93  	Error(args ...any)
    94  
    95  	// Errorf does error level logging similar to fmt.Printf. Use this to log errors which don't immediately halt the application.
    96  	Errorf(format string, args ...any)
    97  
    98  	// Errorw does error level structured logging.
    99  	// Use this to log errors which don't immediately halt the application.
   100  	Errorw(msg string, keysAndValues ...any)
   101  
   102  	// Errorn does error level non-sugared structured logging.
   103  	// Use this to log errors which don't immediately halt the application.
   104  	Errorn(msg string, fields ...Field)
   105  
   106  	// Fatal level logging. Use this to log errors which crash the application.
   107  	Fatal(args ...any)
   108  
   109  	// Fatalf does fatal level logging similar to fmt.Printf. Use this to log errors which crash the application.
   110  	Fatalf(format string, args ...any)
   111  
   112  	// Fatalw does fatal level structured logging.
   113  	// Use this to log errors which crash the application.
   114  	Fatalw(format string, keysAndValues ...any)
   115  
   116  	// Fataln does fatal level non-sugared structured logging.
   117  	// Use this to log errors which crash the application.
   118  	Fataln(format string, fields ...Field)
   119  
   120  	LogRequest(req *http.Request)
   121  
   122  	// Child creates a child logger with the given name
   123  	Child(s string) Logger
   124  
   125  	// With adds the provided key value pairs to the logger context
   126  	With(args ...any) Logger
   127  
   128  	// Withn adds the provided key value pairs to the logger context
   129  	Withn(args ...Field) Logger
   130  }
   131  
   132  type logger struct {
   133  	logConfig  *factoryConfig
   134  	name       string
   135  	zap        *zap.Logger
   136  	sugaredZap *zap.SugaredLogger
   137  	parent     *logger
   138  }
   139  
   140  func (l *logger) Child(s string) Logger {
   141  	if s == "" {
   142  		return l
   143  	}
   144  	cp := *l
   145  	cp.parent = l
   146  	if l.name == "" {
   147  		cp.name = s
   148  	} else {
   149  		cp.name = strings.Join([]string{l.name, s}, ".")
   150  	}
   151  	if l.logConfig.enableNameInLog {
   152  		cp.sugaredZap = l.sugaredZap.Named(s)
   153  		cp.zap = l.zap.Named(s)
   154  	}
   155  	return &cp
   156  }
   157  
   158  // With adds a variadic number of fields to the logging context.
   159  // It accepts a mix of strongly-typed Field objects and loosely-typed key-value pairs.
   160  // When processing pairs, the first element of the pair is used as the field key and the second as the field value.
   161  func (l *logger) With(args ...any) Logger {
   162  	cp := *l
   163  	cp.sugaredZap = cp.sugaredZap.With(args...)
   164  	for i := 0; i < len(args)-1; i += 2 {
   165  		key, ok := args[i].(string)
   166  		if !ok {
   167  			cp.Warnw("Logger.With called with non-string key",
   168  				"parentName", l.parent.name, "name", l.name,
   169  			)
   170  			break
   171  		}
   172  		cp.zap = cp.zap.With(zap.Any(key, args[i+1]))
   173  	}
   174  	return &cp
   175  }
   176  
   177  // Withn adds a variadic number of fields to the logging context.
   178  // It accepts a mix of strongly-typed Field objects and loosely-typed key-value pairs.
   179  // When processing pairs, the first element of the pair is used as the field key and the second as the field value.
   180  func (l *logger) Withn(args ...Field) Logger {
   181  	cp := *l
   182  	cp.zap = l.zap.With(toZap(args)...)
   183  	return &cp
   184  }
   185  
   186  func (l *logger) getLoggingLevel() int {
   187  	return l.logConfig.getOrSetLogLevel(l.name, l.parent.getLoggingLevel)
   188  }
   189  
   190  // IsDebugLevel Returns true is debug lvl is enabled
   191  func (l *logger) IsDebugLevel() bool {
   192  	return levelDebug >= l.getLoggingLevel()
   193  }
   194  
   195  // Debug level logging.
   196  // Most verbose logging level.
   197  func (l *logger) Debug(args ...any) {
   198  	if levelDebug >= l.getLoggingLevel() {
   199  		l.sugaredZap.Debug(args...)
   200  	}
   201  }
   202  
   203  // Info level logging.
   204  // Use this to log the state of the application. Don't use Logger.Info in the flow of individual events. Use Logger.Debug instead.
   205  func (l *logger) Info(args ...any) {
   206  	if levelInfo >= l.getLoggingLevel() {
   207  		l.sugaredZap.Info(args...)
   208  	}
   209  }
   210  
   211  // Warn level logging.
   212  // Use this to log warnings
   213  func (l *logger) Warn(args ...any) {
   214  	if levelWarn >= l.getLoggingLevel() {
   215  		l.sugaredZap.Warn(args...)
   216  	}
   217  }
   218  
   219  // Error level logging.
   220  // Use this to log errors which don't immediately halt the application.
   221  func (l *logger) Error(args ...any) {
   222  	if levelError >= l.getLoggingLevel() {
   223  		l.sugaredZap.Error(args...)
   224  	}
   225  }
   226  
   227  // Fatal level logging.
   228  // Use this to log errors which crash the application.
   229  func (l *logger) Fatal(args ...any) {
   230  	l.sugaredZap.Error(args...)
   231  
   232  	// If enableStackTrace is true, Zaplogger will take care of writing stacktrace to the file.
   233  	// Else, we are force writing the stacktrace to the file.
   234  	if !l.logConfig.enableStackTrace.Load() {
   235  		byteArr := make([]byte, 2048)
   236  		n := runtime.Stack(byteArr, false)
   237  		stackTrace := string(byteArr[:n])
   238  		l.sugaredZap.Error(stackTrace)
   239  	}
   240  	_ = l.sugaredZap.Sync()
   241  }
   242  
   243  // Debugf does debug level logging similar to fmt.Printf.
   244  // Most verbose logging level
   245  func (l *logger) Debugf(format string, args ...any) {
   246  	if levelDebug >= l.getLoggingLevel() {
   247  		l.sugaredZap.Debugf(format, args...)
   248  	}
   249  }
   250  
   251  // Infof does info level logging similar to fmt.Printf.
   252  // Use this to log the state of the application. Don't use Logger.Info in the flow of individual events. Use Logger.Debug instead.
   253  func (l *logger) Infof(format string, args ...any) {
   254  	if levelInfo >= l.getLoggingLevel() {
   255  		l.sugaredZap.Infof(format, args...)
   256  	}
   257  }
   258  
   259  // Warnf does warn level logging similar to fmt.Printf.
   260  // Use this to log warnings
   261  func (l *logger) Warnf(format string, args ...any) {
   262  	if levelWarn >= l.getLoggingLevel() {
   263  		l.sugaredZap.Warnf(format, args...)
   264  	}
   265  }
   266  
   267  // Errorf does error level logging similar to fmt.Printf.
   268  // Use this to log errors which don't immediately halt the application.
   269  func (l *logger) Errorf(format string, args ...any) {
   270  	if levelError >= l.getLoggingLevel() {
   271  		l.sugaredZap.Errorf(format, args...)
   272  	}
   273  }
   274  
   275  // Fatalf does fatal level logging similar to fmt.Printf.
   276  // Use this to log errors which crash the application.
   277  func (l *logger) Fatalf(format string, args ...any) {
   278  	l.sugaredZap.Errorf(format, args...)
   279  
   280  	// If enableStackTrace is true, Zaplogger will take care of writing stacktrace to the file.
   281  	// Else, we are force writing the stacktrace to the file.
   282  	if !l.logConfig.enableStackTrace.Load() {
   283  		byteArr := make([]byte, 2048)
   284  		n := runtime.Stack(byteArr, false)
   285  		stackTrace := string(byteArr[:n])
   286  		l.sugaredZap.Error(stackTrace)
   287  	}
   288  	_ = l.sugaredZap.Sync()
   289  }
   290  
   291  // Debugw does debug level structured logging.
   292  // Most verbose logging level
   293  func (l *logger) Debugw(msg string, keysAndValues ...any) {
   294  	if levelDebug >= l.getLoggingLevel() {
   295  		l.sugaredZap.Debugw(msg, keysAndValues...)
   296  	}
   297  }
   298  
   299  // Infow does info level structured logging.
   300  // Use this to log the state of the application. Don't use Logger.Info in the flow of individual events. Use Logger.Debug instead.
   301  func (l *logger) Infow(msg string, keysAndValues ...any) {
   302  	if levelInfo >= l.getLoggingLevel() {
   303  		l.sugaredZap.Infow(msg, keysAndValues...)
   304  	}
   305  }
   306  
   307  // Warnw does warn level structured logging.
   308  // Use this to log warnings
   309  func (l *logger) Warnw(msg string, keysAndValues ...any) {
   310  	if levelWarn >= l.getLoggingLevel() {
   311  		l.sugaredZap.Warnw(msg, keysAndValues...)
   312  	}
   313  }
   314  
   315  // Errorw does error level structured logging.
   316  // Use this to log errors which don't immediately halt the application.
   317  func (l *logger) Errorw(msg string, keysAndValues ...any) {
   318  	if levelError >= l.getLoggingLevel() {
   319  		l.sugaredZap.Errorw(msg, keysAndValues...)
   320  	}
   321  }
   322  
   323  // Fatalw does fatal level structured logging.
   324  // Use this to log errors which crash the application.
   325  func (l *logger) Fatalw(msg string, keysAndValues ...any) {
   326  	l.sugaredZap.Errorw(msg, keysAndValues...)
   327  
   328  	// If enableStackTrace is true, Zaplogger will take care of writing stacktrace to the file.
   329  	// Else, we are force writing the stacktrace to the file.
   330  	if !l.logConfig.enableStackTrace.Load() {
   331  		byteArr := make([]byte, 2048)
   332  		n := runtime.Stack(byteArr, false)
   333  		stackTrace := string(byteArr[:n])
   334  		l.sugaredZap.Error(stackTrace)
   335  	}
   336  	_ = l.sugaredZap.Sync()
   337  }
   338  
   339  // Debugn does debug level non-sugared structured logging.
   340  func (l *logger) Debugn(msg string, fields ...Field) {
   341  	if levelDebug >= l.getLoggingLevel() {
   342  		l.zap.Debug(msg, toZap(fields)...)
   343  	}
   344  }
   345  
   346  // Infon does info level non-sugared structured logging.
   347  // Use this to log the state of the application.
   348  // Don't use Logger.Info in the flow of individual events. Use Logger.Debug instead.
   349  func (l *logger) Infon(msg string, fields ...Field) {
   350  	if levelInfo >= l.getLoggingLevel() {
   351  		l.zap.Info(msg, toZap(fields)...)
   352  	}
   353  }
   354  
   355  // Warnn does warn level non-sugared structured logging.
   356  // Use this to log warnings
   357  func (l *logger) Warnn(msg string, fields ...Field) {
   358  	if levelWarn >= l.getLoggingLevel() {
   359  		l.zap.Warn(msg, toZap(fields)...)
   360  	}
   361  }
   362  
   363  // Errorn does error level non-sugared structured logging.
   364  // Use this to log errors which don't immediately halt the application.
   365  func (l *logger) Errorn(msg string, fields ...Field) {
   366  	if levelError >= l.getLoggingLevel() {
   367  		l.zap.Error(msg, toZap(fields)...)
   368  	}
   369  }
   370  
   371  // Fataln does fatal level non-sugared structured logging.
   372  // Use this to log errors which crash the application.
   373  func (l *logger) Fataln(msg string, fields ...Field) {
   374  	zapFields := toZap(fields)
   375  	l.zap.Error(msg, zapFields...)
   376  
   377  	// If enableStackTrace is true, Zaplogger will take care of writing stacktrace to the file.
   378  	// Else, we are force writing the stacktrace to the file.
   379  	if !l.logConfig.enableStackTrace.Load() {
   380  		byteArr := make([]byte, 2048)
   381  		n := runtime.Stack(byteArr, false)
   382  		stackTrace := string(byteArr[:n])
   383  		l.zap.Error(stackTrace, zapFields...)
   384  	}
   385  	_ = l.zap.Sync()
   386  }
   387  
   388  // LogRequest reads and logs the request body and resets the body to original state.
   389  func (l *logger) LogRequest(req *http.Request) {
   390  	if levelEvent >= l.getLoggingLevel() {
   391  		defer func() { _ = req.Body.Close() }()
   392  		bodyBytes, _ := io.ReadAll(req.Body)
   393  		bodyString := string(bodyBytes)
   394  		req.Body = io.NopCloser(bytes.NewBuffer(bodyBytes))
   395  		// print raw request body for debugging purposes
   396  		l.zap.Debug("Request Body", zap.String("body", bodyString))
   397  	}
   398  }