123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653 |
- 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()
- }
|