|
@@ -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()
|
|
|
+}
|