logger

package
v0.0.0-...-183aff7 Latest Latest
Warning

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

Go to latest
Published: Mar 14, 2024 License: BSD-3-Clause Imports: 5 Imported by: 55

README

Example

package logger_test

import (
	"bytes"
	"context"
	"log"

	"github.com/facebookincubator/go-belt"
	"github.com/facebookincubator/go-belt/tool/logger"
	"github.com/facebookincubator/go-belt/tool/logger/implementation/glog"
	xlogrus "github.com/facebookincubator/go-belt/tool/logger/implementation/logrus"
	"github.com/facebookincubator/go-belt/tool/logger/implementation/stdlib"
	"github.com/facebookincubator/go-belt/tool/logger/implementation/zap"
	"github.com/sirupsen/logrus"
)

func Example() {
	// easy to use:
	l := xlogrus.Default()
	someFunction(1, l)

	// implementation agnostic:
	l = zap.Default()
	someFunction(2, l)

	// various implementations:
	l = glog.New()
	someFunction(3, l)

	// one may still reuse all the features of the backend logger:
	logrusInstance := logrus.New()
	logrusInstance.Formatter = &logrus.JSONFormatter{}
	l = xlogrus.New(logrusInstance)
	someFunction(4, l)

	// just another example:
	var buf bytes.Buffer
	stdLogInstance := log.New(&buf, "", log.Llongfile)
	l = stdlib.New(stdLogInstance, logger.LevelDebug)
	someFunction(5, l)

	// use go-belt to manage the logger
	obs := belt.New()
	obs = logger.BeltWithLogger(obs, l)
	someBeltyFunction(6, obs)

	// use context to manage the logger
	ctx := context.Background()
	ctx = logger.CtxWithLogger(ctx, l)
	someContextyFunction(ctx, 7)

	// use a singletony Logger:
	logger.Default = func() logger.Logger {
		return l
	}
	oneMoreFunction(8)
}

func someFunction(arg int, l logger.Logger) {
	// experience close to logrus/zap:
	l = l.WithField("arg", arg)
	anotherFunction(l)
}

func anotherFunction(l logger.Logger) {
	l.Debugf("hello world, %T!", l)
}

func someBeltyFunction(arg int, obs *belt.Belt) {
	obs = obs.WithField("arg", arg)
	anotherBeltyFunction(obs)
}

func anotherBeltyFunction(obs *belt.Belt) {
	logger.FromBelt(obs).Debugf("hello world!")
}

func someContextyFunction(ctx context.Context, arg int) {
	ctx = belt.WithField(ctx, "arg", arg)
	anotherContextyFunction(ctx)
}

func anotherContextyFunction(ctx context.Context) {
	logger.FromCtx(ctx).Debugf("hello world!")
	// or a shorter form:
	logger.Debugf(ctx, "hello world!")
}

func oneMoreFunction(arg int) {
	logger.Default().WithField("arg", arg).Debugf("hello world!")
}

Interface

type Logger interface {
	// Logf logs an unstructured message. Though, of course, all
	// contextual structured fields will also be logged.
	//
	// This method exists mostly for convenience, for people who
	// has not got used to proper structured logging, yet.
	// See `LogFields` and `Log`. If one have variables they want to
	// log, it is better for scalable observability to log them
	// as structured values, instead of injecting them into a
	// non-structured string.
	Logf(level Level, format string, args ...any)

	// LogFields logs structured fields with a explanation message.
	//
	// Anything that implements field.AbstractFields might be used
	// as a collection of fields to be logged.
	//
	// Examples:
	//
	// 	l.LogFields(logger.LevelDebug, "new_request", field.Fields{{Key: "user_id", Value: userID}, {Key: "group_id", Value: groupID}})
	// 	l.LogFields(logger.LevelInfo, "affected entries", field.Field{Key: "mysql_affected", Value: affectedRows})
	// 	l.LogFields(logger.LevelError, "unable to fetch user info", request) // where `request` implements field.AbstractFields
	//
	// Sometimes it is inconvenient to manually describe each field,
	// and for such cases see method `Log`.
	LogFields(level Level, message string, fields field.AbstractFields)

	// Log extracts structured fields from provided values, joins
	// the rest into an unstructured message and logs the result.
	//
	// This function provides convenience (relatively to LogFields)
	// at cost of a bit of performance.
	//
	// There are few ways to extract structured fields, which are
	// applied for each value from `values` (in descending priority order):
	// 1. If a `value` is an `*Entry` then the Entry is used (with its fields)
	// 2. If a `value` implements field.AbstractFields then ForEachField method
	//    is used (so it is become similar to LogFields).
	// 3. If a `value` is a structure (or a pointer to a structure) then
	//    fields of the structure are interpreted as structured fields
	//    to be logged (see explanation below).
	// 4. If a `value` is a map then fields a constructed out of this map.
	//
	// Everything that does not fit into any of the rules above is just
	// joined into an nonstructured message (and works the same way
	// as `message` in LogFields).
	//
	// How structures are parsed:
	// Structures are parsed recursively. Each field name of the path in a tree
	// of structures is added to the resulting field name (for example int "struct{A struct{B int}}"
	// the field name will be `A.B`).
	// To enforce another name use tag `log` (for example "struct{A int `log:"anotherName"`}"),
	// to prevent a field from logging use tag `log:"-"`.
	//
	// Examples:
	//
	// 	user, err := getUser()
	// 	if err != nil {
	// 		l.Log(logger.LevelError, err)
	// 		return err
	// 	}
	// 	l.Log(logger.LevelDebug, "current user", user) // fields of structure "user" will be logged
	// 	l.Log(logger.LevelDebug, map[string]any{"user_id": user.ID, "group_id", user.GroupID})
	// 	l.Log(logger.LevelDebug, field.Fields{{Key: "user_id", Value: user.ID}, {Key: "group_id", Value: user.GroupID}})
	// 	l.Log(logger.LevelDebug, "current user ID is ", user.ID, " and group ID is ", user.GroupID) // will result into message "current user ID is 1234 and group ID is 5678".
	Log(level Level, values ...any)

	// Emitter returns the Emitter (see the description of interface "Emitter").
	Emitter() Emitter

	// Level returns the current logging level (see description of "Level").
	Level() Level

	// TraceFields is just a shorthand for LogFields(logger.LevelTrace, ...)
	TraceFields(message string, fields field.AbstractFields)

	// DebugFields is just a shorthand for LogFields(logger.LevelDebug, ...)
	DebugFields(message string, fields field.AbstractFields)

	// InfoFields is just a shorthand for LogFields(logger.LevelInfo, ...)
	InfoFields(message string, fields field.AbstractFields)

	// WarnFields is just a shorthand for LogFields(logger.LevelWarn, ...)
	WarnFields(message string, fields field.AbstractFields)

	// ErrorFields is just a shorthand for LogFields(logger.LevelError, ...)
	ErrorFields(message string, fields field.AbstractFields)

	// PanicFields is just a shorthand for LogFields(logger.LevelPanic, ...)
	//
	// Be aware: Panic level also triggers a `panic`.
	PanicFields(message string, fields field.AbstractFields)

	// FatalFields is just a shorthand for LogFields(logger.LevelFatal, ...)
	//
	// Be aware: Panic level also triggers an `os.Exit`.
	FatalFields(message string, fields field.AbstractFields)

	// Trace is just a shorthand for Log(logger.LevelTrace, ...)
	Trace(values ...any)

	// Debug is just a shorthand for Log(logger.LevelDebug, ...)
	Debug(values ...any)

	// Info is just a shorthand for Log(logger.LevelInfo, ...)
	Info(values ...any)

	// Warn is just a shorthand for Log(logger.LevelWarn, ...)
	Warn(values ...any)

	// Error is just a shorthand for Log(logger.LevelError, ...)
	Error(values ...any)

	// Panic is just a shorthand for Log(logger.LevelPanic, ...)
	//
	// Be aware: Panic level also triggers a `panic`.
	Panic(values ...any)

	// Fatal is just a shorthand for Log(logger.LevelFatal, ...)
	//
	// Be aware: Fatal level also triggers an `os.Exit`.
	Fatal(values ...any)

	// Tracef is just a shorthand for Logf(logger.LevelTrace, ...)
	Tracef(format string, args ...any)

	// Debugf is just a shorthand for Logf(logger.LevelDebug, ...)
	Debugf(format string, args ...any)

	// Infof is just a shorthand for Logf(logger.LevelInfo, ...)
	Infof(format string, args ...any)

	// Warnf is just a shorthand for Logf(logger.LevelWarn, ...)
	Warnf(format string, args ...any)

	// Errorf is just a shorthand for Logf(logger.LevelError, ...)
	Errorf(format string, args ...any)

	// Panicf is just a shorthand for Logf(logger.LevelPanic, ...)
	//
	// Be aware: Panic level also triggers a `panic`.
	Panicf(format string, args ...any)

	// Fatalf is just a shorthand for Logf(logger.LevelFatal, ...)
	//
	// Be aware: Fatal level also triggers an `os.Exit`.
	Fatalf(format string, args ...any)

	// WithLevel returns a logger with logger level set to the given argument.
	//
	// See also the description of type "Level".
	WithLevel(Level) Logger

	// WithPreHooks returns a Logger which includes/appends pre-hooks from the arguments.
	//
	// See also description of "PreHook".
	//
	// Special case: to reset hooks use `WithHooks()` (without any arguments).
	WithPreHooks(...PreHook) Logger

	// WithHooks returns a Logger which includes/appends hooks from the arguments.
	//
	// See also description of "Hook".
	//
	// Special case: to reset hooks use `WithHooks()` (without any arguments).
	WithHooks(...Hook) Logger

	// WithField returns the logger with the added field (used for structured logging).
	WithField(key string, value any, props ...field.Property) Logger

	// WithMoreFields returns the logger with the added fields (used for structured logging)
	WithMoreFields(fields field.AbstractFields) Logger

	// WithMessagePrefix adds a string to all messages logged through the derived logger.
	WithMessagePrefix(prefix string) Logger

	// WithContextFields sets new context-defined fields. Supposed to be called
	// only by an Belt.
	//
	// allFields contains all fields as a chain of additions in a reverse-chronological order,
	// while newFieldsCount tells about how much of the fields are new (since last
	// call of WithContextFields). Thus if one will call
	// field.Slice(allFields, 0, newFieldsCount) they will get only the new fields.
	// At the same time some Tool-s may prefer just to re-set all the fields instead of adding
	// only new fields (due to performance reasons) and they may just use `allFields`.
	WithContextFields(allFields *field.FieldsChain, newFieldsCount int) Tool

	// WithTraceIDs sets new context-defined TraceIDs. Supposed to be called
	// only by an Belt.
	//
	// traceIDs and newTraceIDsCount has similar properties as allFields and newFieldsCount
	// in the WithContextFields method.
	WithTraceIDs(traceIDs TraceIDs, newTraceIDsCount int) Tool

	// Flush forces to flush all buffers.
	Flush()
}

Implementations

These implementations are provided out of the box:

Custom implementation

Depending on how many features your logger is ready to provide it should implement one of:

And then you may call adapter.LoggerFromAny and it will convert your logger to Logger by adding everything what is missing in a naive generic way.

So the easiest implementation of a logger might be for example:

import (
	"github.com/facebookincubator/go-belt/tool/logger"
	"github.com/facebookincubator/go-belt/tool/logger/adapter"
)

func getLogger() logger.Logger {
	return adapter.LoggerFromAny(func(format string, args ...any) {
		fmt.Printf("[log]"+format+"\n", args...)
	})
}

If a more complicated logger is required then take a look at existing implementations.

Documentation

Index

Constants

View Source
const (
	// LevelUndefined is the erroneous value of log-level which corresponds
	// to zero-value.
	LevelUndefined = types.LevelUndefined

	// LevelFatal will report about Fatalf-s only.
	LevelFatal = types.LevelFatal

	// LevelPanic will report about Panicf-s and Fatalf-s only.
	LevelPanic = types.LevelPanic

	// LevelError will report about Errorf-s, Panicf-s, ...
	LevelError = types.LevelError

	// LevelWarning will report about Warningf-s, Errorf-s, ...
	LevelWarning = types.LevelWarning

	// LevelInfo will report about Infof-s, Warningf-s, ...
	LevelInfo = types.LevelInfo

	// LevelDebug will report about Debugf-s, Infof-s, ...
	LevelDebug = types.LevelDebug

	// LevelTrace will report about Tracef-s, Debugf-s, ...
	LevelTrace = types.LevelTrace
)

Variables

View Source
var Default = stdlib.Default

Default is the (overridable) function which provides a Logger when no Logger is set. It is used by functions FromBelt and FromCtx.

View Source
var ToolID = toolID{}

ToolID is the unique ToolID which defines a Logger.

Functions

func BeltWithLogger

func BeltWithLogger(belt *belt.Belt, logger Logger) *belt.Belt

BeltWithLogger returns an Belt with the given Logger set.

func CtxWithLogger

func CtxWithLogger(ctx context.Context, logger Logger) context.Context

CtxWithLogger returns a context with the given Logger set.

func Debug

func Debug(ctx context.Context, values ...any)

Debug is just a shorthand for Log(ctx, logger.LevelDebug, ...)

func DebugFields

func DebugFields(ctx context.Context, message string, fields field.AbstractFields)

DebugFields is just a shorthand for LogFields(ctx, logger.LevelDebug, ...)

func Debugf

func Debugf(ctx context.Context, format string, args ...any)

Debugf is just a shorthand for Logf(ctx, logger.LevelDebug, ...)

func Error

func Error(ctx context.Context, values ...any)

Error is just a shorthand for Log(ctx, logger.LevelError, ...)

func ErrorFields

func ErrorFields(ctx context.Context, message string, fields field.AbstractFields)

ErrorFields is just a shorthand for LogFields(ctx, logger.LevelError, ...)

func Errorf

func Errorf(ctx context.Context, format string, args ...any)

Errorf is just a shorthand for Logf(ctx, logger.LevelError, ...)

func Fatal

func Fatal(ctx context.Context, values ...any)

Fatal is just a shorthand for Log(logger.LevelFatal, ...)

Be aware: Fatal level also triggers an `os.Exit`.

func FatalFields

func FatalFields(ctx context.Context, message string, fields field.AbstractFields)

FatalFields is just a shorthand for LogFields(ctx, logger.LevelFatal, ...)

Be aware: Panic level also triggers an `os.Exit`.

func Fatalf

func Fatalf(ctx context.Context, format string, args ...any)

Fatalf is just a shorthand for Logf(ctx, logger.LevelFatal, ...)

Be aware: Fatal level also triggers an `os.Exit`.

func Flush

func Flush(ctx context.Context)

Flush forces to flush all buffers.

func Info

func Info(ctx context.Context, values ...any)

Info is just a shorthand for Log(ctx, logger.LevelInfo, ...)

func InfoFields

func InfoFields(ctx context.Context, message string, fields field.AbstractFields)

InfoFields is just a shorthand for LogFields(ctx, logger.LevelInfo, ...)

func Infof

func Infof(ctx context.Context, format string, args ...any)

Infof is just a shorthand for Logf(ctx, logger.LevelInfo, ...)

func Log

func Log(ctx context.Context, level Level, values ...any)

Log extracts structured fields from provided values, joins the rest into an unstructured message and logs the result.

This function provides convenience (relatively to LogFields) at cost of a bit of performance.

There are few ways to extract structured fields, which are applied for each value from `values` (in descending priority order):

  1. If a `value` is an `*Entry` then the Entry is used (with its fields). This works only if this is the only argument. Otherwise it is threated as a simple structure (see point #3).
  2. If a `value` implements field.AbstractFields then ForEachField method is used (so it is become similar to LogFields).
  3. If a `value` is a structure (or a pointer to a structure) then fields of the structure are interpreted as structured fields to be logged (see explanation below).
  4. If a `value` is a map then fields a constructed out of this map.

Structured arguments are processed effectively the same as they would have through a sequence of WithField/WithFields.

Everything that does not fit into any of the rules above is just joined into an unstructured message (and works the same way as `message` in LogFields).

How structures are parsed: Structures are parsed recursively. Each field name of the path in a tree of structures is added to the resulting field name (for example in "struct{A struct{B int}}" the field name will be `A.B`). To enforce another name use tag `log` (for example "struct{A int `log:"anotherName"`}"), to prevent a field from logging use tag `log:"-"`.

Examples:

user, err := getUser()
if err != nil {
	logger.Log(ctx, logger.LevelError, err)
	return err
}
logger.Log(ctx, logger.LevelDebug, "current user", user) // fields of structure "user" will be logged
logger.Log(ctx, logger.LevelDebug, map[string]any{"user_id": user.ID, "group_id", user.GroupID})
logger.Log(ctx, logger.LevelDebug, field.Fields{{Key: "user_id", Value: user.ID}, {Key: "group_id", Value: user.GroupID}})
logger.Log(ctx, logger.LevelDebug, "current user ID is ", user.ID, " and group ID is ", user.GroupID) // will result into message "current user ID is 1234 and group ID is 5678".

func LogFields

func LogFields(ctx context.Context, level Level, message string, fields field.AbstractFields)

LogFields logs structured fields with a explanation message.

Anything that implements field.AbstractFields might be used as a collection of fields to be logged.

Examples:

logger.LogFields(ctx, logger.LevelDebug, "new_request", field.Fields{{Key: "user_id", Value: userID}, {Key: "group_id", Value: groupID}})
logger.LogFields(ctx, logger.LevelInfo, "affected entries", field.Field{Key: "mysql_affected", Value: affectedRows})
logger.LogFields(ctx, logger.LevelError, "unable to fetch user info", request) // where `request` implements field.AbstractFields

Sometimes it is inconvenient to manually describe each field, and for such cases see method `Log`.

func Logf

func Logf(ctx context.Context, level Level, format string, args ...any)

Logf logs an unstructured message. All contextual structured fields are also logged.

This method exists mostly for convenience, for people who has not got used to proper structured logging, yet. See `LogFields` and `Log`. If one have variables they want to log, it is better for scalable observability to log them as structured values, instead of injecting them into a non-structured string.

func Panic

func Panic(ctx context.Context, values ...any)

Panic is just a shorthand for Log(ctx, logger.LevelPanic, ...)

Be aware: Panic level also triggers a `panic`.

func PanicFields

func PanicFields(ctx context.Context, message string, fields field.AbstractFields)

PanicFields is just a shorthand for LogFields(ctx, logger.LevelPanic, ...)

Be aware: Panic level also triggers a `panic`.

func Panicf

func Panicf(ctx context.Context, format string, args ...any)

Panicf is just a shorthand for Logf(ctx, logger.LevelPanic, ...)

Be aware: Panic level also triggers a `panic`.

func Trace

func Trace(ctx context.Context, values ...any)

Trace is just a shorthand for Log(ctx, logger.LevelTrace, ...)

func TraceFields

func TraceFields(ctx context.Context, message string, fields field.AbstractFields)

TraceFields is just a shorthand for LogFields(ctx, logger.LevelTrace, ...)

func Tracef

func Tracef(ctx context.Context, format string, args ...any)

Tracef is just a shorthand for Logf(ctx, logger.LevelTrace, ...)

func Warn

func Warn(ctx context.Context, values ...any)

Warn is just a shorthand for Log(ctx, logger.LevelWarn, ...)

func WarnFields

func WarnFields(ctx context.Context, message string, fields field.AbstractFields)

WarnFields is just a shorthand for LogFields(ctx, logger.LevelWarn, ...)

func Warnf

func Warnf(ctx context.Context, format string, args ...any)

Warnf is just a shorthand for Logf(ctx, logger.LevelWarn, ...)

Types

type Emitter

type Emitter = types.Emitter

Emitter is just a type-alias for logger/types.Emitter for convenience.

func GetEmitter

func GetEmitter(ctx context.Context) Emitter

GetEmitter returns the Emitter (see the description of interface "Emitter").

type Entry

type Entry = types.Entry

Entry is just a type-alias for logger/types.Entry for convenience.

type EntryProperty

type EntryProperty = types.EntryProperty

EntryProperty is just a type-alias for logger/types.EntryProperty for convenience.

type Hook

type Hook = types.Hook

Hook is just a type-alias for logger/types.Hook for convenience.

type Hooks

type Hooks = types.Hooks

Hooks is just a type-alias for logger/types.Hooks for convenience.

type Level

type Level = types.Level

Level is used to define severity of messages to be reported.

func GetLevel

func GetLevel(ctx context.Context) Level

GetLevel returns the current logging level (see description of "Level").

type Logger

type Logger = types.Logger

Logger is just a type-alias for logger/types.Logger for convenience.

func FromBelt

func FromBelt(belt *belt.Belt) Logger

FromBelt returns the Logger defined in the Belt. If one is not defined, then the default Logger is returned (see function "Default").

func FromCtx

func FromCtx(ctx context.Context) Logger

FromCtx returns the Logger defined in the context. If one is not defined, then the default Logger is returned (see function "Default").

func WithContextFields

func WithContextFields(ctx context.Context, allFields *field.FieldsChain, newFieldsCount int) Logger

WithContextFields is a shorthand for FromCtx(ctx).WithContextFields

func WithField

func WithField(ctx context.Context, key string, value any, props ...field.Property) Logger

WithField returns the logger with the added field (used for structured logging).

func WithFields

func WithFields(ctx context.Context, fields field.AbstractFields) Logger

WithFields returns the logger with the added fields (used for structured logging)

func WithHooks

func WithHooks(ctx context.Context, hooks ...Hook) Logger

WithHooks returns a Logger which includes/appends hooks from the arguments.

See also description of "Hook".

Special case: to reset hooks use `WithHooks()` (without any arguments).

func WithLevel

func WithLevel(ctx context.Context, level Level) Logger

WithLevel returns a logger with logger level set to the given argument.

See also the description of type "Level".

func WithMessagePrefix

func WithMessagePrefix(ctx context.Context, prefix string) Logger

WithMessagePrefix adds a string to all messages logged through the derived logger.

func WithPreHooks

func WithPreHooks(ctx context.Context, preHooks ...PreHook) Logger

WithPreHooks returns a Logger which includes/appends pre-hooks from the arguments.

See also description of "PreHook".

Special case: to reset hooks use `WithPreHooks()` (without any arguments).

func WithTraceIDs

func WithTraceIDs(ctx context.Context, traceIDs belt.TraceIDs, newTraceIDsCount int) Logger

WithTraceIDs is a shorthand for FromCtx(ctx).WithTraceIDs.

type PreHook

type PreHook = types.PreHook

PreHook is just a type-alias for logger/types.PreHook for convenience.

type PreHooks

type PreHooks = types.PreHooks

PreHooks is just a type-alias for logger/types.PreHooks for convenience.

Directories

Path Synopsis
hooks
implementation
zap
Package types of logger unifies different types of loggers into interfaces Logger.
Package types of logger unifies different types of loggers into interfaces Logger.

Jump to

Keyboard shortcuts

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