-
Notifications
You must be signed in to change notification settings - Fork 32
/
logging.go
151 lines (136 loc) · 4.33 KB
/
logging.go
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
// 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/v2"
"github.com/juju/loggo/v2/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.
return loggo.ConfigureLoggers(log.Config)
}
// 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)
}