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.

307 lines
5.4 KiB
Go

package log
import (
"bytes"
"fmt"
"io"
"os"
"runtime"
"strconv"
"strings"
"sync"
"time"
"github.com/subchen/gstack/gls"
)
const (
// log level
L_DEBUG = iota
L_INFO
L_WARN
L_ERROR
L_FATAL
// Bits or'ed together to control what's printed.
F_TIME = 1 << iota
F_LONG_FILE
F_SHORT_FILE
F_PID
F_GID
F_COLOR
// default flags
DEFAULT_FLAGS = F_TIME | F_SHORT_FILE | F_PID
// default time format
DEFAULT_TIME_FORMAT = "2006-01-02 15:04:05,000"
)
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",
}
buffer = sync.Pool{
New: func() interface{} {
return new(bytes.Buffer)
},
}
)
func New(out io.Writer) *Logger {
return &Logger{
writer: out,
level: L_INFO,
pid: os.Getpid(),
name: "",
timeFormat: DEFAULT_TIME_FORMAT,
flags: DEFAULT_FLAGS,
callerSkip: 2,
}
}
type Logger struct {
m sync.Mutex
writer io.Writer
level int
pid int
name string
timeFormat string
flags int
callerSkip int
}
func (l *Logger) GetLevel() int {
return l.level
}
func (l *Logger) SetLevel(level int) *Logger {
if level < L_DEBUG || level > L_FATAL {
panic("unknown log level")
}
l.m.Lock()
l.level = level
l.m.Unlock()
return l
}
func (l *Logger) GetLevelName() string {
return levelStr[l.level]
}
func (l *Logger) SetLevelName(level string) {
level = strings.ToUpper(level)
for i, v := range levelStr {
if v == level {
l.SetLevel(i)
return
}
}
panic("unknown log level: " + level)
}
func (l *Logger) GetFlags() int {
return l.flags
}
func (l *Logger) SetFlags(flags int) *Logger {
l.m.Lock()
l.flags = flags
l.m.Unlock()
return l
}
func (l *Logger) GetAppName() string {
return l.name
}
func (l *Logger) SetAppName(name string) *Logger {
l.m.Lock()
l.name = name
l.m.Unlock()
return l
}
func (l *Logger) GetTimeFormat() string {
return l.timeFormat
}
func (l *Logger) SetTimeFormat(format string) *Logger {
l.m.Lock()
l.timeFormat = format
l.m.Unlock()
return l
}
func (l *Logger) SkipCaller(skip int) *Logger {
l.m.Lock()
defer l.m.Unlock()
l.callerSkip = skip
return l
}
func (l *Logger) SetWriter(w io.Writer) *Logger {
l.m.Lock()
defer l.m.Unlock()
l.writer = w
return l
}
func (l *Logger) IsDebugEnabled() bool {
return l.level <= L_DEBUG
}
func (l *Logger) IsInfoEnabled() bool {
return l.level <= L_INFO
}
func (l *Logger) IsWarnEnabled() bool {
return l.level <= L_WARN
}
func (l *Logger) IsErrorEnabled() bool {
return l.level <= L_ERROR
}
func (l *Logger) Debug(obj ...interface{}) {
if l.level <= L_DEBUG {
l.log(L_DEBUG, fmt.Sprint(obj...))
}
}
func (l *Logger) Info(obj ...interface{}) {
if l.level <= L_INFO {
l.log(L_INFO, fmt.Sprint(obj...))
}
}
func (l *Logger) Warn(obj ...interface{}) {
if l.level <= L_WARN {
l.log(L_WARN, fmt.Sprint(obj...))
}
}
func (l *Logger) Error(obj ...interface{}) {
if l.level <= L_ERROR {
l.log(L_ERROR, fmt.Sprint(obj...))
}
}
func (l *Logger) Fatal(obj ...interface{}) {
if l.level <= L_FATAL {
l.log(L_FATAL, fmt.Sprint(obj...))
}
}
func (l *Logger) Debugf(msg string, args ...interface{}) {
if l.level <= L_DEBUG {
l.log(L_DEBUG, fmt.Sprintf(msg, args...))
}
}
func (l *Logger) Infof(msg string, args ...interface{}) {
if l.level <= L_INFO {
l.log(L_INFO, fmt.Sprintf(msg, args...))
}
}
func (l *Logger) Warnf(msg string, args ...interface{}) {
if l.level <= L_WARN {
l.log(L_WARN, fmt.Sprintf(msg, args...))
}
}
func (l *Logger) Errorf(msg string, args ...interface{}) {
if l.level <= L_ERROR {
l.log(L_ERROR, fmt.Sprintf(msg, args...))
}
}
func (l *Logger) Fatalf(msg string, args ...interface{}) {
l.log(L_FATAL, fmt.Sprintf(msg, args...))
}
func (l *Logger) log(level int, msg string) {
// 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 := buffer.Get().(*bytes.Buffer)
defer buffer.Put(buf)
if l.flags&F_TIME != 0 {
timeStr := time.Now().Format(l.timeFormat)
buf.WriteString(timeStr)
buf.WriteByte(' ')
}
if l.flags&F_PID != 0 {
buf.WriteString(strconv.Itoa(l.pid))
buf.WriteByte(' ')
}
if l.name != "" {
buf.WriteString(l.name)
buf.WriteByte(' ')
}
if l.flags&F_GID != 0 {
buf.WriteString(strconv.FormatUint(gls.GoroutineID(), 10))
buf.WriteByte(' ')
}
if l.flags&F_COLOR != 0 {
buf.WriteString(levelStrWithColor[level])
} else {
buf.WriteString(levelStr[level])
}
buf.WriteByte(' ')
if l.flags&(F_LONG_FILE|F_SHORT_FILE) != 0 {
_, file, line, ok := runtime.Caller(l.callerSkip)
if !ok {
file = "???"
line = 0
} else if l.flags&F_SHORT_FILE != 0 {
if index := strings.LastIndex(file, "/"); index >= 0 {
file = file[index+1:]
} else if index = strings.LastIndex(file, "\\"); index >= 0 {
file = file[index+1:]
}
}
buf.WriteString(file)
buf.WriteByte(':')
buf.WriteString(strconv.Itoa(line))
buf.WriteByte(' ')
}
buf.WriteString(msg)
buf.WriteByte('\n')
if level == L_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)
}
}
line := buf.Bytes()
buf.Reset()
l.m.Lock()
defer l.m.Unlock()
l.writer.Write(line)
}