init
This commit is contained in:
+94
@@ -0,0 +1,94 @@
|
||||
# mini-logger
|
||||
mini but flexible and powerful logger for go
|
||||
# Notice
|
||||
1.Do not call runtime.Goexit() in main , it will be blocking logger.Flush().
|
||||
# Features
|
||||
<ul>
|
||||
<li>Console</li>
|
||||
<li>File,supoort rotate base size</li>
|
||||
</ul>
|
||||
# Usage Example
|
||||
<pre>
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/snail007/mini-logger"
|
||||
"github.com/snail007/mini-logger/writers/console"
|
||||
"github.com/snail007/mini-logger/writers/files"
|
||||
)
|
||||
|
||||
var log logger.MiniLogger
|
||||
var accessLog logger.MiniLogger
|
||||
|
||||
//initLog
|
||||
func initLog() {
|
||||
var level uint8
|
||||
switch cfg.GetString("log.console-level") {
|
||||
case "debug":
|
||||
level = logger.AllLevels
|
||||
case "info":
|
||||
level = logger.InfoLevel | logger.WarnLevel | logger.ErrorLevel | logger.FatalLevel
|
||||
case "warn":
|
||||
level = logger.WarnLevel | logger.ErrorLevel | logger.FatalLevel
|
||||
case "error":
|
||||
level = logger.ErrorLevel | logger.FatalLevel
|
||||
case "fatal":
|
||||
level = logger.FatalLevel
|
||||
default:
|
||||
level = 0
|
||||
}
|
||||
log = logger.New(false, nil)
|
||||
log.AddWriter(console.NewDefault(), level)
|
||||
cfgF := files.GetDefaultFileConfig()
|
||||
cfgF.LogPath = cfg.GetString("log.dir")
|
||||
cfgF.MaxBytes = cfg.GetInt64("log.FileMaxSize")
|
||||
cfgF.MaxCount = cfg.GetInt("log.MaxCount")
|
||||
cfgLevels := cfg.GetStringSlice("log.level")
|
||||
if ok, _ := inArray("debug", cfgLevels); ok {
|
||||
cfgF.FileNameSet["debug"] = logger.AllLevels
|
||||
}
|
||||
if ok, _ := inArray("info", cfgLevels); ok {
|
||||
cfgF.FileNameSet["info"] = logger.InfoLevel
|
||||
}
|
||||
if ok, _ := inArray("error", cfgLevels); ok {
|
||||
cfgF.FileNameSet["error"] = logger.WarnLevel | logger.ErrorLevel | logger.FatalLevel
|
||||
}
|
||||
log.AddWriter(files.New(cfgF), logger.AllLevels)
|
||||
|
||||
accessLog = logger.New(false, nil)
|
||||
//accessLog.AddWriter(console.NewDefault(), logger.AllLevels)
|
||||
if cfg.GetBool("log.access") {
|
||||
accessCfg := files.GetDefaultFileConfig()
|
||||
accessCfg.LogPath = cfg.GetString("log.dir")
|
||||
accessCfg.MaxBytes = cfg.GetInt64("log.FileMaxSize")
|
||||
accessCfg.MaxCount = cfg.GetInt("log.MaxCount")
|
||||
accessCfg.FileNameSet = map[string]uint8{"access": logger.InfoLevel}
|
||||
accessLog.AddWriter(files.New(accessCfg), logger.InfoLevel)
|
||||
}
|
||||
|
||||
log.With(logger.Fields{"func": "getMqConnection", "call": "pools.Get"}).Errorf("fail,%s", err)
|
||||
}
|
||||
|
||||
//MiniLogger is a interface below:
|
||||
type MiniLogger interface {
|
||||
Debug(v ...interface{}) MiniLogger
|
||||
Info(v ...interface{}) MiniLogger
|
||||
Warn(v ...interface{}) MiniLogger
|
||||
Error(v ...interface{}) MiniLogger
|
||||
Fatal(v ...interface{})
|
||||
Debugf(format string, v ...interface{}) MiniLogger
|
||||
Infof(format string, v ...interface{}) MiniLogger
|
||||
Warnf(format string, v ...interface{}) MiniLogger
|
||||
Errorf(format string, v ...interface{}) MiniLogger
|
||||
Fatalf(format string, v ...interface{})
|
||||
Debugln(v ...interface{}) MiniLogger
|
||||
Infoln(v ...interface{}) MiniLogger
|
||||
Warnln(v ...interface{}) MiniLogger
|
||||
Errorln(v ...interface{}) MiniLogger
|
||||
Fatalln(v ...interface{})
|
||||
AddWriter(w Writer, levels uint8) MiniLogger
|
||||
Safe() MiniLogger
|
||||
Unsafe() MiniLogger
|
||||
With(fields Fields) MiniLogger
|
||||
}
|
||||
</pre>
|
||||
+331
@@ -0,0 +1,331 @@
|
||||
package logger
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"sync"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
DebugLevel uint8 = 1
|
||||
InfoLevel uint8 = 1 << 1
|
||||
WarnLevel uint8 = 1 << 2
|
||||
ErrorLevel uint8 = 1 << 3
|
||||
FatalLevel uint8 = 1 << 4
|
||||
AllLevels uint8 = byte(DebugLevel | InfoLevel | WarnLevel | ErrorLevel | FatalLevel)
|
||||
)
|
||||
|
||||
var (
|
||||
flush = &sync.WaitGroup{}
|
||||
exitchn = make(chan bool, 1)
|
||||
)
|
||||
|
||||
type Fields map[string]string
|
||||
type Writer interface {
|
||||
Write(e Entry)
|
||||
Init() error
|
||||
}
|
||||
|
||||
type Logger struct {
|
||||
writersMap *map[int][]*WrappedWriter
|
||||
mu sync.Mutex
|
||||
modeSafe bool
|
||||
wait sync.WaitGroup
|
||||
fields Fields
|
||||
fieldsString string
|
||||
}
|
||||
|
||||
type WrappedWriter struct {
|
||||
lock *sync.Mutex
|
||||
chn chan Entry
|
||||
writer Writer
|
||||
wait sync.WaitGroup
|
||||
}
|
||||
type Entry struct {
|
||||
Content string
|
||||
Timestamp int64
|
||||
Milliseconds int64
|
||||
TimestampMilliseconds int64
|
||||
Level uint8
|
||||
LevelString string
|
||||
Fields map[string]string
|
||||
FieldsString string
|
||||
}
|
||||
type MiniLogger interface {
|
||||
Debug(v ...interface{}) MiniLogger
|
||||
Info(v ...interface{}) MiniLogger
|
||||
Warn(v ...interface{}) MiniLogger
|
||||
Error(v ...interface{}) MiniLogger
|
||||
Fatal(v ...interface{})
|
||||
Debugf(format string, v ...interface{}) MiniLogger
|
||||
Infof(format string, v ...interface{}) MiniLogger
|
||||
Warnf(format string, v ...interface{}) MiniLogger
|
||||
Errorf(format string, v ...interface{}) MiniLogger
|
||||
Fatalf(format string, v ...interface{})
|
||||
Debugln(v ...interface{}) MiniLogger
|
||||
Infoln(v ...interface{}) MiniLogger
|
||||
Warnln(v ...interface{}) MiniLogger
|
||||
Errorln(v ...interface{}) MiniLogger
|
||||
Fatalln(v ...interface{})
|
||||
AddWriter(w Writer, levels uint8) MiniLogger
|
||||
Safe() MiniLogger
|
||||
Unsafe() MiniLogger
|
||||
With(fields Fields) MiniLogger
|
||||
}
|
||||
|
||||
func getLevelString(level uint8) string {
|
||||
switch level {
|
||||
case DebugLevel:
|
||||
return "DEBUG"
|
||||
case InfoLevel:
|
||||
return "INFO"
|
||||
case WarnLevel:
|
||||
return "WARN"
|
||||
case ErrorLevel:
|
||||
return "ERROR"
|
||||
case FatalLevel:
|
||||
return "FATAL"
|
||||
}
|
||||
return "UNKOWN"
|
||||
}
|
||||
|
||||
// New returns a new logger
|
||||
//modeSafe when false : must call logger.Flush() in main defer
|
||||
//if not do that, may be lost message,but this mode has a highest performance
|
||||
//when true : each message can be processed , but this mode may be has a little lower performance
|
||||
//beacuse of logger must wait for all writers process done with each messsage.
|
||||
//note: if you do logging before call os.Exit(), you had better to set safeMode to true before call os.Exit().
|
||||
//fields same as With()'s args,more about fields to see With().
|
||||
func New(modeSafe bool, fields Fields) MiniLogger {
|
||||
if fields == nil {
|
||||
fields = Fields{}
|
||||
}
|
||||
return &Logger{
|
||||
fields: fields,
|
||||
fieldsString: "",
|
||||
mu: sync.Mutex{},
|
||||
modeSafe: modeSafe,
|
||||
wait: sync.WaitGroup{},
|
||||
writersMap: &map[int][]*WrappedWriter{
|
||||
int(DebugLevel): []*WrappedWriter{},
|
||||
int(InfoLevel): []*WrappedWriter{},
|
||||
int(WarnLevel): []*WrappedWriter{},
|
||||
int(ErrorLevel): []*WrappedWriter{},
|
||||
int(FatalLevel): []*WrappedWriter{},
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
//Flush wait for process all left entry
|
||||
func Flush() {
|
||||
exitchn <- true
|
||||
flush.Wait()
|
||||
}
|
||||
|
||||
//With return a new MiniLogger,which it's modeSafe inited same as caller MiniLogger,
|
||||
//and revecived levels same as caller MiniLogger,
|
||||
//and set it's fields.
|
||||
//you can use it as a standard MiniLogger.
|
||||
func (l *Logger) With(fields Fields) MiniLogger {
|
||||
for k, v := range l.fields {
|
||||
fields[k] = v
|
||||
}
|
||||
s, _ := json.Marshal(fields)
|
||||
return &Logger{
|
||||
mu: sync.Mutex{},
|
||||
modeSafe: l.modeSafe,
|
||||
wait: sync.WaitGroup{},
|
||||
writersMap: l.writersMap,
|
||||
fields: fields,
|
||||
fieldsString: string(s),
|
||||
}
|
||||
}
|
||||
|
||||
//Unsafe : if you call Unsafe() , then you must call logger.Flush() in main defer
|
||||
//if not do that, may be lost message,but this mode has a highest performance
|
||||
//note: if you do logging before call os.Exit(), you had better to call Safe() before call os.Exit().
|
||||
//you call call func Safe() or Unsafe() in any where any time to switch safe mode.
|
||||
|
||||
func (l *Logger) Unsafe() MiniLogger {
|
||||
l.modeSafe = false
|
||||
return l
|
||||
}
|
||||
|
||||
//Safe : if you call Safe(), each message can be processed immediately,
|
||||
//but this mode may be has lower performance,beacuse of logger must wait for
|
||||
//all writers process done with each messsage.
|
||||
//note: if you do logging before call os.Exit(), you had better to call Safe() before call os.Exit().
|
||||
//you call func Safe() or Unsafe() in any where any time to switch safe mode.
|
||||
func (l *Logger) Safe() MiniLogger {
|
||||
l.modeSafe = true
|
||||
return l
|
||||
}
|
||||
func (l *Logger) AddWriter(writer Writer, levels byte) MiniLogger {
|
||||
w := &WrappedWriter{
|
||||
lock: &sync.Mutex{},
|
||||
writer: writer,
|
||||
chn: make(chan Entry, 1024),
|
||||
}
|
||||
m := *l.writersMap
|
||||
if DebugLevel&levels == DebugLevel {
|
||||
m[int(DebugLevel)] = append(m[int(DebugLevel)], w)
|
||||
}
|
||||
if InfoLevel&levels == InfoLevel {
|
||||
m[int(InfoLevel)] = append(m[int(InfoLevel)], w)
|
||||
}
|
||||
if WarnLevel&levels == WarnLevel {
|
||||
m[int(WarnLevel)] = append(m[int(WarnLevel)], w)
|
||||
}
|
||||
if ErrorLevel&levels == ErrorLevel {
|
||||
m[int(ErrorLevel)] = append(m[int(ErrorLevel)], w)
|
||||
}
|
||||
if FatalLevel&levels == FatalLevel {
|
||||
m[int(FatalLevel)] = append(m[int(FatalLevel)], w)
|
||||
}
|
||||
flush.Add(1)
|
||||
go func() {
|
||||
defer func() {
|
||||
_ = recover()
|
||||
flush.Done()
|
||||
}()
|
||||
if err := w.writer.Init(); err != nil {
|
||||
fmt.Printf("init writer fail,%s", err)
|
||||
return
|
||||
}
|
||||
for {
|
||||
select {
|
||||
case entry, ok := <-w.chn:
|
||||
if ok {
|
||||
w.writer.Write(entry)
|
||||
if l.modeSafe {
|
||||
l.wait.Done()
|
||||
}
|
||||
if entry.Level == FatalLevel {
|
||||
os.Exit(0)
|
||||
}
|
||||
} else {
|
||||
return
|
||||
}
|
||||
case exit := <-exitchn:
|
||||
if exit {
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
return l
|
||||
}
|
||||
func (l *Logger) callWriter(level byte, t, foramt string, v ...interface{}) {
|
||||
if level == FatalLevel {
|
||||
l.Safe()
|
||||
}
|
||||
c := ""
|
||||
if t == "f" && len(v) == 0 {
|
||||
v = append(v, foramt)
|
||||
foramt = "%s"
|
||||
}
|
||||
switch t {
|
||||
case "ln":
|
||||
c = fmt.Sprintln(v...)
|
||||
case "f":
|
||||
c = fmt.Sprintf(foramt, v...)
|
||||
default:
|
||||
c = fmt.Sprint(v...)
|
||||
}
|
||||
m := *l.writersMap
|
||||
for _, w := range m[int(level)] {
|
||||
now := time.Now().UnixNano()
|
||||
nowUnix := time.Now().Unix()
|
||||
mili := (now / 1000000) - nowUnix*1000
|
||||
w.chn <- Entry{
|
||||
Timestamp: nowUnix,
|
||||
TimestampMilliseconds: now / 10000000,
|
||||
Milliseconds: mili,
|
||||
Content: c,
|
||||
Level: level,
|
||||
LevelString: getLevelString(level),
|
||||
Fields: l.fields,
|
||||
FieldsString: l.fieldsString,
|
||||
}
|
||||
if l.modeSafe {
|
||||
l.wait.Add(1)
|
||||
}
|
||||
}
|
||||
if l.modeSafe {
|
||||
l.wait.Wait()
|
||||
}
|
||||
}
|
||||
|
||||
func (l *Logger) Fatal(v ...interface{}) {
|
||||
l.callWriter(FatalLevel, "", "", v...)
|
||||
}
|
||||
|
||||
func (l *Logger) Error(v ...interface{}) MiniLogger {
|
||||
l.callWriter(ErrorLevel, "", "", v...)
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *Logger) Warn(v ...interface{}) MiniLogger {
|
||||
l.callWriter(WarnLevel, "", "", v...)
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *Logger) Info(v ...interface{}) MiniLogger {
|
||||
l.callWriter(InfoLevel, "", "", v...)
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *Logger) Debug(v ...interface{}) MiniLogger {
|
||||
l.callWriter(DebugLevel, "", "", v...)
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *Logger) Fatalf(format string, v ...interface{}) {
|
||||
l.callWriter(FatalLevel, "f", format, v...)
|
||||
}
|
||||
|
||||
func (l *Logger) Errorf(format string, v ...interface{}) MiniLogger {
|
||||
l.callWriter(ErrorLevel, "f", format, v...)
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *Logger) Warnf(format string, v ...interface{}) MiniLogger {
|
||||
l.callWriter(WarnLevel, "f", format, v...)
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *Logger) Infof(format string, v ...interface{}) MiniLogger {
|
||||
l.callWriter(InfoLevel, "f", format, v...)
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *Logger) Debugf(format string, v ...interface{}) MiniLogger {
|
||||
l.callWriter(DebugLevel, "f", format, v...)
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *Logger) Fatalln(v ...interface{}) {
|
||||
l.callWriter(FatalLevel, "ln", "", v...)
|
||||
}
|
||||
|
||||
func (l *Logger) Errorln(v ...interface{}) MiniLogger {
|
||||
l.callWriter(ErrorLevel, "ln", "", v...)
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *Logger) Warnln(v ...interface{}) MiniLogger {
|
||||
l.callWriter(WarnLevel, "ln", "", v...)
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *Logger) Infoln(v ...interface{}) MiniLogger {
|
||||
l.callWriter(InfoLevel, "ln", "", v...)
|
||||
return l
|
||||
}
|
||||
|
||||
func (l *Logger) Debugln(v ...interface{}) MiniLogger {
|
||||
l.callWriter(DebugLevel, "ln", "", v...)
|
||||
return l
|
||||
}
|
||||
Generated
Vendored
Executable
+75
@@ -0,0 +1,75 @@
|
||||
package console
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"encoding/json"
|
||||
|
||||
"github.com/fatih/color"
|
||||
"github.com/snail007/mini-logger"
|
||||
)
|
||||
|
||||
const (
|
||||
T_JSON = iota
|
||||
T_TEXT
|
||||
)
|
||||
|
||||
type ConsoleWriterConfig struct {
|
||||
Format string
|
||||
Type int
|
||||
}
|
||||
type ConsoleWriter struct {
|
||||
c ConsoleWriterConfig
|
||||
}
|
||||
|
||||
func New(config ConsoleWriterConfig) *ConsoleWriter {
|
||||
return &ConsoleWriter{
|
||||
c: config,
|
||||
}
|
||||
}
|
||||
func NewDefault() *ConsoleWriter {
|
||||
return &ConsoleWriter{
|
||||
c: ConsoleWriterConfig{
|
||||
Format: "[{level}] [{date} {time}.{mili}] {fields} {text}",
|
||||
Type: T_TEXT,
|
||||
},
|
||||
}
|
||||
}
|
||||
func (w *ConsoleWriter) Init() (err error) {
|
||||
return
|
||||
}
|
||||
func (w *ConsoleWriter) Write(e logger.Entry) {
|
||||
fg := color.New(color.FgHiWhite).SprintFunc()
|
||||
switch e.Level {
|
||||
case logger.InfoLevel:
|
||||
fg = color.New(color.FgHiGreen).SprintFunc()
|
||||
case logger.WarnLevel:
|
||||
fg = color.New(color.FgHiYellow).SprintFunc()
|
||||
case logger.ErrorLevel:
|
||||
fg = color.New(color.FgHiRed).SprintFunc()
|
||||
case logger.FatalLevel:
|
||||
fg = color.New(color.FgHiRed, color.FgHiMagenta).SprintFunc()
|
||||
}
|
||||
date := time.Unix(e.Timestamp, 0).Format("2006/01/02")
|
||||
time := time.Unix(e.Timestamp, 0).Format("15:04:05")
|
||||
var c string
|
||||
if w.c.Type == T_TEXT {
|
||||
c = strings.Replace(w.c.Format, "{level}", fmt.Sprintf("%-5s", e.LevelString), -1)
|
||||
c = strings.Replace(c, "{date}", date, -1)
|
||||
c = strings.Replace(c, "{time}", time, -1)
|
||||
c = strings.Replace(c, "{fields}", e.FieldsString, -1)
|
||||
c = strings.Replace(c, "{mili}", fmt.Sprintf("%03d", e.Milliseconds), -1)
|
||||
c = strings.Replace(c, "{text}", e.Content, -1)
|
||||
} else if w.c.Type == T_JSON {
|
||||
e.LevelString = strings.TrimRight(e.LevelString, " ")
|
||||
e.FieldsString = ""
|
||||
v, _ := json.Marshal(e)
|
||||
c = string(v)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
fmt.Fprintln(os.Stdout, fg(c))
|
||||
}
|
||||
Generated
Vendored
Executable
+233
@@ -0,0 +1,233 @@
|
||||
package files
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"regexp"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"bufio"
|
||||
|
||||
"os"
|
||||
|
||||
"path"
|
||||
|
||||
"compress/gzip"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"strconv"
|
||||
|
||||
"path/filepath"
|
||||
|
||||
"github.com/snail007/mini-logger"
|
||||
)
|
||||
|
||||
const (
|
||||
T_JSON = iota
|
||||
T_TEXT
|
||||
)
|
||||
|
||||
type FileConfig struct {
|
||||
IsRotate bool
|
||||
MaxBytes int64
|
||||
MaxCount int
|
||||
LogPath string
|
||||
FileNameSet map[string]uint8
|
||||
IsCompress bool
|
||||
Type int
|
||||
Format string
|
||||
}
|
||||
type bufWriter struct {
|
||||
file *os.File
|
||||
writer *bufio.Writer
|
||||
}
|
||||
type FileWriter struct {
|
||||
filePtrMap map[string]*bufWriter
|
||||
c FileConfig
|
||||
}
|
||||
|
||||
func New(config FileConfig) *FileWriter {
|
||||
return &FileWriter{
|
||||
c: config,
|
||||
filePtrMap: map[string]*bufWriter{},
|
||||
}
|
||||
}
|
||||
|
||||
func NewDefault() *FileWriter {
|
||||
return &FileWriter{
|
||||
filePtrMap: map[string]*bufWriter{},
|
||||
c: GetDefaultFileConfig(),
|
||||
}
|
||||
}
|
||||
|
||||
//GetDefaultFileConfig config for new FileWriter
|
||||
//IsRotate : whether to rotate log file.
|
||||
//MaxBytes : when RotateType is file.T_SIZE ,this need to be set.
|
||||
//MaxCount : how many log files can be remain.
|
||||
//LogPath : the folder which store log files,must be exists.
|
||||
//FileNameSet : key is filename of log file,no extesion.value is levels
|
||||
//IsCompress : whether to compress rotate log file.
|
||||
//Type:output format,should be files.T_JSON or files.T_TEXT
|
||||
//Format:when Type is files.T_TEXT,this can setting output text format,
|
||||
// default is : [{level}] [{date} {time}.{mili}] {text} {fields}
|
||||
func GetDefaultFileConfig() FileConfig {
|
||||
return FileConfig{
|
||||
IsRotate: true,
|
||||
MaxBytes: 100 * 1024 * 1024,
|
||||
MaxCount: 10,
|
||||
LogPath: "log",
|
||||
FileNameSet: map[string]uint8{
|
||||
"info": logger.InfoLevel,
|
||||
"error": logger.WarnLevel | logger.ErrorLevel | logger.FatalLevel,
|
||||
},
|
||||
IsCompress: true,
|
||||
Type: T_JSON,
|
||||
Format: "[{level}] [{date} {time}.{mili}] {text} {fields}",
|
||||
}
|
||||
}
|
||||
func getFilePath(logPath, filename string) string {
|
||||
|
||||
return path.Join(logPath, filename+".log")
|
||||
|
||||
}
|
||||
func (w *FileWriter) Init() (err error) {
|
||||
if _, err := os.Stat(w.c.LogPath); os.IsNotExist(err) {
|
||||
os.Mkdir(w.c.LogPath, 0700)
|
||||
}
|
||||
for filename := range w.c.FileNameSet {
|
||||
f, err := os.OpenFile(getFilePath(w.c.LogPath, filename), os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bf := bufio.NewWriter(f)
|
||||
w.filePtrMap[filename] = &bufWriter{
|
||||
file: f,
|
||||
writer: bf,
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
func (w *FileWriter) Write(e logger.Entry) {
|
||||
for filename, levels := range w.c.FileNameSet {
|
||||
if e.Level&levels == e.Level {
|
||||
date := time.Unix(e.Timestamp, 0).Format("2006/01/02")
|
||||
time := time.Unix(e.Timestamp, 0).Format("15:04:05")
|
||||
var c string
|
||||
if w.c.Type == T_TEXT {
|
||||
c = strings.Replace(w.c.Format, "{level}", fmt.Sprintf("%-5s", e.LevelString), -1)
|
||||
c = strings.Replace(c, "{date}", date, -1)
|
||||
c = strings.Replace(c, "{time}", time, -1)
|
||||
c = strings.Replace(c, "{fields}", e.FieldsString, -1)
|
||||
c = strings.Replace(c, "{mili}", fmt.Sprintf("%03d", e.Milliseconds), -1)
|
||||
c = strings.Replace(c, "{text}", e.Content, -1)
|
||||
} else if w.c.Type == T_JSON {
|
||||
e.LevelString = strings.TrimRight(e.LevelString, " ")
|
||||
e.FieldsString = ""
|
||||
v, _ := json.Marshal(e)
|
||||
c = string(v)
|
||||
} else {
|
||||
return
|
||||
}
|
||||
w.filePtrMap[filename].writer.WriteString(fmt.Sprintln(c))
|
||||
w.filePtrMap[filename].writer.Flush()
|
||||
if w.c.IsRotate {
|
||||
var filepath = getFilePath(w.c.LogPath, filename)
|
||||
if stat, err := os.Stat(filepath); err == nil {
|
||||
if stat.Size() > w.c.MaxBytes {
|
||||
w.filePtrMap[filename].file.Close()
|
||||
var i = findI(w, filename)
|
||||
var p = ""
|
||||
if w.c.IsCompress {
|
||||
p = path.Join(w.c.LogPath, fmt.Sprintf("%s.%d", filename, i)+".log.gz")
|
||||
} else {
|
||||
p = path.Join(w.c.LogPath, fmt.Sprintf("%s.%d", filename, i)+".log")
|
||||
}
|
||||
if ok, err := exists(p); (err == nil && !ok) || err != nil {
|
||||
if err != nil {
|
||||
fmt.Printf("ERROR fail to get stat of log file,%s", err)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
for k := i - w.c.MaxCount; k >= 0; k-- {
|
||||
p1 := getFilePath(w.c.LogPath, fmt.Sprintf("%s.%d", filename, k))
|
||||
p2 := p1 + ".gz"
|
||||
p1e, _ := exists(p1)
|
||||
p2e, _ := exists(p2)
|
||||
os.Remove(p1)
|
||||
os.Remove(p2)
|
||||
if !p1e && !p2e {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
newfilepath := getFilePath(w.c.LogPath, fmt.Sprintf("%s.%d", filename, i))
|
||||
e := os.Rename(filepath, newfilepath)
|
||||
if e != nil {
|
||||
fmt.Printf("ERROR fail to Rename log file,%s", e)
|
||||
return
|
||||
}
|
||||
if w.c.IsCompress {
|
||||
go func() {
|
||||
f, _ := os.OpenFile(newfilepath+".gz", os.O_CREATE|os.O_WRONLY, 0600)
|
||||
rf, _ := os.OpenFile(newfilepath, os.O_RDONLY, 0600)
|
||||
gz, err := gzip.NewWriterLevel(f, gzip.BestCompression)
|
||||
b, _ := ioutil.ReadAll(rf)
|
||||
if err == nil {
|
||||
gz.Write(b)
|
||||
gz.Flush()
|
||||
gz.Close()
|
||||
f.Close()
|
||||
rf.Close()
|
||||
os.Remove(newfilepath)
|
||||
} else {
|
||||
fmt.Printf("ERROR fail to compress log file,%s", err)
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
f, e := os.OpenFile(filepath, os.O_WRONLY|os.O_CREATE|os.O_APPEND, 0600)
|
||||
if e != nil {
|
||||
fmt.Println("ERROR fail to create log file")
|
||||
return
|
||||
}
|
||||
bf := bufio.NewWriter(f)
|
||||
w.filePtrMap[filename] = &bufWriter{
|
||||
file: f,
|
||||
writer: bf,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
func exists(path string) (bool, error) {
|
||||
_, err := os.Stat(path)
|
||||
if err == nil {
|
||||
return true, nil
|
||||
}
|
||||
if os.IsNotExist(err) {
|
||||
return false, nil
|
||||
}
|
||||
return true, err
|
||||
}
|
||||
func findI(w *FileWriter, filename string) int {
|
||||
subfix := ""
|
||||
if w.c.IsCompress {
|
||||
subfix = ".gz"
|
||||
}
|
||||
p := w.c.LogPath + "/" + filename + ".*.log" + subfix
|
||||
files, _ := filepath.Glob(p)
|
||||
l := len(files)
|
||||
if l == 0 {
|
||||
return 0
|
||||
}
|
||||
re := regexp.MustCompile(filename + "\\.(\\d+)\\.log" + subfix)
|
||||
matched := re.FindStringSubmatch(files[l-1])
|
||||
if len(matched) == 2 {
|
||||
i, _ := strconv.Atoi(matched[1])
|
||||
return i + 1
|
||||
}
|
||||
return 0
|
||||
}
|
||||
Reference in New Issue
Block a user