forked from sandia-minimega/minimega
/
log_cli.go
328 lines (279 loc) · 7.35 KB
/
log_cli.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
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
// Copyright (2012) Sandia Corporation.
// Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
// the U.S. Government retains certain rights in this software.
package main
import (
"errors"
"fmt"
"minicli"
log "minilog"
"os"
"path/filepath"
"strconv"
"strings"
)
var (
// current log level
logLevel log.Level
// file that we are currently logging to
logFile *os.File
logRing *log.Ring
)
var logCLIHandlers = []minicli.Handler{
{ // log level
HelpShort: "set or print the log level",
HelpLong: `
Set the log level to one of [debug,info,warn,error,fatal]. Log levels inherit
lower levels, so setting the level to error will also log fatal, and setting
the mode to debug will log everything.`,
Patterns: []string{
"log level [debug,info,warn,error,fatal]",
},
Call: wrapSimpleCLI(cliLogLevel),
},
{ // log stderr
HelpShort: "enable or disable logging to stderr",
Patterns: []string{
"log stderr [true,false]",
},
Call: wrapSimpleCLI(cliLogStderr),
},
{ // log file
HelpShort: "enable logging to a file",
HelpLong: `
Log to a file. To disable file logging, call "clear log file".`,
Patterns: []string{
"log file [file]",
},
Call: wrapSimpleCLI(cliLogFile),
},
{ // log ring
HelpShort: "enable, disable, or dump log ring",
HelpLong: `
The log ring contains recent log messages, if it is enabled. By default
the ring is not enabled. When enabling it, the user can specify a size. The
larger the size, the more memory the logs will consume. The log ring can be
cleared by re-enabling it with the same (or different) size.
To disable the log ring, call "clear log ring".`,
Patterns: []string{
"log ring [size]",
},
Call: wrapSimpleCLI(cliLogRing),
},
{ // log filter
HelpShort: "filter logging messages",
HelpLong: `
Control what data gets logged based on matching text. For example, to filter
out all logging messages containing the word "foo":
log filter foo`,
Patterns: []string{
"log filter [filter]",
},
Call: wrapSimpleCLI(cliLogFilter),
},
{ // log syslog
HelpShort: "log to syslog",
HelpLong: `
Log to a syslog daemon on the provided network and address. For example, to log
over UDP to a syslog server foo on port 514:
log syslog udp foo:514`,
Patterns: []string{
"log syslog remote <tcp,udp> <address>",
"log syslog <local,>",
},
Call: wrapSimpleCLI(cliLogSyslog),
},
{ // clear log
HelpShort: "reset state for logging",
HelpLong: `
Resets state for logging. See "help log ..." for more information.`,
Patterns: []string{
"clear log",
"clear log <file,>",
"clear log <level,>",
"clear log <stderr,>",
"clear log <filter,>",
"clear log <syslog,>",
"clear log <ring,>",
},
Call: wrapSimpleCLI(cliLogClear),
},
}
func cliLogLevel(ns *Namespace, c *minicli.Command, resp *minicli.Response) error {
if len(c.BoolArgs) == 0 {
// Print the level
resp.Response = logLevel.String()
return nil
}
// Bool args should only have a single key that is the log level
for k := range c.BoolArgs {
level, _ := log.ParseLevel(k)
logLevel = level
log.SetLevelAll(level)
}
return nil
}
func cliLogStderr(ns *Namespace, c *minicli.Command, resp *minicli.Response) error {
if c.BoolArgs["false"] {
// Turn off logging to stderr
log.DelLogger("stderr")
} else if len(c.BoolArgs) == 0 {
// Print true or false depending on whether stderr is enabled
_, err := log.GetLevel("stderr")
resp.Response = strconv.FormatBool(err == nil)
} else if c.BoolArgs["true"] {
// Enable stderr logging if not already enabled
if _, err := log.GetLevel("stderr"); err != nil {
log.AddLogger("stderr", os.Stderr, logLevel, true)
}
}
return nil
}
func cliLogFile(ns *Namespace, c *minicli.Command, resp *minicli.Response) error {
if len(c.StringArgs) == 0 {
// Print true or false depending on whether file is enabled
if logFile != nil {
resp.Response = logFile.Name()
}
return nil
}
// Enable logging to file if it's not already enabled
if logFile != nil {
if err := stopFileLogger(); err != nil {
return err
}
}
err := os.MkdirAll(filepath.Dir(c.StringArgs["file"]), 0755)
if err != nil {
return err
}
flags := os.O_WRONLY | os.O_APPEND | os.O_CREATE
logFile, err = os.OpenFile(c.StringArgs["file"], flags, 0660)
if err != nil {
return err
}
log.AddLogger("file", logFile, logLevel, false)
return nil
}
func cliLogRing(ns *Namespace, c *minicli.Command, resp *minicli.Response) error {
if c.StringArgs["size"] == "" {
// must want a log dump
if logRing == nil {
return errors.New("cannot dump log ring, not enabled")
}
resp.Response = strings.Join(logRing.Dump(), "")
return nil
}
// make sure they passed a valid size
size, err := strconv.Atoi(c.StringArgs["size"])
if err != nil {
return err
}
if logRing != nil {
log.Info("re-enabling log ring")
log.DelLogger("ring")
}
logRing = log.NewRing(size)
log.AddLogRing("ring", logRing, logLevel)
return nil
}
func cliLogSyslog(ns *Namespace, c *minicli.Command, resp *minicli.Response) error {
var network string
var address string
if c.BoolArgs["local"] {
network = "local"
} else {
address = c.StringArgs["address"]
if c.BoolArgs["tcp"] {
network = "tcp"
} else {
network = "udp"
}
}
return log.AddSyslog(network, address, "minimega", logLevel)
}
func cliLogFilter(ns *Namespace, c *minicli.Command, resp *minicli.Response) error {
if len(c.StringArgs) == 0 {
var filters []string
loggers := log.Loggers()
for _, l := range loggers {
filt, _ := log.Filters(l)
for _, f := range filt {
var found bool
for _, v := range filters {
if v == f {
found = true
}
}
if !found {
filters = append(filters, f)
}
}
}
if len(filters) != 0 {
resp.Response = fmt.Sprintf("%v", filters)
}
return nil
}
filter := c.StringArgs["filter"]
for _, l := range log.Loggers() {
err := log.AddFilter(l, filter)
if err != nil {
return err
}
}
return nil
}
func cliLogClear(ns *Namespace, c *minicli.Command, resp *minicli.Response) error {
// Reset file if explicitly cleared or we're clearing everything
if c.BoolArgs["file"] || len(c.BoolArgs) == 0 {
if err := stopFileLogger(); err != nil {
return err
}
}
// Reset syslog if explicitly cleared or we're clearing everything
if c.BoolArgs["syslog"] || len(c.BoolArgs) == 0 {
log.DelLogger("syslog")
}
// Reset level if explicitly cleared or we're clearing everything
if c.BoolArgs["level"] || len(c.BoolArgs) == 0 {
// Reset to level from command line flags
logLevel = log.LevelFlag
log.SetLevelAll(logLevel)
}
// Reset stderr if explicitly cleared or we're clearing everything
if c.BoolArgs["stderr"] || len(c.BoolArgs) == 0 {
// Delete logger to stdout
log.DelLogger("stderr")
}
// Reset log ring if explicitly cleared or we're clearing everything
if c.BoolArgs["ring"] || len(c.BoolArgs) == 0 {
log.DelLogger("ring")
logRing = nil
}
if c.BoolArgs["filter"] || len(c.BoolArgs) == 0 {
loggers := log.Loggers()
for _, l := range loggers {
filt, _ := log.Filters(l)
for _, f := range filt {
log.DelFilter(l, f)
}
}
}
return nil
}
// stopFileLogger gets rid of the previous file logger
func stopFileLogger() error {
log.DelLogger("file")
// no op
if logFile == nil {
return nil
}
err := logFile.Close()
if err != nil {
log.Error("error closing log file: %v", err)
} else {
logFile = nil
}
return err
}