github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/lib/log/serverlogger/impl.go (about) 1 package serverlogger 2 3 import ( 4 "errors" 5 "fmt" 6 "log" 7 "os" 8 "regexp" 9 "runtime" 10 "strings" 11 "sync" 12 "time" 13 14 liblog "github.com/Cloud-Foundations/Dominator/lib/log" 15 "github.com/Cloud-Foundations/Dominator/lib/logbuf" 16 "github.com/Cloud-Foundations/Dominator/lib/srpc" 17 "github.com/Cloud-Foundations/Dominator/lib/srpc/serverutil" 18 proto "github.com/Cloud-Foundations/Dominator/proto/logger" 19 ) 20 21 type loggerMapT struct { 22 *serverutil.PerUserMethodLimiter 23 sync.Mutex 24 loggerMap map[string]*Logger 25 } 26 27 type grabWriter struct { 28 data []byte 29 } 30 31 var loggerMap *loggerMapT = &loggerMapT{ 32 loggerMap: make(map[string]*Logger), 33 PerUserMethodLimiter: serverutil.NewPerUserMethodLimiter( 34 map[string]uint{ 35 "Debug": 1, 36 "Print": 1, 37 "SetDebugLevel": 1, 38 "Watch": 1, 39 }), 40 } 41 42 func init() { 43 srpc.RegisterName("Logger", loggerMap) 44 } 45 46 func getCallerName(depth int) string { 47 if pc, _, _, ok := runtime.Caller(depth); !ok { 48 return "UNKNOWN" 49 } else if fi := runtime.FuncForPC(pc); fi == nil { 50 return "UNKNOWN" 51 } else if splitName := strings.Split(fi.Name(), "."); len(splitName) < 1 { 52 return "UNKNOWN" 53 } else { 54 return splitName[len(splitName)-1] 55 } 56 } 57 58 func (w *grabWriter) Write(p []byte) (int, error) { 59 w.data = p 60 return len(p), nil 61 } 62 63 func newLogger(name string, options logbuf.Options, flags int) *Logger { 64 loggerMap.Lock() 65 defer loggerMap.Unlock() 66 if _, ok := loggerMap.loggerMap[name]; ok { 67 panic("logger already exists: " + name) 68 } 69 circularBuffer := logbuf.NewWithOptions(options) 70 logger := &Logger{ 71 circularBuffer: circularBuffer, 72 flags: flags, 73 level: int16(*initialLogDebugLevel), 74 streamers: make(map[*streamerType]struct{}), 75 } 76 if logger.level < -1 { 77 logger.level = -1 78 } 79 logger.maxLevel = logger.level 80 // Ensure this satisfies the published interface. 81 var debugLogger liblog.FullDebugLogger 82 debugLogger = logger 83 _ = debugLogger 84 loggerMap.loggerMap[name] = logger 85 return logger 86 } 87 88 func (l *Logger) checkAuth(authInfo *srpc.AuthInformation) error { 89 if authInfo.HaveMethodAccess { 90 return nil 91 } 92 if accessChecker := l.accessChecker; accessChecker == nil { 93 return errors.New("no access to resource") 94 } else if accessChecker(getCallerName(3), authInfo) { 95 return nil 96 } else { 97 return errors.New("no access to resource") 98 } 99 } 100 101 func (l *Logger) debug(level int16, v ...interface{}) { 102 if l.maxLevel >= level { 103 l.log(level, fmt.Sprint(v...), false) 104 } 105 } 106 107 func (l *Logger) debugf(level int16, format string, v ...interface{}) { 108 if l.maxLevel >= level { 109 l.log(level, fmt.Sprintf(format, v...), false) 110 } 111 } 112 113 func (l *Logger) debugln(level int16, v ...interface{}) { 114 if l.maxLevel >= level { 115 l.log(level, fmt.Sprintln(v...), false) 116 } 117 } 118 119 func (l *Logger) fatals(msg string) { 120 l.log(-1, msg, true) 121 os.Exit(1) 122 } 123 124 func (l *Logger) log(level int16, msg string, dying bool) { 125 buffer := &grabWriter{} 126 rawLogger := log.New(buffer, "", l.flags) 127 rawLogger.Output(4, msg) 128 if l.level >= level { 129 l.circularBuffer.Write(buffer.data) 130 } 131 recalculateLevels := false 132 l.mutex.Lock() 133 defer l.mutex.Unlock() 134 for streamer := range l.streamers { 135 if streamer.debugLevel >= level && 136 (streamer.includeRegex == nil || 137 streamer.includeRegex.Match(buffer.data)) && 138 (streamer.excludeRegex == nil || 139 !streamer.excludeRegex.Match(buffer.data)) { 140 select { 141 case streamer.output <- buffer.data: 142 default: 143 delete(l.streamers, streamer) 144 close(streamer.output) 145 recalculateLevels = true 146 } 147 } 148 } 149 if dying { 150 for streamer := range l.streamers { 151 delete(l.streamers, streamer) 152 close(streamer.output) 153 } 154 l.circularBuffer.Flush() 155 time.Sleep(time.Millisecond * 10) 156 } else if recalculateLevels { 157 l.updateMaxLevel() 158 } 159 } 160 161 func (l *Logger) makeStreamer(request proto.WatchRequest) ( 162 *streamerType, error) { 163 if request.DebugLevel < -1 { 164 request.DebugLevel = -1 165 } 166 streamer := &streamerType{debugLevel: request.DebugLevel} 167 if request.ExcludeRegex != "" { 168 var err error 169 streamer.excludeRegex, err = regexp.Compile(request.ExcludeRegex) 170 if err != nil { 171 return nil, err 172 } 173 } 174 if request.IncludeRegex != "" { 175 var err error 176 streamer.includeRegex, err = regexp.Compile(request.IncludeRegex) 177 if err != nil { 178 return nil, err 179 } 180 } 181 return streamer, nil 182 } 183 184 func (l *Logger) panics(msg string) { 185 l.log(-1, msg, true) 186 panic(msg) 187 } 188 189 func (l *Logger) prints(msg string) { 190 l.log(-1, msg, false) 191 } 192 193 func (l *Logger) setLevel(maxLevel int16) { 194 if maxLevel < -1 { 195 maxLevel = -1 196 } 197 l.level = maxLevel 198 l.mutex.Lock() 199 l.updateMaxLevel() 200 l.mutex.Unlock() 201 } 202 203 func (l *Logger) updateMaxLevel() { 204 maxLevel := l.level 205 for streamer := range l.streamers { 206 if streamer.debugLevel > maxLevel { 207 maxLevel = streamer.debugLevel 208 } 209 } 210 l.maxLevel = maxLevel 211 } 212 213 func (l *Logger) watch(conn *srpc.Conn, streamer *streamerType) { 214 channel := make(chan []byte, 256) 215 streamer.output = channel 216 l.mutex.Lock() 217 l.streamers[streamer] = struct{}{} 218 l.updateMaxLevel() 219 l.mutex.Unlock() 220 timer := time.NewTimer(time.Millisecond * 100) 221 flushPending := false 222 closeNotifier := conn.GetCloseNotifier() 223 for keepGoing := true; keepGoing; { 224 select { 225 case <-closeNotifier: 226 keepGoing = false 227 break 228 case data, ok := <-channel: 229 if !ok { 230 keepGoing = false 231 break 232 } 233 if _, err := conn.Write(data); err != nil { 234 keepGoing = false 235 break 236 } 237 if !flushPending { 238 timer.Reset(time.Millisecond * 100) 239 flushPending = true 240 } 241 case <-timer.C: 242 if conn.Flush() != nil { 243 keepGoing = false 244 break 245 } 246 flushPending = false 247 } 248 } 249 if flushPending { 250 conn.Flush() 251 } 252 l.mutex.Lock() 253 delete(l.streamers, streamer) 254 l.updateMaxLevel() 255 l.mutex.Unlock() 256 // Drain the channel. 257 for { 258 select { 259 case <-channel: 260 default: 261 return 262 } 263 } 264 } 265 266 func (t *loggerMapT) getLogger(name string, 267 authInfo *srpc.AuthInformation) (*Logger, error) { 268 loggerMap.Lock() 269 defer loggerMap.Unlock() 270 if logger, ok := loggerMap.loggerMap[name]; !ok { 271 return nil, errors.New("unknown logger: " + name) 272 } else if err := logger.checkAuth(authInfo); err != nil { 273 return nil, err 274 } else { 275 return logger, nil 276 } 277 } 278 279 func (t *loggerMapT) Debug(conn *srpc.Conn, 280 request proto.DebugRequest, reply *proto.DebugResponse) error { 281 authInfo := conn.GetAuthInformation() 282 if logger, err := t.getLogger(request.Name, authInfo); err != nil { 283 return err 284 } else { 285 logger.Debugf(request.Level, "Logger.Debug(%d): %s\n", 286 request.Level, strings.Join(request.Args, " ")) 287 return nil 288 } 289 } 290 291 func (t *loggerMapT) Print(conn *srpc.Conn, 292 request proto.PrintRequest, 293 reply *proto.PrintResponse) error { 294 authInfo := conn.GetAuthInformation() 295 if logger, err := t.getLogger(request.Name, authInfo); err != nil { 296 return err 297 } else { 298 logger.Println("Logger.Print():", strings.Join(request.Args, " ")) 299 return nil 300 } 301 } 302 303 func (t *loggerMapT) SetDebugLevel(conn *srpc.Conn, 304 request proto.SetDebugLevelRequest, 305 reply *proto.SetDebugLevelResponse) error { 306 authInfo := conn.GetAuthInformation() 307 if logger, err := t.getLogger(request.Name, authInfo); err != nil { 308 return err 309 } else { 310 logger.Printf("Logger.SetDebugLevel(%d)\n", request.Level) 311 logger.SetLevel(request.Level) 312 return nil 313 } 314 } 315 316 func (t *loggerMapT) Watch(conn *srpc.Conn) error { 317 var request proto.WatchRequest 318 if err := conn.Decode(&request); err != nil { 319 return err 320 } 321 authInfo := conn.GetAuthInformation() 322 if logger, err := t.getLogger(request.Name, authInfo); err != nil { 323 return conn.Encode(proto.WatchResponse{Error: err.Error()}) 324 } else if streamer, err := logger.makeStreamer(request); err != nil { 325 return conn.Encode(proto.WatchResponse{Error: err.Error()}) 326 } else { 327 if err := conn.Encode(proto.WatchResponse{}); err != nil { 328 return err 329 } 330 if err := conn.Flush(); err != nil { 331 return err 332 } 333 logger.watch(conn, streamer) 334 return srpc.ErrorCloseClient 335 } 336 }