clog

by go-clog

go-clog / clog

Package clog is a channel-based logging package for Go

139 Stars 15 Forks Last release: 9 months ago (v2.1.0) MIT License 49 Commits 12 Releases

Available items

No Items, yet!

The developer of this repository has not created any items for sale yet. Need a bug fixed? Help with integration? A different license? Create a request here:

Clog

GitHub Workflow Status codecov GoDoc Sourcegraph

Package clog is a channel-based logging package for Go.

This package supports multiple loggers across different levels of logging. It uses Go's native channel feature to provide goroutine-safe mechanism on large concurrency.

Installation

The minimum requirement of Go is 1.11.

go get unknwon.dev/clog/v2

Please apply

-u
flag to update in the future.

Getting Started

It is extremely easy to create one with all default settings. Generally, you would want to create new logger inside

init
or
main
function.

Let's create a logger that prints logs to the console:

import (
    log "unknwon.dev/clog/v2"
)

func init() { err := log.NewConsole() if err != nil { panic("unable to create new logger: " + err.Error()) } }

func main() { log.Trace("Hello %s!", "World") // YYYY/MM/DD 12:34:56 [TRACE] Hello World! log.Info("Hello %s!", "World") // YYYY/MM/DD 12:34:56 [ INFO] Hello World! log.Warn("Hello %s!", "World") // YYYY/MM/DD 12:34:56 [ WARN] Hello World!

// Graceful stopping all loggers before exiting the program.
log.Stop()

}

The code inside

init
function is equivalent to the following:
func init() {
    err := log.NewConsole(0, log.ConsoleConfig{
        Level: log.LevelTrace,
    })
    if err != nil {
        panic("unable to create new logger: " + err.Error())
    }
}
  • The
    0
    is an integer type so it is used as underlying buffer size. In this case,
    0
    creates synchronized logger (call hangs until write is finished).
  • Any non-integer type is used as the config object, in this case
    ConsoleConfig
    is the respective config object for the console logger.
  • The
    LevelTrace
    used here is the lowest logging level, meaning prints every log to the console. All levels from lowest to highest are:
    LevelTrace
    ,
    LevelInfo
    ,
    LevelWarn
    ,
    LevelError
    ,
    LevelFatal
    , each of them has at least one respective function, e.g.
    log.Trace
    ,
    log.Info
    ,
    log.Warn
    ,
    log.Error
    and
    log.Fatal
    .

In production, you may want to make log less verbose and be asynchronous:

func init() {
    // The buffer size mainly depends on number of logs could be produced at the same time, 
    // 100 is a good default.
    err := log.NewConsole(100, log.ConsoleConfig{
        Level:      log.LevelInfo,
    })
    if err != nil {
        panic("unable to create new logger: " + err.Error())
    }
}
  • When you set level to be
    LevelInfo
    , calls to the
    log.Trace
    will be simply noop.
  • The console logger comes with color output, but for non-colorable destination, the color output will be disabled automatically.

Other builtin loggers are file (

log.NewFile
), Slack (
log.NewSlack
) and Discord (
log.NewDiscord
), see later sections in the documentation for usage details.

Multiple Loggers

You can have multiple loggers in different modes across levels.

func init() {
    err := log.NewConsole()
    if err != nil {
        panic("unable to create new logger: " + err.Error())
    }
    err := log.NewFile(log.FileConfig{
        Level:    log.LevelInfo,
        Filename: "clog.log",
    })
    if err != nil {
        panic("unable to create new logger: " + err.Error())
    }
}

In this example, all logs will be printed to console, and only logs with level Info or higher (i.e. Warn, Error and Fatal) will be written into file.

Caller Location

When using

log.Error
and
log.Fatal
functions, the caller location is written along with logs.
func main() {
    log.Error("So bad... %v", err) // YYYY/MM/DD 12:34:56 [ERROR] [...er/main.go:64 main()] ...
    log.Fatal("Boom! %v", err)     // YYYY/MM/DD 12:34:56 [FATAL] [...er/main.go:64 main()] ...

// ...

}

  • Calling
    log.Fatal
    will exit the program.
  • If you want to have different skip depth than the default, use
    log.ErrorDepth
    or
    log.FatalDepth
    .

Clean Exit

You should always call

log.Stop()
to wait until all logs are processed before program exits.

Builtin Loggers

File Logger

File logger is the single most powerful builtin logger, it has the ability to rotate based on file size, line, and date:

func init() {
    err := log.NewFile(100, log.FileConfig{
        Level:              log.LevelInfo,
        Filename:           "clog.log",  
        FileRotationConfig: log.FileRotationConfig {
            Rotate: true,
            Daily:  true,
        },
    })
    if err != nil {
        panic("unable to create new logger: " + err.Error())
    }
}

In case you have some other packages that write to a file, and you want to take advatange of this file rotation feature. You can do so by using the

log.NewFileWriter
function. It acts like a standard
io.Writer
.
func init() {
    w, err := log.NewFileWriter("filename", log.FileRotationConfig{
        Rotate: true,
        Daily:  true,
    })
    if err != nil {
        panic("unable to create new logger: " + err.Error())
    }
}

Slack Logger

Slack logger is also supported in a simple way:

func init() {
    err := log.NewSlack(100, log.SlackConfig{
        Level: log.LevelInfo,
        URL:   "https://url-to-slack-webhook",
    })
    if err != nil {
        panic("unable to create new logger: " + err.Error())
    }
}

This logger also works for Discord Slack endpoint.

Discord Logger

Discord logger is supported in rich format via Embed Object:

func init() {
    err := log.NewDiscord(100, log.DiscordConfig{
        Level: log.LevelInfo,
        URL:   "https://url-to-discord-webhook",
    })
    if err != nil {
        panic("unable to create new logger: " + err.Error())
    }
}

This logger automatically retries up to 3 times if hits rate limit with respect to

retry_after
.

Build Your Own Logger

You can implement your own logger and all the concurrency stuff are handled automatically!

Here is an example which sends all logs to a channel, we call it

chanLogger
here:
import log "unknwon.dev/clog/v2"

type chanConfig struct { c chan string }

var _ log.Logger = (*chanLogger)(nil)

type chanLogger struct { name string level log.Level c chan string }

func (l *chanLogger) Name() string { return l.name } func (l *chanLogger) Level() log.Level { return l.level }

func (l *chanLogger) Write(m log.Messager) error { l.c

Have fun!

Credits

License

This project is under MIT License. See the LICENSE file for the full license text.

We use cookies. If you continue to browse the site, you agree to the use of cookies. For more information on our use of cookies please see our Privacy Policy.