| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689 |
- package logger
- import (
- "fmt"
- "log"
- "os"
- "path/filepath"
- "strconv"
- "strings"
- "sync"
- "time"
- "git.shuncheng.lu/bigthing/gocommon/pkg/conf"
- "git.shuncheng.lu/bigthing/gocommon/pkg/internal/util"
- )
- type (
- Level int
- )
- const (
- LevelFatal = iota
- LevelError
- LevelWarning
- LevelInfo
- LevelDebug
- )
- var levelName = []string{
- LevelFatal: "output",
- LevelDebug: "debug",
- LevelInfo: "info",
- LevelWarning: "warn",
- LevelError: "error",
- }
- var _fileLogWriter Logger
- // fileLogWriter implements LoggerInterface.
- // It writes messages by lines limit, file size limit, or time frequency.
- type fileLogWriter struct {
- sync.RWMutex // write log order by order and atomic incr maxLinesCurLines and maxSizeCurSize
- logSync sync.RWMutex
- accessSync sync.RWMutex
- monitorSync sync.RWMutex
- taskSync sync.RWMutex
- thirdSync sync.RWMutex
- // The opened file
- _logFile map[Level]*os.File
- _accesslogFile *os.File
- _monitorlogFile *os.File
- _tasklogFile *os.File
- _thirdlogFile *os.File
- // logs map cache
- _logs map[Level]*logger
- _accesslog *logger
- _monitorlog *logger
- _tasklog *logger
- _thirdlog *logger
- ThirdLogFile, TaskLogFile, AccessLogFile, MonitorLogFile, ProjectLogFile, LogFilePath string
- // Rotate daily
- Daily bool `json:"daily"`
- dailyOpenDate int
- dailyOpenTime time.Time
- logFileTime map[Level]time.Time
- Rotate bool `json:"rotate"`
- Perm string `json:"perm"`
- RotatePerm string `json:"rotateperm"`
- _accesslogFileNameOnly, _accesslogSuffix string // like "project.log", project is fileNameOnly and .log is suffix
- _monitorlogFileNameOnly, _monitorlogSuffix string // like "project.log", project is fileNameOnly and .log is suffix
- _tasklogFileNameOnly, _tasklogSuffix string // like "project.log", project is fileNameOnly and .log is suffix
- _thirdlogFileNameOnly, _thirdlogSuffix string // like "project.log", project is fileNameOnly and .log is suffix
- _projectLogFileNameOnly, _projectLogSuffix string // like "project.log", project is fileNameOnly and .log is suffix
- }
- // Logger defines the behavior of a log provider.
- type Logger interface {
- Init() error
- Log(level Level) *logger
- Accesslog() *logger
- Monitorlog() *logger
- Tasklog() *logger
- Thirdlog() *logger
- }
- // 若没有配置 log.xxx_log 的位置和名称,则日志默认添加到运行时文件的目录里
- func loadLogConfig(assertNil util.SetAndAssertNil) (map[string]string, error) {
- var (
- config = make(map[string]string)
- )
- if err := assertNil(config, "log", "project_log", "project_log.log"); err != nil {
- return nil, err
- }
- if err := assertNil(config, "log", "access_log", "access_log.log"); err != nil {
- return nil, err
- }
- if err := assertNil(config, "log", "task_log", "task_log.log"); err != nil {
- return nil, err
- }
- if err := assertNil(config, "log", "third_log", "third.log"); err != nil {
- return nil, err
- }
- if err := assertNil(config, "log", "monitor_log", "monitor_log.log"); err != nil {
- return nil, err
- }
- util.Debugf("Logger load config success, config=%+v.", config)
- return config, nil
- }
- // newFileWriter create a FileLogWriter returning as LoggerInterface.
- func newFileWriter() (Logger, error) {
- config, err := loadLogConfig(conf.SetAndAssertNil)
- if err != nil {
- return nil, err
- }
- w := &fileLogWriter{
- Daily: true,
- Rotate: true,
- RotatePerm: "0444",
- Perm: "0664",
- _logs: make(map[Level]*logger, 0),
- _logFile: make(map[Level]*os.File, 0),
- logFileTime: make(map[Level]time.Time, 0),
- AccessLogFile: config["access_log"],
- MonitorLogFile: config["monitor_log"],
- TaskLogFile: config["task_log"],
- ThirdLogFile: config["third_log"],
- ProjectLogFile: config["project_log"],
- }
- return w, nil
- }
- func (w *fileLogWriter) Monitorlog() *logger {
- w.monitorSync.RLock()
- defer w.monitorSync.RUnlock()
- return w._monitorlog
- }
- func (w *fileLogWriter) Accesslog() *logger {
- w.accessSync.RLock()
- defer w.accessSync.RUnlock()
- return w._accesslog
- }
- func (w *fileLogWriter) Tasklog() *logger {
- w.taskSync.RLock()
- defer w.taskSync.RUnlock()
- return w._tasklog
- }
- func (w *fileLogWriter) Thirdlog() *logger {
- w.thirdSync.RLock()
- defer w.thirdSync.RUnlock()
- return w._thirdlog
- }
- func (w *fileLogWriter) Log(level Level) *logger {
- w.logSync.Lock()
- defer w.logSync.Unlock()
- if w._logs[level] == nil || w.checkLogNeedRotate(level) {
- err := w.createLogFile(level)
- if err != nil {
- fmt.Printf("err %#v", err.Error())
- }
- }
- return w._logs[level]
- }
- func (w *fileLogWriter) checkLogNeedRotate(level Level) bool {
- if time.Now().Hour() != w.logFileTime[level].Hour() {
- return true
- }
- return false
- }
- func (w *fileLogWriter) Init() error {
- w._accesslogSuffix = filepath.Ext(w.AccessLogFile)
- w._accesslogFileNameOnly = strings.TrimSuffix(w.AccessLogFile, w._accesslogSuffix)
- if w._accesslogSuffix == "" {
- w._accesslogSuffix = ".log"
- }
- w._monitorlogSuffix = filepath.Ext(w.MonitorLogFile)
- w._monitorlogFileNameOnly = strings.TrimSuffix(w.MonitorLogFile, w._monitorlogSuffix)
- if w._monitorlogSuffix == "" {
- w._monitorlogSuffix = ".log"
- }
- w._tasklogSuffix = filepath.Ext(w.TaskLogFile)
- w._tasklogFileNameOnly = strings.TrimSuffix(w.TaskLogFile, w._tasklogSuffix)
- if w._tasklogSuffix == "" {
- w._tasklogSuffix = ".log"
- }
- w._thirdlogSuffix = filepath.Ext(w.ThirdLogFile)
- w._thirdlogFileNameOnly = strings.TrimSuffix(w.ThirdLogFile, w._thirdlogSuffix)
- if w._thirdlogSuffix == "" {
- w._thirdlogSuffix = ".log"
- }
- w._projectLogSuffix = filepath.Ext(w.ProjectLogFile)
- w._projectLogFileNameOnly = strings.TrimSuffix(w.ProjectLogFile, w._projectLogSuffix)
- if w._projectLogSuffix == "" {
- w._projectLogSuffix = ".log"
- }
- err := w.startLogger()
- return err
- }
- // start file logger. create log file and set to locker-inside file writer.
- func (w *fileLogWriter) startLogger() error {
- err := w.createAccessLogFile()
- if err != nil {
- return err
- }
- err = w.createMonitorLogFile()
- if err != nil {
- return err
- }
- err = w.createTaskLogFile()
- if err != nil {
- return err
- }
- err = w.createThirdLogFile()
- if err != nil {
- return err
- }
- return w.initFd()
- }
- func (w *fileLogWriter) initFd() error {
- _, err := w._accesslogFile.Stat()
- if err != nil {
- return err
- }
- _, err = w._monitorlogFile.Stat()
- if err != nil {
- return err
- }
- _, err = w._tasklogFile.Stat()
- if err != nil {
- return err
- }
- _, err = w._thirdlogFile.Stat()
- if err != nil {
- return err
- }
- w.dailyOpenTime = time.Now()
- w.dailyOpenDate = w.dailyOpenTime.Day()
- if w.Daily {
- go w.dailyRotate(w.dailyOpenTime)
- }
- return nil
- }
- func (w *fileLogWriter) dailyRotate(openTime time.Time) {
- y, m, d := openTime.Add(24 * time.Hour).Date()
- nextDay := time.Date(y, m, d, 0, 0, 0, 0, openTime.Location())
- tm := time.NewTimer(time.Duration(nextDay.UnixNano() - openTime.UnixNano() + 100))
- <-tm.C
- w.Lock()
- if w.needRotate(time.Now().Day()) {
- if err := w.doRotate(time.Now()); err != nil {
- fmt.Fprintf(os.Stderr, "FileLogWriter: %s\n", err)
- }
- }
- w.Unlock()
- }
- func (w *fileLogWriter) cuttingLog(fileName, fileNameOnly, suffix string, rotatePerm int64, file *os.File) error {
- //fmt.Printf("fileName: %s, fileNameOnly: %s, suffix: %s \n", fileName, fileNameOnly, suffix)
- num := 1
- logfName := ""
- _, err := os.Lstat(fileName)
- if err != nil {
- //even if the file is not exist or other ,we should RESTART the logger
- return err
- }
- logfName = fmt.Sprintf("%s%s.%s", fileNameOnly, suffix, w.dailyOpenTime.Format("2006-01-02"))
- _, err = os.Lstat(logfName)
- for ; err == nil && num <= 10000; num++ {
- logfName = fileNameOnly +
- fmt.Sprintf(".%03d%s.%s", num, suffix, w.dailyOpenTime.Format("2006-01-02"))
- _, err = os.Lstat(logfName)
- }
- // return error if the last file checked still existed
- if err == nil {
- return err
- }
- // close fileWriter before rename
- //file.Close()
- // Rename the file to its new found name
- // even if occurs error,we MUST guarantee to restart new logger
- err = os.Rename(fileName, logfName)
- if err != nil {
- return err
- }
- err = os.Chmod(logfName, os.FileMode(rotatePerm))
- if err != nil {
- return err
- }
- return nil
- }
- // DoRotate means it need to write file in new file.
- // new file name like xx.2013-01-01.log (daily) or xx.001.log (by line or size)
- func (w *fileLogWriter) doRotate(logTime time.Time) error {
- // file exists
- // Find the next available number
- rotatePerm, err := strconv.ParseInt(w.RotatePerm, 8, 64)
- if err != nil {
- return err
- }
- //accessLogFile
- err1 := w.cuttingLog(w.AccessLogFile, w._accesslogFileNameOnly, w._accesslogSuffix, rotatePerm, w._accesslogFile)
- if err1 != nil {
- fmt.Fprintf(os.Stderr, "doRotate err: %s", err1.Error())
- goto RESTART_LOGGER
- }
- //monitorLogFile
- err1 = w.cuttingLog(w.MonitorLogFile, w._monitorlogFileNameOnly, w._monitorlogSuffix, rotatePerm, w._monitorlogFile)
- if err1 != nil {
- fmt.Fprintf(os.Stderr, "doRotate err: %s", err1.Error())
- goto RESTART_LOGGER
- }
- //taskLogFile
- err1 = w.cuttingLog(w.TaskLogFile, w._tasklogFileNameOnly, w._tasklogSuffix, rotatePerm, w._tasklogFile)
- if err1 != nil {
- fmt.Fprintf(os.Stderr, "doRotate err: %s", err1.Error())
- goto RESTART_LOGGER
- }
- //thirdLogFile
- err1 = w.cuttingLog(w.ThirdLogFile, w._thirdlogFileNameOnly, w._thirdlogSuffix, rotatePerm, w._thirdlogFile)
- if err1 != nil {
- fmt.Fprintf(os.Stderr, "doRotate err: %s", err1.Error())
- goto RESTART_LOGGER
- }
- RESTART_LOGGER:
- startLoggerErr := w.startLogger()
- if startLoggerErr != nil {
- return startLoggerErr
- }
- return nil
- }
- func (w *fileLogWriter) needRotate(day int) bool {
- return (w.Daily && day != w.dailyOpenDate)
- }
- func (w *fileLogWriter) createLogFile(level Level) error {
- //basePath := w.LogFilePath
- t := time.Now()
- year, month, day := t.Date()
- //if !strings.HasSuffix(basePath, "/") {
- // basePath += "/"
- //}
- perm, err := strconv.ParseInt(w.Perm, 8, 64)
- if err != nil {
- return err
- }
- hour := t.Hour()
- fileName := fmt.Sprintf("%s-%04d-%02d-%02d-%02d-%s.log", w._projectLogFileNameOnly, year, month, day, hour, levelName[level])
- //fname := filepath.Join(basePath, fileName)
- //正常日志
- logFileFd, err := os.OpenFile(fileName, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0666)
- if err != nil {
- return err
- }
- // Make sure file perm is user set perm cause of `os.OpenFile` will obey umask
- os.Chmod(fileName, os.FileMode(perm))
- w.logFileTime[level] = t
- //close the old file
- if w._logFile[level] != nil {
- w._logFile[level].Close()
- }
- w._logFile[level] = logFileFd
- if w._logs[level] != nil {
- w._logs[level]._log.SetOutput(logFileFd)
- } else {
- w._logs[level] = &logger{_log: log.New(logFileFd, "", log.Lshortfile|log.LstdFlags), logLevel: LevelDebug}
- }
- return nil
- }
- func (w *fileLogWriter) createAccessLogFile() error {
- // Open the log file
- perm, err := strconv.ParseInt(w.Perm, 8, 64)
- if err != nil {
- return err
- }
- w.accessSync.Lock()
- defer w.accessSync.Unlock()
- //accessLogFile
- accessLogFileFd, err := os.OpenFile(w.AccessLogFile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(perm))
- if err != nil {
- return err
- }
- os.Chmod(w.AccessLogFile, os.FileMode(perm))
- if w._accesslogFile != nil {
- w._accesslogFile.Close()
- }
- w._accesslogFile = accessLogFileFd
- if w._accesslog != nil {
- w._accesslog._log.SetOutput(accessLogFileFd)
- } else {
- w._accesslog = &logger{_log: log.New(accessLogFileFd, "", 0), logLevel: LevelDebug}
- }
- return nil
- }
- func (w *fileLogWriter) createMonitorLogFile() error {
- // Open the log file
- perm, err := strconv.ParseInt(w.Perm, 8, 64)
- if err != nil {
- return err
- }
- w.monitorSync.Lock()
- defer w.monitorSync.Unlock()
- //monitorLogFile
- monitorLogFileFd, err := os.OpenFile(w.MonitorLogFile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(perm))
- if err != nil {
- return err
- }
- os.Chmod(w.MonitorLogFile, os.FileMode(perm))
- if w._monitorlogFile != nil {
- w._monitorlogFile.Close()
- }
- w._monitorlogFile = monitorLogFileFd
- if w._monitorlog != nil {
- w._monitorlog._log.SetOutput(monitorLogFileFd)
- } else {
- w._monitorlog = &logger{_log: log.New(monitorLogFileFd, "", log.Lshortfile|log.LstdFlags), logLevel: LevelDebug}
- }
- return nil
- }
- func (w *fileLogWriter) createTaskLogFile() error {
- // Open the log file
- perm, err := strconv.ParseInt(w.Perm, 8, 64)
- if err != nil {
- return err
- }
- w.taskSync.Lock()
- defer w.taskSync.Unlock()
- //taskLogFile
- taskLogFileFd, err := os.OpenFile(w.TaskLogFile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(perm))
- if err != nil {
- return err
- }
- os.Chmod(w.TaskLogFile, os.FileMode(perm))
- if w._tasklogFile != nil {
- w._tasklogFile.Close()
- }
- w._tasklogFile = taskLogFileFd
- if w._tasklog != nil {
- w._tasklog._log.SetOutput(taskLogFileFd)
- } else {
- w._tasklog = &logger{_log: log.New(taskLogFileFd, "", log.LUTC), logLevel: LevelDebug}
- }
- return nil
- }
- func (w *fileLogWriter) createThirdLogFile() error {
- // Open the log file
- perm, err := strconv.ParseInt(w.Perm, 8, 64)
- if err != nil {
- return err
- }
- w.thirdSync.Lock()
- defer w.thirdSync.Unlock()
- //thirdLogFile
- thirdLogFileFd, err := os.OpenFile(w.ThirdLogFile, os.O_WRONLY|os.O_APPEND|os.O_CREATE, os.FileMode(perm))
- if err != nil {
- return err
- }
- os.Chmod(w.ThirdLogFile, os.FileMode(perm))
- if w._thirdlogFile != nil {
- w._thirdlogFile.Close()
- }
- w._thirdlogFile = thirdLogFileFd
- if w._thirdlog != nil {
- w._thirdlog._log.SetOutput(thirdLogFileFd)
- } else {
- w._thirdlog = &logger{_log: log.New(thirdLogFileFd, "", log.LUTC), logLevel: LevelDebug}
- }
- return nil
- }
- func Init() error {
- writer, err := newFileWriter()
- if err != nil {
- return err
- }
- _fileLogWriter = writer
- return writer.Init()
- }
- type logger struct {
- _log *log.Logger
- //小于等于该级别的level才会被记录
- logLevel Level
- }
- // 输出无格式的
- func (l *logger) RawOutput(s string) error {
- return l._log.Output(0, s)
- }
- func (l *logger) Output(level Level, s string) error {
- if l.logLevel < level {
- return nil
- }
- formatStr := "[UNKNOWN] " + s
- switch level {
- case LevelFatal:
- formatStr = "\033[35m[FATAL]\033[0m " + s
- case LevelError:
- formatStr = "\033[31m[ERROR]\033[0m " + s
- case LevelWarning:
- formatStr = "\033[33m[WARN]\033[0m " + s
- case LevelInfo:
- formatStr = "\033[32m[INFO]\033[0m " + s
- case LevelDebug:
- formatStr = "\033[36m[DEBUG]\033[0m " + s
- }
- return l._log.Output(3, formatStr)
- }
- func (l *logger) Fatal(s string) {
- err := l.Output(LevelFatal, s)
- if err != nil {
- fmt.Fprintf(os.Stderr, "write Fatal log to file fail, err: %s", err.Error())
- }
- os.Exit(1)
- }
- func (l *logger) Fatalf(format string, v ...interface{}) {
- err := l.Output(LevelFatal, fmt.Sprintf(format, v...))
- if err != nil {
- fmt.Fprintf(os.Stderr, "write Fatalf log to file fail, err: %s", err.Error())
- }
- os.Exit(1)
- }
- func (l *logger) Error(s string) {
- err := l.Output(LevelError, s)
- if err != nil {
- fmt.Fprintf(os.Stderr, "write Error log to file fail, err: %s", err.Error())
- }
- }
- func (l *logger) Errorf(format string, v ...interface{}) {
- err := l.Output(LevelError, fmt.Sprintf(format, v...))
- if err != nil {
- panic("Errorf")
- fmt.Fprintf(os.Stderr, "write Errorf log to file fail, err: %s", err.Error())
- }
- }
- func (l *logger) Warn(s string) {
- err := l.Output(LevelWarning, s)
- if err != nil {
- fmt.Fprintf(os.Stderr, "write Warn log to file fail, err: %s", err.Error())
- }
- }
- func (l *logger) Warnf(format string, v ...interface{}) {
- err := l.Output(LevelWarning, fmt.Sprintf(format, v...))
- if err != nil {
- fmt.Fprintf(os.Stderr, "write Warnf log to file fail, err: %s", err.Error())
- }
- }
- func (l *logger) Info(s string) {
- err := l.Output(LevelInfo, s)
- if err != nil {
- fmt.Fprintf(os.Stderr, "write Info log to file fail, err: %s", err.Error())
- }
- }
- func (l *logger) Infof(format string, v ...interface{}) {
- err := l.Output(LevelInfo, fmt.Sprintf(format, v...))
- if err != nil {
- panic("Errorf")
- fmt.Fprintf(os.Stderr, "write Infof log to file fail, err: %s", err.Error())
- }
- }
- func (l *logger) Debug(s string) {
- err := l.Output(LevelDebug, s)
- if err != nil {
- fmt.Fprintf(os.Stderr, "write Debug log to file fail, err: %s", err.Error())
- }
- }
- func (l *logger) Debugf(format string, v ...interface{}) {
- err := l.Output(LevelDebug, fmt.Sprintf(format, v...))
- if err != nil {
- fmt.Fprintf(os.Stderr, "write Debugf log to file fail, err: %s", err.Error())
- }
- }
- func (l *logger) SetLogLevel(level Level) {
- l.logLevel = level
- }
- func (l *logger) taskOutput(level Level, s string) error {
- var cstSh, _ = time.LoadLocation("Asia/Shanghai") //时区上海
- //时间前缀 毫秒级别,默认的点换句号
- formatStr := strings.Replace(time.Now().In(cstSh).Format("2006-01-02 15:04:05.000"), ".", ",", -1)
- switch level {
- case LevelError:
- formatStr += " ERROR - " + s
- case LevelWarning:
- formatStr += " WARN - " + s
- case LevelInfo:
- formatStr += " INFO - " + s
- }
- return l._log.Output(3, formatStr)
- }
- func (l *logger) thirdOutput(s string) error {
- var cstSh, _ = time.LoadLocation("Asia/Shanghai") //时区上海
- //时间前缀 毫秒级别,默认的.替换为:
- formatStr := strings.Replace(time.Now().In(cstSh).Format("2006-01-02 15:04:05.000"), ".", ":", -1)
- formatStr += s
- return l._log.Output(3, formatStr)
- }
|