All Projects → francoispqt → Onelog

francoispqt / Onelog

Licence: mit
Dead simple, super fast, zero allocation and modular logger for Golang

Programming Languages

go
31211 projects - #10 most used programming language
golang
3204 projects

Projects that are alternatives of or similar to Onelog

Gf
GoFrame is a modular, powerful, high-performance and enterprise-class application development framework of Golang.
Stars: ✭ 6,501 (+1571.21%)
Mutual labels:  modular, logging, logger
Golog
A high-performant Logging Foundation for Go Applications. X3 faster than the rest leveled loggers.
Stars: ✭ 208 (-46.53%)
Mutual labels:  modular, logging, logger
gxlog
A concise, functional, flexible and extensible logger for go.
Stars: ✭ 65 (-83.29%)
Mutual labels:  logger, logging
Rz Go
Ripzap - Fast and 0 allocs leveled JSON logger for Go ⚡️. Dependency free.
Stars: ✭ 256 (-34.19%)
Mutual labels:  logging, fast
Nestjs Pino
Platform agnostic logger for NestJS based on Pino with REQUEST CONTEXT IN EVERY LOG
Stars: ✭ 283 (-27.25%)
Mutual labels:  logging, logger
pio
Low-level package that provides an easy way to centralize different output targets. Supports colors and text decoration to all popular terminals
Stars: ✭ 21 (-94.6%)
Mutual labels:  modular, logger
node-perj
A fast, flexible JSON logger.
Stars: ✭ 16 (-95.89%)
Mutual labels:  fast, logger
Tty Logger
A readable, structured and beautiful logging for the terminal
Stars: ✭ 280 (-28.02%)
Mutual labels:  logging, logger
Tslog
📝 tslog - Expressive TypeScript Logger for Node.js.
Stars: ✭ 321 (-17.48%)
Mutual labels:  logging, logger
Caterpillar
Caterpillar is the ultimate logging system for Deno, Node.js, and Web Browsers. Log levels are implemented to the RFC standard. Log entries can be filtered and piped to various streams, including coloured output to the terminal, the browser's console, and debug files. You can even write your own transforms.
Stars: ✭ 330 (-15.17%)
Mutual labels:  logging, logger
Electron Timber
Pretty logger for Electron apps
Stars: ✭ 337 (-13.37%)
Mutual labels:  logging, logger
mini-async-log-c
Mini async log C port. Now with C++ wrappers.
Stars: ✭ 69 (-82.26%)
Mutual labels:  fast, logger
Monolog
Requirements
Stars: ✭ 19,361 (+4877.12%)
Mutual labels:  logger, logging
BaseDevelop
an android project for now fashion open source framework
Stars: ✭ 24 (-93.83%)
Mutual labels:  fast, logger
Casadi
CasADi is a symbolic framework for numeric optimization implementing automatic differentiation in forward and reverse modes on sparse matrix-valued computational graphs. It supports self-contained C-code generation and interfaces state-of-the-art codes such as SUNDIALS, IPOPT etc. It can be used from C++, Python or Matlab/Octave.
Stars: ✭ 714 (+83.55%)
Mutual labels:  modular, optimization
Pygogo
A Python logging library with superpowers
Stars: ✭ 265 (-31.88%)
Mutual labels:  logging, logger
Timber Elixir
🌲 Great Elixir logging made easy
Stars: ✭ 226 (-41.9%)
Mutual labels:  logging, logger
Chipmunk
log analysis tool
Stars: ✭ 247 (-36.5%)
Mutual labels:  logging, logger
Analog
PHP logging library that is highly extendable and simple to use.
Stars: ✭ 314 (-19.28%)
Mutual labels:  logging, logger
Cocoadebug
iOS Debugging Tool 🚀
Stars: ✭ 3,769 (+868.89%)
Mutual labels:  logging, logger

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
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].