log

package module
v1.2.0 Latest Latest
Warning

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

Go to latest
Published: Jan 18, 2023 License: MIT Imports: 6 Imported by: 0

README

log

This is an opinionated, "batteries included" log package that caters to how I think logging should happen. The functionalities that are being included are:

  • rudimentary configuration
  • structured logging
  • providing PII in log fields with the ability to apply hashing or other means to it or remove them altogether

For further info, have a look at the examples section.

Warning: When using the plain logging functions of the library instead of instantiating a logger, you have no configuration options and no way to handle PII in any special or different way.

Base parameters

  • Log levels:
    • Debug
    • Info
    • Warn
    • Error
    • Panic
    • Fatal
  • Timestamp format: RFC 3339
  • Log format: JSON
  • Output destinations: stdout, stderr or split between the two at warn level (everything below to stdout all else to stderr)
  • Caller info: included
  • Stacktrace: only enabled for warn and above
  • Key names:
    • Application name key: "app" (set by user)
    • Version key: "version" (set by user)
    • Message key: "msg"
    • Level key: "lvl"
    • Time key: "ts"
    • Name key: "name"
    • Caller key: "caller"
    • Function key: "func"
    • Stacktrace key: "stacktrace"
  • Available modes for dealing with PII:
    • none (leaves fields as is)
    • hash (hashes the value with SHA256)
    • mask (uses a custom mask function to mask values -- mask function needs to be provided by the user, when choosing this mode -- log.MaskFunc)
    • remove (removes the whole field from logs)

Examples

Instantiate the most basic logger

package main

import "github.com/Rapix-x/log"

func main() {
	// There will be no "app" or "version" field on the logger/the logs
	// The implied log level is "info"
	// The implied PII mode is "none", so PII will be logged as is
	logger := log.MustNewLogger(log.Configuration{})
    defer logger.Sync()
	
    logger.Info("log something")
	// output: {"lvl":"info","ts":"1970-01-01T04:02:00+01:00","caller":"main/main.go:12","func":"main.main","msg":"log something"}
}

Instantiate a production logger

package main

import "github.com/Rapix-x/log"

func main() {
	logger, err := log.NewLogger(log.Configuration{
        ApplicationName: "example-app",
        Version:         "1.0.0",
        MinimumLogLevel: log.WarnLevel,
        PIIMode:         log.PIIModeRemove,
    }) 
    if err != nil {
        log.Fatalf("error occurred while instantiating new logger: %v", err)
    }
    defer logger.Sync()

    logger.Warn("Log something")
	// output: {"lvl":"warn","ts":"1970-01-01T04:02:00+01:00","caller":"main/main.go:19","func":"main.main","msg":"Log something","app":"example-app",
	// "version":"1.0.0","stacktrace":"main.main\n\t/log/main/main.go:17\nruntime.main\n\t/opt/homebrew/Cellar/go/1.19.3/libexec/src/runtime/proc.go:250"}
}

Configuration

As seen in the examples above, the actual configuration of a logger only has four properties to configure.

package main

import "github.com/Rapix-x/log"

func main() {
    conf := log.Configuration{
        ApplicationName: "example-app",
        Version:         "1.0.0",
        MinimumLogLevel: log.WarnLevel,
        PIIMode:         log.PIIModeRemove,
    }
}

PII Mode and beyond

This package provides the capability to handle PII in any logs. All you have to do is to attach the PII as a field using log statements with fields and wrap it with the provided functions. Below are three examples that show the vanilla PII functionality, how to provide a custom function when selecting the PIIModeMask and lastly some PII handling with a custom function just for one data set.

Vanilla PII Handling
package main

import "github.com/Rapix-x/log"

func main() {
	logger := log.MustNewLogger(log.Configuration{
        PIIMode: log.PIIModeHash,
    })
    defer logger.Sync()
	
    logger.Infow("Log PII fields", log.PII("username", "abc@example.com"))
	// output: {"lvl":"info","ts":"1970-01-01T04:02:00+01:00","caller":"main/main.go:14","func":"main.main","msg":"Log PII fields","username":"9eceb13483d7f187ec014fd6d4854d1420cfc634328af85f51d0323ba8622e21"}
}
Custom Function for "mask" PII Mode
package main

import "github.com/Rapix-x/log"

func main() {
  log.MaskFunc = maskIt
  logger := log.MustNewLogger(log.Configuration{
    PIIMode: log.PIIModeMask,
  })
  defer logger.Sync()

  logger.Infow("Log PII fields", log.PII("usernam", "abc@example.com"))
  // output: {"lvl":"info","ts":"1970-01-01T04:02:00+01:00","caller":"main/main.go:12","func":"main.main","msg":"Log PII fields","username":"gotcha value, hehe"}
}

func maskIt(key, value string) log.ResolvedPIIField {
  field := log.ResolvedPIIField{}
  field.Key = key + "e" // let's fix our typo here, shall we?
  field.Value = "gotcha value, hehe"

  return field
}
Custom Function for Single PII Field
package main

import "github.com/Rapix-x/log"

func main() {
  logger := log.MustNewLogger(log.Configuration{
    PIIMode: log.PIIModeHash,
  })
  defer logger.Sync()

  logger.Infow("Log PII fields", log.CustomPII("username", "abc@example.com", singleFieldMask))
  // output: {"lvl":"info","ts":"1970-01-01T04:02:00+01:00","caller":"main/main.go:11","func":"main.main","msg":"Log PII fields","username":"let's assume this is a hash *coughs in hex*"}
}

func singleFieldMask(mode log.PIIMode, key, value string) log.ResolvedPIIField {
  switch mode {
  case log.PIIModeHash:
    return log.ResolvedPIIField{Key: key, Value: "let's assume this is a hash *coughs in hex*"}
  case log.PIIModeMask:
    return log.ResolvedPIIField{Key: key, Value: "this one is masked"}
  case log.PIIModeRemove:
    return log.ResolvedPIIField{}
  default:
    return log.ResolvedPIIField{Key: key, Value: value}
  }
}

Documentation

Index

Constants

View Source
const (
	DebugLevel = Level(zapcore.DebugLevel)
	InfoLevel  = Level(zapcore.InfoLevel)
	WarnLevel  = Level(zapcore.WarnLevel)
	ErrorLevel = Level(zapcore.ErrorLevel)
	PanicLevel = Level(zapcore.PanicLevel)
	FatalLevel = Level(zapcore.FatalLevel)
)

Variables

View Source
var (

	// MaskFunc gets called on PII resolvers, when PII mode "mask" is chosen.
	// The function shall be thread-safe. When no function is provided, but
	// the mask PII mode is chosen, any PII fields will be omitted.
	MaskFunc func(key, value string) ResolvedPIIField
)

Functions

func CustomPII

func CustomPII(key, value string, resolveFunc CustomResolveFunc) *customPIIField

CustomPII is used to create a PII field with a custom resolve function of type CustomResolveFunc. When the field gets logged the actual PII shall be handled appropriately by the custom function based on the given PII mode from the logger. The custom resolve function shall be thread-safe.

func Debug

func Debug(v ...any)

Debug logs all inputs on the debug level.

func Debugf

func Debugf(format string, v ...any)

Debugf formats and logs all inputs on the debug level.

func Debugw

func Debugw(msg string, keyValuePairs ...any)

Debugw logs all inputs and fields on the debug level.

func Error

func Error(v ...any)

Error logs all inputs on the error level.

func Errorf

func Errorf(format string, v ...any)

Errorf formats and logs all inputs on the error level.

func Errorw

func Errorw(msg string, keyValuePairs ...any)

Errorw logs all inputs and fields on the error level.

func Fatal

func Fatal(v ...any)

Fatal logs all inputs on the fatal level and runs os.exit(1) at the end.

func Fatalf

func Fatalf(format string, v ...any)

Fatalf formats and logs all inputs on the fatal level and runs os.exit(1) at the end.

func Fatalw

func Fatalw(msg string, keyValuePairs ...any)

Fatalw logs all inputs and fields on the fatal level and runs os.exit(1) at the end.

func Info

func Info(v ...any)

Info logs all inputs on the info level.

func Infof

func Infof(format string, v ...any)

Infof formats and logs all inputs on the info level.

func Infow

func Infow(msg string, keyValuePairs ...any)

Infow logs all inputs and fields on the info level.

func PII

func PII(key, value string) *field

PII is used to create standard PII field. When the field gets logged the actual PII is handled based on the current PII mode of the logger.

func Sync

func Sync() error

func Warn

func Warn(v ...any)

Warn logs all inputs on the warn level.

func Warnf

func Warnf(format string, v ...any)

Warnf formats and logs all inputs on the warn level.

func Warnw

func Warnw(msg string, keyValuePairs ...any)

Warnw logs all inputs and fields on the warn level.

Types

type Configuration

type Configuration struct {
	// ApplicationName holds the value for the "app" field in log
	// statements indicating the name of the current application.
	// If the value is set to "", the field will be omitted.
	ApplicationName string

	// Version holds the value for the "version" field in log
	// statements indicating the version of the current application.
	// If the value is set to "", the field will be omitted.
	Version string

	// MinimumLogLevel sets the minim level of logs that will get
	// logged by the respective logger. The DebugLevel is the lowest
	// while the FatalLevel is the highest. If set to Debug, everything
	// will be logged, while when set to Fatal, only Fatal statements
	// will be logged.
	MinimumLogLevel Level

	// PIIMode indicates how to the logger resolves PII fields in log
	// statements.
	PIIMode PIIMode

	// OutputMode indicates where the logs will be written. Logs can
	// either be published to stdout, stderr or split between the two.
	OutputMode OutputMode

	// KeyNames lets you overwrite the standard key names for common
	// log fields.
	KeyNames KeyNames
}

Configuration represents a Configuration object for a logger.

type CustomResolveFunc

type CustomResolveFunc func(mode PIIMode, key, value string) ResolvedPIIField

The CustomResolveFunc is passed to the CustomPII function of this package to handle the PII resolution in a customised way before a specific field gets logged.

type ILogger added in v1.1.0

type ILogger interface {
	Debug(v ...any)
	Debugf(format string, v ...any)
	Debugw(msg string, keyValuePairs ...any)
	Error(v ...any)
	Errorf(format string, v ...any)
	Errorw(msg string, keyValuePairs ...any)
	Fatal(v ...any)
	Fatalf(format string, v ...any)
	Fatalw(msg string, keyValuePairs ...any)
	Info(v ...any)
	Infof(format string, v ...any)
	Infow(msg string, keyValuePairs ...any)
	Sync() error
	Warn(v ...any)
	Warnf(format string, v ...any)
	Warnw(msg string, keyValuePairs ...any)
	With(keyValuePairs ...any) *Logger
}

type KeyNames added in v1.2.0

type KeyNames struct {
	MessageKey    string
	LevelKey      string
	TimeKey       string
	NameKey       string
	CallerKey     string
	FunctionKey   string
	StacktraceKey string
}

type Level

type Level zapcore.Level

Level specifies a log level. Usually it is used to indicate the minimum log level for a logger.

type Logger

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

The Logger struct resembles the actual loggers.

func MustNewLogger

func MustNewLogger(c Configuration) *Logger

MustNewLogger wraps NewLogger and panics, when an error is encountered.

func NewLogger

func NewLogger(conf Configuration) (*Logger, error)

NewLogger creates a new logger based on the configuration inputs and returns a pointer to it. If the validation of the input configuration fails an error will be issued.

func NewNOPLogger

func NewNOPLogger() *Logger

NewNOPLogger creates a new no-operation logger that does not write any log statements anywhere and is therefore tremendously helpful, when you need to fulfill the Interface, but you don't want to actually log anything.

func (*Logger) Debug

func (l *Logger) Debug(v ...any)

Debug logs all inputs on the debug level.

func (*Logger) Debugf

func (l *Logger) Debugf(format string, v ...any)

Debugf formats and logs all inputs on the debug level.

func (*Logger) Debugw

func (l *Logger) Debugw(msg string, keyValuePairs ...any)

Debugw logs all inputs and fields on the debug level.

func (*Logger) Error

func (l *Logger) Error(v ...any)

Error logs all inputs on the error level.

func (*Logger) Errorf

func (l *Logger) Errorf(format string, v ...any)

Errorf formats and logs all inputs on the error level.

func (*Logger) Errorw

func (l *Logger) Errorw(msg string, keyValuePairs ...any)

Errorw logs all inputs and fields on the error level.

func (*Logger) Fatal

func (l *Logger) Fatal(v ...any)

Fatal logs all inputs on the fatal level and runs os.exit(1) at the end.

func (*Logger) Fatalf

func (l *Logger) Fatalf(format string, v ...any)

Fatalf formats and logs all inputs on the fatal level and runs os.exit(1) at the end.

func (*Logger) Fatalw

func (l *Logger) Fatalw(msg string, keyValuePairs ...any)

Fatalw logs all inputs and fields on the fatal level and runs os.exit(1) at the end.

func (*Logger) Info

func (l *Logger) Info(v ...any)

Info logs all inputs on the info level.

func (*Logger) Infof

func (l *Logger) Infof(format string, v ...any)

Infof formats and logs all inputs on the info level.

func (*Logger) Infow

func (l *Logger) Infow(msg string, keyValuePairs ...any)

Infow logs all inputs and fields on the info level.

func (*Logger) Sync

func (l *Logger) Sync() error

func (*Logger) Warn

func (l *Logger) Warn(v ...any)

Warn logs all inputs on the warn level.

func (*Logger) Warnf

func (l *Logger) Warnf(format string, v ...any)

Warnf formats and logs all inputs on the warn level.

func (*Logger) Warnw

func (l *Logger) Warnw(msg string, keyValuePairs ...any)

Warnw logs all inputs and fields on the warn level.

func (*Logger) With

func (l *Logger) With(keyValuePairs ...any) *Logger

With returns a pointer to a new logger containing the added fields.

type OutputMode added in v1.2.0

type OutputMode uint8
const (
	OutputStdOut          OutputMode = 0
	OutputStdOutAndStdErr OutputMode = 1
	OutputStdErr          OutputMode = 2
)

type PIIMode

type PIIMode uint8

PIIMode indicates how to resolve PII fields in log statements.

const (
	// PIIModeNone indicates that PII fields shall be left as is.
	PIIModeNone PIIMode = 0

	// PIIModeHash indicates that the value part of a PII field shall
	// be hashed (SHA256). The key of the field stays untouched.
	PIIModeHash PIIMode = 1

	// PIIModeMask indicates that the value part of a PII field shall
	// be masked. If this mode is selected a mask function needs to be
	// provided under the MaskFunc property of this package. If no
	// MaskFunc is provided, PII fields will be omitted in the logs
	// using this mode.
	PIIModeMask PIIMode = 2

	// PIIModeRemove indicates that PII fields shall be omitted
	// completely from the final logs.
	PIIModeRemove PIIMode = 3
)

type PIIResolver

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

The PIIResolver interface is what the logger checks against, when trying to resolve PII fields in log statements before writing the logs.

type ResolvedPIIField

type ResolvedPIIField struct {
	Key   string
	Value string
}

Jump to

Keyboard shortcuts

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