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