|
|
|
|
package logm
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"fmt"
|
|
|
|
|
toolm2 "github.com/han-joker/moo-layout/api/moo/toolm"
|
|
|
|
|
"github.com/sirupsen/logrus"
|
|
|
|
|
"io"
|
|
|
|
|
"os"
|
|
|
|
|
"path"
|
|
|
|
|
"time"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
//常量
|
|
|
|
|
|
|
|
|
|
//日志文件后缀
|
|
|
|
|
const fileExt = ".log"
|
|
|
|
|
|
|
|
|
|
//日志格式
|
|
|
|
|
const (
|
|
|
|
|
Text = iota
|
|
|
|
|
Json
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// OutMode 预设值
|
|
|
|
|
const (
|
|
|
|
|
Console = iota // 控制台
|
|
|
|
|
File // 文件,单文件模式
|
|
|
|
|
FilePerDay // 每天一个文件
|
|
|
|
|
FilePerWeek // 每周一个文件
|
|
|
|
|
FilePerMonth // 每月一个文件
|
|
|
|
|
FilePerHour // 每小时一个文件
|
|
|
|
|
FilePerSize // 文件固定大小
|
|
|
|
|
User
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
//变量
|
|
|
|
|
|
|
|
|
|
//池
|
|
|
|
|
var pool = map[string]*log{}
|
|
|
|
|
|
|
|
|
|
//格式集合
|
|
|
|
|
var fmtContainer = []int{Text, Json}
|
|
|
|
|
|
|
|
|
|
//输出模式
|
|
|
|
|
var outModeContainer = []int{Console, File, FilePerDay, FilePerWeek, FilePerMonth, FilePerHour, FilePerSize, User}
|
|
|
|
|
|
|
|
|
|
//默认选项
|
|
|
|
|
var optionDefault = Option{
|
|
|
|
|
Fmt: Text,
|
|
|
|
|
Caller: false,
|
|
|
|
|
OutMode: Console,
|
|
|
|
|
Path: "./logs/",
|
|
|
|
|
FilePrefix: "moo",
|
|
|
|
|
SizeMax: 100 * 1024 * 1024, // 100 M Bytes
|
|
|
|
|
Output: os.Stdout,
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
//类型
|
|
|
|
|
|
|
|
|
|
//日志
|
|
|
|
|
type log struct {
|
|
|
|
|
*logrus.Logger
|
|
|
|
|
option Option
|
|
|
|
|
// cache
|
|
|
|
|
writers map[string]*os.File
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Option 选项
|
|
|
|
|
type Option struct {
|
|
|
|
|
Name string
|
|
|
|
|
|
|
|
|
|
Fmt int // Text, Json
|
|
|
|
|
Caller bool // true, false
|
|
|
|
|
OutMode int // Console, FILE, User
|
|
|
|
|
Path string // some path
|
|
|
|
|
FilePrefix string //
|
|
|
|
|
SizeMax int64
|
|
|
|
|
Output io.Writer
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
type Fields = logrus.Fields
|
|
|
|
|
|
|
|
|
|
// New 创建对象
|
|
|
|
|
func New(option ...Option) *log {
|
|
|
|
|
verifiedOption := optionVerify(option...)
|
|
|
|
|
l := create(verifiedOption)
|
|
|
|
|
l.refreshOutMode()
|
|
|
|
|
return l
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get 存在直接返回,否则创建、存储再返回
|
|
|
|
|
func Get(option ...Option) *log {
|
|
|
|
|
verifiedOption := optionVerify(option...)
|
|
|
|
|
if !Has(verifiedOption.Name) {
|
|
|
|
|
pool[verifiedOption.Name] = create(verifiedOption)
|
|
|
|
|
}
|
|
|
|
|
pool[verifiedOption.Name].refreshOutMode()
|
|
|
|
|
return pool[verifiedOption.Name]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Has 存在返回 true,否则返回 false
|
|
|
|
|
func Has(name string) bool {
|
|
|
|
|
_, has := pool[name]
|
|
|
|
|
return has
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (l *log) syncLoggerOption() *log {
|
|
|
|
|
switch l.option.Fmt {
|
|
|
|
|
case Json:
|
|
|
|
|
l.Logger.SetFormatter(&logrus.JSONFormatter{})
|
|
|
|
|
case Text:
|
|
|
|
|
l.Logger.SetFormatter(&logrus.TextFormatter{})
|
|
|
|
|
default:
|
|
|
|
|
l.Logger.SetFormatter(&logrus.TextFormatter{})
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
l.Logger.SetReportCaller(l.option.Caller)
|
|
|
|
|
|
|
|
|
|
l.Logger.SetOutput(l.option.Output)
|
|
|
|
|
|
|
|
|
|
return l
|
|
|
|
|
}
|
|
|
|
|
func (l *log) refreshOutMode() *log {
|
|
|
|
|
// 初始默认值
|
|
|
|
|
l.Logger.SetOutput(optionDefault.Output)
|
|
|
|
|
filename := ""
|
|
|
|
|
switch l.option.OutMode {
|
|
|
|
|
case File:
|
|
|
|
|
filename = l.option.Path + "/" + l.option.FilePrefix + fileExt
|
|
|
|
|
case FilePerHour:
|
|
|
|
|
now := time.Now()
|
|
|
|
|
filename = l.option.Path + "/" +
|
|
|
|
|
l.option.FilePrefix +
|
|
|
|
|
fmt.Sprintf("%04d-%02d-%02d-%02d", now.Year(), now.Month(), now.Day(), now.Hour()) +
|
|
|
|
|
fileExt
|
|
|
|
|
case FilePerDay:
|
|
|
|
|
now := time.Now()
|
|
|
|
|
filename = l.option.Path + "/" +
|
|
|
|
|
l.option.FilePrefix +
|
|
|
|
|
fmt.Sprintf("%04d-%02d-%02d", now.Year(), now.Month(), now.Day()) +
|
|
|
|
|
fileExt
|
|
|
|
|
case FilePerMonth:
|
|
|
|
|
now := time.Now()
|
|
|
|
|
filename = l.option.Path + "/" +
|
|
|
|
|
l.option.FilePrefix +
|
|
|
|
|
fmt.Sprintf("%04d-%02d", now.Year(), now.Month()) +
|
|
|
|
|
fileExt
|
|
|
|
|
case FilePerWeek:
|
|
|
|
|
now := time.Now()
|
|
|
|
|
year, week := now.ISOWeek()
|
|
|
|
|
filename = l.option.Path + "/" +
|
|
|
|
|
l.option.FilePrefix +
|
|
|
|
|
fmt.Sprintf("%04d-%02d", year, week) +
|
|
|
|
|
fileExt
|
|
|
|
|
case FilePerSize:
|
|
|
|
|
filename = l.option.Path + "/" + l.option.FilePrefix + fileExt
|
|
|
|
|
if fileinfo, err := os.Stat(filename); err == nil {
|
|
|
|
|
if fileinfo.Size() >= l.option.SizeMax {
|
|
|
|
|
basename := path.Base(filename)
|
|
|
|
|
if file, exists := l.writers[basename]; exists {
|
|
|
|
|
if err := file.Close(); err != nil {
|
|
|
|
|
l.Info("can not close file")
|
|
|
|
|
} else {
|
|
|
|
|
delete(l.writers, basename)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
now := time.Now()
|
|
|
|
|
newFilename := l.option.Path + "/" +
|
|
|
|
|
l.option.FilePrefix +
|
|
|
|
|
fmt.Sprintf("%s", now.Format("2006-01-02-15-04-05")) +
|
|
|
|
|
fileExt
|
|
|
|
|
if err := os.Rename(filename, newFilename); err != nil {
|
|
|
|
|
l.Info("can not rename log file")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
case User:
|
|
|
|
|
l.Logger.SetOutput(l.option.Output)
|
|
|
|
|
default:
|
|
|
|
|
l.Logger.SetOutput(optionDefault.Output)
|
|
|
|
|
}
|
|
|
|
|
if filename != "" {
|
|
|
|
|
filepath := path.Dir(filename)
|
|
|
|
|
if err := os.MkdirAll(filepath, 0644); err == nil || os.IsExist(err) {
|
|
|
|
|
basename := path.Base(filename)
|
|
|
|
|
file, exists := l.writers[basename]
|
|
|
|
|
if !exists {
|
|
|
|
|
if file, err = os.OpenFile(filename, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666); err != nil {
|
|
|
|
|
l.Info("can not create log file")
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if file != nil {
|
|
|
|
|
l.Logger.SetOutput(file)
|
|
|
|
|
l.writers[basename] = file
|
|
|
|
|
} else {
|
|
|
|
|
l.Info("can not open log file")
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
l.Info("can not make log path")
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return l
|
|
|
|
|
}
|
|
|
|
|
func create(option Option) *log {
|
|
|
|
|
return (&log{
|
|
|
|
|
Logger: logrus.New(),
|
|
|
|
|
option: option,
|
|
|
|
|
writers: map[string]*os.File{},
|
|
|
|
|
}).syncLoggerOption()
|
|
|
|
|
}
|
|
|
|
|
func optionVerify(option ...Option) Option {
|
|
|
|
|
opt := optionDefault
|
|
|
|
|
if len(option) > 0 {
|
|
|
|
|
//设置选项
|
|
|
|
|
opt.Name = option[0].Name
|
|
|
|
|
if toolm2.IntSliceContains(option[0].Fmt, fmtContainer) {
|
|
|
|
|
opt.Fmt = option[0].Fmt
|
|
|
|
|
}
|
|
|
|
|
opt.Caller = option[0].Caller
|
|
|
|
|
if toolm2.IntSliceContains(option[0].OutMode, outModeContainer) {
|
|
|
|
|
opt.OutMode = option[0].OutMode
|
|
|
|
|
}
|
|
|
|
|
opt.Path = option[0].Path
|
|
|
|
|
opt.FilePrefix = toolm2.StringDefault(option[0].FilePrefix, opt.Name, optionDefault.FilePrefix)
|
|
|
|
|
opt.SizeMax = toolm2.Int64Default(option[0].SizeMax, optionDefault.SizeMax)
|
|
|
|
|
if _, ok := option[0].Output.(io.Writer); ok {
|
|
|
|
|
opt.Output = option[0].Output
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return opt
|
|
|
|
|
}
|