logging

package
v0.1.68 Latest Latest
Warning

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

Go to latest
Published: Apr 29, 2023 License: Apache-2.0 Imports: 4 Imported by: 0

README

Logging

A common API for structured and unstructured logging with support for runtime-pluggable backends.

The main design goal is to allow for libraries that can integrate well with others without having to change their logging code. For example, a library can plug into the klog backend if it's being used together with the Kubernetes client.

A secondary design goal is to provide a home for a full-featured unstructured logging library, which we here call the "simple" backend. It supports rich formatting, including ANSI coloring when logging to a terminal, including on Windows.

API features:

  • Fine-grained control over verbosity via hierarchical log names. For example, "engine.parser.background" inherits from "engine.parser", which in turn inherits from "engine". The empty name is the root of the hierarchy. Each name's default verbosity is that of its parent, which you can then override with logging.SetMaxLevel.
  • Support for call stack depth. This can be used by a backend (for example, klog) to find out where in the code the logging happened.
  • No need to create loggers. The true API entrypoint is the global function logging.NewMessage, which you provide with a name and a level. The default logger type is just a convenient wrapper around it that provides the familiar unstructured functions.
  • That said, the logging.Logger type is an interface, allowing you to more easily switch implementations. For example, you can assign the logging.MOCK_LOGGER to disable a logger without changing your code. (It's unfortunate that Go's Logger type is a struct.)

Basic Usage

The easiest way to plug in a backend is to anonymously import the correct sub-package into your program's main package:

import (
    _ "github.com/tliron/kutil/logging/simple"
)

This should enable the backend with sensible defaults. Specifically it will log to stderr with verbosity at the "notice" max level.

Example of structured logging:

import (
    "github.com/tliron/kutil/logging"
    _ "github.com/tliron/kutil/logging/simple"
    "github.com/tliron/kutil/util"
)

func main() {
    if m := logging.NewMessage([]string{"engine", "parser"}, logging.Error, 0); m != nil {
        m.Set("message", "Hello world!").Set("myfloat", 10.2).Send()
    }
    util.Exit(0)
}

Note that logging.NewMessage will return nil if the message cannot be created, for example if the message level is higher than the max level for that name.

Set can accept any key and value, but two keys are recognized by the API:

  • message: The main description of the message. This is the key used by unstructured logging.
  • scope: An optional identifier that can be used to group messages, making them easier to filter (e.g. by grep). Backends may handle this specially. Unstructured backends may, for example, add it as a bracketed prefix for messages.

Also note that calling util.Exit(0) to exit your program is not absolutely necessary, however it's good practice because it makes sure to flush buffered log messages for some backends.

Example of unstructured logging:

import (
    "github.com/tliron/kutil/logging"
    _ "github.com/tliron/kutil/logging/simple"
    "github.com/tliron/kutil/util"
)

var log = logging.GetLogger("engine.parser")

func main() {
    log.Errorf("Hello %s!", "world")
    util.Exit(0)
}

Use conditional logging to optimize for costly unstructured message creation, e.g.:

if log.AllowLevel(logging.Debug) {
    log.Debugf("Status is: %s", getStatusFromDatabase())
}

The scope logger can be used to automatically set the "scope" key for another logger. It automatically detects nesting, in which case it appends the new scope separated by a ".", e.g.:

var log = logging.GetLogger("engine.parser")
var validationLog = logging.NewScopeLogger(log, "validation")
var syntaxLog = logging.NewScopeLogger(validationLog, "syntax")

func main() {
    // Nested scope will be "validation.syntax"
    syntaxLog.Errorf("Hello %s!", "world")
    ...
}

Configuration

All backends can be configured via the same API. For example, to increase verbosity and write to a file:

func main() {
    path := "myapp.log"
    logging.Configure(1, &path)
    ...
}

Backends may also have their own (non-portable) configuration APIs.

You can set the max level (verbosity) using either the global API or a logger. For example, here is a way to make all logging verbose by default, except for one name:

func init() {
    logging.SetMaxLevel(nil, logging.Debug) // nil = the root
    logging.SetMaxLevel([]string{"engine", "parser"}, logging.Error)
}

Note that descendents of "engine.parser", e.g. "engine.parser.analysis", would acquire its log levels rather than the root's. Here's the same effect using loggers:

var rootLog = logging.GetLogger("")
var parserLog = logging.GetLogger("engine.parser")

func init() {
    rootLog.SetMaxLevel(logging.Debug)
    parserLog.SetMaxLevel(logging.Error)
}

It's important to note that the configuration APIs are not thread safe. This includes Configure and SetMaxLevel. Thus, make sure to get all your configuration done before you start sending log messages. A good place for this is init() or main() functions.

Color

For the simple backend you must explicitly attempt to enable ANSI color if desired. Note that if it's unsupported by the terminal then no ANSI codes will be sent (unless you force it via terminal.EnableColor(true)):

import (
    "github.com/tliron/kutil/logging"
    _ "github.com/tliron/kutil/logging/simple"
    "github.com/tliron/kutil/terminal"
    "github.com/tliron/kutil/util"
)

func main() {
    terminal.EnableColor(false)
    logging.GetLogger("engine.parser").Error("Hello world!") // errors are in red
    util.Exit(0)
}

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AllowLevel added in v0.1.53

func AllowLevel(name []string, level Level) bool

func CallAndLogError added in v0.1.59

func CallAndLogError(f func() error, task string, log Logger)

func Configure

func Configure(verbosity int, path *string)

func GetWriter added in v0.1.22

func GetWriter() io.Writer

func SetBackend

func SetBackend(backend_ Backend)

func SetMaxLevel

func SetMaxLevel(name []string, level Level)

Types

type Backend

type Backend interface {
	// If "path" is nil will log to stdout, colorized if possible
	// The default "verbosity" 0 will log criticals, errors, warnings, and notices.
	// "verbosity" 1 will add infos. "verbosity" 2 will add debugs.
	// Set "verbostiy" to -1 to disable the log.
	Configure(verbosity int, path *string)
	GetWriter() io.Writer

	NewMessage(name []string, level Level, depth int) Message
	AllowLevel(name []string, level Level) bool
	SetMaxLevel(name []string, level Level)
	GetMaxLevel(name []string) Level
}

type BackendLogger added in v0.1.53

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

func NewBackendLogger added in v0.1.53

func NewBackendLogger(id []string) BackendLogger

func (BackendLogger) AllowLevel added in v0.1.53

func (self BackendLogger) AllowLevel(level Level) bool

Logger interface

func (BackendLogger) Critical added in v0.1.53

func (self BackendLogger) Critical(message string)

Logger interface

func (BackendLogger) Criticalf added in v0.1.53

func (self BackendLogger) Criticalf(format string, values ...any)

Logger interface

func (BackendLogger) Debug added in v0.1.53

func (self BackendLogger) Debug(message string)

Logger interface

func (BackendLogger) Debugf added in v0.1.53

func (self BackendLogger) Debugf(format string, values ...any)

Logger interface

func (BackendLogger) Error added in v0.1.53

func (self BackendLogger) Error(message string)

Logger interface

func (BackendLogger) Errorf added in v0.1.53

func (self BackendLogger) Errorf(format string, values ...any)

Logger interface

func (BackendLogger) GetMaxLevel added in v0.1.64

func (self BackendLogger) GetMaxLevel() Level

Logger interface

func (BackendLogger) Info added in v0.1.53

func (self BackendLogger) Info(message string)

Logger interface

func (BackendLogger) Infof added in v0.1.53

func (self BackendLogger) Infof(format string, values ...any)

Logger interface

func (BackendLogger) Log added in v0.1.53

func (self BackendLogger) Log(level Level, depth int, message string)

Logger interface

func (BackendLogger) Logf added in v0.1.53

func (self BackendLogger) Logf(level Level, depth int, format string, values ...any)

Logger interface

func (BackendLogger) NewMessage added in v0.1.53

func (self BackendLogger) NewMessage(level Level, depth int) Message

Logger interface

func (BackendLogger) Notice added in v0.1.53

func (self BackendLogger) Notice(message string)

Logger interface

func (BackendLogger) Noticef added in v0.1.53

func (self BackendLogger) Noticef(format string, values ...any)

Logger interface

func (BackendLogger) SetMaxLevel added in v0.1.53

func (self BackendLogger) SetMaxLevel(level Level)

Logger interface

func (BackendLogger) Warning added in v0.1.53

func (self BackendLogger) Warning(message string)

Logger interface

func (BackendLogger) Warningf added in v0.1.53

func (self BackendLogger) Warningf(format string, values ...any)

Logger interface

type Hierarchy added in v0.1.53

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

func NewMaxLevelHierarchy added in v0.1.54

func NewMaxLevelHierarchy() *Hierarchy

func (*Hierarchy) AllowLevel added in v0.1.53

func (self *Hierarchy) AllowLevel(name []string, level Level) bool

func (*Hierarchy) GetMaxLevel added in v0.1.54

func (self *Hierarchy) GetMaxLevel(name []string) Level

func (*Hierarchy) SetMaxLevel added in v0.1.53

func (self *Hierarchy) SetMaxLevel(name []string, level Level)

type Level

type Level int
const (
	None     Level = 0
	Critical Level = 1
	Error    Level = 2
	Warning  Level = 3
	Notice   Level = 4
	Info     Level = 5
	Debug    Level = 6
)

func GetMaxLevel added in v0.1.64

func GetMaxLevel(name []string) Level

func VerbosityToMaxLevel added in v0.1.53

func VerbosityToMaxLevel(verbosity int) Level

func (Level) String

func (self Level) String() string

fmt.Stringify interface

type Logger

type Logger interface {
	NewMessage(level Level, depth int) Message
	AllowLevel(level Level) bool
	SetMaxLevel(level Level)
	GetMaxLevel() Level

	Log(level Level, depth int, message string)
	Logf(level Level, depth int, format string, values ...any)

	Critical(message string)
	Criticalf(format string, values ...any)
	Error(message string)
	Errorf(format string, values ...any)
	Warning(message string)
	Warningf(format string, values ...any)
	Notice(message string)
	Noticef(format string, values ...any)
	Info(message string)
	Infof(format string, values ...any)
	Debug(message string)
	Debugf(format string, values ...any)
}

func GetLogger

func GetLogger(name string) Logger

func GetLoggerf

func GetLoggerf(format string, values ...any) Logger

type Message added in v0.1.53

type Message interface {
	Set(key string, value any) Message
	Send()
}

func NewMessage added in v0.1.53

func NewMessage(name []string, level Level, depth int) Message

type MockLogger added in v0.1.53

type MockLogger struct{}
var MOCK_LOGGER MockLogger

func (MockLogger) AllowLevel added in v0.1.53

func (self MockLogger) AllowLevel(level Level) bool

Logger interface

func (MockLogger) Critical added in v0.1.53

func (self MockLogger) Critical(message string)

Logger interface

func (MockLogger) Criticalf added in v0.1.53

func (self MockLogger) Criticalf(format string, values ...any)

Logger interface

func (MockLogger) Debug added in v0.1.53

func (self MockLogger) Debug(message string)

Logger interface

func (MockLogger) Debugf added in v0.1.53

func (self MockLogger) Debugf(format string, values ...any)

Logger interface

func (MockLogger) Error added in v0.1.53

func (self MockLogger) Error(message string)

Logger interface

func (MockLogger) Errorf added in v0.1.53

func (self MockLogger) Errorf(format string, values ...any)

Logger interface

func (MockLogger) GetMaxLevel added in v0.1.64

func (self MockLogger) GetMaxLevel() Level

Logger interface

func (MockLogger) Info added in v0.1.53

func (self MockLogger) Info(message string)

Logger interface

func (MockLogger) Infof added in v0.1.53

func (self MockLogger) Infof(format string, values ...any)

Logger interface

func (MockLogger) Log added in v0.1.53

func (self MockLogger) Log(level Level, depth int, message string)

Logger interface

func (MockLogger) Logf added in v0.1.53

func (self MockLogger) Logf(level Level, depth int, format string, values ...any)

Logger interface

func (MockLogger) NewMessage added in v0.1.53

func (self MockLogger) NewMessage(level Level, depth int) Message

Logger interface

func (MockLogger) Notice added in v0.1.53

func (self MockLogger) Notice(message string)

Logger interface

func (MockLogger) Noticef added in v0.1.53

func (self MockLogger) Noticef(format string, values ...any)

Logger interface

func (MockLogger) SetMaxLevel added in v0.1.53

func (self MockLogger) SetMaxLevel(level Level)

Logger interface

func (MockLogger) Warning added in v0.1.53

func (self MockLogger) Warning(message string)

Logger interface

func (MockLogger) Warningf added in v0.1.53

func (self MockLogger) Warningf(format string, values ...any)

Logger interface

type Node added in v0.1.53

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

func NewNode added in v0.1.53

func NewNode() *Node

type ScopeLogger added in v0.1.54

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

func NewScopeLogger added in v0.1.54

func NewScopeLogger(logger Logger, scope string) ScopeLogger

func (ScopeLogger) AllowLevel added in v0.1.54

func (self ScopeLogger) AllowLevel(level Level) bool

Logger interface

func (ScopeLogger) Critical added in v0.1.54

func (self ScopeLogger) Critical(message string)

Logger interface

func (ScopeLogger) Criticalf added in v0.1.54

func (self ScopeLogger) Criticalf(format string, values ...any)

Logger interface

func (ScopeLogger) Debug added in v0.1.54

func (self ScopeLogger) Debug(message string)

Logger interface

func (ScopeLogger) Debugf added in v0.1.54

func (self ScopeLogger) Debugf(format string, values ...any)

Logger interface

func (ScopeLogger) Error added in v0.1.54

func (self ScopeLogger) Error(message string)

Logger interface

func (ScopeLogger) Errorf added in v0.1.54

func (self ScopeLogger) Errorf(format string, values ...any)

Logger interface

func (ScopeLogger) GetMaxLevel added in v0.1.64

func (self ScopeLogger) GetMaxLevel() Level

Logger interface

func (ScopeLogger) Info added in v0.1.54

func (self ScopeLogger) Info(message string)

Logger interface

func (ScopeLogger) Infof added in v0.1.54

func (self ScopeLogger) Infof(format string, values ...any)

Logger interface

func (ScopeLogger) Log added in v0.1.54

func (self ScopeLogger) Log(level Level, depth int, message string)

Logger interface

func (ScopeLogger) Logf added in v0.1.54

func (self ScopeLogger) Logf(level Level, depth int, format string, values ...any)

Logger interface

func (ScopeLogger) NewMessage added in v0.1.54

func (self ScopeLogger) NewMessage(level Level, depth int) Message

Logger interface

func (ScopeLogger) Notice added in v0.1.54

func (self ScopeLogger) Notice(message string)

Logger interface

func (ScopeLogger) Noticef added in v0.1.54

func (self ScopeLogger) Noticef(format string, values ...any)

Logger interface

func (ScopeLogger) SetMaxLevel added in v0.1.54

func (self ScopeLogger) SetMaxLevel(level Level)

Logger interface

func (ScopeLogger) Warning added in v0.1.54

func (self ScopeLogger) Warning(message string)

Logger interface

func (ScopeLogger) Warningf added in v0.1.54

func (self ScopeLogger) Warningf(format string, values ...any)

Logger interface

type SendUnstructuredMessageFunc added in v0.1.53

type SendUnstructuredMessageFunc func(message string)

type UnstructuredMessage added in v0.1.53

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

func NewUnstructuredMessage added in v0.1.53

func NewUnstructuredMessage(send SendUnstructuredMessageFunc) *UnstructuredMessage

func (*UnstructuredMessage) Send added in v0.1.53

func (self *UnstructuredMessage) Send()

func (*UnstructuredMessage) Set added in v0.1.53

func (self *UnstructuredMessage) Set(key string, value any) Message

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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