File: logging.go

package info (click to toggle)
golang-github-juju-cmd 3.0.14-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 424 kB
  • sloc: makefile: 7
file content (152 lines) | stat: -rw-r--r-- 4,430 bytes parent folder | download
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)
}