From 8b2c7901ee2512c4adfd33ee59be5c3b787c05e7 Mon Sep 17 00:00:00 2001 From: Ola Bini Date: Thu, 26 Mar 2020 14:20:13 +0000 Subject: [PATCH] Use MultiWriter to simplify the writing implementation. Also make it possible to initialize the log target to variable amounts of writers, and doesn't hardcode the use of StdErr as output --- cmd/grumble/grumble.go | 2 +- pkg/logtarget/logtarget.go | 62 ++++++++++++++++++++++---------------- 2 files changed, 37 insertions(+), 27 deletions(-) diff --git a/cmd/grumble/grumble.go b/cmd/grumble/grumble.go index 04f87c7..139eadc 100644 --- a/cmd/grumble/grumble.go +++ b/cmd/grumble/grumble.go @@ -37,7 +37,7 @@ func main() { dataDir.Close() // Set up logging - logtarget.Default, err = logtarget.OpenFile(Args.LogPath) + logtarget.Default, err = logtarget.OpenFile(Args.LogPath, os.Stderr) if err != nil { fmt.Fprintf(os.Stderr, "Unable to open log file (%v): %v", Args.LogPath, err) return diff --git a/pkg/logtarget/logtarget.go b/pkg/logtarget/logtarget.go index 5cf99da..1b69a3b 100644 --- a/pkg/logtarget/logtarget.go +++ b/pkg/logtarget/logtarget.go @@ -21,57 +21,66 @@ type LogTarget interface { Rotate() error } -type fileLogTarget struct { +// logTarget is the default implementation of a log +// target. It can write to more than one writer at the same time +// but only rotate one file +type logTarget struct { mu sync.Mutex logfn string file *os.File + w io.Writer + ws []io.Writer } +// Default is the default log target for the application +// It has to be initialized before used var Default LogTarget +// OpenWriters returns a log target that will +// log to all the given writers at the same time +func OpenWriters(ws ...io.Writer) LogTarget { + target := &logTarget{} + target.w = io.MultiWriter(ws...) + return target +} + // OpenFile creates a LogTarget pointing to a log file // and returns it. // This method will open the file in append-only mode. -func OpenFile(fileName string) (t LogTarget, err error) { - target := &fileLogTarget{} +// It also takes a variable number of writers that are +// other log targets +func OpenFile(fileName string, ws ...io.Writer) (t LogTarget, err error) { + target := &logTarget{} target.logfn = fileName target.file, err = os.OpenFile(fileName, os.O_CREATE|os.O_WRONLY|os.O_APPEND, 0650) if err != nil { return nil, err } + target.ws = ws + target.w = io.MultiWriter(append(ws, target.file)...) return target, nil } // Write writes a log message to all registered io.Writers -func (target *fileLogTarget) Write(in []byte) (int, error) { +func (target *logTarget) Write(out []byte) (int, error) { + target.mu.Lock() + defer target.mu.Unlock() + + return target.Write(out) +} + +// Rotate rotates the current log file, if one is opened. +// This method holds a lock while rotating the log file, +// and all log writes will be held back until the rotation +// is complete. +func (target *logTarget) Rotate() error { target.mu.Lock() defer target.mu.Unlock() if target.file == nil { - panic("no log file opened") + return nil } - n, err := os.Stderr.Write(in) - if err != nil { - return n, err - } - - n, err = target.file.Write(in) - if err != nil { - return n, err - } - - return len(in), nil -} - -// Rotate rotates the current log file. -// This method holds a lock while rotating the log file, -// and all log writes will be held back until the rotation -// is complete. -func (target *fileLogTarget) Rotate() error { - target.mu.Lock() - defer target.mu.Unlock() - // Close the existing log file err := target.file.Close() if err != nil { @@ -82,6 +91,7 @@ func (target *fileLogTarget) Rotate() error { if err != nil { return err } + target.w = io.MultiWriter(append(target.ws, target.file)...) return nil }