Categorygithub.com/krostar/logger
modulepackage
1.2.0
Repository: https://github.com/krostar/logger.git
Documentation: pkg.go.dev

# README

logger

godoc Licence Latest version

Build Status Code quality Code coverage

One logger to rule them all.

Motivation

I was using logrus for some times when I discovered zap. I wanted to try out zap in a project where I already used logrus. I was first thinking it should only be a matter of replacing all logrus occurence with zap's one modulo some renaming. But it was not that easy: interfaces were different, exposed API contained special types (for instance logrus.Field) or tooks differents parameters, logrus was handling standard output redirection differently than zap, etc. The cost of trying zap over logrus was high and the outcome was not that much a blast. All loggers are differents and no-one can expect one to work like another. Well, why the last sentence should be true ?

From that question is born this project which:

  • expose a unique abstract way of logging through a unique interface
  • expose a unique configuration
  • expose a unique way of redirect standard output
  • expose a unique way of writing to an io.Writer
  • is modulable (can use already built zap or logrus instances, or any other logger)
  • is easily mockable
  • help to test logs

Example

// var log = <some way of building one>

// same interface we are used to see
log.WithField(key, value).Info(args...)

// easy way to get a io.Writer to inject in every components that require a logger
logger.WriterLevel(log, logger.LevelError)

// easy way of redirecting golang log standard library
logger.RedirectStdLog(log, logger.LevelWarn)

Changing the underlying logger takes literraly one line.

package main

import (
    "github.com/krostar/logger"
    "github.com/krostar/logger/logrus"
    "github.com/krostar/logger/zap"
)

func main() {
    var config = logger.Config{
        Formatter: "json",
    }

    // create a logrus-based logger with configuration
    log := logrus.New(logrus.WithConfig(config))
    log.Info("i'm a logrus-based logger")

    // as any logger keep the same interface
    // switching to a zap-based logger with configuration is easy
    log = zap.New(zap.WithConfig(config))
    log.Info("i'm a zap-based logger")
}

You have a lot of code that:

  • is using io.Writer or
  • is logging directly with standard library log
  • takes a *log.Logger parameter

There is actually few methods to help with that:

func setupHTTP(addr string, logger *log.Logger) *http.Server {
    return &http.Server{
		Addr:     addr,
		ErrorLog: logger,
	}
}

func doSomethingElse(doIt func() error, errWriter io.Writer) {
    if err := doIt(); err != nil {
        io.WriteString(errWriter, err.String())
    }
}

func main() {
    var (
        log         logger.Logger // init it the way you want (zap, logrus, ...)
        stdlog    = logger.StdLog(log, logger.LevelError)
        writerlog = logger.WriterLevel(log, logger.LevelError)
    )

    setupHTTP(":80", stdlog)

    doSomethingElse(func()error{
        return errors.New("bim bam boum")
    }, stdlog)
}

License

This project is under the MIT licence, please see the LICENCE file.

# Packages

Package logmid is a net/http middleware that uses httpinfo and logger.Logger to log HTTP requests.
Package logrus implements the logger.Logger interface using sirupsen/logrus implementation.
Package zap implements the logger.Logger interface using uber/zap implementation.

# Functions

CaptureOutput catpures and merge stdout and stderr.
LogAtLevelFunc returns a function that can log to the provided level.
LogFAtLevelFunc is the same as LogAtLevelFunc but with log formatting.
NewInMemory returns a logger that stores log in memory.
ParseLevel converts a string representation of a level to a level type.
RedirectStdLog redirects standard logger calls to the underlying logger.
StdLog returns a standard logger which will use the provided logger internally.
WriteCloserLevel returns a writer that writes as if LogAtLevelFunc(l) were called, with a closer.
WriterLevel returns a writer that writes as if LogAtLevelFunc(l) were called.

# Constants

FieldErrorKey is the name of the field set by WithError.
LevelDebug logs are typically voluminous, and are usually disabled in production.
LevelError logs are high-priority and should require a human review.
LevelInfo is the default logging priority.
LevelQuiet hide everything.
LevelWarn logs are more important than Info, and may need individual human review.

# Structs

Config defines all the configurable options for the logger.
InMemory defines a memory logger.
InMemoryEntry stores a log entry for the Memory logger.
Noop defines a no-operation logger.

# Interfaces

Logger defines the way logs can be handled.

# Type aliases

A Level is a logging priority.