package logger
import (
"fmt"
"log"
"os"
"runtime"
"strconv"
"sync"
"time"
)
const (
_VER string = "1.0.0"
)
type LEVEL int32
var logLevel LEVEL = 1
var maxFileSize int64
var maxFileCount int32
var dailyRolling bool = true
var consoleAppender bool = true
var RollingFile bool = false
var logObj *_FILE
const DATEFORMAT = "2006-01-02"
type UNIT int64
const (
_ = iota
KB UNIT = 1 << (iota * 10)
MB
GB
TB
)
const (
ALL LEVEL = iota
DEBUG
INFO
WARN
ERROR
FATAL
OFF
)
type _FILE struct {
dir string
filename string
_suffix int
isCover bool
_date *time.Time
mu *sync.RWMutex
logfile *os.File
lg *log.Logger
}
func SetConsole(isConsole bool) {
consoleAppender = isConsole
}
func SetLevel(_level LEVEL) {
logLevel = _level
}
func SetRollingFile(fileDir, fileName string, maxNumber int32, maxSize int64, _unit UNIT) {
maxFileCount = maxNumber
maxFileSize = maxSize * int64(_unit)
RollingFile = true
dailyRolling = false
logObj = &_FILE{dir: fileDir, filename: fileName, isCover: false, mu: new(sync.RWMutex)}
logObj.mu.Lock()
defer logObj.mu.Unlock()
for i := 1; i <= int(maxNumber); i++ {
if isExist(fileDir + "/" + fileName + "." + strconv.Itoa(i)) {
logObj._suffix = i
} else {
break
}
}
if !logObj.isMustRename() {
logObj.logfile, _ = os.OpenFile(fileDir+"/"+fileName, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0)
logObj.lg = log.New(logObj.logfile, "\n", log.Ldate|log.Ltime|log.Lshortfile)
} else {
logObj.rename()
}
go fileMonitor()
}
func SetRollingDaily(fileDir, fileName string) {
RollingFile = false
dailyRolling = true
t, _ := time.Parse(DATEFORMAT, time.Now().Format(DATEFORMAT))
logObj = &_FILE{dir: fileDir, filename: fileName, _date: &t, isCover: false, mu: new(sync.RWMutex)}
logObj.mu.Lock()
defer logObj.mu.Unlock()
if !logObj.isMustRename() {
logObj.logfile, _ = os.OpenFile(fileDir+"/"+fileName, os.O_RDWR|os.O_APPEND|os.O_CREATE, 0)
logObj.lg = log.New(logObj.logfile, "\n", log.Ldate|log.Ltime|log.Lshortfile)
} else {
logObj.rename()
}
}
func console(s ...interface{}) {
if consoleAppender {
_, file, line, _ := runtime.Caller(2)
short := file
for i := len(file) - 1; i > 0; i-- {
if file[i] == '/' {
short = file[i+1:]
break
}
}
file = short
log.Println(file+":"+strconv.Itoa(line), s)
}
}
func catchError() {
if err := recover(); err != nil {
log.Println("err", err)
}
}
func Debug(v ...interface{}) {
if dailyRolling {
fileCheck()
}
defer catchError()
logObj.mu.RLock()
defer logObj.mu.RUnlock()
if logLevel <= DEBUG {
logObj.lg.Output(2, fmt.Sprintln("debug", v))
console("debug", v)
}
}
func Info(v ...interface{}) {
if dailyRolling {
fileCheck()
}
defer catchError()
logObj.mu.RLock()
defer logObj.mu.RUnlock()
if logLevel <= INFO {
logObj.lg.Output(2, fmt.Sprintln("info", v))
console("info", v)
}
}
func Warn(v ...interface{}) {
if dailyRolling {
fileCheck()
}
defer catchError()
logObj.mu.RLock()
defer logObj.mu.RUnlock()
if logLevel <= WARN {
logObj.lg.Output(2, fmt.Sprintln("warn", v))
console("warn", v)
}
}
func Error(v ...interface{}) {
if dailyRolling {
fileCheck()
}
defer catchError()
logObj.mu.RLock()
defer logObj.mu.RUnlock()
if logLevel <= ERROR {
logObj.lg.Output(2, fmt.Sprintln("error", v))
console("error", v)
}
}
func Fatal(v ...interface{}) {
if dailyRolling {
fileCheck()
}
defer catchError()
logObj.mu.RLock()
defer logObj.mu.RUnlock()
if logLevel <= FATAL {
logObj.lg.Output(2, fmt.Sprintln("fatal", v))
console("fatal", v)
}
}
func (f *_FILE) isMustRename() bool {
if dailyRolling {
t, _ := time.Parse(DATEFORMAT, time.Now().Format(DATEFORMAT))
if t.After(*f._date) {
return true
}
} else {
if maxFileCount > 1 {
if fileSize(f.dir+"/"+f.filename) >= maxFileSize {
return true
}
}
}
return false
}
func (f *_FILE) rename() {
if dailyRolling {
fn := f.dir + "/" + f.filename + "." + f._date.Format(DATEFORMAT)
if !isExist(fn) && f.isMustRename() {
if f.logfile != nil {
f.logfile.Close()
}
err := os.Rename(f.dir+"/"+f.filename, fn)
if err != nil {
f.lg.Println("rename err", err.Error())
}
t, _ := time.Parse(DATEFORMAT, time.Now().Format(DATEFORMAT))
f._date = &t
f.logfile, _ = os.Create(f.dir + "/" + f.filename)
f.lg = log.New(logObj.logfile, "\n", log.Ldate|log.Ltime|log.Lshortfile)
}
} else {
f.coverNextOne()
}
}
func (f *_FILE) nextSuffix() int {
return int(f._suffix%int(maxFileCount) + 1)
}
func (f *_FILE) coverNextOne() {
f._suffix = f.nextSuffix()
if f.logfile != nil {
f.logfile.Close()
}
if isExist(f.dir + "/" + f.filename + "." + strconv.Itoa(int(f._suffix))) {
os.Remove(f.dir + "/" + f.filename + "." + strconv.Itoa(int(f._suffix)))
}
os.Rename(f.dir+"/"+f.filename, f.dir+"/"+f.filename+"."+strconv.Itoa(int(f._suffix)))
f.logfile, _ = os.Create(f.dir + "/" + f.filename)
f.lg = log.New(logObj.logfile, "\n", log.Ldate|log.Ltime|log.Lshortfile)
}
func fileSize(file string) int64 {
fmt.Println("fileSize", file)
f, e := os.Stat(file)
if e != nil {
fmt.Println(e.Error())
return 0
}
return f.Size()
}
func isExist(path string) bool {
_, err := os.Stat(path)
return err == nil || os.IsExist(err)
}
func fileMonitor() {
timer := time.NewTicker(1 * time.Second)
for {
select {
case <-timer.C:
fileCheck()
}
}
}
func fileCheck() {
defer func() {
if err := recover(); err != nil {
log.Println(err)
}
}()
if logObj != nil && logObj.isMustRename() {
logObj.mu.Lock()
defer logObj.mu.Unlock()
logObj.rename()
}
}
使用方法
package example
import (
"github.com/donnie4w/go-logger/logger"
"runtime"
"strconv"
"testing"
"time"
)
func log(i int) {
logger.Debug("Debug>>>>>>>>>>>>>>>>>>>>>>" + strconv.Itoa(i))
logger.Info("Info>>>>>>>>>>>>>>>>>>>>>>>>>" + strconv.Itoa(i))
logger.Warn("Warn>>>>>>>>>>>>>>>>>>>>>>>>>" + strconv.Itoa(i))
logger.Error("Error>>>>>>>>>>>>>>>>>>>>>>>>>" + strconv.Itoa(i))
logger.Fatal("Fatal>>>>>>>>>>>>>>>>>>>>>>>>>" + strconv.Itoa(i))
}
func Test(t *testing.T) {
runtime.GOMAXPROCS(runtime.NumCPU())
//指定是否控制台打印,默认为true
logger.SetConsole(true)
//指定日志文件备份方式为文件大小的方式
//第一个参数为日志文件存放目录
//第二个参数为日志文件命名
//第三个参数为备份文件最大数量
//第四个参数为备份文件大小
//第五个参数为文件大小的单位
//logger.SetRollingFile("d:/logtest", "test.log", 10, 5, logger.KB)
//指定日志文件备份方式为日期的方式
//第一个参数为日志文件存放目录
//第二个参数为日志文件命名
logger.SetRollingDaily("d:/logtest", "test.log")
//指定日志级别 ALL,DEBUG,INFO,WARN,ERROR,FATAL,OFF 级别由低到高
//一般习惯是测试阶段为debug,生成环境为info以上
logger.SetLevel(logger.ERROR)
for i := 10000; i > 0; i-- {
go log(i)
time.Sleep(1000 * time.Millisecond)
}
time.Sleep(15 * time.Second)
}