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

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())
}