onelog

package module
v0.0.0-...-8c2bb31 Latest Latest
Warning

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

Go to latest
Published: Mar 6, 2019 License: MIT Imports: 6 Imported by: 24

README

Build Status codecov Go Report Card Go doc MIT License

Onelog

Onelog is a dead simple but very efficient JSON logger. It is one of the fastest JSON logger out there. Also, it is one of the logger with the lowest allocation.

It gives more control over log levels enabled by using bitwise operation for setting levels on a logger.

It is also modular as you can add a custom hook, define level text values, level and message keys.

Go 1.9 is required as it uses a type alias over gojay.Encoder.

It is named onelog as a reference to zerolog and because it sounds like One Love song from Bob Marley :)

Get Started

go get github.com/francoispqt/onelog

Basic usage:

import "github.com/francoispqt/onelog"

func main() {
    // create a new Logger
    // first argument is an io.Writer
    // second argument is the level, which is an integer
    logger := onelog.New(
        os.Stdout, 
        onelog.ALL, // shortcut for onelog.DEBUG|onelog.INFO|onelog.WARN|onelog.ERROR|onelog.FATAL,
    )
    logger.Info("hello world !") // {"level":"info","message":"hello world"}
}

Levels

Levels are ints mapped to a string. The logger will check if level is enabled with an efficient bitwise &(AND), if disabled, it returns right away which makes onelog the fastest when running disabled logging with 0 allocs and less than 1ns/op. See benchmarks

When creating a logger you must use the | operator with different levels to toggle bytes.

Example if you want levels INFO and WARN:

logger := onelog.New(
    os.Stdout, 
    onelog.INFO|onelog.WARN,
)

This allows you to have a logger with different levels, for example you can do:

var logger *onelog.Logger

func init() {
    // if we are in debug mode, enable DEBUG lvl
    if os.Getenv("DEBUG") != "" {
        logger = onelog.New(
            os.Stdout, 
            onelog.ALL, // shortcut for onelog.DEBUG|onelog.INFO|onelog.WARN|onelog.ERROR|onelog.FATAL
        )
        return
    }
    logger = onelog.New(
        os.Stdout, 
        onelog.INFO|onelog.WARN|onelog.ERROR|onelog.FATAL,
    )
}

Available levels:

  • onelog.DEBUG
  • onelog.INFO
  • onelog.WARN
  • onelog.ERROR
  • onelog.FATAL

You can change their textual values by doing, do this only once at runtime as it is not thread safe:

onelog.LevelText(onelog.INFO, "INFO")

Hook

You can define a hook which will be run for every log message.

Example:

logger := onelog.New(
    os.Stdout, 
    onelog.ALL,
)
logger.Hook(func(e onelog.Entry) {
    e.String("time", time.Now().Format(time.RFC3339))
})
logger.Info("hello world !") // {"level":"info","message":"hello world","time":"2018-05-06T02:21:01+08:00"}

Context

Context allows enforcing a grouping format where all logs fields key-values pairs from all logging methods (With, Info, Debug, InfoWith, InfoWithEntry, ...etc) except for values from using logger.Hook, will be enclosed in giving context name provided as it's key. For example using a context key "params" as below

logger := onelog.NewContext(
    os.Stdout, 
    onelog.INFO|onelog.WARN,
    "params"
)

logger.InfoWithFields("breaking news !", func(e onelog.Entry) {
    e.String("userID", "123455")
}) 

// {"level":"info","message":"breaking news !", "params":{"userID":"123456"}}

This principle also applies when inheriting from a previous created logger as below

parentLogger := onelog.New(
    os.Stdout, 
    onelog.INFO|onelog.WARN,
)


logger := parentLogger.WithContext("params")
logger.InfoWithFields("breaking news !", func(e onelog.Entry) {
    e.String("userID", "123455")
}) 

// {"level":"info","message":"breaking news !", "params":{"userID":"123456"}}

You can always reset the context by calling WithContext("") to create a no-context logger from a context logger parent.

Logging

Without extra fields

Logging without extra fields is easy as:

logger := onelog.New(
    os.Stdout, 
    onelog.ALL,
)
logger.Debug("i'm not sure what's going on") // {"level":"debug","message":"i'm not sure what's going on"}
logger.Info("breaking news !") // {"level":"info","message":"breaking news !"}
logger.Warn("beware !") // {"level":"warn","message":"beware !"}
logger.Error("my printer is on fire") // {"level":"error","message":"my printer is on fire"}
logger.Fatal("oh my...") // {"level":"fatal","message":"oh my..."}
With extra fields

Logging with extra fields is quite simple, specially if you have used gojay:

logger := onelog.New(
    os.Stdout, 
    onelog.ALL,
)

logger.DebugWithFields("i'm not sure what's going on", func(e onelog.Entry) {
    e.String("string", "foobar")
    e.Int("int", 12345)
    e.Int64("int64", 12345)
    e.Float("float64", 0.15)
    e.Bool("bool", true)
    e.Err("err", errors.New("someError"))
    e.ObjectFunc("user", func(e Entry) {
        e.String("name", "somename")
    })
}) 
// {"level":"debug","message":"i'm not sure what's going on","string":"foobar","int":12345,"int64":12345,"float64":0.15,"bool":true,"err":"someError","user":{"name":"somename"}}

logger.InfoWithFields("breaking news !", func(e onelog.Entry) {
    e.String("userID", "123455")
}) 
// {"level":"info","message":"breaking news !","userID":"123456"}

logger.WarnWithFields("beware !", func(e onelog.Entry) {
    e.String("userID", "123455")
}) 
// {"level":"warn","message":"beware !","userID":"123456"}

logger.ErrorWithFields("my printer is on fire", func(e onelog.Entry) {
    e.String("userID", "123455")
}) 
// {"level":"error","message":"my printer is on fire","userID":"123456"}

logger.FatalWithFields("oh my...", func(e onelog.Entry) {
    e.String("userID", "123455")
}) 
// {"level":"fatal","message":"oh my...","userID":"123456"}

Alternatively, you can use the chain syntax:

logger.InfoWith("foo bar").
    Int("testInt", 1).
    Int64("testInt64", 2).
    Float("testFloat", 1.15234).
    String("testString", "string").
    Bool("testBool", true).
    ObjectFunc("testObj", func(e Entry) {
        e.Int("testInt", 100)
    }).
    Object("testObj2", testObj). // implementation of gojay.MarshalerJSONObject
    Array("testArr", testArr). // implementation of gojay.MarshalerJSONArray
    Err("testErr", errors.New("my printer is on fire !")).
    Write() // don't forget to call this method! 

Accumulate context

You can create get a logger with some accumulated context that will be included on all logs created by this logger.

To do that, you must call the With method on a logger. Internally it creates a copy of the current logger and returns it.

Example:

logger := onelog.New(
    os.Stdout, 
    onelog.ALL,
).With(func(e onelog.Entry) {
    e.String("userID", "123456")
})

logger.Info("user logged in") // {"level":"info","message":"user logged in","userID":"123456"}

logger.Debug("wtf?") // {"level":"debug","message":"wtf?","userID":"123456"}

logger.ErrorWithFields("Oops", func(e onelog.Entry) {
    e.String("error_code", "ROFL")
}) // {"level":"error","message":"oops","userID":"123456","error_code":"ROFL"}

Change levels txt values, message and/or level keys

You can change globally the levels values by calling the function:

onelog.LevelText(onelog.INFO, "INFO")

You can change the key of the message by calling the function:

onelog.MsgKey("msg")

You can change the key of the level by calling the function:

onelog.LevelKey("lvl")

Beware, these changes are global (affects all instances of the logger). Also, these function should be called only once at runtime to avoid any data race issue.

Benchmarks

For thorough benchmarks please see the results in the bench suite created by the author of zerolog here: https://github.com/rs/logbench

The benchmarks data presented below is the one from Uber's benchmark suite where we added onelog.

Benchmarks are here: https://github.com/francoispqt/zap/tree/onelog-bench/benchmarks

Disabled Logging

ns/op bytes/op allocs/op
Zap 8.73 0 0
zerolog 2.45 0 0
logrus 12.1 16 1
onelog 0.74 0 0

Disabled with fields

ns/op bytes/op allocs/op
Zap 208 768 5
zerolog 68.7 128 4
logrus 721 1493 12
onelog 1.31 0 0
onelog-chain 68.2 0 0

Logging basic message

ns/op bytes/op allocs/op
Zap 205 0 0
zerolog 135 0 0
logrus 1256 1554 24
onelog 84.8 0 0

Logging basic message and accumulated context

ns/op bytes/op allocs/op
Zap 276 0 0
zerolog 141 0 0
logrus 1256 1554 24
onelog 82.4 0 0

Logging message with extra fields

ns/op bytes/op allocs/op
Zap 1764 770 5
zerolog 1210 128 4
logrus 13211 13584 129
onelog 971 128 4
onelog-chain 1030 128 4

Documentation

Overview

Package onelog is a fast, low allocation and modular JSON logger.

It uses github.com/francoispqt/gojay as JSON encoder.

Basic usage:

import "github.com/francoispqt/onelog/log"

log.Info("hello world !") // {"level":"info","message":"hello world !", "time":1494567715}

You can create your own logger:

import "github.com/francoispqt/onelog

var logger = onelog.New(os.Stdout, onelog.ALL)

func main() {
	logger.Info("hello world !") // {"level":"info","message":"hello world !"}
}

Index

Constants

View Source
const (
	// INFO is the numeric code for INFO log level
	INFO = uint8(0x1)
	// DEBUG is the numeric code for DEBUG log level
	DEBUG = uint8(0x2)
	// WARN is the numeric code for WARN log level
	WARN = uint8(0x4)
	// ERROR is the numeric code for ERROR log level
	ERROR = uint8(0x8)
	// FATAL is the numeric code for FATAL log level
	FATAL = uint8(0x10)
)

Variables

View Source
var ALL = uint8(INFO | DEBUG | WARN | ERROR | FATAL)

ALL is a shortcut to INFO | DEBUG | WARN | ERROR | FATAL to enable all logging levels

View Source
var Levels = make([]string, 256)

Levels is the mapping between int log levels and their string value

Functions

func LevelKey

func LevelKey(s string)

LevelKey sets the key for the level field.

func LevelText

func LevelText(level uint8, txt string)

LevelText personalises the text for a specific level.

func MsgKey

func MsgKey(s string)

MsgKey sets the key for the message field.

Types

type ChainEntry

type ChainEntry struct {
	Entry
	// contains filtered or unexported fields
}

ChainEntry is for chaining calls to the entry.

func (ChainEntry) Any

func (e ChainEntry) Any(k string, obj interface{}) ChainEntry

Any adds anything stuff to the log entry based on it's type

func (ChainEntry) Array

Array adds an object to the log entry by passing an implementation of gojay.MarshalerJSONObject.

func (ChainEntry) Bool

func (e ChainEntry) Bool(k string, v bool) ChainEntry

Bool adds a bool to the log entry.

func (ChainEntry) Err

func (e ChainEntry) Err(k string, v error) ChainEntry

Err adds an error to the log entry.

func (ChainEntry) Float

func (e ChainEntry) Float(k string, v float64) ChainEntry

Float adds a float64 to the log entry.

func (ChainEntry) Int

func (e ChainEntry) Int(k string, v int) ChainEntry

Int adds an int to the log entry.

func (ChainEntry) Int64

func (e ChainEntry) Int64(k string, v int64) ChainEntry

Int64 adds an int64 to the log entry.

func (ChainEntry) Object

Object adds an object to the log entry by passing an implementation of gojay.MarshalerJSONObject.

func (ChainEntry) ObjectFunc

func (e ChainEntry) ObjectFunc(k string, v func(Entry)) ChainEntry

ObjectFunc adds an object to the log entry by calling a function.

func (ChainEntry) String

func (e ChainEntry) String(k, v string) ChainEntry

String adds a string to the log entry.

func (ChainEntry) Write

func (e ChainEntry) Write()

Info logs an entry with INFO level.

type Encoder

type Encoder = gojay.Encoder

Encoder is an alias to gojay.Encoder.

type Entry

type Entry struct {
	Level   uint8
	Message string
	// contains filtered or unexported fields
}

Entry is the structure wrapping a pointer to the current encoder. It provides easy API to work with GoJay's encoder.

func (Entry) Array

func (e Entry) Array(k string, obj gojay.MarshalerJSONArray) Entry

Array adds an object to the log entry by passing an implementation of gojay.MarshalerJSONObject.

func (Entry) Bool

func (e Entry) Bool(k string, v bool) Entry

Bool adds a bool to the log entry.

func (Entry) Err

func (e Entry) Err(k string, v error) Entry

Err adds an error to the log entry.

func (Entry) Float

func (e Entry) Float(k string, v float64) Entry

Float adds a float64 to the log entry.

func (Entry) Int

func (e Entry) Int(k string, v int) Entry

Int adds an int to the log entry.

func (Entry) Int64

func (e Entry) Int64(k string, v int64) Entry

Int64 adds an int64 to the log entry.

func (Entry) Object

func (e Entry) Object(k string, obj gojay.MarshalerJSONObject) Entry

Object adds an object to the log entry by passing an implementation of gojay.MarshalerJSONObject.

func (Entry) ObjectFunc

func (e Entry) ObjectFunc(k string, v func(Entry)) Entry

ObjectFunc adds an object to the log entry by calling a function.

func (Entry) String

func (e Entry) String(k, v string) Entry

String adds a string to the log entry.

type ExitFunc

type ExitFunc func(int)

ExitFunc is used to exit the app, `os.Exit()` is set as default on `New()`

type Logger

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

Logger is the type representing a logger.

func New

func New(w io.Writer, levels uint8) *Logger

New returns a fresh onelog Logger with default values.

func NewContext

func NewContext(w io.Writer, levels uint8, contextName string) *Logger

NewContext returns a fresh onelog Logger with default values and context name set to provided contextName value.

func (*Logger) Caller

func (l *Logger) Caller(n int) string

Caller returns the caller in the stack trace, skipped n times.

func (*Logger) Debug

func (l *Logger) Debug(msg string)

Debug logs an entry with DEBUG level.

func (*Logger) DebugWith

func (l *Logger) DebugWith(msg string) ChainEntry

DebugWith return ChainEntry with DEBUG level.

func (*Logger) DebugWithFields

func (l *Logger) DebugWithFields(msg string, fields func(Entry))

DebugWithFields logs an entry with DEBUG level and custom fields.

func (*Logger) Error

func (l *Logger) Error(msg string)

Error logs an entry with ERROR level

func (*Logger) ErrorWith

func (l *Logger) ErrorWith(msg string) ChainEntry

ErrorWith returns a ChainEntry with ERROR level.

func (*Logger) ErrorWithFields

func (l *Logger) ErrorWithFields(msg string, fields func(Entry))

ErrorWithFields logs an entry with ERROR level and custom fields.

func (*Logger) Fatal

func (l *Logger) Fatal(msg string)

Fatal logs an entry with FATAL level.

func (*Logger) FatalWith

func (l *Logger) FatalWith(msg string) ChainEntry

FatalWith returns a ChainEntry with FATAL level.

func (*Logger) FatalWithFields

func (l *Logger) FatalWithFields(msg string, fields func(Entry))

FatalWithFields logs an entry with FATAL level and custom fields.

func (*Logger) Hook

func (l *Logger) Hook(h func(Entry)) *Logger

Hook sets a hook to run for all log entries to add generic fields

func (*Logger) Info

func (l *Logger) Info(msg string)

Info logs an entry with INFO level.

func (*Logger) InfoWith

func (l *Logger) InfoWith(msg string) ChainEntry

InfoWith return an ChainEntry with INFO level.

func (*Logger) InfoWithFields

func (l *Logger) InfoWithFields(msg string, fields func(Entry))

InfoWithFields logs an entry with INFO level and custom fields.

func (*Logger) Warn

func (l *Logger) Warn(msg string)

Warn logs an entry with WARN level.

func (*Logger) WarnWith

func (l *Logger) WarnWith(msg string) ChainEntry

WarnWith returns a ChainEntry with WARN level

func (*Logger) WarnWithFields

func (l *Logger) WarnWithFields(msg string, fields func(Entry))

WarnWithFields logs an entry with WARN level and custom fields.

func (*Logger) With

func (l *Logger) With(f func(Entry)) *Logger

With copies the current Logger and adds it a given context by running func f.

func (*Logger) WithContext

func (l *Logger) WithContext(contextName string) *Logger

WithContext copies current logger enforcing all entry fields to be set into a map with the contextName set as the key name for giving map. This allows allocating all future uses of the logging methods to follow such formatting. The only exception are values provided by added hooks which will remain within the root level of generated json.

type Object

type Object = gojay.EncodeObjectFunc

Object is an alias to gojay.EncodeObjectFunc.

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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