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