I needed to write my own lightweight GO multi-output logger for a work project and I've come up with https://github.com/asticode/go-logger.
Basically the projects works like this:
// Create the logger
l, e := logger.NewLogger("myproject", logger.LevelDebug, "%message%")
// Add a stream handler
h, e := logger.NewHandlerStream("")
l.AddHandler("stream", h)
// Add a syslog handler
h, e = logger.NewHandlerSyslog("[%level%]%message%", "syslog_id")
l.AddHandler("syslog", h)
// Add a file handler
h, e = logger.NewHandlerFile("", "/path/to/file")
l.AddHandler("file", h)
// Log a warning message
l.Warning("This is a warning message")
And this will output the message to the specified handlers :)
I'm not here to advertise at all and I'm rather looking for Gopher's opinions (good or bad) on my code and my implementation since I'd like to know whether I've done things right or whether I've gone completely off tracks.
My points of emphasis would be:
- Have I made good use of interfaces?
- Have I made good use of const and global vars?
- Have I made good use of GO project structure?
- Have I made good use of GO unit testing package?
- ...
For instance here's how I expose the main logger (https://github.com/asticode/go-logger/blob/master/logger/logger.go):
type Logger interface {
ID() int
SetID(id int)
Prefix() string
SetPrefix(p string)
Level() Level
SetLevel(l Level)
Format() string
SetFormat(f string)
Handlers() map[string]Handler
AddHandler(k string, h Handler)
GetHandler(k string) (Handler, error)
DelHandler(k string)
Emergency(message string) error
Critical(message string) error
Error(message string) error
Warning(message string) error
Notice(message string) error
Info(message string) error
Debug(message string) error
}
type logger struct {
id int
prefix string
level Level
format string
handlers map[string]Handler
}
And I use a global func to create a new logger such as:
func NewLogger(prefix string, level Level, format string) (Logger, error) {
// Check level
if _, ok := levelToInt[level]; !ok {
return nil, ErrUndefinedLevel
}
// Check Format
if len(format) == 0 {
format = defaultFormat
}
// Increment count
count++
// Return
return &logger{
id: count,
prefix: prefix,
level: level,
format: format,
handlers: make(map[string]Handler),
}, nil
}
Is this the proper way to do it ?
Logger.PrintX
could log a message with level "info". So that one can use your logger as a drop in replacement for the standard libraries logger, which makes adoption much easier. Changing say 30 lines of code is something entirely different than to change a couple of hundred lines of code, as levels would need to get adjusted. Extra point for the option of mapping the log levels via configuration. \$\endgroup\$