github.com/qioalice/ekago/v3@v3.3.2-0.20221202205325-5c262d586ee4/ekalog/logger.go (about)

     1  // Copyright © 2020. All rights reserved.
     2  // Author: Ilya Stroy.
     3  // Contacts: iyuryevich@pm.me, https://github.com/qioalice
     4  // License: https://opensource.org/licenses/MIT
     5  
     6  package ekalog
     7  
     8  import (
     9  	"fmt"
    10  	"time"
    11  
    12  	"github.com/qioalice/ekago/v3/internal/ekaclike"
    13  	"github.com/qioalice/ekago/v3/internal/ekaletter"
    14  )
    15  
    16  type (
    17  	// Logger is used to generate and write log messages.
    18  	//
    19  	// You can instantiate as many your own loggers with different behaviour,
    20  	// different Integrator, as you want.
    21  	// But also you can just use package level logger, modify it and configure it
    22  	// the same way as any instantiated Logger object.
    23  	//
    24  	// Inheritance.
    25  	//
    26  	// Remember!
    27  	// No one func or method does not change the current object.
    28  	// They always creates and returns a copy of the current object with applied
    29  	// your changes except in cases in which the opposite is not explicitly indicated.
    30  	//
    31  	// Of course you can chain all setters and then call log message generator, like this:
    32  	//
    33  	// 		WithString("key", "value").Warn("It's dangerous!")
    34  	//
    35  	// But in that case, after finishing execution of second line,
    36  	// 'log' variable won't contain added field "key".
    37  	// Want a different behaviour and want to have Logger with these fields?
    38  	// No problem, save generated Logger object:
    39  	//
    40  	// 		log := WithString("key", "value")
    41  	// 		log.Warn("It's dangerous!")
    42  	//
    43  	// Because of all finishers (methods that actually writes a log message, e.g:
    44  	// Debug, Debugf, Debugw, Warn, Warnf, Warnw, etc...) also returns a Logger
    45  	// object that is used to generate log Entry, you can save it too, and finally
    46  	// it's the same as in the example above:
    47  	//
    48  	// 		log := log.WithString("key", "value").Warn("It's dangerous!")
    49  	//
    50  	// but it's strongly not recommended to do so, because it made code less clear.
    51  	Logger struct {
    52  
    53  		// integrator is the way how Entry will be encoded and what encoded Entry
    54  		// will be written to.
    55  		integrator Integrator
    56  
    57  		// entry is _WHAT_ log message is.
    58  		// entry is it's stacktrace, caller info, timestamp, level, message, group,
    59  		// flags, etc.
    60  		entry *Entry
    61  	}
    62  )
    63  
    64  // ------------------------------ COMMON METHODS ------------------------------ //
    65  // ---------------------------------------------------------------------------- //
    66  
    67  // IsValid reports whether current Logger is valid.
    68  // Nil safe.
    69  //
    70  // It returns false if Logger is nil or has not been initialized properly
    71  // (instantiated manually instead of Logger's constructors calling).
    72  func (l *Logger) IsValid() bool {
    73  
    74  	// Integrator and Entry (if they're not nil) can not be invalid (internally)
    75  	// because they are created only by internal functions and they're private.
    76  	// So 3 nil checks are enough here and of course check ptr equal.
    77  
    78  	return l != nil && l.integrator != nil && l.entry != nil && l == l.entry.l
    79  }
    80  
    81  // Copy returns a copy of the current Logger. Does nothing for 'nopLogger'.
    82  //
    83  // Copy is useful when you need to build your Entry step-by-step,
    84  // adding fields, messages, etc.
    85  func (l *Logger) Copy() (copy *Logger) {
    86  	l.assert()
    87  	if l == nopLogger {
    88  		return nopLogger
    89  	}
    90  	return l.derive()
    91  }
    92  
    93  // Sync forces to flush all Integrator buffers of current Logger
    94  // and makes sure all pending Entry are written.
    95  // Nil safe.
    96  //
    97  // Requirements:
    98  //   - Logger != nil, panic otherwise;
    99  //   - Logger is initialized properly and has registered Integrator, panic otherwise.
   100  func (l *Logger) Sync() error {
   101  	l.assert()
   102  	if l == nopLogger {
   103  		return nil
   104  	}
   105  	return l.integrator.Sync()
   106  }
   107  
   108  // --------------------------- FIELDS ADDING METHODS -------------------------- //
   109  // ---------------------------------------------------------------------------- //
   110  
   111  // Methods below are code-generated.
   112  
   113  // With adds presented ekaletter.LetterField to the current Logger if it's addable.
   114  // With DO NOT makes a copy of current Logger and adds field in-place.
   115  // If you need to construct an logger with your own fields that will be used later,
   116  // you need to call Copy() manually before.
   117  func (l *Logger) With(f ekaletter.LetterField) *Logger {
   118  	return l.addField(f)
   119  }
   120  
   121  func (l *Logger) WithBool(key string, value bool) *Logger {
   122  	return l.addField(ekaletter.FBool(key, value))
   123  }
   124  func (l *Logger) WithInt(key string, value int) *Logger {
   125  	return l.addField(ekaletter.FInt(key, value))
   126  }
   127  func (l *Logger) WithInt8(key string, value int8) *Logger {
   128  	return l.addField(ekaletter.FInt8(key, value))
   129  }
   130  func (l *Logger) WithInt16(key string, value int16) *Logger {
   131  	return l.addField(ekaletter.FInt16(key, value))
   132  }
   133  func (l *Logger) WithInt32(key string, value int32) *Logger {
   134  	return l.addField(ekaletter.FInt32(key, value))
   135  }
   136  func (l *Logger) WithInt64(key string, value int64) *Logger {
   137  	return l.addField(ekaletter.FInt64(key, value))
   138  }
   139  func (l *Logger) WithUint(key string, value uint) *Logger {
   140  	return l.addField(ekaletter.FUint(key, value))
   141  }
   142  func (l *Logger) WithUint8(key string, value uint8) *Logger {
   143  	return l.addField(ekaletter.FUint8(key, value))
   144  }
   145  func (l *Logger) WithUint16(key string, value uint16) *Logger {
   146  	return l.addField(ekaletter.FUint16(key, value))
   147  }
   148  func (l *Logger) WithUint32(key string, value uint32) *Logger {
   149  	return l.addField(ekaletter.FUint32(key, value))
   150  }
   151  func (l *Logger) WithUint64(key string, value uint64) *Logger {
   152  	return l.addField(ekaletter.FUint64(key, value))
   153  }
   154  func (l *Logger) WithUintptr(key string, value uintptr) *Logger {
   155  	return l.addField(ekaletter.FUintptr(key, value))
   156  }
   157  func (l *Logger) WithFloat32(key string, value float32) *Logger {
   158  	return l.addField(ekaletter.FFloat32(key, value))
   159  }
   160  func (l *Logger) WithFloat64(key string, value float64) *Logger {
   161  	return l.addField(ekaletter.FFloat64(key, value))
   162  }
   163  func (l *Logger) WithComplex64(key string, value complex64) *Logger {
   164  	return l.addField(ekaletter.FComplex64(key, value))
   165  }
   166  func (l *Logger) WithComplex128(key string, value complex128) *Logger {
   167  	return l.addField(ekaletter.FComplex128(key, value))
   168  }
   169  func (l *Logger) WithString(key string, value string) *Logger {
   170  	return l.addField(ekaletter.FString(key, value))
   171  }
   172  func (l *Logger) WithStringFromBytes(key string, value []byte) *Logger {
   173  	return l.addField(ekaletter.FStringFromBytes(key, value))
   174  }
   175  func (l *Logger) WithBoolp(key string, value *bool) *Logger {
   176  	return l.addField(ekaletter.FBoolp(key, value))
   177  }
   178  func (l *Logger) WithIntp(key string, value *int) *Logger {
   179  	return l.addField(ekaletter.FIntp(key, value))
   180  }
   181  func (l *Logger) WithInt8p(key string, value *int8) *Logger {
   182  	return l.addField(ekaletter.FInt8p(key, value))
   183  }
   184  func (l *Logger) WithInt16p(key string, value *int16) *Logger {
   185  	return l.addField(ekaletter.FInt16p(key, value))
   186  }
   187  func (l *Logger) WithInt32p(key string, value *int32) *Logger {
   188  	return l.addField(ekaletter.FInt32p(key, value))
   189  }
   190  func (l *Logger) WithInt64p(key string, value *int64) *Logger {
   191  	return l.addField(ekaletter.FInt64p(key, value))
   192  }
   193  func (l *Logger) WithUintp(key string, value *uint) *Logger {
   194  	return l.addField(ekaletter.FUintp(key, value))
   195  }
   196  func (l *Logger) WithUint8p(key string, value *uint8) *Logger {
   197  	return l.addField(ekaletter.FUint8p(key, value))
   198  }
   199  func (l *Logger) WithUint16p(key string, value *uint16) *Logger {
   200  	return l.addField(ekaletter.FUint16p(key, value))
   201  }
   202  func (l *Logger) WithUint32p(key string, value *uint32) *Logger {
   203  	return l.addField(ekaletter.FUint32p(key, value))
   204  }
   205  func (l *Logger) WithUint64p(key string, value *uint64) *Logger {
   206  	return l.addField(ekaletter.FUint64p(key, value))
   207  }
   208  func (l *Logger) WithFloat32p(key string, value *float32) *Logger {
   209  	return l.addField(ekaletter.FFloat32p(key, value))
   210  }
   211  func (l *Logger) WithFloat64p(key string, value *float64) *Logger {
   212  	return l.addField(ekaletter.FFloat64p(key, value))
   213  }
   214  func (l *Logger) WithStringp(key string, value *string) *Logger {
   215  	return l.addField(ekaletter.FStringp(key, value))
   216  }
   217  func (l *Logger) WithType(key string, value any) *Logger {
   218  	return l.addField(ekaletter.FType(key, value))
   219  }
   220  func (l *Logger) WithStringer(key string, value fmt.Stringer) *Logger {
   221  	return l.addField(ekaletter.FStringer(key, value))
   222  }
   223  func (l *Logger) WithAddr(key string, value any) *Logger {
   224  	return l.addField(ekaletter.FAddr(key, value))
   225  }
   226  func (l *Logger) WithUnixFromStd(key string, value time.Time) *Logger {
   227  	return l.addField(ekaletter.FUnixFromStd(key, value))
   228  }
   229  func (l *Logger) WithUnixNanoFromStd(key string, value time.Time) *Logger {
   230  	return l.addField(ekaletter.FUnixNanoFromStd(key, value))
   231  }
   232  func (l *Logger) WithUnix(key string, value int64) *Logger {
   233  	return l.addField(ekaletter.FUnix(key, value))
   234  }
   235  func (l *Logger) WithUnixNano(key string, value int64) *Logger {
   236  	return l.addField(ekaletter.FUnixNano(key, value))
   237  }
   238  func (l *Logger) WithDuration(key string, value time.Duration) *Logger {
   239  	return l.addField(ekaletter.FDuration(key, value))
   240  }
   241  func (l *Logger) WithArray(key string, value any) *Logger {
   242  	return l.addField(ekaletter.FArray(key, value))
   243  }
   244  func (l *Logger) WithObject(key string, value any) *Logger {
   245  	return l.addField(ekaletter.FObject(key, value))
   246  }
   247  func (l *Logger) WithMap(key string, value any) *Logger {
   248  	return l.addField(ekaletter.FMap(key, value))
   249  }
   250  func (l *Logger) WithExtractedMap(key string, value map[string]any) *Logger {
   251  	return l.addField(ekaletter.FExtractedMap(key, value))
   252  }
   253  func (l *Logger) WithAny(key string, value any) *Logger {
   254  	return l.addField(ekaletter.FAny(key, value))
   255  }
   256  func (l *Logger) WithMany(fields ...ekaletter.LetterField) *Logger {
   257  	return l.addFields(fields)
   258  }
   259  func (l *Logger) WithManyAny(fields ...any) *Logger {
   260  	return l.addFieldsParse(fields)
   261  }
   262  
   263  // ------------------------ CONDITIONAL LOGGING METHODS ----------------------- //
   264  // ---------------------------------------------------------------------------- //
   265  
   266  // If returns current Logger if cond is true, otherwise nop Logger is returned.
   267  // Thus it's useful to chaining methods - next methods in chaining will be done
   268  // only if cond is true.
   269  //
   270  // Requirements:
   271  //   - Logger is not nil, panic otherwise.
   272  func (l *Logger) If(cond bool) *Logger {
   273  	l.assert()
   274  	if cond {
   275  		return l
   276  	} else {
   277  		return nopLogger
   278  	}
   279  }
   280  
   281  // ------------------------------ UTILITY METHODS ----------------------------- //
   282  // ---------------------------------------------------------------------------- //
   283  
   284  // ReplaceIntegrator replaces Integrator for the current Logger object
   285  // to the passed one.
   286  //
   287  // Requirements:
   288  //   - Logger is not nil, panic otherwise;
   289  //   - Integrator is not nil (even typed nil), panic otherwise;
   290  //   - If Integrator is CommonIntegrator
   291  //     it must not be registered with some Logger before, panic otherwise;
   292  //   - If Integrator is CommonIntegrator
   293  //     it must have at least 1 registered io.Writer, panic otherwise.
   294  //
   295  // WARNING.
   296  // Replacing Integrator will drop all pre-encoded ekaletter.LetterField fields
   297  // that are might be added already to the current Integrator.
   298  func (l *Logger) ReplaceIntegrator(newIntegrator Integrator) {
   299  	l.assert()
   300  	if l == nopLogger {
   301  		return
   302  	}
   303  	if ekaclike.TakeRealAddr(newIntegrator) == nil {
   304  		panic("Failed to change Integrator. New Integrator is nil.")
   305  	}
   306  	if ci, ok := newIntegrator.(*CommonIntegrator); ok {
   307  		ci.build()
   308  	}
   309  	baseLogger.setIntegrator(newIntegrator)
   310  }