github.com/XiaoMi/Gaea@v1.2.5/log/xlog/file.go (about)

     1  // Copyright 2019 The Gaea Authors. All Rights Reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  
    15  package xlog
    16  
    17  import (
    18  	"fmt"
    19  	"os"
    20  	"path/filepath"
    21  	"strconv"
    22  	"sync"
    23  	"time"
    24  )
    25  
    26  // XFileLog is the file logger
    27  type XFileLog struct {
    28  	filename string
    29  	path     string
    30  	level    int
    31  
    32  	skip     int
    33  	runtime  bool
    34  	file     *os.File
    35  	errFile  *os.File
    36  	hostname string
    37  	service  string
    38  	split    sync.Once
    39  	mu       sync.Mutex
    40  }
    41  
    42  // constants of XFileLog
    43  const (
    44  	XFileLogDefaultLogID = "900000001"
    45  	SpliterDelay         = 5
    46  	CleanDays            = -3
    47  )
    48  
    49  // NewXFileLog is the constructor of XFileLog
    50  //生成一个日志实例,service用来标识业务的服务名。
    51  //比如:logger := xlog.NewXFileLog("gaea")
    52  func NewXFileLog() XLogger {
    53  	return &XFileLog{
    54  		skip: XLogDefSkipNum,
    55  	}
    56  }
    57  
    58  // Init implements XLogger
    59  func (p *XFileLog) Init(config map[string]string) (err error) {
    60  
    61  	path, ok := config["path"]
    62  	if !ok {
    63  		err = fmt.Errorf("init XFileLog failed, not found path")
    64  		return
    65  	}
    66  
    67  	filename, ok := config["filename"]
    68  	if !ok {
    69  		err = fmt.Errorf("init XFileLog failed, not found filename")
    70  		return
    71  	}
    72  
    73  	level, ok := config["level"]
    74  	if !ok {
    75  		err = fmt.Errorf("init XFileLog failed, not found level")
    76  		return
    77  	}
    78  
    79  	service, _ := config["service"]
    80  	if len(service) > 0 {
    81  		p.service = service
    82  	}
    83  
    84  	runtime, ok := config["runtime"]
    85  	if !ok || runtime == "true" || runtime == "TRUE" {
    86  		p.runtime = true
    87  	} else {
    88  		p.runtime = false
    89  	}
    90  
    91  	skip, _ := config["skip"]
    92  	if len(skip) > 0 {
    93  		skipNum, err := strconv.Atoi(skip)
    94  		if err == nil {
    95  			p.skip = skipNum
    96  		}
    97  	}
    98  
    99  	isDir, err := isDir(path)
   100  	if err != nil || !isDir {
   101  		err = os.MkdirAll(path, 0755)
   102  		if err != nil {
   103  			return newError("Mkdir failed, err:%v", err)
   104  		}
   105  	}
   106  
   107  	p.path = path
   108  	p.filename = filename
   109  	p.level = LevelFromStr(level)
   110  
   111  	hostname, _ := os.Hostname()
   112  	p.hostname = hostname
   113  	body := func() {
   114  		go p.spliter()
   115  	}
   116  	doSplit, ok := config["dosplit"]
   117  	if !ok {
   118  		doSplit = "true"
   119  	}
   120  	if doSplit == "true" {
   121  		p.split.Do(body)
   122  	}
   123  	return p.ReOpen()
   124  }
   125  
   126  // split the log file
   127  func (p *XFileLog) spliter() {
   128  	preHour := time.Now().Hour()
   129  	splitTime := time.Now().Format("2006010215")
   130  	defer p.Close()
   131  	for {
   132  		time.Sleep(time.Second * SpliterDelay)
   133  		if time.Now().Hour() != preHour {
   134  			p.clean()
   135  			p.rename(splitTime)
   136  			preHour = time.Now().Hour()
   137  			splitTime = time.Now().Format("2006010215")
   138  		}
   139  	}
   140  }
   141  
   142  // SetLevel implements XLogger
   143  func (p *XFileLog) SetLevel(level string) {
   144  	p.level = LevelFromStr(level)
   145  }
   146  
   147  // SetSkip implements XLogger
   148  func (p *XFileLog) SetSkip(skip int) {
   149  	p.skip = skip
   150  }
   151  
   152  func (p *XFileLog) openFile(filename string) (*os.File, error) {
   153  	file, err := os.OpenFile(filename,
   154  		os.O_CREATE|os.O_APPEND|os.O_WRONLY,
   155  		0644,
   156  	)
   157  
   158  	if err != nil {
   159  		return nil, newError("open %s failed, err:%v", filename, err)
   160  	}
   161  
   162  	return file, err
   163  }
   164  
   165  func delayClose(fp *os.File) {
   166  	if fp == nil {
   167  		return
   168  	}
   169  	time.Sleep(1000 * time.Millisecond)
   170  	fp.Close()
   171  }
   172  
   173  func (p *XFileLog) clean() (err error) {
   174  	deadline := time.Now().AddDate(0, 0, CleanDays)
   175  	var files []string
   176  	files, err = filepath.Glob(fmt.Sprintf("%s/%s.log*", p.path, p.filename))
   177  	if err != nil {
   178  		return
   179  	}
   180  	var fileInfo os.FileInfo
   181  	for _, file := range files {
   182  		if filepath.Base(file) == fmt.Sprintf("%s.log", p.filename) {
   183  			continue
   184  		}
   185  		if filepath.Base(file) == fmt.Sprintf("%s.log.wf", p.filename) {
   186  			continue
   187  		}
   188  		if fileInfo, err = os.Stat(file); err == nil {
   189  			if fileInfo.ModTime().Before(deadline) {
   190  				os.Remove(file)
   191  			} else if fileInfo.Size() == 0 {
   192  				os.Remove(file)
   193  			}
   194  		}
   195  	}
   196  	return
   197  }
   198  
   199  func (p *XFileLog) rename(shuffix string) (err error) {
   200  	p.mu.Lock()
   201  	defer p.mu.Unlock()
   202  	defer p.ReOpen()
   203  	if p.file == nil {
   204  		return
   205  	}
   206  	var fileInfo os.FileInfo
   207  	normalLog := p.path + "/" + p.filename + ".log"
   208  	warnLog := normalLog + ".wf"
   209  	newLog := fmt.Sprintf("%s/%s.log-%s.log", p.path, p.filename, shuffix)
   210  	newWarnLog := fmt.Sprintf("%s/%s.log.wf-%s.log.wf", p.path, p.filename, shuffix)
   211  	if fileInfo, err = os.Stat(normalLog); err == nil && fileInfo.Size() == 0 {
   212  		return
   213  	}
   214  	if _, err = os.Stat(newLog); err == nil {
   215  		return
   216  	}
   217  	if err = os.Rename(normalLog, newLog); err != nil {
   218  		return
   219  	}
   220  	if fileInfo, err = os.Stat(warnLog); err == nil && fileInfo.Size() == 0 {
   221  		return
   222  	}
   223  	if _, err = os.Stat(newWarnLog); err == nil {
   224  		return
   225  	}
   226  	if err = os.Rename(warnLog, newWarnLog); err != nil {
   227  		return
   228  	}
   229  	return
   230  }
   231  
   232  // ReOpen implements XLogger
   233  func (p *XFileLog) ReOpen() error {
   234  	go delayClose(p.file)
   235  	go delayClose(p.errFile)
   236  
   237  	normalLog := p.path + "/" + p.filename + ".log"
   238  	file, err := p.openFile(normalLog)
   239  	if err != nil {
   240  		return err
   241  	}
   242  
   243  	p.file = file
   244  	warnLog := normalLog + ".wf"
   245  	p.errFile, err = p.openFile(warnLog)
   246  	if err != nil {
   247  		p.file.Close()
   248  		p.file = nil
   249  		return err
   250  	}
   251  
   252  	return nil
   253  }
   254  
   255  // Warn implements XLogger
   256  func (p *XFileLog) Warn(format string, a ...interface{}) error {
   257  	if p.level > WarnLevel {
   258  		return nil
   259  	}
   260  
   261  	return p.warnx(XFileLogDefaultLogID, format, a...)
   262  }
   263  
   264  // Warnx implements XLogger
   265  func (p *XFileLog) Warnx(logID, format string, a ...interface{}) error {
   266  	if p.level > WarnLevel {
   267  		return nil
   268  	}
   269  
   270  	return p.warnx(logID, format, a...)
   271  }
   272  
   273  func (p *XFileLog) warnx(logID, format string, a ...interface{}) error {
   274  	logText := formatValue(format, a...)
   275  	fun, filename, lineno := getRuntimeInfo(p.skip)
   276  	logText = formatLineInfo(p.runtime, fun, filepath.Base(filename), logText, lineno)
   277  	//logText = fmt.Sprintf("[%s:%s:%d] %s", fun, filepath.Base(filename), lineno, logText)
   278  
   279  	return p.write(WarnLevel, &logText, logID)
   280  }
   281  
   282  // Fatal implements XLogger
   283  func (p *XFileLog) Fatal(format string, a ...interface{}) error {
   284  	if p.level > FatalLevel {
   285  		return nil
   286  	}
   287  
   288  	return p.fatalx(XFileLogDefaultLogID, format, a...)
   289  }
   290  
   291  // Fatalx implements XLogger
   292  func (p *XFileLog) Fatalx(logID, format string, a ...interface{}) error {
   293  	if p.level > FatalLevel {
   294  		return nil
   295  	}
   296  
   297  	return p.fatalx(logID, format, a...)
   298  }
   299  
   300  func (p *XFileLog) fatalx(logID, format string, a ...interface{}) error {
   301  	logText := formatValue(format, a...)
   302  	fun, filename, lineno := getRuntimeInfo(p.skip)
   303  	logText = formatLineInfo(p.runtime, fun, filepath.Base(filename), logText, lineno)
   304  	//logText = fmt.Sprintf("[%s:%s:%d] %s", fun, filepath.Base(filename), lineno, logText)
   305  
   306  	return p.write(FatalLevel, &logText, logID)
   307  }
   308  
   309  // Notice implements XLogger
   310  func (p *XFileLog) Notice(format string, a ...interface{}) error {
   311  	if p.level > NoticeLevel {
   312  		return nil
   313  	}
   314  	return p.noticex(XFileLogDefaultLogID, format, a...)
   315  }
   316  
   317  // Noticex implements XLogger
   318  func (p *XFileLog) Noticex(logID, format string, a ...interface{}) error {
   319  	if p.level > NoticeLevel {
   320  		return nil
   321  	}
   322  	return p.noticex(logID, format, a...)
   323  }
   324  
   325  func (p *XFileLog) noticex(logID, format string, a ...interface{}) error {
   326  	logText := formatValue(format, a...)
   327  	fun, filename, lineno := getRuntimeInfo(p.skip)
   328  	logText = formatLineInfo(p.runtime, fun, filepath.Base(filename), logText, lineno)
   329  
   330  	return p.write(NoticeLevel, &logText, logID)
   331  }
   332  
   333  // Trace implements XLogger
   334  func (p *XFileLog) Trace(format string, a ...interface{}) error {
   335  	return p.tracex(XFileLogDefaultLogID, format, a...)
   336  }
   337  
   338  // Tracex implements XLogger
   339  func (p *XFileLog) Tracex(logID, format string, a ...interface{}) error {
   340  	return p.tracex(logID, format, a...)
   341  }
   342  
   343  func (p *XFileLog) tracex(logID, format string, a ...interface{}) error {
   344  	if p.level > TraceLevel {
   345  		return nil
   346  	}
   347  
   348  	logText := formatValue(format, a...)
   349  	fun, filename, lineno := getRuntimeInfo(p.skip)
   350  	logText = formatLineInfo(p.runtime, fun, filepath.Base(filename), logText, lineno)
   351  	//logText = fmt.Sprintf("[%s:%s:%d] %s", fun, filepath.Base(filename), lineno, logText)
   352  
   353  	return p.write(TraceLevel, &logText, logID)
   354  }
   355  
   356  // Debug implements XLogger
   357  func (p *XFileLog) Debug(format string, a ...interface{}) error {
   358  	return p.debugx(XFileLogDefaultLogID, format, a...)
   359  }
   360  
   361  func (p *XFileLog) debugx(logID, format string, a ...interface{}) error {
   362  	if p.level > DebugLevel {
   363  		return nil
   364  	}
   365  
   366  	logText := formatValue(format, a...)
   367  	fun, filename, lineno := getRuntimeInfo(p.skip)
   368  	logText = formatLineInfo(p.runtime, fun, filepath.Base(filename), logText, lineno)
   369  
   370  	return p.write(DebugLevel, &logText, logID)
   371  }
   372  
   373  // Debugx implements XLogger
   374  func (p *XFileLog) Debugx(logID, format string, a ...interface{}) error {
   375  	return p.debugx(logID, format, a...)
   376  }
   377  
   378  // Close implements XLogger
   379  func (p *XFileLog) Close() {
   380  	p.mu.Lock()
   381  	defer p.mu.Unlock()
   382  	if p.file != nil {
   383  		p.file.Close()
   384  		p.file = nil
   385  	}
   386  
   387  	if p.errFile != nil {
   388  		p.errFile.Close()
   389  		p.errFile = nil
   390  	}
   391  }
   392  
   393  // GetHost getter of hostname
   394  func (p *XFileLog) GetHost() string {
   395  	return p.hostname
   396  }
   397  
   398  func (p *XFileLog) write(level int, msg *string, logID string) error {
   399  	levelText := levelTextArray[level]
   400  	time := time.Now().Format("2006-01-02 15:04:05")
   401  
   402  	logText := formatLog(msg, time, p.service, p.hostname, levelText, logID)
   403  	file := p.file
   404  	if level >= WarnLevel {
   405  		file = p.errFile
   406  	}
   407  
   408  	file.Write([]byte(logText))
   409  	return nil
   410  }
   411  
   412  func isDir(path string) (bool, error) {
   413  	stat, err := os.Stat(path)
   414  	if err != nil {
   415  		return false, err
   416  	}
   417  	return stat.IsDir(), nil
   418  }