Package clog is a channel-based logging package for Go
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.
The minimum requirement of Go is 1.11.
go get unknwon.dev/clog/v2
Please apply
-uflag to update in the future.
It is extremely easy to create one with all default settings. Generally, you would want to create new logger inside
initor
mainfunction.
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
initfunction 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()) } }
Or expand further:
func init() { err := log.NewConsoleWithName(log.DefaultConsoleName, 0, log.ConsoleConfig{ Level: log.LevelTrace, }, ) if err != nil { panic("unable to create new logger: " + err.Error()) } }
0is an integer type so it is used as underlying buffer size. In this case,
0creates synchronized logger (call hangs until write is finished).
ConsoleConfigis the respective config object for the console logger.
LevelTraceused 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.Errorand
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()) } }
LevelInfo, calls to the
log.Tracewill be simply noop.
Other builtin loggers are file (
log.NewFile), Slack (
log.NewSlack) and Discord (
log.NewDiscord), see later sections in the documentation for usage details.
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.
When multiple loggers are registered, it is also possible to write logs to a special logger by giving its name.
func main() { log.TraceTo(log.DefaultConsoleName, "Hello %s!", "World") log.InfoTo(log.DefaultConsoleName, "Hello %s!", "World") log.WarnTo(log.DefaultConsoleName, "Hello %s!", "World") log.ErrorTo(log.DefaultConsoleName, "So bad... %v", err) log.FatalTo(log.DefaultConsoleName, "Boom! %v", err)// ...
}
When using
log.Errorand
log.Fatalfunctions, 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()] ...// ...
}
log.Fatalwill exit the program.
log.ErrorDepthor
log.FatalDepth.
You should always call
log.Stop()to wait until all logs are processed before program exits.
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.NewFileWriterfunction. 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 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 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.
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
chanLoggerhere:
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
This project is under MIT License. See the LICENSE file for the full license text.