github.com/avicd/go-utilx@v0.1.0/logx/file.go (about) 1 package logx 2 3 import ( 4 "github.com/avicd/go-utilx/conv" 5 "log" 6 "os" 7 "path/filepath" 8 "strings" 9 "sync" 10 "time" 11 ) 12 13 const dayMilliSecs = 86400000 14 const Levels = 6 15 16 type Split = [Levels]bool 17 18 type RollCycle int 19 20 const ( 21 Day RollCycle = iota 22 Minute 23 Hour 24 Week 25 Month 26 Year 27 Second 28 ) 29 30 var Layouts = []string{ 31 Year: "2006", 32 Month: "2006-01", 33 Day: "2006-01-02", 34 Hour: "2006-01-02 15:00", 35 Minute: "2006-01-02 15:04", 36 Second: "2006-01-02 15:04:05", 37 } 38 39 type FileAppender struct { 40 loggers [Levels]*log.Logger 41 files [Levels]*os.File 42 cycleIds [Levels]int 43 mutex [Levels]*sync.Mutex 44 locker sync.Mutex 45 TimeLayout string // layout for time formatting 46 MaxSize int64 // max size of log file 47 Name string // name of log file 48 FileFlag int // FileFlag to open log file 49 FileMode os.FileMode // FileMode to open log file 50 Prefix string // prefix of messages 51 CycleOff bool // do not use the period cycle 52 Cycle RollCycle // rolling cycle 53 OutDir string // output directory cycle 54 Split Split // split different level into different log file 55 } 56 57 func (it *FileAppender) getKey(level Level) Level { 58 if it.Split[level] { 59 return level 60 } 61 return ALL 62 } 63 64 func (it *FileAppender) getCycleId() (int, time.Time) { 65 timeRef := time.Now() 66 var id int 67 switch it.Cycle { 68 case Second: 69 id = timeRef.Second() 70 case Minute: 71 id = timeRef.Minute() 72 case Hour: 73 id = timeRef.Hour() 74 case Day: 75 id = timeRef.Day() 76 case Week: 77 _, id = timeRef.ISOWeek() 78 case Month: 79 id = int(timeRef.Month()) 80 case Year: 81 id = timeRef.Year() 82 } 83 return id, timeRef 84 } 85 86 func (it *FileAppender) init(key Level) { 87 if it.mutex[key] == nil { 88 it.locker.Lock() 89 if it.mutex[key] == nil { 90 it.mutex[key] = &sync.Mutex{} 91 } 92 it.locker.Unlock() 93 } 94 } 95 96 func (it *FileAppender) sizeOver(file *os.File) bool { 97 if it.MaxSize > 0 { 98 info, _ := file.Stat() 99 if info.Size() > it.MaxSize { 100 return true 101 } 102 } 103 return false 104 } 105 106 func (it *FileAppender) fileName(level Level) string { 107 name := it.Name 108 if name == "" { 109 exeFile, _ := os.Executable() 110 name = filepath.Base(exeFile) 111 name = strings.TrimSuffix(name, filepath.Ext(name)) 112 it.Name = name 113 } 114 if it.Split[level] { 115 name = conv.Append(name, strings.ToLower(Labels[level]), "_") 116 } 117 return name 118 } 119 120 func (it *FileAppender) rollLogFile(level Level) { 121 key := it.getKey(level) 122 var shortName string 123 var cycleId int 124 logFile := it.files[key] 125 if !it.CycleOff { 126 var timeRef time.Time 127 cycleId, timeRef = it.getCycleId() 128 if logFile != nil { 129 if it.cycleIds[key] == cycleId { 130 if !it.sizeOver(it.files[key]) { 131 return 132 } 133 } 134 } 135 var cycleName string 136 if it.Cycle == Week { 137 year := time.Date(timeRef.Year(), 1, 1, 0, 0, 0, 0, time.Local) 138 start := time.UnixMilli(year.UnixMilli() + int64(cycleId-1)*7*dayMilliSecs) 139 end := time.UnixMilli(year.UnixMilli() + (int64(cycleId)*7-1)*dayMilliSecs) 140 cycleName = start.Format(Layouts[Day]) + "--" + end.Format(Layouts[Day]) 141 } else { 142 cycleName = timeRef.Format(Layouts[it.Cycle]) 143 } 144 shortName = conv.Append(it.fileName(level), cycleName, "_") 145 } else if logFile != nil && !it.sizeOver(logFile) { 146 return 147 } else { 148 shortName = it.fileName(level) 149 } 150 it.mutex[key].Lock() 151 if it.files[key] == logFile { 152 if logFile != nil { 153 logFile.Close() 154 } 155 } else { 156 it.mutex[key].Unlock() 157 return 158 } 159 index := 0 160 fileFlag := it.FileFlag 161 if fileFlag == 0 { 162 fileFlag = os.O_WRONLY | os.O_APPEND | os.O_CREATE 163 } 164 fileMode := it.FileMode 165 if fileMode == 0 { 166 fileMode = 0666 167 } 168 for { 169 fileName := filepath.Join(it.OutDir, shortName) 170 if index > 0 { 171 fileName = conv.Append(fileName, index, "_") 172 } 173 fileName += ".log" 174 index++ 175 file, err := os.OpenFile(fileName, fileFlag, fileMode) 176 if err != nil { 177 panic(err) 178 } 179 if it.sizeOver(file) { 180 continue 181 } 182 it.files[key] = file 183 if !it.CycleOff { 184 it.cycleIds[key] = cycleId 185 } 186 break 187 } 188 it.loggers[key] = log.New(it.files[key], it.Prefix, 0) 189 it.mutex[key].Unlock() 190 } 191 192 func (it *FileAppender) Write(level Level, args ...any) { 193 key := it.getKey(level) 194 it.init(key) 195 it.rollLogFile(level) 196 var dest []any 197 dest = append(dest, timeNow(it.TimeLayout)) 198 label := Labels[level] + " " 199 dest = append(dest, label) 200 dest = append(dest, args...) 201 it.loggers[key].Print(dest...) 202 }