ligno

package module
v0.0.0-...-93f5fbf Latest Latest
Warning

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

Go to latest
Published: Apr 18, 2017 License: BSD-3-Clause Imports: 17 Imported by: 1

README

Ligno

Go Report Card Build Status codecov status GoDoc

Ligno is async structured logging library for golang.

Two main features that separate ligno from other logging libraries are that all messages are logged asynchronously, which means that all expensive operations related to logging (like writing to file or sending log message over network) will not block execution of main application.

Other main feature is format of log record. Log record in ligno is just a map holding key-value pairs. That's it. Because of this, records can be formatted to any structured format (JSON, YAML, key=value) or to traditional formats that we are used to seeing in log files.

Install

Run go get github.com/delicb/ligno from command line.

Example

Just a quick example of how usage might look like.

package main

import (
	"os"

	"github.com/delicb/ligno"
)

func main() {
	// Create new logger with some context and handlers.
	l := ligno.GetLoggerOptions("myLogger", ligno.LoggerOptions{
		Context:            ligno.Ctx{"always": "present"},
		Handler:            ligno.StreamHandler(os.Stdout, ligno.JSONFormat(true)),
		Level:              ligno.INFO,
		BufferSize:         256,
		PreventPropagation: true,
	})

	// Log message. This will result in records in following format:
	//	{
	//    "time": "2016-01-07T01:06:10.937122038Z",
	//    "level": "INFO",
	//    "message": "Some event occurred.",
	//    "context": {
	//        "always": "present",
	//        "key": "value",
	//        "key2": "value2"
	//    }
	//	}
	l.Info("Some event occurred.", "key", "value", "key2", "value2")

	// Log message in DEBUG level, which will be ignored since this logger is
	// configured to log only INFO messages and above
	l.Debug("Will be discarded.")

	// Since logger is async, wait for it to finish processing log messages
	l.Wait()
}

Output would look like:

{
    "time": "2016-02-09T20:58:39.313122319Z",
    "level": "INFO",
    "message": "Some event occurred.",
    "context": {
        "always": "present",
        "key": "value",
        "key2": "value2"
    }
}

Setup of new logger still has some boilerplate code, I intend to spend some time on figuring out better API for it.

Benchmarks

I have not used builtin golang benchmarks to measure performance yet, but I did hack up small script that compares ligno with bunch of other logging frameworks, including golang stdlib. With every logger some number of messages are logged to stdout (1024 by default) and two numbers are produced for every logger: average time and total time. Average time is time spent in logging library (overhead that application using logger sees) for one message. Total is time that logging library took to process all messages. Since ligno is async, this is not simply average * number of messages, it will be higher, so I included it to compare overall performance with other logging libraries. For me, time spent in logging library for single message is much more important then total time spent processing log messages (since that overhead will come at the end of program execution), but I think that it is only fair to include both.

Benchmarking script can be found in benchmark folder, but this is example of one output:

Logging 1024 messages.
Ligno           average time:      697ns, total time:     49.149567ms
resenje-logging average time:      824ns, total time:     78.261047ms
stdlib          average time:   39.565µs, total time:     40.569742ms
gommon          average time:   42.024µs, total time:     43.096594ms
seelog          average time:   63.808µs, total time:     66.869716ms
Log15           average time:   65.806µs, total time:     67.447484ms
logrus          average time:   67.856µs, total time:     69.542356ms
logxi           average time:   76.046µs, total time:     77.920668ms
Total execution time: 78.463182ms

Credits

I was reading bunch of articles and source code for existing logging libraries so if you recognize some pattern from somewhere else, it is quite possible that I have seen it there.

Note

At this point, ligno is just an idea that has been written down. But instead of using pen and paper, I used editor and wrote it in code. So, expect breaking changes and do not use it in production just yet - I am not (but I intend to).

TODO

This is only just a skeleton, much is left.

  • Implementation of other handlers.
  • Implementation of other formatters, like feature rich JSON formatter, YAML formatter, etc...
  • Integration with other frameworks (logging, web or other).
  • Documentation.
  • Tests.

Documentation

Overview

Package ligno is async structured logging library for golang.

Two main features that separate ligno from other logging libraries are that all messages are logged asynchronously, which means that all expensive operations related to logging (like writing to file or sending log message over network) will not block execution of main application.

Other main feature is format of log record. Log record in ligno is just a map holding key-value pairs. That's it. Because of this, records can be formatted to any structured format (JSON, YAML, key=value) or to traditional formats that we are used to seeing in log files.

Index

Constants

View Source
const (
	NOTSET   Level = iota
	DEBUG          = iota * 10
	INFO           = iota * 10
	WARNING        = iota * 10
	ERROR          = iota * 10
	CRITICAL       = iota * 10
)

Levels of log records. Additional can be created, these are just defaults offered by library.

View Source
const DefaultTimeFormat = "2006-01-02 15:05:06.0000"

DefaultTimeFormat is default time format.

Variables

View Source
var (
	// DefaultTheme defines theme used by default.
	DefaultTheme = &theme{
		timeColor:     color.New(color.FgWhite, color.Faint).SprintfFunc(),
		debugColor:    color.New(color.FgWhite).SprintfFunc(),
		infoColor:     color.New(color.FgHiWhite).SprintfFunc(),
		warningColor:  color.New(color.FgYellow).SprintfFunc(),
		errorColor:    color.New(color.FgHiRed).SprintfFunc(),
		criticalColor: color.New(color.BgRed, color.FgHiWhite).SprintfFunc(),
	}

	// NoColorTheme defines theme that does not color any output.
	NoColorTheme = &theme{
		timeColor:     fmt.Sprintf,
		debugColor:    fmt.Sprintf,
		infoColor:     fmt.Sprintf,
		warningColor:  fmt.Sprintf,
		errorColor:    fmt.Sprintf,
		criticalColor: fmt.Sprintf,
	}
)

Functions

func Critical

func Critical(event string, pairs ...interface{})

Critical creates log record and queues it for processing with CRITICAL level. Additional parameters have same semantics as in Log method.

func CriticalCtx

func CriticalCtx(message string, ctx Ctx)

CriticalCtx logs message in CRITICAL level with provided context.

func Debug

func Debug(event string, pairs ...interface{})

Debug creates log record and queues it for processing with DEBUG level. Additional parameters have same semantics as in Log method.

func DebugCtx

func DebugCtx(message string, ctx Ctx)

DebugCtx logs message in DEBUG level with provided context.

func Error

func Error(event string, pairs ...interface{})

Error creates log record and queues it for processing with ERROR level. Additional parameters have same semantics as in Log method.

func ErrorCtx

func ErrorCtx(message string, ctx Ctx)

ErrorCtx logs message in ERROR level with provided context.

func Fatal

func Fatal(v ...interface{})

Fatal formats message according to stdlib rules, logs it in CRITICAL level and exists application.

func Fatalf

func Fatalf(format string, v ...interface{})

Fatalf formats message according to stdlib rules, logs it in CRITICAL level and exists application.

func Fatalln

func Fatalln(v ...interface{})

Fatalln formats message according to stdlib rules, logs it in CRITICAL level and exists application.

func Info

func Info(event string, pairs ...interface{})

Info creates log record and queues it for processing with INFO level. Additional parameters have same semantics as in Log method.

func InfoCtx

func InfoCtx(message string, ctx Ctx)

InfoCtx logs message in INFO level with provided context.

func Log

func Log(level Level, event string, pairs ...interface{})

Log creates record and queues it for processing. Required parameters are level for record and event that occurred. Any additional parameters will be transformed to key-value pairs for record in order in which they were provided. There should be even number of them, but in case that there is on number of parameters, empty string is appended. Example:

defaultLog.Log(INFO, "User logged in", "user_id", user_id, "platform", PLATFORM_NAME)

will be translated into log record with following keys:

{LEVEL: INFO", EVENT: "User logged in", "user_id": user_id, "platform": PLATFORM_NAME}

func LogCtx

func LogCtx(level Level, message string, ctx Ctx)

LogCtx adds provided message in specified level.

func Panic

func Panic(v ...interface{})

Panic formats message according to stdlib rules, logs it in CRITICAL level and panics.

func Panicf

func Panicf(format string, v ...interface{})

Panicf formats message according to stdlib rules, logs it in CRITICAL level and panics.

func Panicln

func Panicln(v ...interface{})

Panicln formats message according to stdlib rules, logs it in CRITICAL level and panics.

func Print

func Print(v ...interface{})

Print formats message according to stdlib rules and logs it in INFO level.

func Printf

func Printf(format string, v ...interface{})

Printf formats message according to stdlib rules and logs it in INFO level.

func Println

func Println(v ...interface{})

Println formats message according to stdlib rules and logs it in INFO level.

func SetHandler

func SetHandler(handler Handler)

SetHandler sets new handler for default logger.

func WaitAll

func WaitAll()

WaitAll blocks until all loggers are finished with message processing.

func WaitAllTimeout

func WaitAllTimeout(t time.Duration) bool

WaitAllTimeout blocks until all messages send to all loggers are processed or max specified amount of time. Boolean return value indicates if function returned because all messages were processed (true) or because timeout has expired (false).

func Warning

func Warning(event string, pairs ...interface{})

Warning creates log record and queues it for processing with WARNING level. Additional parameters have same semantics as in Log method.

func WarningCtx

func WarningCtx(message string, ctx Ctx)

WarningCtx logs message in WARNING level with provided context.

Types

type Ctx

type Ctx map[string]interface{}

Ctx is additional context for log record.

type Formatter

type Formatter interface {
	Format(record Record) []byte
}

Formatter is interface for converting log record to string representation.

func JSONFormat

func JSONFormat(pretty bool) Formatter

JSONFormat is simple formatter that only marshals log record to json.

func SimpleFormat

func SimpleFormat() Formatter

SimpleFormat returns formatter that formats record with bare minimum of information. Intention of this formatter is to simulate standard library formatter.

func TerminalFormat

func TerminalFormat() Formatter

TerminalFormat returns ThemeTerminalFormat with default theme set.

func ThemedTerminalFormat

func ThemedTerminalFormat(theme Theme) Formatter

ThemedTerminalFormat returns formatter that produces records formatted for easy reading in terminal, but that are a bit richer then SimpleFormat (this one includes context keys)

type FormatterFunc

type FormatterFunc func(Record) []byte

FormatterFunc is function type that implements Formatter interface.

func (FormatterFunc) Format

func (ff FormatterFunc) Format(record Record) []byte

Format is implementation of Formatter interface. It just calls function.

type Handler

type Handler interface {
	// Handle processes provided log record.
	Handle(Record) error
}

Handler processes log records and writes them to appropriate destination.

func CombiningHandler

func CombiningHandler(handlers ...Handler) Handler

CombiningHandler creates and returns handler that passes records to all provided handlers.

func FileHandler

func FileHandler(fileName string, formatter Formatter) Handler

FileHandler writes log records to file with provided name.

func FilterHandler

func FilterHandler(predicate Predicate, handler Handler) Handler

FilterHandler checks records if by using predicate to check if they should be processed and only if they do, record is passed to provided handler.

func FilterLevelHandler

func FilterLevelHandler(level Level, handler Handler) Handler

FilterLevelHandler is FilterHandler with default predicate function that filters all records below provided level.

func NullHandler

func NullHandler() Handler

NullHandler returns handler that discards all records.

func StreamHandler

func StreamHandler(out io.Writer, formatter Formatter) Handler

StreamHandler writes records to provided io.Writer

func SyslogHandler

func SyslogHandler(formatter Formatter, tag string, priority syslog.Priority) Handler

SyslogHandler creates new syslog handler with provided config variables.

type HandlerCloser

type HandlerCloser interface {
	Close()
}

HandlerCloser is interface that allows handlers to be closed. If handler implements this interface, when logger is stopped, Close will be called.

type HandlerFunc

type HandlerFunc func(Record) error

HandlerFunc is function that implements Handler interface.

func (HandlerFunc) Handle

func (hf HandlerFunc) Handle(record Record) error

Handle just calls HandlerFunc.

type InspectHandler

type InspectHandler interface {
	Handler
	Messages() []string
}

InspectHandler is handler that is able to restore logged message and return them for inspection.

func MemoryHandler

func MemoryHandler(formatter Formatter) InspectHandler

MemoryHandler returns handler instance that saves all message to memory.

type Level

type Level uint

Level represents rank of message importance. Log records can contain level and filters can decide not to process records based on this level.

func AddLevel

func AddLevel(name string, rank Level) (Level, error)

AddLevel add new level to system with provided name and rank. It is forbidden to register levels that already exist.

func (Level) MarshalJSON

func (l Level) MarshalJSON() ([]byte, error)

MarshalJSON returns levels JSON representation (implementation of json.Marshaler)

func (Level) String

func (l Level) String() string

String returns level's string representation.

func (*Level) UnmarshalJSON

func (l *Level) UnmarshalJSON(b []byte) error

UnmarshalJSON recreates level from JSON representation (implementation of json.Unmarshaler)

type Logger

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

Logger is central data type in ligno which represents logger itself. Logger is first level of processing events. It creates them and queues for async processing. It holds slice of Handlers that process messages and context (set of key-value pairs that will be include in every log record).

func GetLogger

func GetLogger(name string) *Logger

GetLogger returns logger with provided name (creating it if needed). Name is dot-separated string with parent logger names and this function will create all intermediate loggers with default options.

func GetLoggerOptions

func GetLoggerOptions(name string, options LoggerOptions) *Logger

GetLoggerOptions returns logger with provided name (creating it if needed). Name is dot-separated string with parent loggers and this function will create all intermediate loggers with default options. Provided options will be applied only to last logger in chain. If all loggers in chain already exist, no new loggers will be creates and provided options will be discarded. If options different then default are needed for intermediate loggers, create them first with appropriate options.

func (*Logger) Critical

func (l *Logger) Critical(message string, pairs ...interface{})

Critical creates log record and queues it for processing with CRITICAL level. Additional parameters have same semantics as in Log method.

func (*Logger) CriticalCtx

func (l *Logger) CriticalCtx(message string, ctx Ctx)

CriticalCtx logs message in CRITICAL level with provided context.

func (*Logger) Debug

func (l *Logger) Debug(message string, pairs ...interface{})

Debug creates log record and queues it for processing with DEBUG level. Additional parameters have same semantics as in Log method.

func (*Logger) DebugCtx

func (l *Logger) DebugCtx(message string, ctx Ctx)

DebugCtx logs message in DEBUG level with provided context.

func (*Logger) Error

func (l *Logger) Error(message string, pairs ...interface{})

Error creates log record and queues it for processing with ERROR level. Additional parameters have same semantics as in Log method.

func (*Logger) ErrorCtx

func (l *Logger) ErrorCtx(message string, ctx Ctx)

ErrorCtx logs message in ERROR level with provided context.

func (*Logger) Fatal

func (l *Logger) Fatal(v ...interface{})

Fatal formats message according to stdlib rules, logs it in CRITICAL level and exists application.

func (*Logger) Fatalf

func (l *Logger) Fatalf(format string, v ...interface{})

Fatalf formats message according to stdlib rules, logs it in CRITICAL level and exists application.

func (*Logger) Fatalln

func (l *Logger) Fatalln(v ...interface{})

Fatalln formats message according to stdlib rules, logs it in CRITICAL level and exists application.

func (*Logger) FullName

func (l *Logger) FullName() string

FullName returns name of this logger prefixed with name of its parent, separated by ".". This happens recursively, so return value will contain names of all parents.

func (*Logger) Handler

func (l *Logger) Handler() Handler

Handler returns current handler for this logger

func (*Logger) Info

func (l *Logger) Info(message string, pairs ...interface{})

Info creates log record and queues it for processing with INFO level. Additional parameters have same semantics as in Log method.

func (*Logger) InfoCtx

func (l *Logger) InfoCtx(message string, ctx Ctx)

InfoCtx logs message in INFO level with provided context.

func (*Logger) IsCritical

func (l *Logger) IsCritical() bool

IsCritical returns true if logger will process messages in Critical level

func (*Logger) IsDebug

func (l *Logger) IsDebug() bool

IsDebug returns true if logger will process messages in DEBUG level

func (*Logger) IsEnabledFor

func (l *Logger) IsEnabledFor(level Level) bool

IsEnabledFor returns true if logger will process records with provided level.

func (*Logger) IsError

func (l *Logger) IsError() bool

IsError returns true if logger will process messages in ERROR level

func (*Logger) IsInfo

func (l *Logger) IsInfo() bool

IsInfo returns true if logger will process messages in INFO level

func (*Logger) IsLevel

func (l *Logger) IsLevel(level Level) bool

IsLevel return true if logger will process messages in provided level.

func (*Logger) IsRunning

func (l *Logger) IsRunning() bool

IsRunning returns boolean indicating if this logger is still running.

func (*Logger) IsWarning

func (l *Logger) IsWarning() bool

IsWarning returns true if logger will process messages in WARNING level

func (*Logger) Level

func (l *Logger) Level() Level

Level returns minimal level that this logger will process.

func (*Logger) Log

func (l *Logger) Log(calldepth int, level Level, message string, pairs ...interface{})

Log creates record and queues it for processing. Required parameters are level for record and event that occurred. Any additional parameters will be transformed to key-value pairs for record in order in which they were provided. There should be even number of them, but in case that there is on number of parameters, empty string is appended. Example:

l.Log(INFO, "User logged in", "user_id", user_id, "platform", PLATFORM_NAME)

will be translated into log record with following keys:

{LEVEL: INFO", EVENT: "User logged in", "user_id": user_id, "platform": PLATFORM_NAME}

func (*Logger) LogCtx

func (l *Logger) LogCtx(calldepth int, level Level, message string, data Ctx)

LogCtx adds provided message in specified level.

func (*Logger) Name

func (l *Logger) Name() string

Name returns name of this logger.

func (*Logger) Panic

func (l *Logger) Panic(v ...interface{})

Panic formats message according to stdlib rules, logs it in CRITICAL level and panics.

func (*Logger) Panicf

func (l *Logger) Panicf(format string, v ...interface{})

Panicf formats message according to stdlib rules, logs it in CRITICAL level and panics.

func (*Logger) Panicln

func (l *Logger) Panicln(v ...interface{})

Panicln formats message according to stdlib rules, logs it in CRITICAL level and panics.

func (*Logger) Print

func (l *Logger) Print(v ...interface{})

Print formats message according to stdlib rules and logs it in INFO level.

func (*Logger) Printf

func (l *Logger) Printf(format string, v ...interface{})

Printf formats message according to stdlib rules and logs it in INFO level.

func (*Logger) Println

func (l *Logger) Println(v ...interface{})

Println formats message according to stdlib rules and logs it in INFO level.

func (*Logger) SetHandler

func (l *Logger) SetHandler(handler Handler)

SetHandler set handler to this logger to be used from now on.

func (*Logger) StopAndWait

func (l *Logger) StopAndWait()

StopAndWait stops listening for new messages sent to this logger and blocks until all previously arrived messages are processed. Records already sent will be processed, but all new messages will silently be dropped.

func (*Logger) StopAndWaitTimeout

func (l *Logger) StopAndWaitTimeout(t time.Duration) (finished bool)

StopAndWaitTimeout stops listening for new messages sent to this logger and blocks until all previously sent message are processed or max provided duration. Records already sent will be processed, but all new messages will silently be dropped. Return value indicates if all messages are processed (true) or if provided timeout expired (false)

func (*Logger) SubLogger

func (l *Logger) SubLogger(name string) *Logger

SubLogger creates new logger that has current logger as parent with default options and starts it so it is ready for message processing.

func (*Logger) SubLoggerOptions

func (l *Logger) SubLoggerOptions(name string, options LoggerOptions) *Logger

SubLoggerOptions creates new logger that has current logger as parent with provided options and starts it so it is ready for message processing.

func (*Logger) Wait

func (l *Logger) Wait()

Wait block until all messages sent to logger are processed. If timeout is needed, see WaitTimeout.

func (*Logger) WaitTimeout

func (l *Logger) WaitTimeout(t time.Duration) (finished bool)

WaitTimeout blocks until all messages send to logger are processed or max specified amount of time. Boolean return value indicates if function returned because all messages were processed (true) or because timeout has expired (false).

func (*Logger) Warning

func (l *Logger) Warning(message string, pairs ...interface{})

Warning creates log record and queues it for processing with WARNING level. Additional parameters have same semantics as in Log method.

func (*Logger) WarningCtx

func (l *Logger) WarningCtx(message string, ctx Ctx)

WarningCtx logs message in WARNING level with provided context.

func (*Logger) Write

func (l *Logger) Write(p []byte) (n int, err error)

Write is implementation of io.Writer interface for logger. It writes provided message in INFO level to logger.

type LoggerOptions

type LoggerOptions struct {
	// Context that logger should have.
	Context Ctx
	// Handler for processing records.
	Handler Handler
	// Level is minimal level that logger will process.
	Level Level
	// BufferSize is size of buffer for records that will be process async.
	BufferSize int
	// PreventPropagation is flag that indicates if records should be passed
	// to parent logger for processing.
	PreventPropagation bool
	// Flag that indicates that file and line of place where logging took place
	// should be kept. Note that this is expensive, so use with care. If this
	// information will be shown depends on formatter.
	IncludeFileAndLine bool
}

LoggerOptions is container for configuration options for logger instances. Empty value is valid for initializing logger.

type Predicate

type Predicate func(Record) bool

Predicate is function that returns true if record should be logged, false otherwise.

type Record

type Record struct {
	Time    time.Time `json:"time"`
	Level   Level     `json:"level"`
	Message string    `json:"message"`
	Context Ctx       `json:"context"`
	Logger  *Logger   `json:"-"`
	File    string    `json:"file"`
	Line    int       `json:"line"`
}

Record holds information about one log message.

type Theme

type Theme interface {
	Time(msg string, args ...interface{}) string
	Debug(msg string, args ...interface{}) string
	Info(msg string, args ...interface{}) string
	Warning(msg string, args ...interface{}) string
	Error(msg string, args ...interface{}) string
	Critical(msg string, args ...interface{}) string
	ForLevel(level Level) func(msg string, args ...interface{}) string
}

Theme is definition of interface needed for colorizing log message to console.

Directories

Path Synopsis
example

Jump to

Keyboard shortcuts

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