github.com/gitbundle/modules@v0.0.0-20231025071548-85b91c5c3b01/log/writer.go (about) 1 // Copyright 2023 The GitBundle Inc. All rights reserved. 2 // Copyright 2017 The Gitea Authors. All rights reserved. 3 // Use of this source code is governed by a MIT-style 4 // license that can be found in the LICENSE file. 5 6 package log 7 8 import ( 9 "bytes" 10 "fmt" 11 "io" 12 "regexp" 13 "strings" 14 "sync" 15 ) 16 17 type byteArrayWriter []byte 18 19 func (b *byteArrayWriter) Write(p []byte) (int, error) { 20 *b = append(*b, p...) 21 return len(p), nil 22 } 23 24 // WriterLogger represent a basic logger for GitBundle 25 type WriterLogger struct { 26 out io.WriteCloser 27 mu sync.Mutex 28 29 Level Level `json:"level"` 30 StacktraceLevel Level `json:"stacktraceLevel"` 31 Flags int `json:"flags"` 32 Prefix string `json:"prefix"` 33 Colorize bool `json:"colorize"` 34 Expression string `json:"expression"` 35 regexp *regexp.Regexp 36 } 37 38 // NewWriterLogger creates a new WriterLogger from the provided WriteCloser. 39 // Optionally the level can be changed at the same time. 40 func (logger *WriterLogger) NewWriterLogger(out io.WriteCloser, level ...Level) { 41 logger.mu.Lock() 42 defer logger.mu.Unlock() 43 logger.out = out 44 switch logger.Flags { 45 case 0: 46 logger.Flags = LstdFlags 47 case -1: 48 logger.Flags = 0 49 } 50 if len(level) > 0 { 51 logger.Level = level[0] 52 } 53 logger.createExpression() 54 } 55 56 func (logger *WriterLogger) createExpression() { 57 if len(logger.Expression) > 0 { 58 var err error 59 logger.regexp, err = regexp.Compile(logger.Expression) 60 if err != nil { 61 logger.regexp = nil 62 } 63 } 64 } 65 66 // GetLevel returns the logging level for this logger 67 func (logger *WriterLogger) GetLevel() Level { 68 return logger.Level 69 } 70 71 // GetStacktraceLevel returns the stacktrace logging level for this logger 72 func (logger *WriterLogger) GetStacktraceLevel() Level { 73 return logger.StacktraceLevel 74 } 75 76 // Copy of cheap integer to fixed-width decimal to ascii from logger. 77 func itoa(buf *[]byte, i, wid int) { 78 var logger [20]byte 79 bp := len(logger) - 1 80 for i >= 10 || wid > 1 { 81 wid-- 82 q := i / 10 83 logger[bp] = byte('0' + i - q*10) 84 bp-- 85 i = q 86 } 87 // i < 10 88 logger[bp] = byte('0' + i) 89 *buf = append(*buf, logger[bp:]...) 90 } 91 92 func (logger *WriterLogger) createMsg(buf *[]byte, event *Event) { 93 *buf = append(*buf, logger.Prefix...) 94 t := event.time 95 if logger.Flags&(Ldate|Ltime|Lmicroseconds) != 0 { 96 if logger.Colorize { 97 *buf = append(*buf, fgCyanBytes...) 98 } 99 if logger.Flags&LUTC != 0 { 100 t = t.UTC() 101 } 102 if logger.Flags&Ldate != 0 { 103 year, month, day := t.Date() 104 itoa(buf, year, 4) 105 *buf = append(*buf, '/') 106 itoa(buf, int(month), 2) 107 *buf = append(*buf, '/') 108 itoa(buf, day, 2) 109 *buf = append(*buf, ' ') 110 } 111 if logger.Flags&(Ltime|Lmicroseconds) != 0 { 112 hour, min, sec := t.Clock() 113 itoa(buf, hour, 2) 114 *buf = append(*buf, ':') 115 itoa(buf, min, 2) 116 *buf = append(*buf, ':') 117 itoa(buf, sec, 2) 118 if logger.Flags&Lmicroseconds != 0 { 119 *buf = append(*buf, '.') 120 itoa(buf, t.Nanosecond()/1e3, 6) 121 } 122 *buf = append(*buf, ' ') 123 } 124 if logger.Colorize { 125 *buf = append(*buf, resetBytes...) 126 } 127 128 } 129 if logger.Flags&(Lshortfile|Llongfile) != 0 { 130 if logger.Colorize { 131 *buf = append(*buf, fgGreenBytes...) 132 } 133 file := event.filename 134 if logger.Flags&Lmedfile == Lmedfile { 135 startIndex := len(file) - 20 136 if startIndex > 0 { 137 file = "..." + file[startIndex:] 138 } 139 } else if logger.Flags&Lshortfile != 0 { 140 startIndex := strings.LastIndexByte(file, '/') 141 if startIndex > 0 && startIndex < len(file) { 142 file = file[startIndex+1:] 143 } 144 } 145 *buf = append(*buf, file...) 146 *buf = append(*buf, ':') 147 itoa(buf, event.line, -1) 148 if logger.Flags&(Lfuncname|Lshortfuncname) != 0 { 149 *buf = append(*buf, ':') 150 } else { 151 if logger.Colorize { 152 *buf = append(*buf, resetBytes...) 153 } 154 *buf = append(*buf, ' ') 155 } 156 } 157 if logger.Flags&(Lfuncname|Lshortfuncname) != 0 { 158 if logger.Colorize { 159 *buf = append(*buf, fgGreenBytes...) 160 } 161 funcname := event.caller 162 if logger.Flags&Lshortfuncname != 0 { 163 lastIndex := strings.LastIndexByte(funcname, '.') 164 if lastIndex > 0 && len(funcname) > lastIndex+1 { 165 funcname = funcname[lastIndex+1:] 166 } 167 } 168 *buf = append(*buf, funcname...) 169 if logger.Colorize { 170 *buf = append(*buf, resetBytes...) 171 } 172 *buf = append(*buf, ' ') 173 174 } 175 if logger.Flags&(Llevel|Llevelinitial) != 0 { 176 // level := strings.ToUpper(event.level.String()) 177 if logger.Colorize { 178 *buf = append(*buf, levelToColor[event.level]...) 179 } 180 *buf = append(*buf, '[') 181 *buf = append(*buf, event.level.Icon()...) 182 // if logger.Flags&Llevelinitial != 0 { 183 // *buf = append(*buf, level[0]) 184 // } else { 185 // *buf = append(*buf, level...) 186 // } 187 *buf = append(*buf, ']') 188 if logger.Colorize { 189 *buf = append(*buf, resetBytes...) 190 } 191 *buf = append(*buf, ' ') 192 } 193 194 msg := []byte(event.msg) 195 if len(msg) > 0 && msg[len(msg)-1] == '\n' { 196 msg = msg[:len(msg)-1] 197 } 198 199 pawMode := allowColor 200 if !logger.Colorize { 201 pawMode = removeColor 202 } 203 204 baw := byteArrayWriter(*buf) 205 (&protectedANSIWriter{ 206 w: &baw, 207 mode: pawMode, 208 }).Write(msg) 209 *buf = baw 210 211 if event.stacktrace != "" && logger.StacktraceLevel <= event.level { 212 lines := bytes.Split([]byte(event.stacktrace), []byte("\n")) 213 if len(lines) > 1 { 214 for _, line := range lines { 215 *buf = append(*buf, "\n\t"...) 216 *buf = append(*buf, line...) 217 } 218 } 219 *buf = append(*buf, '\n') 220 } 221 *buf = append(*buf, '\n') 222 } 223 224 // LogEvent logs the event to the internal writer 225 func (logger *WriterLogger) LogEvent(event *Event) error { 226 if logger.Level > event.level { 227 return nil 228 } 229 230 logger.mu.Lock() 231 defer logger.mu.Unlock() 232 if !logger.Match(event) { 233 return nil 234 } 235 var buf []byte 236 logger.createMsg(&buf, event) 237 _, err := logger.out.Write(buf) 238 return err 239 } 240 241 // Match checks if the given event matches the logger's regexp expression 242 func (logger *WriterLogger) Match(event *Event) bool { 243 if logger.regexp == nil { 244 return true 245 } 246 if logger.regexp.Match( 247 []byte(fmt.Sprintf("%s:%d:%s", event.filename, event.line, event.caller)), 248 ) { 249 return true 250 } 251 // Match on the non-colored msg - therefore strip out colors 252 var msg []byte 253 baw := byteArrayWriter(msg) 254 (&protectedANSIWriter{ 255 w: &baw, 256 mode: removeColor, 257 }).Write([]byte(event.msg)) 258 msg = baw 259 return logger.regexp.Match(msg) 260 } 261 262 // Close the base logger 263 func (logger *WriterLogger) Close() { 264 logger.mu.Lock() 265 defer logger.mu.Unlock() 266 if logger.out != nil { 267 logger.out.Close() 268 } 269 } 270 271 // GetName returns empty for these provider loggers 272 func (logger *WriterLogger) GetName() string { 273 return "" 274 }