Last active
May 11, 2020 18:00
-
-
Save jo-makar/3f2b1dd6b4bb2f9d72208e980dea7210 to your computer and use it in GitHub Desktop.
Logging framework
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package logger | |
import ( | |
"fmt" | |
"io" | |
"path" | |
"runtime" | |
"strings" | |
"sync" | |
"time" | |
) | |
const ( | |
TraceLevel = iota | |
DebugLevel | |
InfoLevel | |
WarningLevel | |
ErrorLevel | |
PanicLevel | |
) | |
var levels = map[int]string{ | |
TraceLevel: "trace", | |
DebugLevel: "debug", | |
InfoLevel: "info", | |
WarningLevel: "warning", | |
ErrorLevel: "error", | |
PanicLevel: "panic", | |
} | |
type Logger struct { | |
Level int | |
Format []string | |
Writer io.Writer | |
mux sync.Mutex | |
} | |
func (logger *Logger) log(level int, format string, values ...interface{}) string { | |
logger.mux.Lock() | |
defer logger.mux.Unlock() | |
now := time.Now() | |
if _, ok := levels[level]; !ok { | |
panic(fmt.Sprintf("unknown level %d", level)) | |
} | |
pc, file, line, ok := runtime.Caller(2) | |
if !ok { | |
panic("unknown caller: runtime.Caller() !ok") | |
} | |
funcobj := runtime.FuncForPC(pc) | |
if funcobj == nil { | |
panic("unknown caller: runtime.FuncForPC() nil") | |
} | |
t := strings.Split(funcobj.Name(), ".") | |
if len(t) == 1 { | |
panic(fmt.Sprintf("unexpected pkg/func name %q", funcobj.Name())) | |
} | |
pkgname := strings.Join(t[:len(t)-1], ".") | |
funcname := t[len(t)-1] | |
formatters := map[string]func()string{ | |
"date": func() string { return now.Format("2006/01/02") }, | |
"time": func() string { return now.Format("15:04:05") }, | |
"time_ms": func() string { return now.Format("15:04:05") + fmt.Sprintf(".%03d", now.Nanosecond() / 1000000) }, | |
"time_us": func() string { return now.Format("15:04:05") + fmt.Sprintf(".%06d", now.Nanosecond() / 1000) }, | |
"level": func() string { return levels[level] }, | |
"name": func() string { return funcobj.Name() }, | |
"pkg": func() string { return pkgname }, | |
"func": func() string { return funcname }, | |
"path": func() string { return fmt.Sprintf("%s:%d", file, line) }, | |
"file": func() string { return fmt.Sprintf("%s:%d", path.Base(file), line) }, | |
} | |
var builder strings.Builder | |
for _, f := range logger.Format { | |
prefix := "" | |
if builder.Len() > 0 { | |
prefix = " " | |
} | |
if formatter, ok := formatters[f]; ok { | |
if _, err := builder.WriteString(prefix + formatter()); err != nil { | |
panic(err) | |
} | |
} else { | |
panic(fmt.Sprintf("unsupported formatter %q", f)) | |
} | |
} | |
if _, err := fmt.Fprintf(&builder, ": " + format, values...); err != nil { | |
panic(err) | |
} | |
entry := builder.String() | |
if !strings.HasSuffix(entry, "\n") { | |
entry += "\n" | |
} | |
if _, err := logger.Writer.Write([]byte(entry)); err != nil { | |
panic(err) | |
} | |
return entry | |
} | |
func (l *Logger) Trace(f string, v ...interface{}) { | |
if l.Level <= TraceLevel { | |
l.log(TraceLevel, f, v...) | |
} | |
} | |
func (l *Logger) Debug(f string, v ...interface{}) { | |
if l.Level <= DebugLevel { | |
l.log(DebugLevel, f, v...) | |
} | |
} | |
func (l *Logger) Info(f string, v ...interface{}) { | |
if l.Level <= InfoLevel { | |
l.log(InfoLevel, f, v...) | |
} | |
} | |
func (l *Logger) Warning(f string, v ...interface{}) { | |
if l.Level <= WarningLevel { | |
l.log(WarningLevel, f, v...) | |
} | |
} | |
func (l *Logger) Error(f string, v ...interface{}) { | |
if l.Level <= ErrorLevel { | |
l.log(ErrorLevel, f, v...) | |
} | |
} | |
func (l *Logger) Panic(f string, v ...interface{}) { | |
s := l.log(PanicLevel, f, v...) | |
panic(s) | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"logger" | |
"os" | |
) | |
func main() { | |
logger := logger.Logger{ | |
Level: logger.InfoLevel, | |
Format: []string{"date", "time_ms", "level", "file"}, | |
Writer: os.Stderr, | |
} | |
logger.Info("example") | |
logger.Info("another: %d %s %d", 1, "foo", 2) | |
// Outputs: | |
// 2020/05/11 13:54:03.816 info main.go:15: example | |
// 2020/05/11 13:54:03.816 info main.go:16: another: 1 foo 2 | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment