diff options
Diffstat (limited to 'vendor/github.com/alecthomas/log4go/filelog.go')
-rw-r--r-- | vendor/github.com/alecthomas/log4go/filelog.go | 264 |
1 files changed, 264 insertions, 0 deletions
diff --git a/vendor/github.com/alecthomas/log4go/filelog.go b/vendor/github.com/alecthomas/log4go/filelog.go new file mode 100644 index 00000000..ee0ab0c0 --- /dev/null +++ b/vendor/github.com/alecthomas/log4go/filelog.go @@ -0,0 +1,264 @@ +// Copyright (C) 2010, Kyle Lemons <kyle@kylelemons.net>. All rights reserved. + +package log4go + +import ( + "fmt" + "os" + "time" +) + +// This log writer sends output to a file +type FileLogWriter struct { + rec chan *LogRecord + rot chan bool + + // The opened file + filename string + file *os.File + + // The logging format + format string + + // File header/trailer + header, trailer string + + // Rotate at linecount + maxlines int + maxlines_curlines int + + // Rotate at size + maxsize int + maxsize_cursize int + + // Rotate daily + daily bool + daily_opendate int + + // Keep old logfiles (.001, .002, etc) + rotate bool + maxbackup int +} + +// This is the FileLogWriter's output method +func (w *FileLogWriter) LogWrite(rec *LogRecord) { + w.rec <- rec +} + +func (w *FileLogWriter) Close() { + close(w.rec) + w.file.Sync() +} + +// NewFileLogWriter creates a new LogWriter which writes to the given file and +// has rotation enabled if rotate is true. +// +// If rotate is true, any time a new log file is opened, the old one is renamed +// with a .### extension to preserve it. The various Set* methods can be used +// to configure log rotation based on lines, size, and daily. +// +// The standard log-line format is: +// [%D %T] [%L] (%S) %M +func NewFileLogWriter(fname string, rotate bool) *FileLogWriter { + w := &FileLogWriter{ + rec: make(chan *LogRecord, LogBufferLength), + rot: make(chan bool), + filename: fname, + format: "[%D %T] [%L] (%S) %M", + rotate: rotate, + maxbackup: 999, + } + + // open the file for the first time + if err := w.intRotate(); err != nil { + fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err) + return nil + } + + go func() { + defer func() { + if w.file != nil { + fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()})) + w.file.Close() + } + }() + + for { + select { + case <-w.rot: + if err := w.intRotate(); err != nil { + fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err) + return + } + case rec, ok := <-w.rec: + if !ok { + return + } + now := time.Now() + if (w.maxlines > 0 && w.maxlines_curlines >= w.maxlines) || + (w.maxsize > 0 && w.maxsize_cursize >= w.maxsize) || + (w.daily && now.Day() != w.daily_opendate) { + if err := w.intRotate(); err != nil { + fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err) + return + } + } + + // Perform the write + n, err := fmt.Fprint(w.file, FormatLogRecord(w.format, rec)) + if err != nil { + fmt.Fprintf(os.Stderr, "FileLogWriter(%q): %s\n", w.filename, err) + return + } + + // Update the counts + w.maxlines_curlines++ + w.maxsize_cursize += n + } + } + }() + + return w +} + +// Request that the logs rotate +func (w *FileLogWriter) Rotate() { + w.rot <- true +} + +// If this is called in a threaded context, it MUST be synchronized +func (w *FileLogWriter) intRotate() error { + // Close any log file that may be open + if w.file != nil { + fmt.Fprint(w.file, FormatLogRecord(w.trailer, &LogRecord{Created: time.Now()})) + w.file.Close() + } + + // If we are keeping log files, move it to the next available number + if w.rotate { + _, err := os.Lstat(w.filename) + if err == nil { // file exists + // Find the next available number + num := 1 + fname := "" + if w.daily && time.Now().Day() != w.daily_opendate { + yesterday := time.Now().AddDate(0, 0, -1).Format("2006-01-02") + + for ; err == nil && num <= 999; num++ { + fname = w.filename + fmt.Sprintf(".%s.%03d", yesterday, num) + _, err = os.Lstat(fname) + } + // return error if the last file checked still existed + if err == nil { + return fmt.Errorf("Rotate: Cannot find free log number to rename %s\n", w.filename) + } + } else { + num = w.maxbackup - 1 + for ; num >= 1; num-- { + fname = w.filename + fmt.Sprintf(".%d", num) + nfname := w.filename + fmt.Sprintf(".%d", num+1) + _, err = os.Lstat(fname) + if err == nil { + os.Rename(fname, nfname) + } + } + } + + w.file.Close() + // Rename the file to its newfound home + err = os.Rename(w.filename, fname) + if err != nil { + return fmt.Errorf("Rotate: %s\n", err) + } + } + } + + // Open the log file + fd, err := os.OpenFile(w.filename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0660) + if err != nil { + return err + } + w.file = fd + + now := time.Now() + fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: now})) + + // Set the daily open date to the current date + w.daily_opendate = now.Day() + + // initialize rotation values + w.maxlines_curlines = 0 + w.maxsize_cursize = 0 + + return nil +} + +// Set the logging format (chainable). Must be called before the first log +// message is written. +func (w *FileLogWriter) SetFormat(format string) *FileLogWriter { + w.format = format + return w +} + +// Set the logfile header and footer (chainable). Must be called before the first log +// message is written. These are formatted similar to the FormatLogRecord (e.g. +// you can use %D and %T in your header/footer for date and time). +func (w *FileLogWriter) SetHeadFoot(head, foot string) *FileLogWriter { + w.header, w.trailer = head, foot + if w.maxlines_curlines == 0 { + fmt.Fprint(w.file, FormatLogRecord(w.header, &LogRecord{Created: time.Now()})) + } + return w +} + +// Set rotate at linecount (chainable). Must be called before the first log +// message is written. +func (w *FileLogWriter) SetRotateLines(maxlines int) *FileLogWriter { + //fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateLines: %v\n", maxlines) + w.maxlines = maxlines + return w +} + +// Set rotate at size (chainable). Must be called before the first log message +// is written. +func (w *FileLogWriter) SetRotateSize(maxsize int) *FileLogWriter { + //fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateSize: %v\n", maxsize) + w.maxsize = maxsize + return w +} + +// Set rotate daily (chainable). Must be called before the first log message is +// written. +func (w *FileLogWriter) SetRotateDaily(daily bool) *FileLogWriter { + //fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotateDaily: %v\n", daily) + w.daily = daily + return w +} + +// Set max backup files. Must be called before the first log message +// is written. +func (w *FileLogWriter) SetRotateMaxBackup(maxbackup int) *FileLogWriter { + w.maxbackup = maxbackup + return w +} + +// SetRotate changes whether or not the old logs are kept. (chainable) Must be +// called before the first log message is written. If rotate is false, the +// files are overwritten; otherwise, they are rotated to another file before the +// new log is opened. +func (w *FileLogWriter) SetRotate(rotate bool) *FileLogWriter { + //fmt.Fprintf(os.Stderr, "FileLogWriter.SetRotate: %v\n", rotate) + w.rotate = rotate + return w +} + +// NewXMLLogWriter is a utility method for creating a FileLogWriter set up to +// output XML record log messages instead of line-based ones. +func NewXMLLogWriter(fname string, rotate bool) *FileLogWriter { + return NewFileLogWriter(fname, rotate).SetFormat( + ` <record level="%L"> + <timestamp>%D %T</timestamp> + <source>%S</source> + <message>%M</message> + </record>`).SetHeadFoot("<log created=\"%D %T\">", "</log>") +} |