scribe

package
v0.4.1 Latest Latest
Warning

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

Go to latest
Published: Apr 19, 2020 License: BSD-3-Clause Imports: 9 Imported by: 11

Documentation

Overview

Package scribe represents a functional abstraction for unifying loggers. It's much more than a shim-like façade (à la SLF4J), in that it does not have to transform from one API to another. Instead, Scribe uses function pointers to expose a standard printf-style API to the application where this is possible (where the underlying logger provides a printf-style interface out of the box). In that sense, Scribe is more of a mechanism for standardising and organising logger implementations. Where the underlying logger does not expose a printf-style contract, Scribe can be used with a shim binding.

Scribe supports log enrichment, allowing the user to provide additional contextual metadata. (This is called a Scene.) It achieves the same goal as a traditional façade, without creating a layer of indirection and increasing the depth of the call stack. Therefore, the original call site information is preserved. (By contrast, a naive façade/adapter/shim injects itself into the call stack, reporting the wrong file name and line number to the logger.)

Scribe is thread-safe; multiple goroutines may use the same instance.

Index

Constants

View Source
const DefaultEnabledLevel = Trace

DefaultEnabledLevel specifies the level of logging which is enabled by default. This includes all levels having higher ordinals.

Variables

View Source
var Levels = map[Level]LevelSpec{
	All:   {All, "All", "ALL"},
	Trace: {Trace, "Trace", "TRC"},
	Debug: {Debug, "Debug", "DBG"},
	Info:  {Info, "Info", "INF"},
	Warn:  {Warn, "Warn", "WRN"},
	Error: {Error, "Error", "ERR"},
	Off:   {Off, "Off", "WRN"},
}

Levels lists built-in levels (including the two symbolic ones, All and Off), mapping them to their descriptions.

Custom levels can be defined (provided they conform to the Level data type); the knowledge of such levels will remain within the confines of the user application.

Functions

func LevelName

func LevelName(level Level) (string, error)

LevelName gets the name of the given level, if one is known. An error is returned if the level is not among the known Levels map. In the error case, the name will contain its ordinal.

func LevelNameAbbreviated

func LevelNameAbbreviated(level Level) (string, error)

LevelNameAbbreviated gives the abbreviated name for a given level. An error is returned if the level is not among the known Levels map. In the error case, the name will contain its ordinal.

func Nop

func Nop(_ string, _ ...interface{})

Nop is a no-op logger function.

func Space

func Space(buffer *bytes.Buffer)

Space appends a whitespace character to the given buffer if the latter is non-empty. This function is used to separate fields.

func WriteScene

func WriteScene(buffer *bytes.Buffer, scene Scene)

WriteScene is a utility for compactly writing scene contents to an output writer.

Types

type Assertion

type Assertion func(e Entries) *string

Assertion is a verification of Entries that returns a nil string if the assertion passes, or a string describing the nature of the failure otherwise. MockScribe will append the stack trace behind the scenes.

func Count

func Count(expected int) Assertion

Count ensures that the number of entries equals the given expected number.

func CountAtLeast

func CountAtLeast(minimum int) Assertion

CountAtLeast ensures that there is a minimum number of entries.

func CountAtMost

func CountAtMost(maximum int) Assertion

CountAtMost ensures that there is a maximum number of entries.

type DynamicAssertion

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

DynamicAssertion permits the testing of captures housed by the ScribeMock, rather than the Entries snapshot. Owing to this, assertions created from a DynamicAssertion are evaluated against a refreshed Entries snapshot. This is ideal for performing time-based assertions, where a condition might be satisfied after some time (when the application eventually logs the missing entry).

func (DynamicAssertion) Having

Having applies a predicate to the DynamicAssertion.

func (DynamicAssertion) Passes

Passes returns an Assertion that is evaluated against a refreshed Entries snapshot.

type Entries

type Entries interface {
	Having(p Predicate) Entries
	List() []Entry
	Length() int
	Assert(t check.Tester, a Assertion) Entries
}

Entries is an immutable snapshot of captured log calls.

type Entry

type Entry struct {
	Timestamp time.Time
	Level     Level
	Format    string
	Args      []interface{}
	Scene     Scene
}

Entry is a single, captured log entry.

func (Entry) FormattedMessage

func (e Entry) FormattedMessage() string

FormattedMessage returns the application of fmt.Sprintf to Entry.Format and Entry.Args.

func (Entry) String

func (e Entry) String() string

String obtains a textual representation of an entry.

type Fields

type Fields map[string]interface{}

Fields is a free-form set of attributes that can be captured as part of a Scene, supporting log enrichment and structured logging.

type Hook

type Hook func(level Level, scene *Scene, format *string, args *[]interface{})

Hook is a function that can inspect log arguments before they are passed to the underlying logger, and potentially modify these arguments.

The supplied log level cannot be modified, as log factories are provided on a per log level basis. The other arguments can be modified by assignment through dereferencing:

*scene = Scene{...}
*format = "new format with %s %s"
*args = []interface{}{"new", "args"}

func AppendScene

func AppendScene() Hook

AppendScene is a hook that appends the contents of the captured scene after the formatted log message.

type Level

type Level uint8

Level of logging. The lowest ordinal corresponds to the most fine-grained level. By convention, a level subsumes all levels above it, meaning that if a level L is enabled, then any level M, where M > L, is also enabled.

const (
	// All is a symbolic value for the lowest possible level. It does not actually get logged, but is useful for
	// addressing all levels above it (for example, to enable all logging).
	All Level = 0

	// Trace is the most fine-grained level that actually gets logged.
	Trace Level = 10

	// Debug level.
	Debug Level = 20

	// Info level.
	Info Level = 30

	// Warn level
	Warn Level = 40

	// Error is the most coarse-grained level that actually gets logged.
	Error Level = 50

	// Off is a symbolic value for the highest possible level. It does not actually get logged, but is useful for
	// addressing all levels below it (for example, to disable all logging).
	Off Level = 200
)

We allocate ordinals by hand rather than use a iota to ensure that we can add more levels in the future without breaking compatibility. In addition, the user may specify their own log level, provided it conforms to the Level data type.

func (Level) String

func (l Level) String() string

String obtains a textual depiction of the log level.

type LevelSpec

type LevelSpec struct {
	Level       Level
	Name        string
	Abbreviated string
}

LevelSpec describes a log level.

func ParseLevelName

func ParseLevelName(name string) (LevelSpec, error)

ParseLevelName locates a LevelSpec for a given name string, returning an error if none could be matched.

func (LevelSpec) String

func (ls LevelSpec) String() string

String obtains a textual representation of a LevelSpec.

type Logger

type Logger func(format string, args ...interface{})

Logger is a single-use function for logging output. It is meant to be used at the point where the application is ready to submit the log message. This is not a constraint as such; it allows for the capture of contextual scene metadata.

Implementations may reuse a single instance of a logger function if they don't care about scene capture or deal with race-prone state.

type LoggerFactories

type LoggerFactories map[Level]LoggerFactory

LoggerFactories is used to configure Scribe, specifying a LogFactory for each supported level.

func BindFmt

func BindFmt() LoggerFactories

BindFmt creates a binding for the logger used by fmt. There are several issues with fmt:

  1. It's Printf has return values, making it incompatible with Scribe.
  2. It does not add a newline.

As a result of these limitations, this binding is implemented as a shim, rather than a function pointer. In practice, this is of no consequence, as fmt does not care about caller site information.

func BindLogPrintf

func BindLogPrintf(logger ...*log.Logger) LoggerFactories

BindLogPrintf creates a pass-through binding for log.Printf(). An optional Logger instance can be specified; if omitted, the standard logger will be used.

func ShimFacs

func ShimFacs(facs LoggerFactories, hook Hook) LoggerFactories

ShimFacs applies a shim to all factories in facs, using the given hook, returning an equivalent map of shimmed factories.

func StandardBinding

func StandardBinding() LoggerFactories

StandardBinding creates a shim-based binding for log.Printf(), appending scene information. This should be the default binding used by applications that have not yet adopted a logging framework.

type LoggerFactory

type LoggerFactory func(level Level, scene Scene) Logger

LoggerFactory specifies the behaviour for constructing a logger instance. The log factory is called upon each time a logger is requested — every time an application needs to log something.

func Fac

func Fac(logger Logger) LoggerFactory

Fac wraps a given reusable logger function in a factory. Useful for simple loggers that don't care about scene metadata, and willing to recycle the same logging function.

func ShimFac

func ShimFac(fac LoggerFactory, hook Hook) LoggerFactory

ShimFac applies a shim to fac, using the given hook. The result is a LoggerFactory that applies the hook before invoking the shimmed logger.

Note: shimming is an intrusive process that changes the call site from the perspective of the underlying logger. Shimming and hooks within Scribe should only be used for debugging, or to enhance loggers that don't natively support features such as structured logging. Where possible, use the native hooks provided by your chosen logging framework.

type MockScribe

type MockScribe interface {
	Factories() LoggerFactories
	Reset()
	Entries() Entries
	ContainsEntries() DynamicAssertion
}

MockScribe provides a facility for mocking a Scribe, capturing log calls for subsequent inspection, filtering and assertions. This implementation is thread-safe.

func NewMock

func NewMock() MockScribe

NewMock creates a new MockScribe. The returning instance cannot be used to log directly — only to inspect and assert captures. To configure a Scribe to use the mocks for subsequent logging:

 mock := scribe.NewMock()
	scribe := scribe.New(mock.Factories())

type Predicate

type Predicate func(e Entry) bool

Predicate is a condition evaluated against a given entry, returning true if the underlying condition has been satisfied.

func ASceneWith

func ASceneWith(p ScenePredicate) Predicate

ASceneWith returns a conventional (Entry) predicate that satisfies the given ScenePredicate.

func Anything

func Anything() Predicate

Anything is a predicate that matches any entry. It is useful for taking a copy of Entries:

exactCopy := original.Having(scribe.Anything())

func LogLevel

func LogLevel(level Level) Predicate

LogLevel matches all entries that are logged at the given level.

func MessageContaining

func MessageContaining(substr string) Predicate

MessageContaining matches entries where the formatted message contains the given substr.

func MessageEqual

func MessageEqual(expected string) Predicate

MessageEqual matches entries where the formatted message exactly matches the expected string.

func Not

func Not(p Predicate) Predicate

Not produces a logical inverse of a predicate.

type Scene

type Scene struct {
	Fields Fields
	Ctx    context.Context
	Err    error
}

Scene captures additional metadata from the call site that is forwarded to the logger. This is used to facilitate structured logging, pass contexts onto loggers, communicate application errors, and so forth.

func (Scene) IsSet

func (s Scene) IsSet() bool

IsSet returns true if the scene, meaning it has at least one field specified, a context set or carries an error.

func (Scene) String

func (s Scene) String() string

String obtains a textual representation of a Scene.

type ScenePredicate

type ScenePredicate func(scene Scene) bool

ScenePredicate is a refinement of the predicate concept, applying to the Scene field of an Entry (as opposed to the entire Entry struct).

func AField

func AField(name string, value interface{}) ScenePredicate

AField is satisfied if the scene contains a field with the given name-value pair.

func AFieldNamed

func AFieldNamed(name string) ScenePredicate

AFieldNamed is satisfied if the scene contains a field with the given name.

func AnError

func AnError() ScenePredicate

AnError is satisfied if the scene holds an error.

func Content

func Content() ScenePredicate

Content is satisfied if the scene has any of its fields set.

func (ScenePredicate) Invert

func (p ScenePredicate) Invert() ScenePredicate

Invert a scene predicate.

type Scribe

type Scribe interface {
	StdLogAPI
	Enabled() Level
	SetEnabled(level Level)
	Capture(scene Scene) StdLogAPI
}

Scribe is the starting point for invoking a logger. There is no concept of a default Scribe logger; one one must be constructed explicitly using NewScribe() and handed to the application. (Or the application may instantiate a singleton logger and use the same Scribe instance throughout.)

func New

func New(facs LoggerFactories) Scribe

New constructs a Scribe instance from the given facs configuration.

The supplied facs maps a supported log level to a corresponding LoggerFactory. Factories may be supplied individually for each supported log level. The special All level can be used to configure a default factory that will be applied to all built-in log levels that have not been explicitly configured in facs. If one of the built-in levels is not configured, and no default LogFactory is specified for All, this function will panic.

Custom log levels are supported by supplying a mapping for a custom Level. However, the default LogFactory specified for the All level does not apply to custom levels. In other words, each custom level requires an explicit LogFactory.

type StdLogAPI

type StdLogAPI interface {
	L(level Level) Logger
	T() Logger
	D() Logger
	I() Logger
	W() Logger
	E() Logger
}

StdLogAPI represents the standard way of interacting with Scribe.

Directories

Path Synopsis
Package glog provides a Glog binding for Scribe.
Package glog provides a Glog binding for Scribe.
Package log15 provides a Log15 binding for Scribe.
Package log15 provides a Log15 binding for Scribe.
Package logrus provides a Logrus binding for Scribe.
Package logrus provides a Logrus binding for Scribe.
Package overlog provides a reference logging implementation for Scribe.
Package overlog provides a reference logging implementation for Scribe.
Package seelog provides a Seelog binding for Scribe.
Package seelog provides a Seelog binding for Scribe.
Package zap provides a Zap binding for Scribe.
Package zap provides a Zap binding for Scribe.

Jump to

Keyboard shortcuts

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