Browse Source

feat:新增logger包

wangchuanjin 2 năm trước cách đây
mục cha
commit
a36a235957
1 tập tin đã thay đổi với 653 bổ sung0 xóa
  1. 653 0
      logger/logger.go

+ 653 - 0
logger/logger.go

@@ -0,0 +1,653 @@
+package logger
+
+import (
+	"bytes"
+	"errors"
+	"fmt"
+	"io"
+	"log"
+	"os"
+	"regexp"
+	"runtime/debug"
+	"sort"
+	"strings"
+	"sync"
+	"sync/atomic"
+	"time"
+)
+
+/***
+author:donnie email donnie4w@gmail.com
+在控制台打印:直接调用 Debug(***) Info(***) Warn(***) Error(***) Fatal(***)
+可以设置打印格式:SetFormat(FORMAT_SHORTFILENAME|FORMAT_DATE|FORMAT_TIME)
+	无其他格式,只打印日志内容
+	FORMAT_NANO
+	长文件名及行数
+	FORMAT_LONGFILENAME
+	短文件名及行数
+	FORMAT_SHORTFILENAME
+	精确到日期
+	FORMAT_DATE
+	精确到秒
+	FORMAT_TIME
+	精确到微秒
+	FORMAT_MICROSECNDS
+	—————————————————————————————————————————————————————————————————————
+    写日志文件可以获取实例
+    全局实例可以直接调用log := logging.GetStaticLogger()
+    获取新实例可以调用log := logging.NewLogger()
+	1. 按日期分割日志文件
+    	log.SetRollingDaily("d://foldTest", "log.txt")
+	2. 按文件大小分割日志文件
+	log.SetRollingFile("d://foldTest", "log.txt", 300, MB)
+	log.SetConsole(false)控制台不打日志,默认值true
+    日志级别
+***/
+
+const (
+	_VER string = "1.0.1"
+)
+
+type _LEVEL int8
+type _UNIT int64
+type _MODE_TIME uint8
+type _ROLLTYPE int //dailyRolling ,rollingFile
+type _FORMAT int
+
+const _DATEFORMAT_DAY = "20060102"
+const _DATEFORMAT_HOUR = "2006010215"
+const _DATEFORMAT_MONTH = "200601"
+
+var static_mu *sync.Mutex = new(sync.Mutex)
+
+var static_lo *_logger = NewLogger()
+
+const (
+	_        = iota
+	KB _UNIT = 1 << (iota * 10)
+	MB
+	GB
+	TB
+)
+
+const (
+	MODE_HOUR  _MODE_TIME = 1
+	MODE_DAY   _MODE_TIME = 2
+	MODE_MONTH _MODE_TIME = 3
+)
+
+const (
+	/*无其他格式,只打印日志内容*/
+	FORMAT_NANO _FORMAT = 0
+	/*长文件名及行数*/
+	FORMAT_LONGFILENAME = _FORMAT(log.Llongfile)
+	/*短文件名及行数*/
+	FORMAT_SHORTFILENAME = _FORMAT(log.Lshortfile)
+	/*日期时间精确到天*/
+	FORMAT_DATE = _FORMAT(log.Ldate)
+	/*时间精确到秒*/
+	FORMAT_TIME = _FORMAT(log.Ltime)
+	/*时间精确到微秒*/
+	FORMAT_MICROSECNDS = _FORMAT(log.Lmicroseconds)
+)
+
+const (
+	/*日志级别:ALL 最低级别*/
+	LEVEL_ALL _LEVEL = iota
+	/*日志级别:DEBUG 小于INFO*/
+	LEVEL_DEBUG
+	/*日志级别:INFO 小于 WARN*/
+	LEVEL_INFO
+	/*日志级别:WARN 小于 ERROR*/
+	LEVEL_WARN
+	/*日志级别:ERROR 小于 FATAL*/
+	LEVEL_ERROR
+	/*日志级别:FATAL 小于 OFF*/
+	LEVEL_FATAL
+	/*日志级别:off 不打印任何日志*/
+	LEVEL_OFF
+)
+
+const (
+	_DAYLY _ROLLTYPE = iota
+	_ROLLFILE
+)
+
+var default_format _FORMAT = FORMAT_SHORTFILENAME | FORMAT_DATE | FORMAT_TIME
+var default_level = LEVEL_ALL
+
+/*设置打印格式*/
+func SetFormat(format _FORMAT) {
+	default_format = format
+	static_lo.SetFormat(format)
+
+}
+
+/*设置控制台日志级别,默认ALL*/
+func SetLevel(level _LEVEL) {
+	default_level = level
+	static_lo.SetLevel(level)
+}
+
+func SetConsole(on bool) {
+	static_lo.SetConsole(on)
+}
+
+/*获得全局Logger对象*/
+func GetStaticLogger() *_logger {
+	return _staticLogger()
+}
+
+func SetRollingFile(fileDir, fileName string, maxFileSize int64, unit _UNIT) (err error) {
+	return SetRollingFileLoop(fileDir, fileName, maxFileSize, unit, 0)
+}
+
+func SetRollingDaily(fileDir, fileName string) (err error) {
+	return SetRollingByTime(fileDir, fileName, MODE_DAY)
+}
+
+func SetRollingFileLoop(fileDir, fileName string, maxFileSize int64, unit _UNIT, maxFileNum int) (err error) {
+	return static_lo.SetRollingFileLoop(fileDir, fileName, maxFileSize, unit, maxFileNum)
+}
+
+func SetRollingByTime(fileDir, fileName string, mode _MODE_TIME) (err error) {
+	return static_lo.SetRollingByTime(fileDir, fileName, mode)
+}
+
+func _staticLogger() *_logger {
+	return static_lo
+}
+
+func Debug(v ...interface{}) {
+	_print(default_format, LEVEL_DEBUG, default_level, 2, v...)
+}
+func Info(v ...interface{}) {
+	_print(default_format, LEVEL_INFO, default_level, 2, v...)
+}
+func Warn(v ...interface{}) {
+	_print(default_format, LEVEL_WARN, default_level, 2, v...)
+}
+func Error(v ...interface{}) {
+	_print(default_format, LEVEL_ERROR, default_level, 2, v...)
+}
+func Fatal(v ...interface{}) {
+	_print(default_format, LEVEL_FATAL, default_level, 2, v...)
+}
+
+func _print(_format _FORMAT, level, _default_level _LEVEL, calldepth int, v ...interface{}) {
+	if level < _default_level {
+		return
+	}
+	_staticLogger().println(level, k1(calldepth), v...)
+}
+
+func __print(_format _FORMAT, level, _default_level _LEVEL, calldepth int, v ...interface{}) {
+	_console(formatV(v...), getlevelname(level, default_format), _format, k1(calldepth))
+}
+
+func getlevelname(level _LEVEL, format _FORMAT) (levelname string) {
+	if format == FORMAT_NANO {
+		return
+	}
+	switch level {
+	case LEVEL_ALL:
+		levelname = "[ALL]"
+	case LEVEL_DEBUG:
+		levelname = "[DEBUG]"
+	case LEVEL_INFO:
+		levelname = "[INFO]"
+	case LEVEL_WARN:
+		levelname = "[WARN]"
+	case LEVEL_ERROR:
+		levelname = "[ERROR]"
+	case LEVEL_FATAL:
+		levelname = "[FATAL]"
+	default:
+	}
+	return
+}
+
+/*————————————————————————————————————————————————————————————————————————————*/
+type _logger struct {
+	_level      _LEVEL
+	_format     _FORMAT
+	_rwLock     *sync.RWMutex
+	_safe       bool
+	_fileDir    string
+	_fileName   string
+	_maxSize    int64
+	_unit       _UNIT
+	_rolltype   _ROLLTYPE
+	_mode       _MODE_TIME
+	_fileObj    *fileObj
+	_maxFileNum int
+	_isConsole  bool
+}
+
+func NewLogger() (log *_logger) {
+	log = &_logger{_level: LEVEL_DEBUG, _rolltype: _DAYLY, _rwLock: new(sync.RWMutex), _format: FORMAT_SHORTFILENAME | FORMAT_DATE | FORMAT_TIME, _isConsole: true}
+	log.newfileObj()
+	return
+}
+
+//控制台日志是否打开
+func (this *_logger) SetConsole(_isConsole bool) {
+	this._isConsole = _isConsole
+}
+func (this *_logger) Debug(v ...interface{}) {
+	this.println(LEVEL_DEBUG, 2, v...)
+}
+func (this *_logger) Info(v ...interface{}) {
+	this.println(LEVEL_INFO, 2, v...)
+}
+func (this *_logger) Warn(v ...interface{}) {
+	this.println(LEVEL_WARN, 2, v...)
+}
+func (this *_logger) Error(v ...interface{}) {
+	this.println(LEVEL_ERROR, 2, v...)
+}
+func (this *_logger) Fatal(v ...interface{}) {
+	this.println(LEVEL_FATAL, 2, v...)
+}
+func (this *_logger) SetFormat(format _FORMAT) {
+	this._format = format
+}
+func (this *_logger) SetLevel(level _LEVEL) {
+	this._level = level
+}
+
+/*按日志文件大小分割日志文件
+fileDir 日志文件夹路径
+fileName 日志文件名
+maxFileSize  日志文件大小最大值
+unit    日志文件大小单位
+*/
+func (this *_logger) SetRollingFile(fileDir, fileName string, maxFileSize int64, unit _UNIT) (err error) {
+	return this.SetRollingFileLoop(fileDir, fileName, maxFileSize, unit, 0)
+}
+
+/*按日志文件大小分割日志文件,指定保留的最大日志文件数
+fileDir 日志文件夹路径
+fileName 日志文件名
+maxFileSize  日志文件大小最大值
+unit    	日志文件大小单位
+maxFileNum  留的日志文件数
+*/
+func (this *_logger) SetRollingFileLoop(fileDir, fileName string, maxFileSize int64, unit _UNIT, maxFileNum int) (err error) {
+	if fileDir == "" {
+		fileDir, _ = os.Getwd()
+	}
+	if maxFileNum > 0 {
+		maxFileNum--
+	}
+	this._fileDir, this._fileName, this._maxSize, this._maxFileNum, this._unit = fileDir, fileName, maxFileSize, maxFileNum, unit
+	this._rolltype = _ROLLFILE
+	if this._fileObj != nil {
+		this._fileObj.close()
+	}
+	this.newfileObj()
+	err = this._fileObj.openFileHandler()
+	return
+}
+
+/*按日期分割日志文件
+fileDir 日志文件夹路径
+fileName 日志文件名
+*/
+func (this *_logger) SetRollingDaily(fileDir, fileName string) (err error) {
+	return this.SetRollingByTime(fileDir, fileName, MODE_DAY)
+}
+
+/*指定按 小时,天,月 分割日志文件
+fileDir 日志文件夹路径
+fileName 日志文件名
+mode   指定 小时,天,月
+*/
+func (this *_logger) SetRollingByTime(fileDir, fileName string, mode _MODE_TIME) (err error) {
+	if fileDir == "" {
+		fileDir, _ = os.Getwd()
+	}
+	this._fileDir, this._fileName, this._mode = fileDir, fileName, mode
+	this._rolltype = _DAYLY
+	if this._fileObj != nil {
+		this._fileObj.close()
+	}
+	this.newfileObj()
+	err = this._fileObj.openFileHandler()
+	return
+}
+
+func (this *_logger) newfileObj() {
+	this._fileObj = new(fileObj)
+	this._fileObj._fileDir, this._fileObj._fileName, this._fileObj._maxSize, this._fileObj._rolltype, this._fileObj._unit, this._fileObj._maxFileNum, this._fileObj._mode = this._fileDir, this._fileName, this._maxSize, this._rolltype, this._unit, this._maxFileNum, this._mode
+}
+
+func (this *_logger) backUp() (err, openFileErr error) {
+	this._rwLock.Lock()
+	defer this._rwLock.Unlock()
+	if !this._fileObj.isMustBackUp() {
+		return
+	}
+	err = this._fileObj.close()
+	if err != nil {
+		__print(this._format, LEVEL_ERROR, LEVEL_ERROR, 1, err.Error())
+		return
+	}
+	err = this._fileObj.rename()
+	if err != nil {
+		__print(this._format, LEVEL_ERROR, LEVEL_ERROR, 1, err.Error())
+		return
+	}
+	openFileErr = this._fileObj.openFileHandler()
+	if openFileErr != nil {
+		__print(this._format, LEVEL_ERROR, LEVEL_ERROR, 1, openFileErr.Error())
+	}
+	return
+}
+
+func (this *_logger) println(_level _LEVEL, calldepth int, v ...interface{}) {
+	if this._level > _level {
+		return
+	}
+	if this._fileObj._isFileWell {
+		var openFileErr error
+		if this._fileObj.isMustBackUp() {
+			_, openFileErr = this.backUp()
+		}
+		if openFileErr == nil {
+			func() {
+				this._rwLock.RLock()
+				defer this._rwLock.RUnlock()
+				buf := getOutBuffer(formatV(v...), getlevelname(_level, this._format), this._format, k1(calldepth)+1)
+				this._fileObj.write2file(buf.Bytes())
+			}()
+		}
+	}
+	if this._isConsole {
+		__print(this._format, _level, this._level, k1(calldepth), v...)
+	}
+}
+
+/*————————————————————————————————————————————————————————————————————————————*/
+type fileObj struct {
+	_fileDir     string
+	_fileName    string
+	_maxSize     int64
+	_fileSize    int64
+	_unit        _UNIT
+	_fileHandler *os.File
+	_rolltype    _ROLLTYPE
+	_tomorSecond int64
+	_isFileWell  bool
+	_maxFileNum  int
+	_mode        _MODE_TIME
+}
+
+func (this *fileObj) openFileHandler() (e error) {
+	if this._fileDir == "" || this._fileName == "" {
+		e = errors.New("log filePath is null or error")
+		return
+	}
+	e = mkdirDir(this._fileDir)
+	if e != nil {
+		this._isFileWell = false
+		return
+	}
+	fname := fmt.Sprint(this._fileDir, "/", this._fileName)
+	this._fileHandler, e = os.OpenFile(fname, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
+	if e != nil {
+		__print(default_format, LEVEL_ERROR, LEVEL_ERROR, 1, e.Error())
+		this._isFileWell = false
+		return
+	}
+	this._isFileWell = true
+	this._tomorSecond = tomorSecond(this._mode)
+	fs, err := this._fileHandler.Stat()
+	if err == nil {
+		this._fileSize = fs.Size()
+	} else {
+		e = err
+	}
+	return
+}
+
+func (this *fileObj) addFileSize(size int64) {
+	atomic.AddInt64(&this._fileSize, size)
+}
+
+func (this *fileObj) write2file(bs []byte) (e error) {
+	defer catchError()
+	if bs != nil {
+		this.addFileSize(int64(len(bs)))
+		_write2file(this._fileHandler, bs)
+	}
+	return
+}
+
+func (this *fileObj) isMustBackUp() bool {
+	switch this._rolltype {
+	case _DAYLY:
+		if time.Now().Unix() >= this._tomorSecond {
+			return true
+		}
+	case _ROLLFILE:
+		return this._fileSize > 0 && this._fileSize >= this._maxSize*int64(this._unit)
+	}
+	return false
+}
+
+func (this *fileObj) rename() (err error) {
+	bckupfilename := ""
+	if this._rolltype == _DAYLY {
+		bckupfilename = getBackupDayliFileName(this._fileDir, this._fileName, this._mode)
+	} else {
+		bckupfilename, err = getBackupRollFileName(this._fileDir, this._fileName)
+	}
+	if bckupfilename != "" && err == nil {
+		oldPath := fmt.Sprint(this._fileDir, "/", this._fileName)
+		newPath := fmt.Sprint(this._fileDir, "/", bckupfilename)
+		err = os.Rename(oldPath, newPath)
+		if err == nil && this._rolltype == _ROLLFILE && this._maxFileNum > 0 {
+			go _rmOverCountFile(this._fileDir, bckupfilename, this._maxFileNum)
+		}
+	}
+	return
+}
+
+func (this *fileObj) close() (err error) {
+	defer catchError()
+	if this._fileHandler != nil {
+		err = this._fileHandler.Close()
+	}
+	return
+}
+
+func tomorSecond(mode _MODE_TIME) int64 {
+	now := time.Now()
+	switch mode {
+	case MODE_DAY:
+		return time.Date(now.Year(), now.Month(), now.Day()+1, 0, 0, 0, 0, now.Location()).Unix()
+	case MODE_HOUR:
+		return time.Date(now.Year(), now.Month(), now.Day(), now.Hour()+1, 0, 0, 0, now.Location()).Unix()
+	case MODE_MONTH:
+		return time.Date(now.Year(), now.Month()+1, 0, 0, 0, 0, 0, now.Location()).AddDate(0, 0, 1).Unix()
+	default:
+		return time.Date(now.Year(), now.Month(), now.Day()+1, 0, 0, 0, 0, now.Location()).Unix()
+	}
+}
+
+func _yestStr(mode _MODE_TIME) string {
+	now := time.Now()
+	switch mode {
+	case MODE_DAY:
+		return now.AddDate(0, 0, -1).Format(_DATEFORMAT_DAY)
+	case MODE_HOUR:
+		return now.Add(-1 * time.Hour).Format(_DATEFORMAT_HOUR)
+	case MODE_MONTH:
+		return now.AddDate(0, -1, 0).Format(_DATEFORMAT_MONTH)
+	default:
+		return now.AddDate(0, 0, -1).Format(_DATEFORMAT_DAY)
+	}
+}
+
+/*————————————————————————————————————————————————————————————————————————————*/
+func getBackupDayliFileName(dir, filename string, mode _MODE_TIME) (bckupfilename string) {
+	timeStr := _yestStr(mode)
+	index := strings.LastIndex(filename, ".")
+	if index <= 0 {
+		index = len(filename)
+	}
+	fname := filename[:index]
+	suffix := filename[index:]
+	bckupfilename = fmt.Sprint(fname, "_", timeStr, suffix)
+	if isFileExist(fmt.Sprint(dir, "/", bckupfilename)) {
+		bckupfilename = _getBackupfilename(1, dir, fmt.Sprint(fname, "_", timeStr), suffix)
+	}
+	return
+}
+
+func _getDirList(dir string) ([]os.DirEntry, error) {
+	f, err := os.Open(dir)
+	if err != nil {
+		return nil, err
+	}
+	defer f.Close()
+	return f.ReadDir(-1)
+}
+
+func getBackupRollFileName(dir, filename string) (bckupfilename string, er error) {
+	list, err := _getDirList(dir)
+	if err != nil {
+		er = err
+		return
+	}
+	index := strings.LastIndex(filename, ".")
+	if index <= 0 {
+		index = len(filename)
+	}
+	fname := filename[:index]
+	suffix := filename[index:]
+	length := len(list)
+	bckupfilename = _getBackupfilename(length, dir, fname, suffix)
+	return
+}
+
+func _getBackupfilename(count int, dir, filename, suffix string) (bckupfilename string) {
+	bckupfilename = fmt.Sprint(filename, "_", count, suffix)
+	if isFileExist(fmt.Sprint(dir, "/", bckupfilename)) {
+		return _getBackupfilename(count+1, dir, filename, suffix)
+	}
+	return
+}
+
+func _write2file(f *os.File, bs []byte) (e error) {
+	_, e = f.Write(bs)
+	return
+}
+
+func _console(s string, levelname string, flag _FORMAT, calldepth int) {
+	buf := getOutBuffer(s, levelname, flag, k1(calldepth))
+	fmt.Print(&buf)
+}
+
+func outwriter(out io.Writer, prefix string, flag _FORMAT, calldepth int, s string) {
+	l := log.New(out, prefix, int(flag))
+	l.Output(k1(calldepth), s)
+}
+
+func k1(calldepth int) int {
+	return calldepth + 1
+}
+
+func getOutBuffer(s string, levelname string, flag _FORMAT, calldepth int) (buf bytes.Buffer) {
+	outwriter(&buf, levelname, flag, k1(calldepth), s)
+	return
+}
+
+func mkdirDir(dir string) (e error) {
+	_, er := os.Stat(dir)
+	b := er == nil || os.IsExist(er)
+	if !b {
+		if err := os.MkdirAll(dir, 0666); err != nil {
+			if os.IsPermission(err) {
+				e = err
+			}
+		}
+	}
+	return
+}
+
+func isFileExist(path string) bool {
+	_, err := os.Stat(path)
+	return err == nil || os.IsExist(err)
+}
+
+func catchError() {
+	if err := recover(); err != nil {
+		Fatal(string(debug.Stack()))
+	}
+}
+
+func _rmOverCountFile(dir, backupfileName string, maxFileNum int) {
+	static_mu.Lock()
+	defer static_mu.Unlock()
+	f, err := os.Open(dir)
+	if err != nil {
+		return
+	}
+	dirs, _ := f.ReadDir(-1)
+	f.Close()
+	if len(dirs) <= maxFileNum {
+		return
+	}
+	sort.Slice(dirs, func(i, j int) bool {
+		f1, _ := dirs[i].Info()
+		f2, _ := dirs[j].Info()
+		return f1.ModTime().Unix() > f2.ModTime().Unix()
+	})
+	index := strings.LastIndex(backupfileName, "_")
+	indexSuffix := strings.LastIndex(backupfileName, ".")
+	if indexSuffix == 0 {
+		indexSuffix = len(backupfileName)
+	}
+	prefixname := backupfileName[:index+1]
+	suffix := backupfileName[indexSuffix:]
+	suffixlen := len(suffix)
+	rmfiles := make([]string, 0)
+	i := 0
+	for _, f := range dirs {
+		if len(f.Name()) > len(prefixname) && f.Name()[:len(prefixname)] == prefixname && _matchString("^[0-9]+$", f.Name()[len(prefixname):len(f.Name())-suffixlen]) {
+			finfo, err := f.Info()
+			if err == nil && !finfo.IsDir() {
+				i++
+				if i > maxFileNum {
+					rmfiles = append(rmfiles, fmt.Sprint(dir, "/", f.Name()))
+				}
+			}
+		}
+	}
+	if len(rmfiles) > 0 {
+		for _, k := range rmfiles {
+			os.Remove(k)
+		}
+	}
+}
+
+func _matchString(pattern string, s string) bool {
+	b, err := regexp.MatchString(pattern, s)
+	if err != nil {
+		b = false
+	}
+	return b
+}
+
+func formatV(v ...interface{}) string {
+	s := strings.Builder{}
+	for _, vv := range v {
+		if s.Len() > 0 {
+			s.WriteString(" ")
+		}
+		s.WriteString(fmt.Sprint(vv))
+	}
+	return s.String()
+}