|
|
|
package logkit
|
|
|
|
|
|
|
|
import (
|
|
|
|
"flag"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"os"
|
|
|
|
"path"
|
|
|
|
"runtime"
|
|
|
|
"strconv"
|
|
|
|
"strings"
|
|
|
|
"time"
|
|
|
|
)
|
|
|
|
|
|
|
|
var (
|
|
|
|
inited bool
|
|
|
|
auto bool
|
|
|
|
logWriter io.Writer
|
|
|
|
flushInterval time.Duration
|
|
|
|
fileSplitSize uint64
|
|
|
|
logLevel = LevelInfo
|
|
|
|
logLevelName string
|
|
|
|
logName string
|
|
|
|
logPath string
|
|
|
|
channel Channel
|
|
|
|
alsoStdout bool
|
|
|
|
withCaller Caller
|
|
|
|
stdOut io.Writer
|
|
|
|
levelToNames = map[Level]string{
|
|
|
|
LevelFatal: "FATAL",
|
|
|
|
LevelError: "ERROR",
|
|
|
|
LevelWarn: "WARN",
|
|
|
|
LevelInfo: "INFO",
|
|
|
|
LevelDebug: "DEBUG",
|
|
|
|
LevelTrace: "TRACE",
|
|
|
|
}
|
|
|
|
)
|
|
|
|
|
|
|
|
//Level 日志等级
|
|
|
|
type Level int
|
|
|
|
|
|
|
|
const (
|
|
|
|
Default Level = iota
|
|
|
|
LevelTrace
|
|
|
|
LevelDebug
|
|
|
|
LevelInfo
|
|
|
|
LevelWarn
|
|
|
|
LevelError
|
|
|
|
LevelFatal
|
|
|
|
)
|
|
|
|
|
|
|
|
func (l *Level) String() string {
|
|
|
|
return levelToNames[*l]
|
|
|
|
}
|
|
|
|
|
|
|
|
// Get is part of the flag.Value interface.
|
|
|
|
func (l *Level) Get() interface{} {
|
|
|
|
return *l
|
|
|
|
}
|
|
|
|
|
|
|
|
func (l *Level) Set(value string) error {
|
|
|
|
for i, name := range levelToNames {
|
|
|
|
if strings.ToUpper(value) == name {
|
|
|
|
*l = i
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if *l == Default {
|
|
|
|
v, err := strconv.Atoi(value)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
*l = Level(v)
|
|
|
|
}
|
|
|
|
if *l == Default {
|
|
|
|
*l = LevelDebug
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type Channel byte
|
|
|
|
|
|
|
|
const (
|
|
|
|
FIlE Channel = iota
|
|
|
|
SYSLOG
|
|
|
|
KAFKA
|
|
|
|
STDOUT
|
|
|
|
)
|
|
|
|
|
|
|
|
func (c *Channel) String() string {
|
|
|
|
switch *c {
|
|
|
|
case FIlE:
|
|
|
|
return "file"
|
|
|
|
case SYSLOG:
|
|
|
|
return "syslog"
|
|
|
|
case STDOUT:
|
|
|
|
return "none"
|
|
|
|
}
|
|
|
|
return "file"
|
|
|
|
}
|
|
|
|
func (c *Channel) Set(value string) error {
|
|
|
|
switch strings.ToLower(value) {
|
|
|
|
case "file":
|
|
|
|
*c = FIlE
|
|
|
|
case "syslog":
|
|
|
|
*c = SYSLOG
|
|
|
|
case "none":
|
|
|
|
*c = STDOUT
|
|
|
|
default:
|
|
|
|
*c = FIlE
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type Caller byte
|
|
|
|
|
|
|
|
const (
|
|
|
|
_ Caller = iota
|
|
|
|
NONE
|
|
|
|
FullPATHFunc
|
|
|
|
BasePathFunc
|
|
|
|
BasePath
|
|
|
|
)
|
|
|
|
|
|
|
|
func (c *Caller) String() string {
|
|
|
|
switch *c {
|
|
|
|
case NONE:
|
|
|
|
return "none"
|
|
|
|
case FullPATHFunc:
|
|
|
|
return "full"
|
|
|
|
case BasePathFunc:
|
|
|
|
return "file_func"
|
|
|
|
case BasePath:
|
|
|
|
return "file"
|
|
|
|
}
|
|
|
|
return "file"
|
|
|
|
}
|
|
|
|
func (c *Caller) Set(value string) error {
|
|
|
|
switch value {
|
|
|
|
case "file":
|
|
|
|
*c = BasePath
|
|
|
|
case "file_func":
|
|
|
|
*c = BasePathFunc
|
|
|
|
case "full":
|
|
|
|
*c = FullPATHFunc
|
|
|
|
default:
|
|
|
|
*c = BasePathFunc
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
type Writer interface {
|
|
|
|
//Write 写日志
|
|
|
|
Write(msg []byte) (int, error)
|
|
|
|
//Exit 日志退出
|
|
|
|
Close() error
|
|
|
|
}
|
|
|
|
|
|
|
|
func GetWriter()( io.Writer, error) {
|
|
|
|
if logWriter == nil {
|
|
|
|
return nil, fmt.Errorf("logkit not inited")
|
|
|
|
}
|
|
|
|
return logWriter, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func Exit() {
|
|
|
|
if logWriter == nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
logWriter.(io.Closer).Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
func init() {
|
|
|
|
flag.Var(&logLevel, "log.level", "log level, default `INFO`, it can be `DEBUG, INFO, WARN, ERROR, FATAL`")
|
|
|
|
flag.Var(&channel, "log.channel", "write to , it can be `file syslog`")
|
|
|
|
flag.Var(&withCaller, "log.withCaller", "call context, by default filename and func name, it can be `file, file_func, full`")
|
|
|
|
|
|
|
|
flag.BoolVar(&alsoStdout, "log.alsoStdout", false, "log out to stand error as well, default `false`")
|
|
|
|
flag.StringVar(&logName, "log.name", "log", "log name, by default log will out to `/data/logs/{name}.log`")
|
|
|
|
flag.BoolVar(&auto, "log.autoInit", true, "log will be init automatic")
|
|
|
|
flag.DurationVar(&flushInterval, "log.interval", time.Second*5, "duration time on flush to disk")
|
|
|
|
flag.Uint64Var(&fileSplitSize, "log.split", uint64(1204*1024*1800), "log fail split on bytes")
|
|
|
|
}
|
|
|
|
|
|
|
|
//SetDebug set logger debug output
|
|
|
|
func SetDebug(debug bool) {
|
|
|
|
if debug {
|
|
|
|
alsoStdout = true
|
|
|
|
withCaller = BasePathFunc
|
|
|
|
logLevel = LevelDebug
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func Init() (writer io.Writer, err error) {
|
|
|
|
if inited {
|
|
|
|
return nil, fmt.Errorf("logkit has been inited")
|
|
|
|
}
|
|
|
|
|
|
|
|
if logName == "" {
|
|
|
|
return nil, fmt.Errorf("log name must not be empty")
|
|
|
|
}
|
|
|
|
if logWriter == nil && channel == FIlE {
|
|
|
|
if logPath == "" {
|
|
|
|
logPath = "/var/log/" + logName + ".log"
|
|
|
|
}
|
|
|
|
logWriter, err = NewFileLogger(logPath, logName, flushInterval, fileSplitSize, 4*1024)
|
|
|
|
if err != nil {
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if logWriter == nil && channel == SYSLOG {
|
|
|
|
logWriter, _ = NewSyslogWriter("", "", logLevel, logName)
|
|
|
|
}
|
|
|
|
if logWriter == nil && channel == STDOUT {
|
|
|
|
logWriter = os.Stdout
|
|
|
|
}
|
|
|
|
inited = true
|
|
|
|
return logWriter, nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func SetPath(path string) {
|
|
|
|
logPath = path
|
|
|
|
}
|
|
|
|
func getLevelName(level Level) string {
|
|
|
|
levelName, _ := levelToNames[level]
|
|
|
|
return levelName
|
|
|
|
}
|
|
|
|
|
|
|
|
func format(level Level, msg string) string {
|
|
|
|
if withCaller != NONE {
|
|
|
|
var (
|
|
|
|
context string
|
|
|
|
pc uintptr
|
|
|
|
file string
|
|
|
|
line int
|
|
|
|
)
|
|
|
|
pc, file, line, _ = runtime.Caller(3)
|
|
|
|
switch withCaller {
|
|
|
|
case FullPATHFunc:
|
|
|
|
context = fmt.Sprintf("%s:%03d::%-30s", file, line, path.Base(runtime.FuncForPC(pc).Name()))
|
|
|
|
case BasePathFunc:
|
|
|
|
context = fmt.Sprintf("%s:%03d::%-15s", path.Base(file), line, path.Base(runtime.FuncForPC(pc).Name()))
|
|
|
|
case BasePath:
|
|
|
|
context = fmt.Sprintf("%s:%03d", path.Base(file), line)
|
|
|
|
default:
|
|
|
|
context = fmt.Sprintf("%s:%03d", path.Base(file), line)
|
|
|
|
}
|
|
|
|
return fmt.Sprintf("%s\t[%4s]\t%s\t%s\n", time.Now().Format("2006-01-02 15:04:05.999"), getLevelName(level), context, msg)
|
|
|
|
} else {
|
|
|
|
return fmt.Sprintf("%s\t[%4s]\t%s\n", time.Now().Format("2006-01-02 15:04:05.999"), getLevelName(level), msg)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func write(level Level, msg string) (err error) {
|
|
|
|
if !flag.Parsed() {
|
|
|
|
return fmt.Errorf("logkit write must been flag parsed")
|
|
|
|
}
|
|
|
|
if auto && !inited {
|
|
|
|
Init()
|
|
|
|
}
|
|
|
|
if !inited {
|
|
|
|
return fmt.Errorf("logkit has been inited")
|
|
|
|
}
|
|
|
|
messageStr := format(level, msg)
|
|
|
|
_, err = logWriter.Write([]byte(messageStr))
|
|
|
|
if alsoStdout {
|
|
|
|
stdOut.Write([]byte(messageStr))
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func level() Level {
|
|
|
|
return logLevel
|
|
|
|
}
|
|
|
|
|
|
|
|
func Debug(str string) {
|
|
|
|
if level() <= LevelDebug {
|
|
|
|
write(LevelDebug, str)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func Debugs(args ...interface{}) {
|
|
|
|
if level() <= LevelDebug {
|
|
|
|
write(LevelDebug, fmt.Sprintln(args...))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func Debugf(format string, args ...interface{}) {
|
|
|
|
if level() <= LevelDebug {
|
|
|
|
write(LevelDebug, fmt.Sprintf(format, args...))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func Info(str string) {
|
|
|
|
if level() <= LevelInfo {
|
|
|
|
write(LevelInfo, str)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func Infos(args ...interface{}) {
|
|
|
|
if level() <= LevelInfo {
|
|
|
|
write(LevelInfo, fmt.Sprintln(args...))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func Infof(format string, args ...interface{}) {
|
|
|
|
if level() <= LevelInfo {
|
|
|
|
write(LevelInfo, fmt.Sprintf(format, args...))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func Warn(str string) {
|
|
|
|
if level() <= LevelWarn {
|
|
|
|
write(LevelWarn, str)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func Warns(args ...interface{}) {
|
|
|
|
if level() <= LevelWarn {
|
|
|
|
write(LevelWarn, fmt.Sprintln(args...))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func Warnf(format string, args ...interface{}) {
|
|
|
|
if level() <= LevelWarn {
|
|
|
|
write(LevelWarn, fmt.Sprintf(format, args...))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func Error(str string) {
|
|
|
|
if level() <= LevelError {
|
|
|
|
write(LevelError, str)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func Errors(args ...interface{}) {
|
|
|
|
if level() <= LevelError {
|
|
|
|
write(LevelError, fmt.Sprintln(args...))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func Errorf(format string, args ...interface{}) {
|
|
|
|
if level() <= LevelError {
|
|
|
|
write(LevelError, fmt.Sprintf(format, args...))
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func NewLogWriter(level Level) io.Writer {
|
|
|
|
return &stdWriter{level}
|
|
|
|
}
|
|
|
|
|
|
|
|
type stdWriter struct {
|
|
|
|
level Level
|
|
|
|
}
|
|
|
|
|
|
|
|
func (this *stdWriter) Write(data []byte) (int, error) {
|
|
|
|
write(this.level, string(data))
|
|
|
|
return len(data), nil
|
|
|
|
}
|