# README
Log
Go simple yet powerful logging library
Focus
This log library focuses on:
- Human readable logs
- Dependency injected loggers
- Thread safety
- Write to multiple writers
Setup
go get github.com/qdm12/log
Usage
Default logger
The logger constructor log.New()
uses functional options.
By default the logger:
- logs to
os.Stdout
- logs at the
INFO
level - logs the time with the RFC3339 format
package main
import "github.com/qdm12/log"
func main() {
logger := log.New()
logger.Info("my message")
// 2022-03-28T10:03:29Z INFO my message
}
Formatting methods
Each level log method such as Warn(s string)
has a corresponding formatting method with a trailing f
such as Warnf(format string, args ...interface{})
. For example:
package main
import "github.com/qdm12/log"
func main() {
logger := log.New()
logger.Warnf("message number %d", 1)
// 2022-03-29T07:40:12Z WARN message number 1
}
Custom logger
You can customize the logger creation with for example:
package main
import "github.com/qdm12/log"
func main() {
logger := log.New(
log.SetLevel(log.LevelDebug),
log.SetTimeFormat(time.RFC822),
log.SetWriters(os.Stdout, os.Stderr),
log.SetComponent("module"),
log.SetCallerFile(true),
log.SetCallerFunc(true),
log.SetCallerLine(true))
logger.Info("my message")
// 29 Mar 22 07:16 UTC INFO [module] my message main.go:L19:main
// 29 Mar 22 07:16 UTC INFO [module] my message main.go:L19:main
}
Create a logger from a logger
This should be the preferred way to create additional loggers with different settings, since it favors dependency injection.
For example, we create logger loggerB
from loggerA
which inherits the settings from loggerA
and changes the component setting.
package main
import (
"github.com/qdm12/log"
)
func main() {
loggerA := log.New(log.SetComponent("A"))
loggerB := loggerA.New(log.SetComponent("B"))
loggerA.Info("my message")
// 2022-03-29T07:35:08Z INFO [A] my message
loggerB.Info("my message")
// 2022-03-29T07:35:08Z INFO [B] my message
}
Note this is a thread safe operation, and thread safety on the writers is also maintained.
Create global loggers
You can create multiple loggers with the global constructor log.New()
, and writers will be thread safe to write to. For example the following won't write to the buffer at the same time:
package main
import (
"bytes"
"time"
"github.com/qdm12/log"
)
func main() {
writer := bytes.NewBuffer(nil)
timer := time.NewTimer(time.Second)
const parallelism = 2
for i := 0; i < parallelism; i++ {
go func() {
logger := log.New(log.SetWriters(writer))
for {
select {
case <-timer.C:
return
default:
logger.Info("my message")
}
}
}()
}
}
You can try it with CGO_ENABLED=1 go run -race ./examples/global
to ensure there is no data race.
Detailed features
The following features are available:
- Multiple options available
- Set the level
DEBUG
,INFO
,WARN
,ERROR
- Set time format, for example
time.RFC3339
- Set or add one or more
io.Writer
- Set a component string
- Set the level
- Create child loggers inheriting configuration
- Thread safe per
io.Writer
for multiple loggers - Printf-like methods:
Debugf
,Infof
,Warnf
,Errorf
- Automatic coloring of levels depending on tty
- Safety to use
- Full unit test coverage
- End-to-end race tests
- Used in the following projects:
Limitations
Using a lot of writers
This logging library is thread safe for each writer. To achieve, it uses a global map from writer address to mutex pointer.
⚠️ This however means that this map will not shrink at any time. If you want to use this logging library with thousands of different writers, you should not use it as it is, please create an issue.
Contributing
See Contributing
License
This repository is under an MIT license