logger

package
v2.0.0 Latest Latest
Warning

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

Go to latest
Published: May 20, 2022 License: MIT Imports: 6 Imported by: 19

README

Wharf logger

Preview

Sample output of the different pre-packaged Sink types.

pkg/logger/consolepretty

sample colored console output

pkg/logger/consolejson
{"level":"debug","date":"2021-06-18T15:01:29+02:00","caller":"wharf-core/main.go","line":20,"scope":"TEST","message":"Sample","hello":"world"}
{"level":"info","date":"2021-06-18T15:01:29+02:00","caller":"wharf-core/main.go","line":21,"scope":"TEST","message":"Sample","hello":"world","error":"EOF"}
{"level":"warn","date":"2021-06-18T15:01:29+02:00","caller":"wharf-core/main.go","line":22,"scope":"TEST","message":"Sample","hello":"world","someSpan":1000000000}
{"level":"error","date":"2021-06-18T15:01:29+02:00","caller":"wharf-core/main.go","line":23,"scope":"TEST","message":"Sample","hello":"world"}
{"level":"debug","date":"2021-06-18T15:01:29+02:00","caller":"gorm@v1.21.11/callbacks.go","line":133,"scope":"GORM","rows":0,"elapsed":120476,"sql":"SELECT * FROM \"users\" WHERE \"users\".\"id\" = 125 AND \"users\".\"deleted_at\" IS NULL"}
{"level":"warn","date":"2021-06-18T15:01:29+02:00","caller":"fmt/print.go","line":205,"scope":"GIN-debug","message":"Running in \"debug\" mode. Switch to \"release\" mode in production.\n - using env:\texport GIN_MODE=release\n - using code:\tgin.SetMode(gin.ReleaseMode)"}
{"level":"debug","date":"2021-06-18T15:01:29+02:00","caller":"fmt/print.go","line":205,"scope":"GIN-debug","message":"Environment variable PORT is undefined. Using port :8080 by default"}
{"level":"debug","date":"2021-06-18T15:01:29+02:00","caller":"fmt/print.go","line":205,"scope":"GIN-debug","message":"Listening and serving HTTP on :8080"}
{"level":"debug","date":"2021-06-18T15:01:37+02:00","caller":"gin@v1.7.1/logger.go","line":268,"scope":"GIN","clientIp":"::1","method":"GET","path":"/","status":404,"latency":311}
{"level":"debug","date":"2021-06-18T15:01:38+02:00","caller":"gin@v1.7.1/logger.go","line":268,"scope":"GIN","clientIp":"::1","method":"GET","path":"/favicon.ico","status":404,"latency":391}

Documentation

Overview

Package logger contains logging types and functions in a memory-efficient and fast manner.

This package contains abstractions for the different layers of logging: logger.Logger, logger.Event, logger.Sink, and logger.Context.

The Logger interface is what you use to create new log events.

The Event interface is a single log event. It contains methods to populate it with data in a type-safe way. A single Event can contain any number of log Context used to construct the different log messages.

The Sink interface is used to create a new concrete log Context specific to that Sink. Examples are console sinks that either produce JSON- or pretty-formatted logs.

The Context interface is created by the Sink and a concrete implementation holds data about the log Event for that particular Sink. Things like formatted fields, ready to be written out to STDOUT.

Index

Examples

Constants

This section is empty.

Variables

View Source
var (

	// LongestScopeNameLength is updated whenever NewScoped is called, and is
	// the string length of longest scope created. Useful when logging to align
	// the scopes in the output by padding to obtain this width.
	LongestScopeNameLength int
)

Functions

func AddOutput

func AddOutput(minLevel Level, sink Sink)

AddOutput registers a logging sink globally. Multiple sinks can be added, and they will be used in the order of when they are added.

To only use a particular sink for warning or higher logging levels, you pass in the warning log level:

logger.AddOutput(logger.LevelWarn, myLogSink)

To let a particular sink log all messages, use the "debug" logging level:

logger.AddOutput(logger.LevelDebug, myLogSink)
Example
package main

import (
	"github.com/iver-wharf/wharf-core/v2/pkg/logger"
	"github.com/iver-wharf/wharf-core/v2/pkg/logger/consolejson"
	"github.com/iver-wharf/wharf-core/v2/pkg/logger/consolepretty"
)

var prettyConf = consolepretty.Config{
	DisableDate:   true,
	DisableCaller: true,
}
var jsonConf = consolejson.Config{
	DisableDate:   true,
	DisableCaller: true,
}

func main() {
	defer logger.ClearOutputs()

	logger.AddOutput(logger.LevelDebug, consolepretty.New(prettyConf))
	logger.AddOutput(logger.LevelInfo, consolejson.New(jsonConf))

	// will not be used due to too high logger.Level
	logger.AddOutput(logger.LevelError, consolepretty.New(prettyConf))

	log := logger.New()

	log.Info().Message(`first "log".`)
	log.Info().WithInt("id", 5).Message("second log.")
	log.Info().WithString("hello", "world").Message("third log.")

}
Output:

[INFO ] first "log".
{"level":"info","message":"first \"log\"."}
[INFO ] second log.  id=5
{"level":"info","message":"second log.","id":5}
[INFO ] third log.  hello=world
{"level":"info","message":"third log.","hello":"world"}

func ClearOutputs

func ClearOutputs()

ClearOutputs resets the outputs added by AddOutput. Should not be needed in production code, but is quite useful to be called at the beginning of an example test.

func NewWriter

func NewWriter(log Logger, level Level) io.Writer

NewWriter creates a logger that channels everything written to it via a wharf-core logger, using the given logging level for all of the logs.

func SetLevel

func SetLevel(level Level)

SetLevel will suppress all events (no matter if scoped or not) that has a logging level lower than the provided value.

If LevelSilence is used, then all logs will be disabled.

func SetLevelScoped

func SetLevelScoped(level Level, scope string)

SetLevelScoped will suppress all events for a given scope that has a logging level lower than the provided value.

The scope name is case-insensitive.

If an empty string is passed for the scope, then the filter will be applied to events without a scope.

If LevelSilence is used, then this scope will be completely disabled.

Types

type Context

type Context interface {
	// WriteOut sends the log message with the collected data from all the
	// Append... methods
	WriteOut(level Level, message string)

	// SetCaller sets the caller and its line value for this context.
	//
	// Calling this method multiple times shall override the previous value.
	// An empty string on the file name signifies to unset this field.
	//
	// In contrast to AppendString, the logging sink is allowed to render this
	// differently. E.g. some may render it as yet another fields named "caller"
	// and "line", others may render it as a specific HTTP header in a request.
	SetCaller(file string, line int) Context
	// SetError sets the error value for this context.
	//
	// Calling this method multiple times shall override the previous value.
	// An nil signifies to unset this field.
	//
	// In contrast to AppendString, the logging sink is allowed to render this
	// differently. E.g. some may render it as yet another field named "error",
	// others may render it as a specific HTTP header in a request.
	SetError(value error) Context
	// AppendString adds a string value for a specific key to this context.
	//
	// Calling this method multiple times with the same key may lead to
	// unexpected behaviour.
	AppendString(key string, value string) Context
	// AppendRune adds a rune value for a specific key to this context.
	//
	// Calling this method multiple times with the same key may lead to
	// unexpected behaviour.
	AppendRune(key string, value rune) Context
	// AppendBool adds a boolean value for a specific key to this context.
	//
	// Calling this method multiple times with the same key may lead to
	// unexpected behaviour.
	AppendBool(key string, value bool) Context
	// AppendInt adds an integer value for a specific key to this context.
	//
	// Calling this method multiple times with the same key may lead to
	// unexpected behaviour.
	AppendInt(key string, value int) Context
	// AppendInt32 adds an integer value for a specific key to this context.
	//
	// Calling this method multiple times with the same key may lead to
	// unexpected behaviour.
	AppendInt32(key string, value int32) Context
	// AppendInt64 adds an integer value for a specific key to this context.
	//
	// Calling this method multiple times with the same key may lead to
	// unexpected behaviour.
	AppendInt64(key string, value int64) Context
	// AppendUint adds an integer value for a specific key to this context.
	//
	// Calling this method multiple times with the same key may lead to
	// unexpected behaviour.
	AppendUint(key string, value uint) Context
	// AppendUint32 adds an integer value for a specific key to this context.
	//
	// Calling this method multiple times with the same key may lead to
	// unexpected behaviour.
	AppendUint32(key string, value uint32) Context
	// AppendUint64 adds an integer value for a specific key to this context.
	//
	// Calling this method multiple times with the same key may lead to
	// unexpected behaviour.
	AppendUint64(key string, value uint64) Context
	// AppendFloat32 adds a floating point number value for a specific key to
	// this context.
	//
	// Calling this method multiple times with the same key may lead to
	// unexpected behaviour.
	AppendFloat32(key string, value float32) Context
	// AppendFloat64 adds a floating point number value for a specific key to
	// this context.
	//
	// Calling this method multiple times with the same key may lead to
	// unexpected behaviour.
	AppendFloat64(key string, value float64) Context
	// AppendTime adds a timestamp value for a specific key to
	// this context.
	//
	// Calling this method multiple times with the same key may lead to
	// unexpected behaviour.
	AppendTime(key string, value time.Time) Context
	// AppendDuration adds a time duration value for a specific key to
	// this context.
	//
	// Calling this method multiple times with the same key may lead to
	// unexpected behaviour.
	AppendDuration(key string, value time.Duration) Context
}

Context is data held about a certain logging event for a particular sink. The data can be stored in any way that seems suitable for efficiently composing a logging message for that sink.

Most methods return the context itself. It is up to the implementation to take advantage of this or use pointers to the same object throughout.

It is up to the user of this type to honor the specification of feeding the Context around to itself and calling the Append... methods on the return value from any other such method.

Good:

ctx.AppendString("hello", "world").WriteOut(logger.LevelDebug, "")

Good:

ctx = ctx.AppendString("hello", "world")
ctx.WriteOut(logger.LevelDebug, "")

Bad:

ctx.AppendString("hello", "world") // undefined behaviour
ctx.WriteOut(logger.LevelDebug, "")

type DoneFunc

type DoneFunc func(message string)

DoneFunc is the signature of the function that is called at the end of a submitted log event.

type Event

type Event interface {
	// Messagef submits this log event to the different sinks using a formatted
	// message. The formatting is the same applied from the fmt package.
	Messagef(format string, args ...any)

	// Message submits this log event to the different sinks using a message. To
	// submit without a message you may pass an empty string into this method, like
	// so:
	// 	ev.WithString("hello", "world").Message("")
	Message(message string)

	// WithFunc applies a function to the event and then forwards the return value.
	//
	// Useful for reusing "with statements" for multiple logs.
	WithFunc(f func(Event) Event) Event

	// WithCaller adds a caller field to the log contexts inside this log event.
	//
	// This method is called automatically by NewEvent and all Logger methods,
	// though you can override the value set there by calling it again manually.
	//
	// It's up to the logger sink to decide how this error is rendered in the log
	// message. Commonly, but not necessarily, this is rendered as fields with names
	// "caller" and "line".
	WithCaller(file string, line int) Event

	// WithString adds a string field to this logged message. Calling this method
	// multiple times with the same key may lead to unexpected behaviour.
	WithString(key string, value string) Event

	// WithStringf adds a formatted string field to this logged message. The
	// formatting is the same applied from the fmt package. Calling this method
	// multiple times with the same key may lead to unexpected behaviour.
	WithStringf(key string, format string, args ...any) Event

	// WithStringer adds a string field to this logged message using the value
	// from fmt.Stringer.String(). Calling this method multiple times with the
	// same key may lead to unexpected behaviour.
	WithStringer(key string, value fmt.Stringer) Event

	// WithRune adds a rune field to this logged message. Calling this method
	// multiple times with the same key may lead to unexpected behaviour.
	WithRune(key string, value rune) Event

	// WithBool adds a boolean field to this logged message. Calling this method
	// multiple times with the same key may lead to unexpected behaviour.
	WithBool(key string, value bool) Event

	// WithInt adds an integer field to this logged message. Calling this method
	// multiple times with the same key may lead to unexpected behaviour.
	WithInt(key string, value int) Event

	// WithInt64 adds an integer field to this logged message. Calling this method
	// multiple times with the same key may lead to unexpected behaviour.
	WithInt64(key string, value int64) Event

	// WithInt32 adds an integer field to this logged message. Calling this method
	// multiple times with the same key may lead to unexpected behaviour.
	WithInt32(key string, value int32) Event

	// WithUint adds an integer field to this logged message. Calling this method
	// multiple times with the same key may lead to unexpected behaviour.
	WithUint(key string, value uint) Event

	// WithUint64 adds an integer field to this logged message. Calling this method
	// multiple times with the same key may lead to unexpected behaviour.
	WithUint64(key string, value uint64) Event

	// WithUint32 adds an integer field to this logged message. Calling this method
	// multiple times with the same key may lead to unexpected behaviour.
	WithUint32(key string, value uint32) Event

	// WithFloat32 adds a floating point number field to this logged message. Calling
	// this method multiple times with the same key may lead to unexpected behaviour.
	WithFloat32(key string, value float32) Event

	// WithFloat64 adds a floating point number field to this logged message. Calling
	// this method multiple times with the same key may lead to unexpected behaviour.
	WithFloat64(key string, value float64) Event

	// WithError adds an error field to this logged message. Calling this method
	// multiple times may lead to unexpected behaviour.
	//
	// It's up to the logger sink to decide how this error is rendered in the log
	// message. Commonly, but not necessarily, this is rendered as a field with name
	// "error".
	WithError(value error) Event

	// WithTime adds a timestamp field to this logged message. Calling
	// this method multiple times with the same key may lead to unexpected behaviour.
	//
	// It's up to the logger sink to decide how this error is rendered in the log
	// message, e.g. in UNIX timestamp integer form or string formatted datetime.
	WithTime(key string, value time.Time) Event

	// WithDuration adds a timestamp field to this logged message. Calling
	// this method multiple times with the same key may lead to unexpected behaviour.
	//
	// It's up to the logger sink to decide how this error is rendered in the log
	// message, e.g. in milliseconds integer form or string formatted duration.
	WithDuration(key string, value time.Duration) Event
}

Event is a single log message that's aimed to be submitted. It may hold multiple logging contexts created by log sinks used to finally submit to that range of sinks.

func NewEvent

func NewEvent(level Level, scope string, done DoneFunc) Event

NewEvent creates a new event and prepares it to use a list of logging sinks based on the logging level fed into it using the globally registered sinks added using logger.AddOutput(...).

func NewEventFromLogger

func NewEventFromLogger(log Logger, level Level) Event

NewEventFromLogger creates an event using the logger itself based on the logging level. Useful in edge-cases and when testing with a slice of test cases.

type Level

type Level byte

Level is the enum type of different logging levels used throughout this package to categorize and filter different log events.

const (
	// LevelDebug is the "debugging" logging level, and also the lowest logging
	// level available.
	LevelDebug Level = iota
	// LevelInfo is the "information" logging level.
	LevelInfo
	// LevelWarn is the "warning" logging level.
	LevelWarn
	// LevelError is the "error" logging level.
	LevelError
	// LevelPanic is the "panic" logging level, and also the highest logging
	// level available.
	LevelPanic
	// LevelSilence will disable logging when used to configure an output, a
	// scoped minimum logging level, or the global minimum logging level.
	LevelSilence
)

func ParseLevel

func ParseLevel(lvl string) (Level, error)

ParseLevel tries to convert a string to a logging level value. It supports all the outputs from the logging level String() method, and some more.

func (Level) String

func (lvl Level) String() string

String returns a readable representation of the logging level.

Example
package main

import (
	"fmt"

	"github.com/iver-wharf/wharf-core/v2/pkg/logger"
)

func main() {
	for _, level := range []logger.Level{
		logger.LevelDebug,
		logger.LevelInfo,
		logger.LevelWarn,
		logger.LevelError,
		logger.LevelPanic,
	} {
		fmt.Println(level.String())
	}

}
Output:

Debugging
Information
Warning
Error
Panic

type Logger

type Logger interface {
	// Debug creates a new event using new contexts from all registered sinks of
	// "debugging" logging level or higher.
	Debug() Event
	// Info creates a new event using new contexts from all registered sinks of
	// "information" logging level or higher.
	Info() Event
	// Warn creates a new event using new contexts from all registered sinks of
	// "warning" logging level or higher.
	Warn() Event
	// Error creates a new event using new contexts from all registered sinks of
	// "error" logging level or higher.
	Error() Event
	// Panic creates a new event using new contexts from all registered sinks of
	// "panic" logging level or higher.
	//
	// Compared to the other logging events, after submitting the logged
	// messages this method calls panic with the final message string.
	Panic() Event
}

Logger is an interface that is used to initiate logging events of different log levels. This is done before populating the log messages with fields so that those calls can be ignored if no sink listens for that particular logging level.

func New

func New() Logger

New creates a new basic Logger without a scope. Use NewScoped instead to add a "scope" field to each logged message.

func NewScoped

func NewScoped(scope string) Logger

NewScoped creates a new logger and assigns a scope to it. Useful when you want to group logs from different parts of the system on a string name.

For example:

logger.NewScoped("GORM") // use when registering logger to GORM
logger.NewScoped("GIN") // use when registering logger to gin-gonic
logger.New() // use in the apps top-level domain
Example
package main

import (
	"github.com/iver-wharf/wharf-core/v2/pkg/logger"
	"github.com/iver-wharf/wharf-core/v2/pkg/logger/consolepretty"
)

var prettyConf = consolepretty.Config{
	DisableDate:   true,
	DisableCaller: true,
}

func main() {
	var log = logger.NewScoped("example")

	defer logger.ClearOutputs()
	logger.AddOutput(logger.LevelInfo, consolepretty.New(prettyConf))

	log.Info().Message("first log.")

}
Output:

[INFO |example] first log.

type Mock

type Mock struct {
	// Logs is an array of logs recorded. Each logged event is stored as a
	// separate item in this array.
	Logs []MockLog
	// LogMessages is an array of log messages recorded. This complements the
	// array of MockLog for easier assertion that the expected messages have been
	// logged. Empty messages are also stored in this array as empty strings.
	LogMessages []string
}

Mock is a logger and a sink meant to be used in testing. It records all logs sent to it and provides useful fields for you to verify that your application logs as expected.

func NewMock

func NewMock() *Mock

NewMock creates a new Logger interface compatible type that holds additional fields of the logs that have been submitted.

func (*Mock) Debug

func (log *Mock) Debug() Event

Debug creates a new event using new contexts connected to this mock logger of "debugging" logging level or higher.

func (*Mock) Error

func (log *Mock) Error() Event

Error creates a new event using new contexts connected to this mock logger of "error" logging level or higher.

func (*Mock) Info

func (log *Mock) Info() Event

Info creates a new event using new contexts connected to this mock logger of "information" logging level or higher.

func (*Mock) NewContext

func (log *Mock) NewContext(scope string) Context

NewContext creates a new log event context for this mock. The scope is added as a field unless it's an empty string.

func (*Mock) Panic

func (log *Mock) Panic() Event

Panic creates a new event using new contexts connected to this mock logger of "panic" logging level or higher.

Compared to the other logging events, after submitting the logged messages this method calls panic with the final message string.

func (*Mock) Warn

func (log *Mock) Warn() Event

Warn creates a new event using new contexts connected to this mock logger of "warning" logging level or higher.

type MockLog

type MockLog struct {
	// Level is the logging level that was used. If calling MockLogger.Debug(),
	// then this field holds the value of LevelDebug.
	Level Level
	// Message is the final message that was used to submit this logged event.
	Message string
	// Fields holds each field added to this logged event, in addition to the
	// following fields:
	//
	// 	Event.SetScope("foo")   => MockLog.Fields["scope"] = "foo"
	// 	Event.SetError(someErr) => MockLog.Fields["error"] = someErr
	// 	Event.SetCaller("foo", 42)
	// 		=> MockLog.Fields["caller"] = "foo"
	// 		=> MockLog.Fields["line"] = 42
	Fields map[string]any
	// FieldsAdded is a slice of strings with all the keys added to the Fields
	// map. This includes the custom mapping of Event.SetScope,
	// Event.SetError, and Event.SetCaller as mentioned in the Fields docs.
	//
	// If a field is added more than one time, then it will show up in this list
	// equally many times. Useful for checking if fields are misstakenly added
	// multiple times.
	FieldsAdded []string
}

MockLog is a single logged event with additional public fields containing data of the logged event.

type Sink

type Sink interface {
	NewContext(scope string) Context
}

Sink is an interface that creates logging contexts. Each sink could be for different log collectors such as Kibana or Logstash, or simply a console logging sink that outputs all the logs to STDOUT.

Directories

Path Synopsis
Package consolejson is a concrete implementation of the logger.Sink and logger.Context used for outputting JSON-formatted log lines.
Package consolejson is a concrete implementation of the logger.Sink and logger.Context used for outputting JSON-formatted log lines.
Package consolepretty is a concrete implementation of the logger.Sink and logger.Context used for outputting good looking human-readable logs to the console.
Package consolepretty is a concrete implementation of the logger.Sink and logger.Context used for outputting good looking human-readable logs to the console.

Jump to

Keyboard shortcuts

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