github.com/google/syzkaller@v0.0.0-20251211124644-a066d2bc4b02/pkg/log/log.go (about) 1 // Copyright 2016 syzkaller project authors. All rights reserved. 2 // Use of this source code is governed by Apache 2 LICENSE that can be found in the LICENSE file. 3 4 // Package log provides functionality similar to standard log package with some extensions: 5 // - verbosity levels 6 // - global verbosity setting that can be used by multiple packages 7 // - ability to disable all output 8 // - ability to cache recent output in memory 9 package log 10 11 import ( 12 "bytes" 13 "flag" 14 "fmt" 15 golog "log" 16 "strings" 17 "sync" 18 "sync/atomic" 19 "time" 20 ) 21 22 var ( 23 flagV = flag.Int("vv", 0, "verbosity") 24 mu sync.Mutex 25 cacheMem int 26 cacheMaxMem int 27 cachePos int 28 cacheEntries []string 29 cachingEnabled atomic.Bool 30 instanceName string 31 prependTime = true // for testing 32 ) 33 34 // EnableLogCaching enables in memory caching of log output. 35 // Caches up to maxLines, but no more than maxMem bytes. 36 // Cached output can later be queried with CachedOutput. 37 func EnableLogCaching(maxLines, maxMem int) { 38 mu.Lock() 39 defer mu.Unlock() 40 if cacheEntries != nil { 41 Fatalf("log caching is already enabled") 42 } 43 if maxLines < 1 || maxMem < 1 { 44 panic("invalid maxLines/maxMem") 45 } 46 cacheMaxMem = maxMem 47 cacheEntries = make([]string, maxLines) 48 cachingEnabled.Store(true) 49 } 50 51 // Retrieves cached log output. 52 func CachedLogOutput() string { 53 mu.Lock() 54 defer mu.Unlock() 55 buf := new(bytes.Buffer) 56 for i := range cacheEntries { 57 pos := (cachePos + i) % len(cacheEntries) 58 if cacheEntries[pos] == "" { 59 continue 60 } 61 buf.WriteString(cacheEntries[pos]) 62 buf.Write([]byte{'\n'}) 63 } 64 return buf.String() 65 } 66 67 // If the name is set, it will be displayed for all logs. 68 func SetName(name string) { 69 instanceName = name 70 } 71 72 // V reports whether verbosity at the call site is at least the requested level. 73 // See https://pkg.go.dev/github.com/golang/glog#V for details. 74 func V(level int) bool { 75 return level <= *flagV 76 } 77 78 func Log(v int, msg string) { 79 Logf(v, "%v", msg) 80 } 81 82 func Logf(v int, msg string, args ...interface{}) { 83 writeMessage(v, "", msg, args...) 84 } 85 86 func Error(err error) { 87 Errorf("%v", err) 88 } 89 90 func Errorf(msg string, args ...interface{}) { 91 writeMessage(0, "ERROR", msg, args...) 92 } 93 94 func Fatal(err error) { 95 Fatalf("%v", err) 96 } 97 98 func Fatalf(msg string, args ...interface{}) { 99 golog.Fatal(message("FATAL", msg, args...)) 100 } 101 102 func message(severity, msg string, args ...interface{}) string { 103 var sb strings.Builder 104 if severity != "" { 105 fmt.Fprintf(&sb, "[%s] ", severity) 106 } 107 if instanceName != "" { 108 fmt.Fprintf(&sb, "%s: ", instanceName) 109 } 110 fmt.Fprintf(&sb, msg, args...) 111 return sb.String() 112 } 113 114 func writeMessage(v int, severity, msg string, args ...interface{}) { 115 cache := v <= 1 && cachingEnabled.Load() 116 if !V(v) && !cache { 117 return 118 } 119 text := message(severity, msg, args...) 120 if V(v) { 121 golog.Print(text) 122 } 123 if !cache { 124 return 125 } 126 mu.Lock() 127 defer mu.Unlock() 128 cacheMem -= len(cacheEntries[cachePos]) 129 if cacheMem < 0 { 130 panic("log cache size underflow") 131 } 132 timeStr := "" 133 if prependTime { 134 timeStr = time.Now().Format("2006/01/02 15:04:05 ") 135 } 136 cacheEntries[cachePos] = timeStr + text 137 cacheMem += len(cacheEntries[cachePos]) 138 cachePos++ 139 if cachePos == len(cacheEntries) { 140 cachePos = 0 141 } 142 for i := 0; i < len(cacheEntries)-1 && cacheMem > cacheMaxMem; i++ { 143 pos := (cachePos + i) % len(cacheEntries) 144 cacheMem -= len(cacheEntries[pos]) 145 cacheEntries[pos] = "" 146 } 147 if cacheMem < 0 { 148 panic("log cache size underflow") 149 } 150 } 151 152 type VerboseWriter int 153 154 func (w VerboseWriter) Write(data []byte) (int, error) { 155 Logf(int(w), "%s", data) 156 return len(data), nil 157 }