github.com/cloud-foundations/dominator@v0.0.0-20221004181915-6e4fee580046/lib/logbuf/api.go (about) 1 /* 2 Package logbuf provides a circular buffer for writing logs to. 3 4 Package logbuf provides an io.Writer which can be passed to the log.New 5 function to serve as a destination for logs. Logs can be viewed via a HTTP 6 interface and may also be directed to the standard error output. 7 */ 8 package logbuf 9 10 import ( 11 "container/ring" 12 "flag" 13 "io" 14 "net/http" 15 "os" 16 "path" 17 "sync" 18 "time" 19 20 "github.com/Cloud-Foundations/Dominator/lib/bufwriter" 21 "github.com/Cloud-Foundations/Dominator/lib/flagutil" 22 ) 23 24 var ( 25 stdOptions = Options{ 26 HttpServeMux: http.DefaultServeMux, 27 MaxFileSize: 10 << 20, 28 Quota: 100 << 20, 29 } 30 kSoleLogBuffer *LogBuffer 31 kOnce sync.Once 32 ) 33 34 // LogBuffer is a circular buffer suitable for holding logs. It satisfies the 35 // io.Writer interface. It is usually passed to the log.New function. 36 type LogBuffer struct { 37 options Options 38 rwMutex sync.RWMutex 39 buffer *ring.Ring // Always points to next insert position. 40 file *os.File 41 writer *bufwriter.Writer 42 fileSize flagutil.Size 43 usage flagutil.Size 44 writeNotifier chan<- struct{} 45 panicLogfile *string // Name of last invocation logfile if it has a panic. 46 } 47 48 type Options struct { 49 AlsoLogToStderr bool 50 Directory string 51 HttpServeMux *http.ServeMux 52 IdleMarkTimeout time.Duration 53 MaxBufferLines uint // Minimum: 100. 54 MaxFileSize flagutil.Size // Minimum: 16 KiB 55 Quota flagutil.Size // Minimum: 64 KiB. 56 RedirectStderr bool // Only one LogBuffer should set this. 57 } 58 59 // UseFlagSet instructs this package to read its command-line flags from the 60 // given flag set instead of from the command line. Caller must pass the 61 // flag set to this method before calling Parse on it. 62 func UseFlagSet(set *flag.FlagSet) { 63 set.BoolVar(&stdOptions.AlsoLogToStderr, "alsoLogToStderr", false, 64 "If true, also write logs to stderr") 65 set.DurationVar(&stdOptions.IdleMarkTimeout, "idleMarkTimeout", 0, 66 "time after last log before a 'MARK' message is written to logfile") 67 set.UintVar(&stdOptions.MaxBufferLines, "logbufLines", 1024, 68 "Number of lines to store in the log buffer") 69 set.StringVar(&stdOptions.Directory, "logDir", path.Join("/var/log", 70 path.Base(os.Args[0])), 71 "Directory to write log data to. If empty, no logs are written") 72 set.Var(&stdOptions.MaxFileSize, "logFileMaxSize", 73 "Maximum size for a log file. If exceeded, new file is created") 74 set.Var(&stdOptions.Quota, "logQuota", 75 "Log quota. If exceeded, old logs are deleted") 76 } 77 78 // GetStandardOptions will return the standard options. 79 // Only one *LogBuffer should be created per application with these options. 80 // The following command-line flags are registered and used: 81 // -alsoLogToStderr: If true, also write logs to stderr 82 // -logbufLines: Number of lines to store in the log buffer 83 // -logDir: Directory to write log data to. If empty, no logs are 84 // written 85 // -logFileMaxSize: Maximum size for each log file. If exceeded, the logfile 86 // is closed and a new one opened. 87 // If zero, the limit will be 16 KiB 88 // -logQuota: Log quota. If exceeded, old logs are deleted. 89 // If zero, the quota will be 64 KiB 90 func GetStandardOptions() Options { return stdOptions } 91 92 // New returns a new *LogBuffer with the standard options. Note that 93 // RedirectStderr will be set to true if AlsoLogToStderr is false. 94 // Only one should be created per application. 95 func New() *LogBuffer { 96 options := stdOptions 97 if !options.AlsoLogToStderr { 98 options.RedirectStderr = true 99 } 100 return newLogBuffer(options) 101 } 102 103 // NewWithOptions will create a new *LogBuffer with the specified options. 104 // Each *LogBuffer must use a different Directory and HttpServeMux. 105 func NewWithOptions(options Options) *LogBuffer { 106 return newLogBuffer(options) 107 } 108 109 // Get works like New except that successive calls to Get return the same 110 // instance. 111 func Get() *LogBuffer { 112 kOnce.Do(func() { 113 kSoleLogBuffer = New() 114 }) 115 return kSoleLogBuffer 116 117 } 118 119 // Dump will write the contents of the log buffer to w, with a prefix and 120 // postfix string written before and after each line. If recentFirst is true, 121 // the most recently written contents are dumped first. 122 func (lb *LogBuffer) Dump(writer io.Writer, prefix, postfix string, 123 recentFirst bool) error { 124 return lb.dump(writer, prefix, postfix, recentFirst, false) 125 } 126 127 // Flush flushes the open log file (if one is open). This should only be called 128 // just prior to process termination. The log file is automatically flushed 129 // after short periods of inactivity. 130 func (lb *LogBuffer) Flush() error { 131 return lb.flush() 132 } 133 134 // Write will write len(p) bytes from p to the log buffer. It always returns 135 // len(p), nil. 136 func (lb *LogBuffer) Write(p []byte) (n int, err error) { 137 return lb.write(p) 138 } 139 140 // WriteHtml will write the contents of the log buffer to writer, with 141 // appropriate HTML markups. 142 func (lb *LogBuffer) WriteHtml(writer io.Writer) { 143 lb.writeHtml(writer) 144 }