github.com/opentelekomcloud/gophertelekomcloud@v0.9.3/openstack/obs/log.go (about)

     1  package obs
     2  
     3  import (
     4  	"fmt"
     5  	"log"
     6  	"os"
     7  	"path/filepath"
     8  	"runtime"
     9  	"strings"
    10  	"sync"
    11  )
    12  
    13  type Level int
    14  
    15  const (
    16  	LEVEL_OFF   Level = 500
    17  	LEVEL_ERROR Level = 400
    18  	LEVEL_WARN  Level = 300
    19  	LEVEL_INFO  Level = 200
    20  	LEVEL_DEBUG Level = 100
    21  )
    22  
    23  var logLevelMap = map[Level]string{
    24  	LEVEL_OFF:   "[OFF]: ",
    25  	LEVEL_ERROR: "[ERROR]: ",
    26  	LEVEL_WARN:  "[WARN]: ",
    27  	LEVEL_INFO:  "[INFO]: ",
    28  	LEVEL_DEBUG: "[DEBUG]: ",
    29  }
    30  
    31  type logConfType struct {
    32  	level        Level
    33  	logToConsole bool
    34  	logFullPath  string
    35  	maxLogSize   int64
    36  	backups      int
    37  }
    38  
    39  func getDefaultLogConf() logConfType {
    40  	return logConfType{
    41  		level:        LEVEL_WARN,
    42  		logToConsole: false,
    43  		logFullPath:  "",
    44  		maxLogSize:   1024 * 1024 * 30, // 30MB
    45  		backups:      10,
    46  	}
    47  }
    48  
    49  var logConf logConfType
    50  
    51  type loggerWrapper struct {
    52  	fullPath   string
    53  	fd         *os.File
    54  	ch         chan string
    55  	wg         sync.WaitGroup
    56  	queue      []string
    57  	logger     *log.Logger
    58  	index      int
    59  	cacheCount int
    60  	closed     bool
    61  }
    62  
    63  func (lw *loggerWrapper) doInit() {
    64  	lw.queue = make([]string, 0, lw.cacheCount)
    65  	lw.logger = log.New(lw.fd, "", 0)
    66  	lw.ch = make(chan string, lw.cacheCount)
    67  	lw.wg.Add(1)
    68  	go lw.doWrite()
    69  }
    70  
    71  func (lw *loggerWrapper) rotate() {
    72  	stat, err := lw.fd.Stat()
    73  	if err != nil {
    74  		_ = lw.fd.Close()
    75  		panic(err)
    76  	}
    77  	if stat.Size() >= logConf.maxLogSize {
    78  		_err := lw.fd.Sync()
    79  		if _err != nil {
    80  			panic(err)
    81  		}
    82  		_ = lw.fd.Close()
    83  		if lw.index > logConf.backups {
    84  			lw.index = 1
    85  		}
    86  		_err = os.Rename(lw.fullPath, lw.fullPath+"."+IntToString(lw.index))
    87  		if _err != nil {
    88  			panic(err)
    89  		}
    90  		lw.index += 1
    91  
    92  		fd, err := os.OpenFile(lw.fullPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
    93  		if err != nil {
    94  			panic(err)
    95  		}
    96  		lw.fd = fd
    97  		lw.logger.SetOutput(lw.fd)
    98  	}
    99  }
   100  
   101  func (lw *loggerWrapper) doFlush() {
   102  	lw.rotate()
   103  	for _, m := range lw.queue {
   104  		lw.logger.Println(m)
   105  	}
   106  	err := lw.fd.Sync()
   107  	if err != nil {
   108  		panic(err)
   109  	}
   110  }
   111  
   112  func (lw *loggerWrapper) doClose() {
   113  	lw.closed = true
   114  	close(lw.ch)
   115  	lw.wg.Wait()
   116  }
   117  
   118  func (lw *loggerWrapper) doWrite() {
   119  	defer lw.wg.Done()
   120  	for {
   121  		msg, ok := <-lw.ch
   122  		if !ok {
   123  			lw.doFlush()
   124  			_ = lw.fd.Close()
   125  			break
   126  		}
   127  		if len(lw.queue) >= lw.cacheCount {
   128  			lw.doFlush()
   129  			lw.queue = make([]string, 0, lw.cacheCount)
   130  		}
   131  		lw.queue = append(lw.queue, msg)
   132  	}
   133  
   134  }
   135  
   136  func (lw *loggerWrapper) Printf(format string, v ...interface{}) {
   137  	if !lw.closed {
   138  		msg := fmt.Sprintf(format, v...)
   139  		lw.ch <- msg
   140  	}
   141  }
   142  
   143  var consoleLogger *log.Logger
   144  var fileLogger *loggerWrapper
   145  var lock = new(sync.RWMutex)
   146  
   147  func isDebugLogEnabled() bool {
   148  	return logConf.level <= LEVEL_DEBUG
   149  }
   150  
   151  func isErrorLogEnabled() bool {
   152  	return logConf.level <= LEVEL_ERROR
   153  }
   154  
   155  func isWarnLogEnabled() bool {
   156  	return logConf.level <= LEVEL_WARN
   157  }
   158  
   159  func isInfoLogEnabled() bool {
   160  	return logConf.level <= LEVEL_INFO
   161  }
   162  
   163  func reset() {
   164  	if fileLogger != nil {
   165  		fileLogger.doClose()
   166  		fileLogger = nil
   167  	}
   168  	consoleLogger = nil
   169  	logConf = getDefaultLogConf()
   170  }
   171  
   172  func InitLog(logFullPath string, maxLogSize int64, backups int, level Level, logToConsole bool) error {
   173  	return InitLogWithCacheCnt(logFullPath, maxLogSize, backups, level, logToConsole, 50)
   174  }
   175  
   176  func InitLogWithCacheCnt(logFullPath string, maxLogSize int64, backups int, level Level, logToConsole bool, cacheCnt int) error {
   177  	lock.Lock()
   178  	defer lock.Unlock()
   179  	if cacheCnt <= 0 {
   180  		cacheCnt = 50
   181  	}
   182  	reset()
   183  	if fullPath := strings.TrimSpace(logFullPath); fullPath != "" {
   184  		_fullPath, err := filepath.Abs(fullPath)
   185  		if err != nil {
   186  			return err
   187  		}
   188  
   189  		if !strings.HasSuffix(_fullPath, ".log") {
   190  			_fullPath += ".log"
   191  		}
   192  
   193  		stat, err := os.Stat(_fullPath)
   194  		if err == nil && stat.IsDir() {
   195  			return fmt.Errorf("logFullPath:[%s] is a directory", _fullPath)
   196  		} else if err := os.MkdirAll(filepath.Dir(_fullPath), os.ModePerm); err != nil {
   197  			return err
   198  		}
   199  
   200  		fd, err := os.OpenFile(_fullPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0666)
   201  		if err != nil {
   202  			return err
   203  		}
   204  
   205  		if stat == nil {
   206  			stat, err = os.Stat(_fullPath)
   207  			if err != nil {
   208  				_ = fd.Close()
   209  				return err
   210  			}
   211  		}
   212  
   213  		prefix := stat.Name() + "."
   214  		index := 1
   215  		walkFunc := func(path string, info os.FileInfo, err error) error {
   216  			if err == nil {
   217  				if name := info.Name(); strings.HasPrefix(name, prefix) {
   218  					if i := StringToInt(name[len(prefix):], 0); i >= index {
   219  						index = i + 1
   220  					}
   221  				}
   222  			}
   223  			return err
   224  		}
   225  
   226  		if err = filepath.Walk(filepath.Dir(_fullPath), walkFunc); err != nil {
   227  			_ = fd.Close()
   228  			return err
   229  		}
   230  
   231  		fileLogger = &loggerWrapper{fullPath: _fullPath, fd: fd, index: index, cacheCount: cacheCnt, closed: false}
   232  		fileLogger.doInit()
   233  	}
   234  	if maxLogSize > 0 {
   235  		logConf.maxLogSize = maxLogSize
   236  	}
   237  	if backups > 0 {
   238  		logConf.backups = backups
   239  	}
   240  	logConf.level = level
   241  	if logToConsole {
   242  		consoleLogger = log.New(os.Stdout, "", log.LstdFlags)
   243  	}
   244  	return nil
   245  }
   246  
   247  func CloseLog() {
   248  	if logEnabled() {
   249  		lock.Lock()
   250  		defer lock.Unlock()
   251  		reset()
   252  	}
   253  }
   254  
   255  func SyncLog() {
   256  }
   257  
   258  func logEnabled() bool {
   259  	return consoleLogger != nil || fileLogger != nil
   260  }
   261  
   262  func DoLog(level Level, format string, v ...interface{}) {
   263  	doLog(level, format, v...)
   264  }
   265  
   266  func doLog(level Level, format string, v ...interface{}) {
   267  	if logEnabled() && logConf.level <= level {
   268  		msg := fmt.Sprintf(format, v...)
   269  		if _, file, line, ok := runtime.Caller(1); ok {
   270  			index := strings.LastIndex(file, "/")
   271  			if index >= 0 {
   272  				file = file[index+1:]
   273  			}
   274  			msg = fmt.Sprintf("%s:%d|%s", file, line, msg)
   275  		}
   276  		prefix := logLevelMap[level]
   277  		if consoleLogger != nil {
   278  			consoleLogger.Printf("%s%s", prefix, msg)
   279  		}
   280  		if fileLogger != nil {
   281  			nowDate := FormatUtcNow("2006-01-02T15:04:05Z")
   282  			fileLogger.Printf("%s %s%s", nowDate, prefix, msg)
   283  		}
   284  	}
   285  }