blob: 9ab50794ce50b2e0620db3860f5fabad92d214e0 [file] [log] [blame]
package logex
import (
"encoding/json"
"fmt"
"io"
goLog "log"
"os"
"path"
"runtime"
"strconv"
"strings"
)
var DebugLevel = 1
type Logger struct {
depth int
reqid string
Logger *goLog.Logger
}
func NewLogger(l int) *Logger {
return &Logger{l, "", goLogStd}
}
func NewLoggerEx(w io.Writer) *Logger {
return &Logger{0, "", NewGoLog(w)}
}
func NewGoLog(w io.Writer) *goLog.Logger {
return goLog.New(w, "", goLog.LstdFlags)
}
var goLogStd = goLog.New(os.Stderr, "", goLog.LstdFlags)
var std = NewLogger(1)
var ShowCode = true
var (
Println = std.Println
Infof = std.Infof
Info = std.Info
Debug = std.Debug
Debugf = std.Debugf
Error = std.Error
Errorf = std.Errorf
Warn = std.Warn
PrintStack = std.PrintStack
Stack = std.Stack
Panic = std.Panic
Fatal = std.Fatal
Struct = std.Struct
Pretty = std.Pretty
Todo = std.Todo
)
func SetStd(l *Logger) {
std = l
Println = std.Println
Infof = std.Infof
Info = std.Info
Debug = std.Debug
Error = std.Error
Warn = std.Warn
PrintStack = std.PrintStack
Stack = std.Stack
Panic = std.Panic
Fatal = std.Fatal
Struct = std.Struct
Pretty = std.Pretty
Todo = std.Todo
}
var (
INFO = "[INFO] "
ERROR = "[ERROR] "
PANIC = "[PANIC] "
DEBUG = "[DEBUG] "
WARN = "[WARN] "
FATAL = "[FATAL] "
STRUCT = "[STRUCT] "
PRETTY = "[PRETTY] "
TODO = "[TODO] "
)
func color(col, s string) string {
if col == "" {
return s
}
return "\x1b[0;" + col + "m" + s + "\x1b[0m"
}
func init() {
if os.Getenv("DEBUG") != "" {
DebugLevel = 0
ERROR = color("32", ERROR)
}
}
func DownLevel(i int) Logger {
return std.DownLevel(i - 1)
}
// decide to show which level's stack
func (l Logger) DownLevel(i int) Logger {
return Logger{l.depth + i, l.reqid, l.Logger}
}
// output objects to json format
func (l Logger) Pretty(os ...interface{}) {
content := ""
for i := range os {
if ret, err := json.MarshalIndent(os[i], "", "\t"); err == nil {
content += string(ret) + "\n"
}
}
l.Output(2, PRETTY+content)
}
// just print
func (l Logger) Print(o ...interface{}) {
l.Output(2, sprint(o))
}
// just print by format
func (l Logger) Printf(layout string, o ...interface{}) {
l.Output(2, sprintf(layout, o))
}
// just println
func (l Logger) Println(o ...interface{}) {
l.Output(2, " "+sprint(o))
}
func (l Logger) Info(o ...interface{}) {
if DebugLevel > 1 {
return
}
l.Output(2, INFO+sprint(o))
}
func (l Logger) Infof(f string, o ...interface{}) {
if DebugLevel > 1 {
return
}
l.Output(2, INFO+sprintf(f, o))
}
func (l Logger) Debug(o ...interface{}) {
if DebugLevel > 0 {
return
}
l.Output(2, DEBUG+sprint(o))
}
func (l Logger) Debugf(f string, o ...interface{}) {
if DebugLevel > 0 {
return
}
l.Output(2, DEBUG+sprintf(f, o))
}
func (l Logger) Todo(o ...interface{}) {
l.Output(2, TODO+sprint(o))
}
func (l Logger) Error(o ...interface{}) {
l.Output(2, ERROR+sprint(o))
}
func (l Logger) Errorf(f string, o ...interface{}) {
l.Output(2, ERROR+sprintf(f, o))
}
func (l Logger) Warn(o ...interface{}) {
l.Output(2, WARN+sprint(o))
}
func (l Logger) Warnf(f string, o ...interface{}) {
l.Output(2, WARN+sprintf(f, o))
}
func (l Logger) Panic(o ...interface{}) {
l.Output(2, PANIC+sprint(o))
panic(o)
}
func (l Logger) Panicf(f string, o ...interface{}) {
info := sprintf(f, o)
l.Output(2, PANIC+info)
panic(info)
}
func (l Logger) Fatal(o ...interface{}) {
l.Output(2, FATAL+sprint(o))
os.Exit(1)
}
func (l Logger) Fatalf(f string, o ...interface{}) {
l.Output(2, FATAL+sprintf(f, o))
os.Exit(1)
}
func (l Logger) Struct(o ...interface{}) {
items := make([]interface{}, 0, len(o)*2)
for _, item := range o {
items = append(items, item, item)
}
layout := strings.Repeat(", %T(%+v)", len(o))
if len(layout) > 0 {
layout = layout[2:]
}
l.Output(2, STRUCT+sprintf(layout, items))
}
func (l Logger) PrintStack() {
Info(string(l.Stack()))
}
func (l Logger) Stack() []byte {
a := make([]byte, 1024*1024)
n := runtime.Stack(a, true)
return a[:n]
}
func (l Logger) Output(calldepth int, s string) error {
calldepth += l.depth + 1
if l.Logger == nil {
l.Logger = goLogStd
}
return l.Logger.Output(calldepth, l.makePrefix(calldepth)+s)
}
func (l Logger) makePrefix(calldepth int) string {
if !ShowCode {
return ""
}
pc, f, line, _ := runtime.Caller(calldepth)
name := runtime.FuncForPC(pc).Name()
name = path.Base(name) // only use package.funcname
f = path.Base(f) // only use filename
tags := make([]string, 0, 3)
pos := name + ":" + f + ":" + strconv.Itoa(line)
tags = append(tags, pos)
if l.reqid != "" {
tags = append(tags, l.reqid)
}
return "[" + strings.Join(tags, "][") + "]"
}
func Sprint(o ...interface{}) string {
return sprint(o)
}
func Sprintf(f string, o ...interface{}) string {
return sprintf(f, o)
}
func sprint(o []interface{}) string {
decodeTraceError(o)
return joinInterface(o, " ")
}
func sprintf(f string, o []interface{}) string {
decodeTraceError(o)
return fmt.Sprintf(f, o...)
}
func DecodeError(e error) string {
if e == nil {
return ""
}
if e1, ok := e.(*traceError); ok {
return e1.StackError()
}
return e.Error()
}
func decodeTraceError(o []interface{}) {
if !ShowCode {
return
}
for idx, obj := range o {
if te, ok := obj.(*traceError); ok {
o[idx] = te.StackError()
}
}
}