package lxlog import ( "fmt" "os" "runtime" "strconv" "time" ) // 异步写日志 var MyLog *FileLog const ( LogSplitTypeHour = iota LogSplitTypeSize logPath = "myLog" logName = "myLog" chanSize = 5000 ) type FileLog struct { logPath string logName string file *os.File //普通日志 warnFile *os.File //错误日志 级别低 logDataChan chan *LogData logSplitType int //分割类型 logSplitSize int64 //分割体积 lastSplitHour int //分割时间 } type LogData struct { Message string TimeStr string LevelStr string IsWarn bool File string } // _ = tools.InitLog() func InitLog() (log *FileLog) { config := make(map[string]string, 8) config["log_path"] = "." config["log_name"] = "server" config["log_chan_size"] = "50000" //chan size 可以不用 config["log_split_type"] = "size" config["log_split_size"] = strconv.Itoa(1024 * 1024 * 1024) // 1g log, err := NewFileLog(config) if err != nil { panic("init log err") } MyLog = log return log } func NewFileLog(config map[string]string) (logFile *FileLog, err error) { var logSplitType = LogSplitTypeSize var logSplitSize int64 splitType, ok := config["log_split_type"] if !ok { splitType = "hour" } else { if splitType == "size" { splitSize, ok := config["log_split_size"] if !ok { splitSize = "104857600" //100M 以文字来讲很多了 } logSplitSize, err = strconv.ParseInt(splitSize, 10, 64) if err != nil { logSplitSize = 104857600 } logSplitType = LogSplitTypeSize } else { logSplitType = LogSplitTypeHour } } //打开日志文件 exist, err := PathIsExists(logPath) if err != nil { fmt.Printf("get dir error![%v]\n", err) return } if !exist { err = os.Mkdir(logPath, os.ModePerm) if err != nil { fmt.Printf("mkdir failed![%v]\n", err) return } else { fmt.Printf("mkdir success!\n") } } //正常日志 fileName := fmt.Sprintf("%s/%s.log", logPath, logName) file, err := os.OpenFile(fileName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755) //错误日志 fileNameWarn := fmt.Sprintf("%s/%s.log.err", logPath, logName) fileWarn, err := os.OpenFile(fileNameWarn, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755) if err != nil { return } logFile = &FileLog{ logPath: logPath, logName: logName, logDataChan: make(chan *LogData, chanSize), file: file, warnFile: fileWarn, logSplitType: logSplitType, logSplitSize: logSplitSize, lastSplitHour: time.Now().Hour(), } go logFile.writeLogBackGround() return } // 判断文件夹是否存在 func PathIsExists(path string) (bool, error) { _, err := os.Stat(path) if err == nil { return true, nil } if os.IsNotExist(err) { return false, nil } return false, err } func (f *FileLog) Info(message string) { _, file, line, _ := runtime.Caller(1) fi := file + ":" + strconv.Itoa(line) f.print(message, "info", fi) } func (f *FileLog) Debug(message string) { _, file, line, _ := runtime.Caller(1) fi := file + ":" + strconv.Itoa(line) f.print(message, "debug", fi) } func (f *FileLog) Warn(message string) { _, file, line, _ := runtime.Caller(1) fi := file + ":" + strconv.Itoa(line) f.print(message, "warn", fi) } func (f *FileLog) Error(message string) { _, file, line, _ := runtime.Caller(1) fi := file + ":" + strconv.Itoa(line) f.print(message, "error", fi) } func (f *FileLog) print(message, Type string, file string) { isWarn := false if Type == "error" { isWarn = true } msg := &LogData{ Message: message, TimeStr: time.Now().Format("2006-01-02 15:04:05"), LevelStr: Type, IsWarn: isWarn, File: file, } // 怕chan满了插不进去 select { case f.logDataChan <- msg: default: } } func (f *FileLog) writeLogBackGround() { for logData := range f.logDataChan { var file = f.file if logData.IsWarn { file = f.warnFile } f.checkSplitFile(logData.IsWarn) // 检查文件分割类型 fmt.Fprintf(file, "%s %s %s %s\n", logData.TimeStr, logData.LevelStr, logData.Message, logData.File) } } func (f *FileLog) checkSplitFile(isWarn bool) { if f.logSplitType == LogSplitTypeHour { f.splitHour(isWarn) return } f.splitSize(isWarn) } // 根据时间切割 func (f *FileLog) splitHour(isWarn bool) { now := time.Now() hour := now.Hour() if hour == f.lastSplitHour { return } f.lastSplitHour = hour var backupFileName string var fileName string if isWarn { backupFileName = fmt.Sprintf("%s/%s.log.err_%s", f.logPath, f.logName, now.Format("20060102150405")) fileName = fmt.Sprintf("%s/%s.log.err", f.logPath, f.logName) } else { backupFileName = fmt.Sprintf("%s/%s.log_%s", f.logPath, f.logName, now.Format("20060102150405")) fileName = fmt.Sprintf("%s/%s.log", f.logPath, f.logName) } file := f.file if isWarn { file = f.warnFile } file.Close() os.Rename(fileName, backupFileName) file, err := os.OpenFile(fileName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755) if err != nil { return } if isWarn { f.warnFile = file } else { f.file = file } } // 根据文件大小来分割 func (f *FileLog) splitSize(isWarn bool) { file := f.file if isWarn { file = f.warnFile } fileInfo, err := file.Stat() // 可以得到文件的参数 if err != nil { return } fileSize := fileInfo.Size() if fileSize <= f.logSplitSize { return } var backupFileName string var fileName string now := time.Now() if isWarn { backupFileName = fmt.Sprintf("%s/%s.log.err_%s", f.logPath, f.logName, now.Format("20060102150405")) fileName = fmt.Sprintf("%s/%s.log.err", f.logPath, f.logName) } else { backupFileName = fmt.Sprintf("%s/%s.log_%s", f.logPath, f.logName, now.Format("20060102150405")) fileName = fmt.Sprintf("%s/%s.log", f.logPath, f.logName) } file.Close() os.Rename(fileName, backupFileName) file, err = os.OpenFile(fileName, os.O_CREATE|os.O_APPEND|os.O_WRONLY, 0755) if err != nil { return } if isWarn { f.warnFile = file } else { f.file = file } }