logger

package
v0.0.0-...-ceb351d Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 15, 2024 License: Apache-2.0 Imports: 8 Imported by: 0

README

Logger

This is a standard logger implemented using uber's zap package. We have internally dabbled between using logrus and zap and arrived at using zap. Log responsibly :)

Features

  • Supports creating standalone logger and logging
  • Support for adding default keys
  • Support for adding custom keys
  • Support for key value pair chaining
  • Support for creating and fetching from context
  • Built in sentry implementation using zap core
  • Ability to register additional zap cores as extensions
  • Ability to redact value for field based on field passed in secureLogField or fields tagged with piifield:true in struct.

Getting Started

Note: Please refer to the examples folder to see all the use cases
Import the logging package
Create the logger configuration
lgrConfig = logger.Config{
            		LogLevel: logger.Debug, //Look below for the list of debug levels
            		SentryDSN:       "", // In case you want sentry integration, this is the DSN
            		SentryEnabled:   false, // Again, for sentry integration
            		SentryFlushTimeoutInSeconds: 5 // Sentry waits until any buffered events are sent to the Sentry server, blocking for at most the given timeout
            		SentryLogLevel:  "", // For what minimum log level do you want a sentry trigger?
            		ContextString: "context" // Default value. Any additional context variables will have this as the default key. See Section on  additional debugging information below
            	}
Initialize the logger
lgr := logger.NewLogger(lgrConfig)
Starting to log(Standard methods , Debug,Info, Warn, Error, Panic, Fatal)
lgr.Debug("Hello")
...
lgr.Warn("Warning")
...

Usage Examples

Configure with some default keys at bootstrap

defaultLogger := lgr.WithFields(map[string]interface{}{"key1": "value1"})
defaultLogger.Debug("I have the default values for Key1 and Value1")

Configure defaults from golang context

ctxLogger := lgr.WithContext(myctx, []string{"myctxfield1", "myctxfield2")
ctxLogger.Debug("I have default values configured with golang context")

Adding additional custom debugging information

defaultLogger.Debugw("This will log the url too besides key and value above", map[string]interface{}{"url":"http://example.com"})

Field Chaining

lgr := logger.NewLogger(lgrConfig)
defaultLogger := lgr.WithFields(map[string]interface{}{"key1": "value1"}).Withfields(map[string]interface{"key2":"value2"}).Debug("Another message")

With custom error handling

lgr := logger.NewLogger(lgrConfig)
lgr.WithContext(myctx, []string{"myctxfield1", "myctxfield2").WithError(errors.New("Doomed here")).Debug("Error message")

Combining all the above

lgr := logger.NewLogger(lgrConfig)
lgr.WithFields(map[string]interface{}{"key1": "value1"}).Withfields(map[string]interface{"key2":"value2"}).WithError(errors.New("Doomed here")).Debug("Error message")

Set and retrieve logger from context

ctxLogger := lgr.WithContext(myctx, []string{"myctxfield1", "myctxfield2")
ctx := context.WithValue(myctx, logger.LoggerCtxKey, ctxLogger)
......

fromContextLogger, err := logger.Ctx(ctx)
if err != nil {
    fmt.Printf("Error obtaining logger from context")
    os.Exit(1)
}
fromContextLogger.Debug("Logging obtained from existing context")

Apply options to logger

lgr := logger.NewLogger(lgrConfig)

lgr.WithOptions(logger.AddCallerSkip(2)).Debug("Modified logger skip level")

Different log levels for different modules

By default, the entire logger runs at a single logLevel. If you want different modules of the application to run at different log Levels, you can use NewModuleLogger function for passing logger to that module

Here is a sample implementation

config := logger.Config{
  LogLevel:        logger.Error,
  SentryEnabled:   false,
}
outboxLogger, _ := logger.NewModuleLogger(config)
ouboxer := outbox.New(outboxConfig, outboxStore, outboxLogger.WithContext(ctx, nil), mutexClient, encoder, nil)

This way the app can run at info log level whereas the outbox module can run at error level

Suppose the app is running at info log level and for a particular request you want to print the debug logs as well, you can use debug mode. This way all logs are printed irrespective of the log level they are written at.

debugMode := r.Header.Get(DebugModeHeaderKey)
if debugMode == "true" {
  lgr.EnableDebugMode()
}
ctx = context.WithValue(ctx, logger.LoggerCtxKey, lgr)

This should ideally be done in request level middleware or request hooks

TODO

  1. Better mechanism to expose and build custom cores

Documentation

Index

Constants

View Source
const (
	//Debug has verbose message
	Debug = "debug"
	//Info is default log level
	Info = "info"
	//Warn is for logging messages about possible issues
	Warn = "warn"
	//Error is for logging errors
	Error = "error"
	//Fatal is for logging fatal messages. The sytem shutsdown after logging the message.
	Fatal = "fatal"
	//TODO: Should we add panic and DPanic like what zap uses internally?
	//DefaultContextString is for adding extra context
	DefaultContextString = "extra"
	//DefaultFieldConstant for adding existing context
	DefaultFieldConstant = "context"
	//Unique Identifier for context key
	LoggerCtxKey contextKey = iota
	// DefaultStacktraceKey ... default key for stacktrace if none provided in config
	DefaultStacktraceKey = "stacktrace"
	// DefaultStacktraceLevel ... default log level: error for which stacktrace will be added to logs
	DefaultStacktraceLevel = "error"
)

Fields Type to pass when we want to call WithFields for structured logging var DefaultFields = make(map[string]interface{}, 0) var mutex = & sync.RWMutex{}

Variables

View Source
var (

	// Defines the key when adding errors using WithError.
	ErrorKey = "error"

	// DefaultCallerSkip defines the default caller skip setting
	DefaultCallerSkip int = 2
)

Functions

This section is empty.

Types

type Config

type Config struct {
	//LogLevel ... the default log level
	LogLevel string
	//ContextString ... in case of logging extra context information to logs, this should be enabled
	ContextString string
	// StackTraceKey... key for stacktrace data in logs
	StackTraceKey string
	// StackTraceLevel ... log level for & above which stacktrace will be logged
	StackTraceLevel string
	// CallerSkip
	CallerSkip *int
}

Config ... largely kept for extensions and other hooks

type Entry

type Entry struct {
	Logger *ZapLogger

	// Contains all the fields set by the user.
	Data map[string]interface{}

	DebugMode bool
	// contains filtered or unexported fields
}

An entry is the final or intermediate log entry. It contains all the fields passed with WithField{,s}. It's finally logged when Trace, Debug, Info, Warn, Error, Fatal or Panic is called on it. These objects can be reused and passed around as much as you wish to avoid field duplication.

func Ctx

func Ctx(ctx context.Context) (*Entry, error)

Ctx gets logger instance from context if available else creates a new one basic on the existing config and returns a logger object or error

func NewEntry

func NewEntry(logger *ZapLogger) *Entry

func (*Entry) Debug

func (entry *Entry) Debug(format string)

Debug logs a message as is. Default context is added here(if it exists)

func (*Entry) Debugw

func (entry *Entry) Debugw(format string, args map[string]interface{})

Debug logs a message with some additional context. `args` mentioned here as a key value map of extra context logging specific to this log message

func (*Entry) EnableDebugMode

func (entry *Entry) EnableDebugMode() *Entry

func (*Entry) Error

func (entry *Entry) Error(format string)

Error logs a message as is. Default context is added here(if it exists)

func (*Entry) Errorw

func (entry *Entry) Errorw(format string, args map[string]interface{})

Warn logs a message with some additional context. `args` mentioned here as a key value map of extra context logging specific to this log message

func (*Entry) Fatal

func (entry *Entry) Fatal(format string)

Warn logs a message as is. Default context is added here(if it exists)

func (*Entry) Fatalw

func (entry *Entry) Fatalw(format string, args map[string]interface{})

Fatal logs a message with some additional context. `args` mentioned here as a key value map of extra context logging specific to this log message

func (*Entry) Info

func (entry *Entry) Info(format string)

Info logs a message as is. Default context is added here(if it exists)

func (*Entry) Infow

func (entry *Entry) Infow(format string, args map[string]interface{})

Info logs a message with some additional context. `args` mentioned here as a key value map of extra context logging specific to this log message

func (*Entry) Panic

func (entry *Entry) Panic(format string)

Panic logs a message as is. Default context is added here(if it exists)

func (*Entry) Panicw

func (entry *Entry) Panicw(format string, args map[string]interface{})

Panic logs a message with some additional context. `args` mentioned here as a key value map of extra context logging specific to this log message

func (*Entry) Warn

func (entry *Entry) Warn(format string)

Warn logs a message as is. Default context is added here(if it exists)

func (*Entry) Warnw

func (entry *Entry) Warnw(format string, args map[string]interface{})

Warn logs a message with some additional context. `args` mentioned here as a key value map of extra context logging specific to this log message

func (*Entry) WithContext

func (entry *Entry) WithContext(ctx context.Context, ctxFields []string) *Entry

func (*Entry) WithError

func (entry *Entry) WithError(err error) *Entry

Add an error as single field (using the key defined in ErrorKey) to the Entry.

func (*Entry) WithField

func (entry *Entry) WithField(key string, value interface{}) *Entry

Add a single field to the Entry.

func (*Entry) WithFields

func (entry *Entry) WithFields(fields map[string]interface{}) *Entry

Add a map of fields to the Entry.

type MutexWrap

type MutexWrap struct {
	// contains filtered or unexported fields
}

func (*MutexWrap) Disable

func (mw *MutexWrap) Disable()

func (*MutexWrap) Lock

func (mw *MutexWrap) Lock()

func (*MutexWrap) Unlock

func (mw *MutexWrap) Unlock()

type Option

type Option interface {
	// contains filtered or unexported methods
}

An Option configures a ZapLogger.

func AddCallerSkip

func AddCallerSkip(skip int) Option

AddCallerSkip increases the number of callers skipped by caller annotation It adds skip level to the current caller skip level.

type ZapLogger

type ZapLogger struct {
	// contains filtered or unexported fields
}

ZapLogger ... struct for handling the logger and associated methods. Kept public to ensure it can be accessible from tests

func NewLogger

func NewLogger(c Config) (*ZapLogger, error)

NewLogger sets up an instance of the logger or returns error if unable to

func NewModuleLogger

func NewModuleLogger(c Config) (*ZapLogger, error)

NewModuleLogger This is used when you want different loggers at different log levels for different modules

func (*ZapLogger) Debug

func (l *ZapLogger) Debug(format string)

func (*ZapLogger) Debugw

func (l *ZapLogger) Debugw(format string, args map[string]interface{})

Wrapper Functions over entry

func (*ZapLogger) Fatal

func (l *ZapLogger) Fatal(format string)

func (*ZapLogger) Fatalw

func (l *ZapLogger) Fatalw(format string, args map[string]interface{})

func (*ZapLogger) Info

func (l *ZapLogger) Info(format string)

func (*ZapLogger) Infow

func (l *ZapLogger) Infow(format string, args map[string]interface{})

func (*ZapLogger) Panic

func (l *ZapLogger) Panic(format string)

func (*ZapLogger) Panicw

func (l *ZapLogger) Panicw(format string, args map[string]interface{})

func (*ZapLogger) Warn

func (l *ZapLogger) Warn(format string)

func (*ZapLogger) Warnw

func (l *ZapLogger) Warnw(format string, args map[string]interface{})

func (*ZapLogger) WithContext

func (logger *ZapLogger) WithContext(ctx context.Context, ctxFields []string) *Entry

WithContext returns an instance of the logger with the supplied context populated

func (*ZapLogger) WithError

func (logger *ZapLogger) WithError(err error) *Entry

WithError adds an error as single field (using the key defined in ErrorKey) to the Entry.

func (*ZapLogger) WithField

func (logger *ZapLogger) WithField(key string, value interface{}) *Entry

Adds a field to the log entry, note that it doesn't log until you call Debug, Print, Info, Warn, Error, Fatal or Panic. It only creates a log entry. If you want multiple fields, use `WithFields`.

func (*ZapLogger) WithFields

func (logger *ZapLogger) WithFields(fields map[string]interface{}) *Entry

WithFields takes default extra context that needs to be attached to every other log message and adds it as part of a default map from where it is added through every through other log method like Debug, Info etc

func (*ZapLogger) WithOptions

func (logger *ZapLogger) WithOptions(opts ...Option) *ZapLogger

WithOptions applies the given Options and returns a new instance of Logger

Directories

Path Synopsis
examples
basic command
with_context command

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL