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 9package log 10 11import ( 12 "bytes" 13 "flag" 14 "fmt" 15 golog "log" 16 "sync" 17 "time" 18) 19 20var ( 21 flagV = flag.Int("v", 0, "verbosity") 22 mu sync.Mutex 23 cacheMem int 24 cacheMaxMem int 25 cachePos int 26 cacheEntries []string 27 prependTime = true // for testing 28) 29 30// EnableCaching enables in memory caching of log output. 31// Caches up to maxLines, but no more than maxMem bytes. 32// Cached output can later be queried with CachedOutput. 33func EnableLogCaching(maxLines, maxMem int) { 34 mu.Lock() 35 defer mu.Unlock() 36 if cacheEntries != nil { 37 Fatalf("log caching is already enabled") 38 } 39 if maxLines < 1 || maxMem < 1 { 40 panic("invalid maxLines/maxMem") 41 } 42 cacheMaxMem = maxMem 43 cacheEntries = make([]string, maxLines) 44} 45 46// Retrieves cached log output. 47func CachedLogOutput() string { 48 mu.Lock() 49 defer mu.Unlock() 50 buf := new(bytes.Buffer) 51 for i := range cacheEntries { 52 pos := (cachePos + i) % len(cacheEntries) 53 if cacheEntries[pos] == "" { 54 continue 55 } 56 buf.WriteString(cacheEntries[pos]) 57 buf.Write([]byte{'\n'}) 58 } 59 return buf.String() 60} 61 62func Logf(v int, msg string, args ...interface{}) { 63 mu.Lock() 64 doLog := v <= *flagV 65 if cacheEntries != nil && v <= 1 { 66 cacheMem -= len(cacheEntries[cachePos]) 67 if cacheMem < 0 { 68 panic("log cache size underflow") 69 } 70 timeStr := "" 71 if prependTime { 72 timeStr = time.Now().Format("2006/01/02 15:04:05 ") 73 } 74 cacheEntries[cachePos] = fmt.Sprintf(timeStr+msg, args...) 75 cacheMem += len(cacheEntries[cachePos]) 76 cachePos++ 77 if cachePos == len(cacheEntries) { 78 cachePos = 0 79 } 80 for i := 0; i < len(cacheEntries)-1 && cacheMem > cacheMaxMem; i++ { 81 pos := (cachePos + i) % len(cacheEntries) 82 cacheMem -= len(cacheEntries[pos]) 83 cacheEntries[pos] = "" 84 } 85 if cacheMem < 0 { 86 panic("log cache size underflow") 87 } 88 } 89 mu.Unlock() 90 91 if doLog { 92 golog.Printf(msg, args...) 93 } 94} 95 96func Fatal(err error) { 97 golog.Fatal(err) 98} 99 100func Fatalf(msg string, args ...interface{}) { 101 golog.Fatalf(msg, args...) 102} 103