1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152
|
// Copyright 2012, 2013 Canonical Ltd.
// Licensed under the LGPLv3, see LICENSE file for details.
package cmd
import (
"fmt"
"io"
"os"
"github.com/juju/ansiterm"
"github.com/juju/gnuflag"
"github.com/juju/loggo"
"github.com/juju/loggo/loggocolor"
)
// Log supplies the necessary functionality for Commands that wish to set up
// logging.
type Log struct {
// If DefaultConfig is set, it will be used for the
// default logging configuration.
DefaultConfig string
Path string
Verbose bool
Quiet bool
Debug bool
ShowLog bool
Config string
// NewWriter creates a new logging writer for a specified target.
NewWriter func(target io.Writer) loggo.Writer
}
// GetLogWriter returns a logging writer for the specified target.
func (l *Log) GetLogWriter(target io.Writer) loggo.Writer {
if l.NewWriter != nil {
return l.NewWriter(target)
}
return loggocolor.NewWriter(target)
}
// AddFlags adds appropriate flags to f.
func (l *Log) AddFlags(f *gnuflag.FlagSet) {
f.StringVar(&l.Path, "log-file", "", "path to write log to")
f.BoolVar(&l.Verbose, "v", false, "Show more verbose output")
f.BoolVar(&l.Verbose, "verbose", false, "Show more verbose output")
f.BoolVar(&l.Quiet, "q", false, "Show no informational output")
f.BoolVar(&l.Quiet, "quiet", false, "Show no informational output")
f.BoolVar(&l.Debug, "debug", false, "Equivalent to --show-log --logging-config=<root>=DEBUG")
f.StringVar(&l.Config, "logging-config", l.DefaultConfig, "Specify log levels for modules")
f.BoolVar(&l.ShowLog, "show-log", false, "If set, write the log file to stderr")
}
// Start starts logging using the given Context.
func (log *Log) Start(ctx *Context) error {
if log.Verbose && log.Quiet {
return fmt.Errorf(`"verbose" and "quiet" flags clash, please use one or the other, not both`)
}
ctx.quiet = log.Quiet
ctx.verbose = log.Verbose
if log.Path != "" {
path := ctx.AbsPath(log.Path)
target, err := os.OpenFile(path, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0644)
if err != nil {
return err
}
writer := log.GetLogWriter(target)
err = loggo.RegisterWriter("logfile", writer)
if err != nil {
return err
}
}
level := loggo.WARNING
if log.ShowLog {
level = loggo.INFO
}
if log.Debug {
log.ShowLog = true
level = loggo.DEBUG
// override quiet or verbose if set, this way all the information goes
// to the log file.
ctx.quiet = true
ctx.verbose = false
}
if log.ShowLog {
// We replace the default writer to use ctx.Stderr rather than os.Stderr.
writer := log.GetLogWriter(ctx.Stderr)
_, err := loggo.ReplaceDefaultWriter(writer)
if err != nil {
return err
}
} else {
_, _ = loggo.RemoveWriter("default")
// Create a simple writer that doesn't show filenames, or timestamps,
// and only shows warning or above.
writer := NewWarningWriter(ctx.Stderr)
err := loggo.RegisterWriter("warning", writer)
if err != nil {
return err
}
}
// Set the level on the root logger.
root := loggo.GetLogger("")
root.SetLogLevel(level)
// Override the logging config with specified logging config.
loggo.ConfigureLoggers(log.Config)
return nil
}
// NewCommandLogWriter creates a loggo writer for registration
// by the callers of a command. This way the logged output can also
// be displayed otherwise, e.g. on the screen.
func NewCommandLogWriter(name string, out, err io.Writer) loggo.Writer {
return &commandLogWriter{name, out, err}
}
// commandLogWriter filters the log messages for name.
type commandLogWriter struct {
name string
out io.Writer
err io.Writer
}
// Write implements loggo's Writer interface.
func (s *commandLogWriter) Write(entry loggo.Entry) {
if entry.Module == s.name {
if entry.Level <= loggo.INFO {
fmt.Fprintf(s.out, "%s\n", entry.Message)
} else {
fmt.Fprintf(s.err, "%s\n", entry.Message)
}
}
}
type warningWriter struct {
writer *ansiterm.Writer
}
// NewWarningWriter will write out colored severity levels if the writer is
// outputting to a terminal.
func NewWarningWriter(writer io.Writer) loggo.Writer {
w := &warningWriter{ansiterm.NewWriter(writer)}
return loggo.NewMinimumLevelWriter(w, loggo.WARNING)
}
// Write implements Writer.
// WARNING The message...
func (w *warningWriter) Write(entry loggo.Entry) {
loggocolor.SeverityColor[entry.Level].Fprintf(w.writer, entry.Level.String())
fmt.Fprintf(w.writer, " %s\n", entry.Message)
}
|