logging

package module
v0.0.0-...-42fdd30 Latest Latest
Warning

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

Go to latest
Published: Jun 17, 2022 License: MIT Imports: 18 Imported by: 15

README

logging

Yet another logging library for Golang. This one is similar to Python's logging module.

Usage

Initialization

To initialize a logger, do this:

logger := logging.GetLogger("MyLogger")
handler := new(logging.ConsoleHandler)
handler.SetFormatter(logging.DefaultFormatter{})
handler.SetLevel(logging.DebugLevel)
logger.AddHandler(handler)
logger.SetLevel(logging.DebugLevel)

To get an overview of the different types, check out the logging.go file; it specifies the Handler and Formatter protocols.

Logging

Similar to Python's logging, loggers have the following methods:

logger.Debug
logger.Info
logger.Warn
logger.Error

They do not have an exception call because Go obviously doesn't have exceptions. All of the methods have numbered versions like this:

logger.Debug0
logger.Debug1
logger.Debug2
logger.Debug3
logger.Debug4

This is to pass an explicit number of message arguments so an []interface{} slice isn't allocated for every log call (for convenience the non-suffixed version of each method uses a varargs-style []interface{} slice).

I wrote this library with allocations in mind. I try to effectively use sync.Pools to keep old Events and Event.Args []interface{} slices cached to prevent allocations wherever possible. At this point, the only allocations that I am aware of are:

  • Initialization (the Logger struct, any handlers and formatters)
  • the varargs of the non-numbered Debug, Info, etc. methods (or if using a numbered method but there's no existing slice available in the cache)
  • Event objects themselves (again if none are available in the pool).
  • Boxing non-pointer values passed to the logging methods into interface{}s.
  • Possibly when the []interface{} slice is returned in the call to sync.Pool.Get. I'm not sure about that one.

This is my first Go project that actually does anything, so please let me know if there are any bugs or design antipatterns, flaws, or "non-idiomatic Go" in the design; I would appreciate feedback from anyone with Golang experience!

Documentation

Index

Constants

This section is empty.

Variables

View Source
var LoggerTemporary = LoggerOption(func(L *Logger) error {
	if strings.HasSuffix(L.name, ">") {
		return nil
	}
	L.name = strings.Join([]string{
		L.name,
		"<0x",
		strconv.FormatUint(uint64(uintptr(unsafe.Pointer(L))), 16),
		">",
	}, "")
	return nil
})

LoggerTemporary configures a logger to be temporary and not cached.

Functions

func TestingHandler

func TestingHandler(logger *Logger, t *testing.T, options ...HandlerOption) func()

TestingHandler lets you register the given testing.T with the logger so that debug messages are written to the testing.T. It returns a function that when called, no longer tries to log to the testing.T.

Types

type ConsoleHandler

type ConsoleHandler struct {
	HandlerCommon
}

ConsoleHandler implements the Handler interface by logging events to the console.

func (ConsoleHandler) Emit

func (ch ConsoleHandler) Emit(event *Event)

Emit implements the Handler interface.

type DefaultFormatter

type DefaultFormatter struct{}

DefaultFormatter Sprintf's all of the information within its provided Event in an arbitrarily decided format that *I* just happen to like. Your mileage may vary.

func (DefaultFormatter) Format

func (f DefaultFormatter) Format(event *Event) string

Format returns the event with the following layout:

yyyy-mm-dd HH:MM:SS:  Level:  LoggerName:  at FuncName in File, line Line:
	fmt.Sprintf(Msg, Args...)

type EmitFuncHandler

type EmitFuncHandler struct {
	EmitFunc func(e *Event)
	HandlerCommon
}

EmitFuncHandler is a Handler that delegates emitting events to a EmitFunc

func (EmitFuncHandler) Emit

func (h EmitFuncHandler) Emit(e *Event)

Emit delegates to EmitFuncHandler.EmitFunc

type Event

type Event struct {
	// Name tracks the name of the logger that the event was created within.
	// The name field is used by the Formatter and then the Handler to format
	// and write the event, respectively.
	Name string

	// Time stores the time that the event occurred.
	Time time.Time

	Level Level

	// Msg stores the unformatted message within the event.  Only the
	// formatters used in the handlers that "want" the message format them
	// with the Event's Args.
	Msg string

	// Args holds the formatting parameters to the message in Msg.
	Args []interface{}

	// FuncName holds the name of the function where the event came from.
	// Imagine that.
	FuncName string

	// File holds the filename of the function where the event happened.
	File string

	// Line holds the line number within the file where the error occurred.
	Line int
}

Event holds an event that is being handled within logging's internals but is publicly available in case it needs to be expanded upon by others' packages to wrap this one.

type Formatter

type Formatter interface {
	// Format takes the event object and formats it into a string for its
	// Handler to do something with.
	Format(event *Event) string
}

Formatter objects are used by their Handlers to format events into a single string that the Handler can write (or not write) somewhere else. The same Formatter object should not be used by multiple Handlers.

type FormatterFunc

type FormatterFunc func(e *Event) string

FormatterFunc implements the Formatter interface through a single function.

func (FormatterFunc) Format

func (f FormatterFunc) Format(e *Event) string

Format implements Formatter by calling the format message.

type GoFormatter

type GoFormatter struct{}

func (GoFormatter) Format

func (f GoFormatter) Format(event *Event) string

type Handler

type Handler interface {
	// SetFormatter sets the Formatter to be used for this handler.  Handlers
	// only have one formatter at a time.
	SetFormatter(formatter Formatter)
	// SetLevel sets the logging level that this handler is interested in.
	// Handlers are still given every event that gets to the logger, but they
	// can filter events to a certain level within their Emit methods.
	SetLevel(level Level)
	// Emit is how a Logger feeds its handlers.  Ever event that a logger gets
	// is passed into the Emit method of every Handler.  Handlers must not
	// modify the event because it is shared between the other handlers.
	Emit(event *Event)
}

Handler implementers are sent messages by their owning Logger objects to handle however they see fit. They may ignore the message based on their level.

func HandlerFromEmitFunc

func HandlerFromEmitFunc(f func(e *Event)) Handler

HandlerFromEmitFunc creates a handler from an Emit function

type HandlerCommon

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

HandlerCommon is a struct that contains some common Handler state.

func (HandlerCommon) Formatter

func (hc HandlerCommon) Formatter() Formatter

Formatter implements the Handler interface.

func (HandlerCommon) Level

func (hc HandlerCommon) Level() Level

Level implements the Handler interface.

func (*HandlerCommon) SetFormatter

func (hc *HandlerCommon) SetFormatter(formatter Formatter)

SetFormatter implements the Handler interface.

func (*HandlerCommon) SetLevel

func (hc *HandlerCommon) SetLevel(level Level)

SetLevel implements the Handler interface.

type HandlerOption

type HandlerOption func(h Handler) error

HandlerOption configures a handler

func HandlerFormatter

func HandlerFormatter(f Formatter) HandlerOption

HandlerFormatter sets the handler's formatter

func HandlerLevel

func HandlerLevel(lvl Level) HandlerOption

HandlerLevel configures a handler's logging level

type Level

type Level int8

Level ranks the severity of a logging message

const (
	// VerboseLevel includes the most logging information.  As the
	// definition of verbose implies, this level will produce more
	// information than should be needed for all but the most obscure
	// troubleshooting.
	VerboseLevel Level = (10 * iota) - 30

	// DebugLevel is the lowest severity level intended for any practical
	// use.  As its name implies, it should be restricted to debugging.
	DebugLevel

	// InfoLevel is for tracking informational messages that are not actual
	// problems
	InfoLevel

	// WarnLevel is the default severity level and indicates non-critical
	// problems that the program will work around or otherwise recover from
	// but should be made aware to the user.
	WarnLevel

	// ErrorLevel represents errors that the current call-stack / goroutine
	// cannot work around and will terminate its execution but not necessarily
	// the execution of other goroutines.
	ErrorLevel

	// FatalLevel represents an issue that will cause the entire executable to
	// stop running immediately.  It is essentially an assertion.
	FatalLevel

	// WarningLevel is just an alias for the WarnLevel.
	WarningLevel = WarnLevel
)
const EverythingLevel Level = Level(math.MinInt8)

EverythingLevel will capture every log level

func ParseLevel

func ParseLevel(name string) (Level, bool)

ParseLevel takes an log level name as a string and returns the Level value.

func (Level) String

func (L Level) String() string

String returns a string value for the logging level

type LockHandler

type LockHandler struct {
	sync.Locker
	Handler
}

LockHandler is protected with a sync.Locker

func (LockHandler) Emit

func (h LockHandler) Emit(e *Event)

Emit an event to the underlying Handler

type Logger

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

Logger objects expose methods to log events to added handlers if the event exceeds the logger's log level.

func GetLogger

func GetLogger(name string, options ...LoggerOption) *Logger

GetLogger retrieves a logger with the given name. If a logger with that name doesn't exist, one is created. This function is protected by a mutex and can be called concurrently.

func LoggerFromContext

func LoggerFromContext(ctx context.Context) (*Logger, bool)

LoggerFromContext gets the logger associated with the given context. If no logger is associated, returns nil (just like how ctx.Value returns nil)

func (*Logger) AddHandler

func (L *Logger) AddHandler(h Handler)

AddHandler adds a single logging handler to the logger.

func (*Logger) AddHandlers

func (L *Logger) AddHandlers(hs ...Handler)

AddHandlers adds handlers to the logger. This function is threadsafe.

func (*Logger) AddToContext

func (L *Logger) AddToContext(ctx context.Context) context.Context

AddToContext adds the given Logger to the context and returns that new context. If the logger is already in the context, that existing context is returned as-is.

func (*Logger) CreateEvent

func (L *Logger) CreateEvent(time time.Time, level Level, msg string, args []interface{}, funcname, file string, line int) *Event

CreateEvent doesn't always actually create an event but will reuse an event that's been added to the event pool (to reduce allocations).

func (*Logger) CreateEventNow

func (L *Logger) CreateEventNow(level Level, msg string, args []interface{}, funcname, filename string, line int) *Event

CreateEventNow works similarly to CreateEvent but it assumes the time argument is time.Now().

func (*Logger) Debug

func (L *Logger) Debug(msg string, args ...interface{})

Debug calls Log with the DebugLevel level.

func (*Logger) Debug0

func (L *Logger) Debug0(msg string)

Debug0 calls Log0 with the DebugLevel level.

func (*Logger) Debug1

func (L *Logger) Debug1(msg string, arg0 interface{})

Debug1 calls Log1 with the DebugLevel level.

func (*Logger) Debug2

func (L *Logger) Debug2(msg string, arg0, arg1 interface{})

Debug2 calls Log2 with the DebugLevel level.

func (*Logger) Debug3

func (L *Logger) Debug3(msg string, arg0, arg1, arg2 interface{})

Debug3 calls Log3 with the DebugLevel level.

func (*Logger) Debug4

func (L *Logger) Debug4(msg string, arg0, arg1, arg2, arg3 interface{})

Debug4 calls Log4 with the DebugLevel level.

func (*Logger) EffectiveLevel

func (L *Logger) EffectiveLevel() Level

EffectiveLevel gets the minimum level of this logger and any of the parents it can propagate events to. Use this if in order to log something, you need to do extra work to build some representation of it and you don't want to do that unless it's actually going to be logged:

if logger.EffectiveLevel() <= logging.VerboseLevel {
	nvps := make([]NameValuePair, len(names))
	for i, n := range names {
		nvps[i] = NameValuePair{Name: n, Value: values[i]}
	}
	logger.Verbose1("doing work with names and values: %#v", nvps)
}

func (*Logger) Error

func (L *Logger) Error(msg string, args ...interface{})

Error calls Log with the ErrorLevel level.

func (*Logger) Error0

func (L *Logger) Error0(msg string)

Error0 calls Log0 with the ErrorLevel level.

func (*Logger) Error1

func (L *Logger) Error1(msg string, arg0 interface{})

Error1 calls Log1 with the ErrorLevel level.

func (*Logger) Error2

func (L *Logger) Error2(msg string, arg0, arg1 interface{})

Error2 calls Log2 with the ErrorLevel level.

func (*Logger) Error3

func (L *Logger) Error3(msg string, arg0, arg1, arg2 interface{})

Error3 calls Log3 with the ErrorLevel level.

func (*Logger) Error4

func (L *Logger) Error4(msg string, arg0, arg1, arg2, arg3 interface{})

Error4 calls Log4 with the ErrorLevel level.

func (*Logger) Handlers

func (L *Logger) Handlers() []Handler

Handlers returns all the handlers registered with the logger at the time this call was made. Note that by the time the function returns, the set of handlers may have changed. This is meant for debugging.

func (*Logger) Info

func (L *Logger) Info(msg string, args ...interface{})

Info calls Log with the InfoLevel level.

func (*Logger) Info0

func (L *Logger) Info0(msg string)

Info0 calls Log0 with the InfoLevel level.

func (*Logger) Info1

func (L *Logger) Info1(msg string, arg0 interface{})

Info1 calls Log1 with the InfoLevel level.

func (*Logger) Info2

func (L *Logger) Info2(msg string, arg0, arg1 interface{})

Info2 calls Log2 with the InfoLevel level.

func (*Logger) Info3

func (L *Logger) Info3(msg string, arg0, arg1, arg2 interface{})

Info3 calls Log3 with the InfoLevel level.

func (*Logger) Info4

func (L *Logger) Info4(msg string, arg0, arg1, arg2, arg3 interface{})

Info4 calls Log4 with the InfoLevel level.

func (*Logger) Level

func (L *Logger) Level() Level

Level gets the logger's level.

func (*Logger) Log

func (L *Logger) Log(level Level, msg string, args ...interface{})

Log an event to the logger.

func (*Logger) Log0

func (L *Logger) Log0(level Level, msg string)

Log0 logs an event with no arguments to the logger.

func (*Logger) Log1

func (L *Logger) Log1(level Level, msg string, arg0 interface{})

Log1 logs an event with a single argument to the logger.

func (*Logger) Log2

func (L *Logger) Log2(level Level, msg string, arg0, arg1 interface{})

Log2 logs an event with two arguments to the logger.

func (*Logger) Log3

func (L *Logger) Log3(level Level, msg string, arg0, arg1, arg2 interface{})

Log3 logs an event with three arguments to the logger.

func (*Logger) Log4

func (L *Logger) Log4(level Level, msg string, arg0, arg1, arg2, arg3 interface{})

Log4 logs an event with four arguments to the logger.

func (*Logger) LogErr

func (L *Logger) LogErr(err error)

LogErr logs the given error at ErrorLevel

func (*Logger) LogEvent

func (L *Logger) LogEvent(event *Event)

LogEvent emits the event to its handlers and then consumes the event. The event must not be used after a call to LogEvent; it is pooled for future use and its values will be overwritten.

func (*Logger) Name

func (L *Logger) Name() string

Name gets the logger's name.

func (*Logger) Propagate

func (L *Logger) Propagate() bool

Propagate events to the parent logger(s).

func (*Logger) RemoveHandlers

func (L *Logger) RemoveHandlers(hs ...Handler)

RemoveHandlers removes the given list of handlers from the logger.

func (*Logger) SetLevel

func (L *Logger) SetLevel(level Level)

SetLevel sets the logging level of the logger.

func (*Logger) SetPropagate

func (L *Logger) SetPropagate(v bool)

SetPropagate toggles propagating events to parent logger(s).

func (*Logger) Verbose

func (L *Logger) Verbose(msg string, args ...interface{})

Verbose calls Log with the VerboseLevel level.

func (*Logger) Verbose0

func (L *Logger) Verbose0(msg string)

Verbose0 calls Log0 with the VerboseLevel level.

func (*Logger) Verbose1

func (L *Logger) Verbose1(msg string, arg0 interface{})

Verbose1 calls Log1 with the VerboseLevel level.

func (*Logger) Verbose2

func (L *Logger) Verbose2(msg string, arg0, arg1 interface{})

Verbose2 calls Log2 with the VerboseLevel level.

func (*Logger) Verbose3

func (L *Logger) Verbose3(msg string, arg0, arg1, arg2 interface{})

Verbose3 calls Log3 with the VerboseLevel level.

func (*Logger) Verbose4

func (L *Logger) Verbose4(msg string, arg0, arg1, arg2, arg3 interface{})

Verbose4 calls Log4 with the VerboseLevel level.

func (*Logger) Warn

func (L *Logger) Warn(msg string, args ...interface{})

Warn calls Log with the WarnLevel level.

func (*Logger) Warn0

func (L *Logger) Warn0(msg string)

Warn0 calls Log0 with the WarnLevel level.

func (*Logger) Warn1

func (L *Logger) Warn1(msg string, arg0 interface{})

Warn1 calls Log1 with the WarnLevel level.

func (*Logger) Warn2

func (L *Logger) Warn2(msg string, arg0, arg1 interface{})

Warn2 calls Log2 with the WarnLevel level.

func (*Logger) Warn3

func (L *Logger) Warn3(msg string, arg0, arg1, arg2 interface{})

Warn3 calls Log3 with the WarnLevel level.

func (*Logger) Warn4

func (L *Logger) Warn4(msg string, arg0, arg1, arg2, arg3 interface{})

Warn4 calls Log4 with the WarnLevel level.

type LoggerOption

type LoggerOption func(L *Logger) error

LoggerOption configures a logger

func LoggerHandler

func LoggerHandler(h Handler, options ...HandlerOption) LoggerOption

LoggerHandler adds a handler to the logger.

func LoggerLevel

func LoggerLevel(level Level) LoggerOption

LoggerLevel returns a LoggerOption that configures the logger level.

func LoggerPropagate

func LoggerPropagate(propagate bool) LoggerOption

LoggerPropagate returns a logger option that configures the logger's propagation flag to the given value.

type WriterHandler

type WriterHandler struct {
	HandlerCommon

	// L is the mutex that protects the writer from concurrent access.
	L sync.Locker
	// contains filtered or unexported fields
}

WriterHandler implements the Handler interface by writing events into an underlying io.Writer implementation. Access to the writer is synchronized with a sync.Locker.

func NewWriterHandler

func NewWriterHandler(w io.Writer, lock sync.Locker) *WriterHandler

NewWriterHandler creates a new WriterHandler with an optional lock to synchronize access to the writer. If nil, no locking on the writer is performed.

func (*WriterHandler) Emit

func (wh *WriterHandler) Emit(event *Event)

Emit implements the Handler interface.

Jump to

Keyboard shortcuts

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