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  }