You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

log.go 7.1 kB

4 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  1. // Copyright 2019 Huawei Technologies Co.,Ltd.
  2. // Licensed under the Apache License, Version 2.0 (the "License"); you may not use
  3. // this file except in compliance with the License. You may obtain a copy of the
  4. // License at
  5. //
  6. // http://www.apache.org/licenses/LICENSE-2.0
  7. //
  8. // Unless required by applicable law or agreed to in writing, software distributed
  9. // under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  10. // CONDITIONS OF ANY KIND, either express or implied. See the License for the
  11. // specific language governing permissions and limitations under the License.
  12. //nolint:golint, unused
  13. package obs
  14. import (
  15. "fmt"
  16. "log"
  17. "os"
  18. "path/filepath"
  19. "runtime"
  20. "strings"
  21. "sync"
  22. )
  23. // Level defines the level of the log
  24. type Level int
  25. const (
  26. LEVEL_OFF Level = 500
  27. LEVEL_ERROR Level = 400
  28. LEVEL_WARN Level = 300
  29. LEVEL_INFO Level = 200
  30. LEVEL_DEBUG Level = 100
  31. )
  32. var logLevelMap = map[Level]string{
  33. LEVEL_OFF: "[OFF]: ",
  34. LEVEL_ERROR: "[ERROR]: ",
  35. LEVEL_WARN: "[WARN]: ",
  36. LEVEL_INFO: "[INFO]: ",
  37. LEVEL_DEBUG: "[DEBUG]: ",
  38. }
  39. type logConfType struct {
  40. level Level
  41. logToConsole bool
  42. logFullPath string
  43. maxLogSize int64
  44. backups int
  45. }
  46. func getDefaultLogConf() logConfType {
  47. return logConfType{
  48. level: LEVEL_WARN,
  49. logToConsole: false,
  50. logFullPath: "",
  51. maxLogSize: 1024 * 1024 * 30, //30MB
  52. backups: 10,
  53. }
  54. }
  55. var logConf logConfType
  56. type loggerWrapper struct {
  57. fullPath string
  58. fd *os.File
  59. ch chan string
  60. wg sync.WaitGroup
  61. queue []string
  62. logger *log.Logger
  63. index int
  64. cacheCount int
  65. closed bool
  66. }
  67. func (lw *loggerWrapper) doInit() {
  68. lw.queue = make([]string, 0, lw.cacheCount)
  69. lw.logger = log.New(lw.fd, "", 0)
  70. lw.ch = make(chan string, lw.cacheCount)
  71. lw.wg.Add(1)
  72. go lw.doWrite()
  73. }
  74. func (lw *loggerWrapper) rotate() {
  75. stat, err := lw.fd.Stat()
  76. if err != nil {
  77. _err := lw.fd.Close()
  78. if _err != nil {
  79. doLog(LEVEL_WARN, "Failed to close file with reason: %v", _err)
  80. }
  81. panic(err)
  82. }
  83. if stat.Size() >= logConf.maxLogSize {
  84. _err := lw.fd.Sync()
  85. if _err != nil {
  86. panic(err)
  87. }
  88. _err = lw.fd.Close()
  89. if _err != nil {
  90. doLog(LEVEL_WARN, "Failed to close file with reason: %v", _err)
  91. }
  92. if lw.index > logConf.backups {
  93. lw.index = 1
  94. }
  95. _err = os.Rename(lw.fullPath, lw.fullPath+"."+IntToString(lw.index))
  96. if _err != nil {
  97. panic(err)
  98. }
  99. lw.index++
  100. fd, err := os.OpenFile(lw.fullPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
  101. if err != nil {
  102. panic(err)
  103. }
  104. lw.fd = fd
  105. lw.logger.SetOutput(lw.fd)
  106. }
  107. }
  108. func (lw *loggerWrapper) doFlush() {
  109. lw.rotate()
  110. for _, m := range lw.queue {
  111. lw.logger.Println(m)
  112. }
  113. err := lw.fd.Sync()
  114. if err != nil {
  115. panic(err)
  116. }
  117. }
  118. func (lw *loggerWrapper) doClose() {
  119. lw.closed = true
  120. close(lw.ch)
  121. lw.wg.Wait()
  122. }
  123. func (lw *loggerWrapper) doWrite() {
  124. defer lw.wg.Done()
  125. for {
  126. msg, ok := <-lw.ch
  127. if !ok {
  128. lw.doFlush()
  129. _err := lw.fd.Close()
  130. if _err != nil {
  131. doLog(LEVEL_WARN, "Failed to close file with reason: %v", _err)
  132. }
  133. break
  134. }
  135. if len(lw.queue) >= lw.cacheCount {
  136. lw.doFlush()
  137. lw.queue = make([]string, 0, lw.cacheCount)
  138. }
  139. lw.queue = append(lw.queue, msg)
  140. }
  141. }
  142. func (lw *loggerWrapper) Printf(format string, v ...interface{}) {
  143. if !lw.closed {
  144. msg := fmt.Sprintf(format, v...)
  145. lw.ch <- msg
  146. }
  147. }
  148. var consoleLogger *log.Logger
  149. var fileLogger *loggerWrapper
  150. var lock = new(sync.RWMutex)
  151. func isDebugLogEnabled() bool {
  152. return logConf.level <= LEVEL_DEBUG
  153. }
  154. func isErrorLogEnabled() bool {
  155. return logConf.level <= LEVEL_ERROR
  156. }
  157. func isWarnLogEnabled() bool {
  158. return logConf.level <= LEVEL_WARN
  159. }
  160. func isInfoLogEnabled() bool {
  161. return logConf.level <= LEVEL_INFO
  162. }
  163. func reset() {
  164. if fileLogger != nil {
  165. fileLogger.doClose()
  166. fileLogger = nil
  167. }
  168. consoleLogger = nil
  169. logConf = getDefaultLogConf()
  170. }
  171. // InitLog enable logging function with default cacheCnt
  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. // InitLogWithCacheCnt enable logging function
  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. if !strings.HasSuffix(_fullPath, ".log") {
  189. _fullPath += ".log"
  190. }
  191. stat, err := os.Stat(_fullPath)
  192. if err == nil && stat.IsDir() {
  193. return fmt.Errorf("logFullPath:[%s] is a directory", _fullPath)
  194. } else if err = os.MkdirAll(filepath.Dir(_fullPath), os.ModePerm); err != nil {
  195. return err
  196. }
  197. fd, err := os.OpenFile(_fullPath, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0600)
  198. if err != nil {
  199. return err
  200. }
  201. if stat == nil {
  202. stat, err = os.Stat(_fullPath)
  203. if err != nil {
  204. _err := fd.Close()
  205. if _err != nil {
  206. doLog(LEVEL_WARN, "Failed to close file with reason: %v", _err)
  207. }
  208. return err
  209. }
  210. }
  211. prefix := stat.Name() + "."
  212. index := 1
  213. var timeIndex int64 = 0
  214. walkFunc := func(path string, info os.FileInfo, err error) error {
  215. if err == nil {
  216. if name := info.Name(); strings.HasPrefix(name, prefix) {
  217. if i := StringToInt(name[len(prefix):], 0); i >= index && info.ModTime().Unix() >= timeIndex {
  218. timeIndex = info.ModTime().Unix()
  219. index = i + 1
  220. }
  221. }
  222. }
  223. return err
  224. }
  225. if err = filepath.Walk(filepath.Dir(_fullPath), walkFunc); err != nil {
  226. _err := fd.Close()
  227. if _err != nil {
  228. doLog(LEVEL_WARN, "Failed to close file with reason: %v", _err)
  229. }
  230. return err
  231. }
  232. fileLogger = &loggerWrapper{fullPath: _fullPath, fd: fd, index: index, cacheCount: cacheCnt, closed: false}
  233. fileLogger.doInit()
  234. }
  235. if maxLogSize > 0 {
  236. logConf.maxLogSize = maxLogSize
  237. }
  238. if backups > 0 {
  239. logConf.backups = backups
  240. }
  241. logConf.level = level
  242. if logToConsole {
  243. consoleLogger = log.New(os.Stdout, "", log.LstdFlags)
  244. }
  245. return nil
  246. }
  247. // CloseLog disable logging and synchronize cache data to log files
  248. func CloseLog() {
  249. if logEnabled() {
  250. lock.Lock()
  251. defer lock.Unlock()
  252. reset()
  253. }
  254. }
  255. func logEnabled() bool {
  256. return consoleLogger != nil || fileLogger != nil
  257. }
  258. // DoLog writes log messages to the logger
  259. func DoLog(level Level, format string, v ...interface{}) {
  260. doLog(level, format, v...)
  261. }
  262. func doLog(level Level, format string, v ...interface{}) {
  263. if logEnabled() && logConf.level <= level {
  264. msg := fmt.Sprintf(format, v...)
  265. if _, file, line, ok := runtime.Caller(1); ok {
  266. index := strings.LastIndex(file, "/")
  267. if index >= 0 {
  268. file = file[index+1:]
  269. }
  270. msg = fmt.Sprintf("%s:%d|%s", file, line, msg)
  271. }
  272. prefix := logLevelMap[level]
  273. if consoleLogger != nil {
  274. consoleLogger.Printf("%s%s", prefix, msg)
  275. }
  276. if fileLogger != nil {
  277. nowDate := FormatUtcNow("2006-01-02T15:04:05Z")
  278. fileLogger.Printf("%s %s%s", nowDate, prefix, msg)
  279. }
  280. }
  281. }