|
- // Copyright 2019 Huawei Technologies Co.,Ltd.
- // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
- // this file except in compliance with the License. You may obtain a copy of the
- // License at
- //
- // http://www.apache.org/licenses/LICENSE-2.0
- //
- // Unless required by applicable law or agreed to in writing, software distributed
- // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
- // CONDITIONS OF ANY KIND, either express or implied. See the License for the
- // specific language governing permissions and limitations under the License.
-
- //nolint:golint, unused
- package obs
-
- import (
- "fmt"
- "log"
- "os"
- "path/filepath"
- "runtime"
- "strings"
- "sync"
- )
-
- // Level defines the level of the log
- type Level int
-
- const (
- LEVEL_OFF Level = 500
- LEVEL_ERROR Level = 400
- LEVEL_WARN Level = 300
- LEVEL_INFO Level = 200
- LEVEL_DEBUG Level = 100
- )
-
- var logLevelMap = map[Level]string{
- LEVEL_OFF: "[OFF]: ",
- LEVEL_ERROR: "[ERROR]: ",
- LEVEL_WARN: "[WARN]: ",
- LEVEL_INFO: "[INFO]: ",
- LEVEL_DEBUG: "[DEBUG]: ",
- }
-
- type logConfType struct {
- level Level
- logToConsole bool
- logFullPath string
- maxLogSize int64
- backups int
- }
-
- func getDefaultLogConf() logConfType {
- return logConfType{
- level: LEVEL_WARN,
- logToConsole: false,
- logFullPath: "",
- maxLogSize: 1024 * 1024 * 30, //30MB
- backups: 10,
- }
- }
-
- var logConf logConfType
-
- type loggerWrapper struct {
- fullPath string
- fd *os.File
- ch chan string
- wg sync.WaitGroup
- queue []string
- logger *log.Logger
- index int
- cacheCount int
- closed bool
- }
-
- func (lw *loggerWrapper) doInit() {
- lw.queue = make([]string, 0, lw.cacheCount)
- lw.logger = log.New(lw.fd, "", 0)
- lw.ch = make(chan string, lw.cacheCount)
- lw.wg.Add(1)
- go lw.doWrite()
- }
-
- func (lw *loggerWrapper) rotate() {
- stat, err := lw.fd.Stat()
- if err != nil {
- _err := lw.fd.Close()
- if _err != nil {
- doLog(LEVEL_WARN, "Failed to close file with reason: %v", _err)
- }
- panic(err)
- }
- if stat.Size() >= logConf.maxLogSize {
- _err := lw.fd.Sync()
- if _err != nil {
- panic(err)
- }
- _err = lw.fd.Close()
- if _err != nil {
- doLog(LEVEL_WARN, "Failed to close file with reason: %v", _err)
- }
- if lw.index > logConf.backups {
- lw.index = 1
- }
- _err = os.Rename(lw.fullPath, lw.fullPath+"."+IntToString(lw.index))
- if _err != nil {
- panic(err)
- }
- lw.index++
-
- fd, err := os.OpenFile(lw.fullPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
- if err != nil {
- panic(err)
- }
- lw.fd = fd
- lw.logger.SetOutput(lw.fd)
- }
- }
-
- func (lw *loggerWrapper) doFlush() {
- lw.rotate()
- for _, m := range lw.queue {
- lw.logger.Println(m)
- }
- err := lw.fd.Sync()
- if err != nil {
- panic(err)
- }
- }
-
- func (lw *loggerWrapper) doClose() {
- lw.closed = true
- close(lw.ch)
- lw.wg.Wait()
- }
-
- func (lw *loggerWrapper) doWrite() {
- defer lw.wg.Done()
- for {
- msg, ok := <-lw.ch
- if !ok {
- lw.doFlush()
- _err := lw.fd.Close()
- if _err != nil {
- doLog(LEVEL_WARN, "Failed to close file with reason: %v", _err)
- }
- break
- }
- if len(lw.queue) >= lw.cacheCount {
- lw.doFlush()
- lw.queue = make([]string, 0, lw.cacheCount)
- }
- lw.queue = append(lw.queue, msg)
- }
-
- }
-
- func (lw *loggerWrapper) Printf(format string, v ...interface{}) {
- if !lw.closed {
- msg := fmt.Sprintf(format, v...)
- lw.ch <- msg
- }
- }
-
- var consoleLogger *log.Logger
- var fileLogger *loggerWrapper
- var lock = new(sync.RWMutex)
-
- func isDebugLogEnabled() bool {
- return logConf.level <= LEVEL_DEBUG
- }
-
- func isErrorLogEnabled() bool {
- return logConf.level <= LEVEL_ERROR
- }
-
- func isWarnLogEnabled() bool {
- return logConf.level <= LEVEL_WARN
- }
-
- func isInfoLogEnabled() bool {
- return logConf.level <= LEVEL_INFO
- }
-
- func reset() {
- if fileLogger != nil {
- fileLogger.doClose()
- fileLogger = nil
- }
- consoleLogger = nil
- logConf = getDefaultLogConf()
- }
-
- // InitLog enable logging function with default cacheCnt
- func InitLog(logFullPath string, maxLogSize int64, backups int, level Level, logToConsole bool) error {
- return InitLogWithCacheCnt(logFullPath, maxLogSize, backups, level, logToConsole, 50)
- }
-
- // InitLogWithCacheCnt enable logging function
- func InitLogWithCacheCnt(logFullPath string, maxLogSize int64, backups int, level Level, logToConsole bool, cacheCnt int) error {
- lock.Lock()
- defer lock.Unlock()
- if cacheCnt <= 0 {
- cacheCnt = 50
- }
- reset()
- if fullPath := strings.TrimSpace(logFullPath); fullPath != "" {
- _fullPath, err := filepath.Abs(fullPath)
- if err != nil {
- return err
- }
-
- if !strings.HasSuffix(_fullPath, ".log") {
- _fullPath += ".log"
- }
-
- stat, err := os.Stat(_fullPath)
- if err == nil && stat.IsDir() {
- return fmt.Errorf("logFullPath:[%s] is a directory", _fullPath)
- } else if err = os.MkdirAll(filepath.Dir(_fullPath), os.ModePerm); err != nil {
- return err
- }
-
- fd, err := os.OpenFile(_fullPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
- if err != nil {
- return err
- }
-
- if stat == nil {
- stat, err = os.Stat(_fullPath)
- if err != nil {
- _err := fd.Close()
- if _err != nil {
- doLog(LEVEL_WARN, "Failed to close file with reason: %v", _err)
- }
- return err
- }
- }
-
- prefix := stat.Name() + "."
- index := 1
- var timeIndex int64 = 0
- walkFunc := func(path string, info os.FileInfo, err error) error {
- if err == nil {
- if name := info.Name(); strings.HasPrefix(name, prefix) {
- if i := StringToInt(name[len(prefix):], 0); i >= index && info.ModTime().Unix() >= timeIndex {
- timeIndex = info.ModTime().Unix()
- index = i + 1
- }
- }
- }
- return err
- }
-
- if err = filepath.Walk(filepath.Dir(_fullPath), walkFunc); err != nil {
- _err := fd.Close()
- if _err != nil {
- doLog(LEVEL_WARN, "Failed to close file with reason: %v", _err)
- }
- return err
- }
-
- fileLogger = &loggerWrapper{fullPath: _fullPath, fd: fd, index: index, cacheCount: cacheCnt, closed: false}
- fileLogger.doInit()
- }
- if maxLogSize > 0 {
- logConf.maxLogSize = maxLogSize
- }
- if backups > 0 {
- logConf.backups = backups
- }
- logConf.level = level
- if logToConsole {
- consoleLogger = log.New(os.Stdout, "", log.LstdFlags)
- }
- return nil
- }
-
- // CloseLog disable logging and synchronize cache data to log files
- func CloseLog() {
- if logEnabled() {
- lock.Lock()
- defer lock.Unlock()
- reset()
- }
- }
-
- func logEnabled() bool {
- return consoleLogger != nil || fileLogger != nil
- }
-
- // DoLog writes log messages to the logger
- func DoLog(level Level, format string, v ...interface{}) {
- doLog(level, format, v...)
- }
-
- func doLog(level Level, format string, v ...interface{}) {
- if logEnabled() && logConf.level <= level {
- msg := fmt.Sprintf(format, v...)
- if _, file, line, ok := runtime.Caller(1); ok {
- index := strings.LastIndex(file, "/")
- if index >= 0 {
- file = file[index+1:]
- }
- msg = fmt.Sprintf("%s:%d|%s", file, line, msg)
- }
- prefix := logLevelMap[level]
- if consoleLogger != nil {
- consoleLogger.Printf("%s%s", prefix, msg)
- }
- if fileLogger != nil {
- nowDate := FormatUtcNow("2006-01-02T15:04:05Z")
- fileLogger.Printf("%s %s%s", nowDate, prefix, msg)
- }
- }
- }
|