You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
216 lines
3.9 KiB
Go
216 lines
3.9 KiB
Go
package log
|
|
|
|
import (
|
|
"bytes"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"runtime"
|
|
"strconv"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/subchen/gstack/gls"
|
|
)
|
|
|
|
// Log Level
|
|
const (
|
|
DEBUG = iota
|
|
INFO
|
|
WARN
|
|
ERROR
|
|
FATAL
|
|
)
|
|
|
|
var (
|
|
levelStr = []string{
|
|
"DEBUG",
|
|
"INFO",
|
|
"WARN",
|
|
"ERROR",
|
|
"FATAL",
|
|
}
|
|
levelStrWithColor = []string{
|
|
"\033[34mDEBUG\033[0m",
|
|
"\033[32mINFO\033[0m",
|
|
"\033[33mWARN\033[0m",
|
|
"\033[31mERROR\033[0m",
|
|
"\033[35mFATAL\033[0m",
|
|
}
|
|
)
|
|
|
|
func New(out io.Writer) *Logger {
|
|
return &Logger{
|
|
out: out,
|
|
level: INFO,
|
|
pid: os.Getpid(),
|
|
name: "",
|
|
timeLayout: "2006-01-02 15:04:05.000",
|
|
goroutineId: false,
|
|
longFileFormat: false,
|
|
colorizedLevel: false,
|
|
callerSkip: 2,
|
|
}
|
|
}
|
|
|
|
type Logger struct {
|
|
mu sync.Mutex
|
|
out io.Writer
|
|
level int
|
|
pid int
|
|
name string
|
|
timeLayout string
|
|
goroutineId bool
|
|
longFileFormat bool
|
|
colorizedLevel bool
|
|
callerSkip int
|
|
}
|
|
|
|
func (l *Logger) GetLevel() int {
|
|
return l.level
|
|
}
|
|
|
|
func (l *Logger) SetLevel(level int) *Logger {
|
|
l.level = level
|
|
return l
|
|
}
|
|
|
|
func (l *Logger) SetName(name string) *Logger {
|
|
l.name = name
|
|
return l
|
|
}
|
|
|
|
func (l *Logger) SetTimeLayout(layout string) *Logger {
|
|
l.timeLayout = layout
|
|
return l
|
|
}
|
|
|
|
func (l *Logger) EnableGoroutineId(enable bool) *Logger {
|
|
l.goroutineId = enable
|
|
return l
|
|
}
|
|
|
|
func (l *Logger) EnableLongFileFormat(enable bool) *Logger {
|
|
l.longFileFormat = enable
|
|
return l
|
|
}
|
|
|
|
func (l *Logger) EnableColorizedLevel(enable bool) *Logger {
|
|
l.colorizedLevel = enable
|
|
return l
|
|
}
|
|
|
|
func (l *Logger) SkipCaller(skip int) *Logger {
|
|
l.callerSkip = skip
|
|
return l
|
|
}
|
|
|
|
func (l *Logger) SetWriter(w io.Writer) *Logger {
|
|
l.out = w
|
|
return l
|
|
}
|
|
|
|
func (l *Logger) IsDebugEnabled() bool {
|
|
return l.level <= DEBUG
|
|
}
|
|
|
|
func (l *Logger) IsInfoEnabled() bool {
|
|
return l.level <= INFO
|
|
}
|
|
|
|
func (l *Logger) IsWarnEnabled() bool {
|
|
return l.level <= WARN
|
|
}
|
|
|
|
func (l *Logger) IsErrorEnabled() bool {
|
|
return l.level <= ERROR
|
|
}
|
|
|
|
func (l *Logger) Debug(msg string, args ...interface{}) {
|
|
if l.level <= DEBUG {
|
|
l.log(DEBUG, msg, args...)
|
|
}
|
|
}
|
|
|
|
func (l *Logger) Info(msg string, args ...interface{}) {
|
|
if l.level <= INFO {
|
|
l.log(INFO, msg, args...)
|
|
}
|
|
}
|
|
|
|
func (l *Logger) Warn(msg string, args ...interface{}) {
|
|
if l.level <= WARN {
|
|
l.log(WARN, msg, args...)
|
|
}
|
|
}
|
|
|
|
func (l *Logger) Error(msg string, args ...interface{}) {
|
|
if l.level <= ERROR {
|
|
l.log(ERROR, msg, args...)
|
|
}
|
|
}
|
|
|
|
func (l *Logger) Fatal(msg string, args ...interface{}) {
|
|
l.log(FATAL, msg, args...)
|
|
}
|
|
|
|
func (l *Logger) log(level int, msg string, args ...interface{}) {
|
|
_, file, line, ok := runtime.Caller(l.callerSkip)
|
|
if !ok {
|
|
file = "???"
|
|
line = 0
|
|
} else if !l.longFileFormat {
|
|
if index := strings.LastIndex(file, "/"); index >= 0 {
|
|
file = file[index+1:]
|
|
} else if index = strings.LastIndex(file, "\\"); index >= 0 {
|
|
file = file[index+1:]
|
|
}
|
|
}
|
|
|
|
// output format: DATE PID [NAME] [GID] LEVEL file:line message
|
|
// 2001-10-10 12:00:00,000+0800 1234 app 987 INFO main.go:1234 log message ...
|
|
buf := new(bytes.Buffer)
|
|
buf.WriteByte(' ')
|
|
buf.WriteString(strconv.Itoa(l.pid))
|
|
buf.WriteByte(' ')
|
|
if l.name != "" {
|
|
buf.WriteString(l.name)
|
|
buf.WriteByte(' ')
|
|
}
|
|
if l.goroutineId || l.level == DEBUG {
|
|
buf.WriteString(strconv.FormatUint(gls.GoroutineID(), 10))
|
|
buf.WriteByte(' ')
|
|
}
|
|
if l.colorizedLevel {
|
|
buf.WriteString(levelStrWithColor[level])
|
|
} else {
|
|
buf.WriteString(levelStr[level])
|
|
}
|
|
buf.WriteByte(' ')
|
|
buf.WriteString(file)
|
|
buf.WriteByte(':')
|
|
buf.WriteString(strconv.Itoa(line))
|
|
buf.WriteByte(' ')
|
|
fmt.Fprintf(buf, msg, args...)
|
|
buf.WriteByte('\n')
|
|
|
|
if level == FATAL {
|
|
for i := l.callerSkip; ; i++ {
|
|
pc, file, line, ok := runtime.Caller(i)
|
|
if !ok {
|
|
break
|
|
}
|
|
fmt.Fprintf(buf, "\tat %s:%d (0x%x)\n", file, line, pc)
|
|
}
|
|
}
|
|
|
|
l.mu.Lock()
|
|
defer l.mu.Unlock()
|
|
|
|
timeStr := time.Now().Format(l.timeLayout)
|
|
|
|
l.out.Write([]byte(timeStr))
|
|
l.out.Write(buf.Bytes())
|
|
}
|