mirror of https://github.com/docker/docs.git
bugsnag integration
Signed-off-by: Jean-Laurent de Morlhon <jeanlaurent@morlhon.net>
This commit is contained in:
parent
2051e6eeae
commit
4f0c24483b
|
|
@ -88,6 +88,7 @@ func main() {
|
|||
return
|
||||
}
|
||||
|
||||
log.Logger = log.NewLogrusMachineLogger()
|
||||
setDebugOutputLevel()
|
||||
cli.AppHelpTemplate = AppHelpTemplate
|
||||
cli.CommandHelpTemplate = CommandHelpTemplate
|
||||
|
|
@ -149,6 +150,12 @@ func main() {
|
|||
Name: "native-ssh",
|
||||
Usage: "Use the native (Go-based) SSH implementation.",
|
||||
},
|
||||
cli.StringFlag{
|
||||
EnvVar: "MACHINE_BUGSNAG_API_TOKEN",
|
||||
Name: "bugsnag-api-token",
|
||||
Usage: "BugSnag API token for crash reporting",
|
||||
Value: "",
|
||||
},
|
||||
}
|
||||
|
||||
if err := app.Run(os.Args); err != nil {
|
||||
|
|
|
|||
|
|
@ -11,6 +11,7 @@ import (
|
|||
"github.com/docker/machine/commands/mcndirs"
|
||||
"github.com/docker/machine/libmachine"
|
||||
"github.com/docker/machine/libmachine/cert"
|
||||
"github.com/docker/machine/libmachine/crashreport"
|
||||
"github.com/docker/machine/libmachine/host"
|
||||
"github.com/docker/machine/libmachine/log"
|
||||
"github.com/docker/machine/libmachine/mcnutils"
|
||||
|
|
@ -102,6 +103,8 @@ func fatalOnError(command func(commandLine CommandLine, api libmachine.API) erro
|
|||
api.GithubAPIToken = context.GlobalString("github-api-token")
|
||||
api.Filestore.Path = context.GlobalString("storage-path")
|
||||
|
||||
crashreport.Configure(context.GlobalString("bugsnag-api-token"))
|
||||
|
||||
// TODO (nathanleclaire): These should ultimately be accessed
|
||||
// through the libmachine client by the rest of the code and
|
||||
// not through their respective modules. For now, however,
|
||||
|
|
|
|||
|
|
@ -139,3 +139,21 @@ Make sure to specify the machine name as an argument:
|
|||
|
||||
$ docker-machine stop dev
|
||||
$ docker-machine start dev
|
||||
|
||||
# Crash Reporting
|
||||
|
||||
Provisioning a host is a complex matter that can fail for a lot of reasons.
|
||||
Some of those reasons lies in your very workstation that can have a wide
|
||||
variety of shell, network configuration, vpn, proxy and firewalls or from reasons
|
||||
on the other end of the chain: your cloud provider or the network in between.
|
||||
|
||||
To help `docker-machine` be as stable as possible, we added a monitoring of crashes
|
||||
whenever you try to `create` or `upgrade` a host. This will send over https to bugsnag
|
||||
a couple of information : your docker-machine version, build, OS, ARCH, the path to your
|
||||
current shell and the history of the last command as you could see it with a `-D` option.
|
||||
Those data are only there to help us pinpoint recurring issue with docker-machine and will only
|
||||
be transmitted in the case of a crash of docker-machine.
|
||||
|
||||
If you're worried about thatm you can create a `no-error-report` in the `$HOME/.docker/machine`
|
||||
directory, and we will not gather nor send any data.
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,111 @@
|
|||
package crashreport
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"runtime"
|
||||
|
||||
"bytes"
|
||||
|
||||
"os/exec"
|
||||
|
||||
"path/filepath"
|
||||
|
||||
"errors"
|
||||
|
||||
"github.com/bugsnag/bugsnag-go"
|
||||
"github.com/docker/machine/commands/mcndirs"
|
||||
"github.com/docker/machine/libmachine/log"
|
||||
"github.com/docker/machine/version"
|
||||
)
|
||||
|
||||
// We bundle a bugnsag key, but we disable its usage for now
|
||||
// this ease testing just set useDefaultKey to true
|
||||
// and yet we bundle the code without activating it by default.
|
||||
var useDefaultKey = false
|
||||
|
||||
const defaultAPIKey = "a9697f9a010c33ee218a65e5b1f3b0c1"
|
||||
|
||||
var apiKey string
|
||||
|
||||
// Configure the apikey for bugnag
|
||||
func Configure(key string) {
|
||||
if key != "" {
|
||||
apiKey = key
|
||||
return
|
||||
}
|
||||
if useDefaultKey {
|
||||
apiKey = defaultAPIKey
|
||||
}
|
||||
}
|
||||
|
||||
// Send through http the crash report to bugsnag need a call to Configure(apiKey) before
|
||||
func Send(error error) error {
|
||||
if noReportFileExist() {
|
||||
err := errors.New("Not sending report since the optout file exist.")
|
||||
log.Debug(err)
|
||||
return err
|
||||
}
|
||||
|
||||
if apiKey == "" {
|
||||
err := errors.New("Not sending report since no api key has been set.")
|
||||
log.Debug(err)
|
||||
return err
|
||||
}
|
||||
|
||||
bugsnag.Configure(bugsnag.Configuration{
|
||||
APIKey: apiKey,
|
||||
// XXX we need to abuse bugsnag metrics to get the OS/ARCH information as a usable filter
|
||||
// Can do that with either "stage" or "hostname"
|
||||
ReleaseStage: fmt.Sprintf("%s (%s)", runtime.GOOS, runtime.GOARCH),
|
||||
ProjectPackages: []string{"github.com/docker/machine/[^v]*"},
|
||||
AppVersion: version.FullVersion(),
|
||||
Synchronous: true,
|
||||
PanicHandler: func() {},
|
||||
Logger: new(logger),
|
||||
})
|
||||
|
||||
metaData := bugsnag.MetaData{}
|
||||
|
||||
metaData.Add("app", "compiler", fmt.Sprintf("%s (%s)", runtime.Compiler, runtime.Version()))
|
||||
metaData.Add("device", "os", runtime.GOOS)
|
||||
metaData.Add("device", "arch", runtime.GOARCH)
|
||||
|
||||
detectRunningShell(&metaData)
|
||||
detectUname(&metaData)
|
||||
|
||||
var buffer bytes.Buffer
|
||||
for _, message := range log.History() {
|
||||
buffer.WriteString(message + "\n")
|
||||
}
|
||||
metaData.Add("history", "trace", buffer.String())
|
||||
return bugsnag.Notify(error, metaData)
|
||||
}
|
||||
|
||||
func noReportFileExist() bool {
|
||||
optOutFilePath := filepath.Join(mcndirs.GetBaseDir(), "no-error-report")
|
||||
if _, err := os.Stat(optOutFilePath); os.IsNotExist(err) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func detectRunningShell(metaData *bugsnag.MetaData) {
|
||||
shell := os.Getenv("SHELL")
|
||||
if shell != "" {
|
||||
metaData.Add("device", "shell", shell)
|
||||
}
|
||||
shell = os.Getenv("__fish_bin_dir")
|
||||
if shell != "" {
|
||||
metaData.Add("device", "shell", shell)
|
||||
}
|
||||
}
|
||||
|
||||
func detectUname(metaData *bugsnag.MetaData) {
|
||||
cmd := exec.Command("uname", "-s")
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
metaData.Add("device", "uname", string(output))
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package crashreport
|
||||
|
||||
import "github.com/docker/machine/libmachine/log"
|
||||
|
||||
type logger struct{}
|
||||
|
||||
func (d *logger) Printf(fmtString string, args ...interface{}) {
|
||||
log.Debugf(fmtString, args)
|
||||
}
|
||||
|
|
@ -23,8 +23,8 @@ var (
|
|||
)
|
||||
|
||||
const (
|
||||
pluginOutPrefix = "(%s) "
|
||||
pluginErrPrefix = "(%s) DBG | "
|
||||
pluginOut = "(%s) %s"
|
||||
pluginErr = "(%s) DBG | %s"
|
||||
PluginEnvKey = "MACHINE_PLUGIN_TOKEN"
|
||||
PluginEnvVal = "42"
|
||||
PluginEnvDriverName = "MACHINE_PLUGIN_DRIVER_NAME"
|
||||
|
|
@ -210,9 +210,9 @@ func (lbp *Plugin) execServer() error {
|
|||
for {
|
||||
select {
|
||||
case out := <-stdOutCh:
|
||||
log.Info(fmt.Sprintf(pluginOutPrefix, lbp.MachineName), out)
|
||||
log.Infof(pluginOut, lbp.MachineName, out)
|
||||
case err := <-stdErrCh:
|
||||
log.Debug(fmt.Sprintf(pluginErrPrefix, lbp.MachineName), err)
|
||||
log.Debugf(pluginErr, lbp.MachineName, err)
|
||||
case _ = <-lbp.stopCh:
|
||||
stopStdoutCh <- true
|
||||
stopStderrCh <- true
|
||||
|
|
|
|||
|
|
@ -78,11 +78,9 @@ func TestLocalBinaryPluginClose(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestExecServer(t *testing.T) {
|
||||
log.SetDebug(true)
|
||||
machineName := "test"
|
||||
|
||||
logReader, logWriter := io.Pipe()
|
||||
|
||||
log.SetDebug(true)
|
||||
log.SetOutput(logWriter)
|
||||
|
||||
defer func() {
|
||||
|
|
@ -98,6 +96,7 @@ func TestExecServer(t *testing.T) {
|
|||
stderr: stderrReader,
|
||||
}
|
||||
|
||||
machineName := "test"
|
||||
lbp := &Plugin{
|
||||
MachineName: machineName,
|
||||
Executor: fe,
|
||||
|
|
@ -112,12 +111,10 @@ func TestExecServer(t *testing.T) {
|
|||
finalErr <- lbp.execServer()
|
||||
}()
|
||||
|
||||
expectedAddr := "127.0.0.1:12345"
|
||||
expectedPluginOut := "Doing some fun plugin stuff..."
|
||||
expectedPluginErr := "Uh oh, something in plugin went wrong..."
|
||||
|
||||
logScanner := bufio.NewScanner(logReader)
|
||||
|
||||
// Write the ip address
|
||||
expectedAddr := "127.0.0.1:12345"
|
||||
if _, err := io.WriteString(stdoutWriter, expectedAddr+"\n"); err != nil {
|
||||
t.Fatalf("Error attempting to write plugin address: %s", err)
|
||||
}
|
||||
|
|
@ -126,22 +123,24 @@ func TestExecServer(t *testing.T) {
|
|||
t.Fatalf("Expected to read the expected address properly in server but did not")
|
||||
}
|
||||
|
||||
expectedOut := fmt.Sprintf("%s%s", fmt.Sprintf(pluginOutPrefix, machineName), expectedPluginOut)
|
||||
|
||||
// Write a log in stdout
|
||||
expectedPluginOut := "Doing some fun plugin stuff..."
|
||||
if _, err := io.WriteString(stdoutWriter, expectedPluginOut+"\n"); err != nil {
|
||||
t.Fatalf("Error attempting to write to out in plugin: %s", err)
|
||||
}
|
||||
|
||||
expectedOut := fmt.Sprintf(pluginOut, machineName, expectedPluginOut)
|
||||
if logScanner.Scan(); logScanner.Text() != expectedOut {
|
||||
t.Fatalf("Output written to log was not what we expected\nexpected: %s\nactual: %s", expectedOut, logScanner.Text())
|
||||
}
|
||||
|
||||
expectedErr := fmt.Sprintf("%s%s", fmt.Sprintf(pluginErrPrefix, machineName), expectedPluginErr)
|
||||
|
||||
// Write a log in stderr
|
||||
expectedPluginErr := "Uh oh, something in plugin went wrong..."
|
||||
if _, err := io.WriteString(stderrWriter, expectedPluginErr+"\n"); err != nil {
|
||||
t.Fatalf("Error attempting to write to err in plugin: %s", err)
|
||||
}
|
||||
|
||||
expectedErr := fmt.Sprintf(pluginErr, machineName, expectedPluginErr)
|
||||
if logScanner.Scan(); logScanner.Text() != expectedErr {
|
||||
t.Fatalf("Error written to log was not what we expected\nexpected: %s\nactual: %s", expectedErr, logScanner.Text())
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"strings"
|
||||
|
||||
"github.com/docker/machine/libmachine/auth"
|
||||
"github.com/docker/machine/libmachine/crashreport"
|
||||
"github.com/docker/machine/libmachine/drivers"
|
||||
"github.com/docker/machine/libmachine/engine"
|
||||
"github.com/docker/machine/libmachine/log"
|
||||
|
|
@ -134,11 +135,13 @@ func (h *Host) Upgrade() error {
|
|||
|
||||
provisioner, err := provision.DetectProvisioner(h.Driver)
|
||||
if err != nil {
|
||||
crashreport.Send(err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Info("Upgrading docker...")
|
||||
if err := provisioner.Package("docker", pkgaction.Upgrade); err != nil {
|
||||
crashreport.Send(err)
|
||||
return err
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import (
|
|||
"github.com/docker/machine/libmachine/auth"
|
||||
"github.com/docker/machine/libmachine/cert"
|
||||
"github.com/docker/machine/libmachine/check"
|
||||
"github.com/docker/machine/libmachine/crashreport"
|
||||
"github.com/docker/machine/libmachine/drivers"
|
||||
"github.com/docker/machine/libmachine/engine"
|
||||
"github.com/docker/machine/libmachine/host"
|
||||
|
|
@ -96,6 +97,18 @@ func (api *Client) Create(h *host.Host) error {
|
|||
|
||||
log.Info("Creating machine...")
|
||||
|
||||
if err := api.performCreate(h); err != nil {
|
||||
crashreport.Send(err)
|
||||
return err
|
||||
}
|
||||
|
||||
log.Debug("Reticulating splines...")
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (api *Client) performCreate(h *host.Host) error {
|
||||
|
||||
if err := h.Driver.Create(); err != nil {
|
||||
return fmt.Errorf("Error in driver during machine creation: %s", err)
|
||||
}
|
||||
|
|
@ -136,7 +149,6 @@ func (api *Client) Create(h *host.Host) error {
|
|||
log.Info("Docker is up and running!")
|
||||
}
|
||||
|
||||
log.Debug("Reticulating splines...")
|
||||
|
||||
return nil
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,83 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
type FmtMachineLogger struct {
|
||||
out io.Writer
|
||||
err io.Writer
|
||||
debug bool
|
||||
}
|
||||
|
||||
// NewFmtMachineLogger creates a MachineLogger implementation used by the drivers
|
||||
func NewFmtMachineLogger() MachineLogger {
|
||||
return &FmtMachineLogger{
|
||||
out: os.Stdout,
|
||||
err: os.Stderr,
|
||||
debug: false,
|
||||
}
|
||||
}
|
||||
|
||||
func (ml *FmtMachineLogger) RedirectStdOutToStdErr() {
|
||||
ml.out = ml.err
|
||||
}
|
||||
|
||||
func (ml *FmtMachineLogger) SetDebug(debug bool) {
|
||||
ml.debug = debug
|
||||
}
|
||||
|
||||
func (ml *FmtMachineLogger) SetOutput(out io.Writer) {
|
||||
ml.out = out
|
||||
ml.err = out
|
||||
}
|
||||
|
||||
func (ml *FmtMachineLogger) Debug(args ...interface{}) {
|
||||
if ml.debug {
|
||||
fmt.Fprintln(ml.err, args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (ml *FmtMachineLogger) Debugf(fmtString string, args ...interface{}) {
|
||||
if ml.debug {
|
||||
fmt.Fprintf(ml.err, fmtString+"\n", args...)
|
||||
}
|
||||
}
|
||||
|
||||
func (ml *FmtMachineLogger) Error(args ...interface{}) {
|
||||
fmt.Fprintln(ml.out, args...)
|
||||
}
|
||||
|
||||
func (ml *FmtMachineLogger) Errorf(fmtString string, args ...interface{}) {
|
||||
fmt.Fprintf(ml.out, fmtString+"\n", args...)
|
||||
}
|
||||
|
||||
func (ml *FmtMachineLogger) Info(args ...interface{}) {
|
||||
fmt.Fprintln(ml.out, args...)
|
||||
}
|
||||
|
||||
func (ml *FmtMachineLogger) Infof(fmtString string, args ...interface{}) {
|
||||
fmt.Fprintf(ml.out, fmtString+"\n", args...)
|
||||
}
|
||||
|
||||
func (ml *FmtMachineLogger) Fatal(args ...interface{}) {
|
||||
fmt.Fprintln(ml.out, args...)
|
||||
}
|
||||
|
||||
func (ml *FmtMachineLogger) Fatalf(fmtString string, args ...interface{}) {
|
||||
fmt.Fprintf(ml.out, fmtString+"\n", args...)
|
||||
}
|
||||
|
||||
func (ml *FmtMachineLogger) Warn(args ...interface{}) {
|
||||
fmt.Fprintln(ml.out, args...)
|
||||
}
|
||||
|
||||
func (ml *FmtMachineLogger) Warnf(fmtString string, args ...interface{}) {
|
||||
fmt.Fprintf(ml.out, fmtString+"\n", args...)
|
||||
}
|
||||
|
||||
func (ml *FmtMachineLogger) History() []string {
|
||||
return []string{}
|
||||
}
|
||||
|
|
@ -0,0 +1,40 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestFmtDebug(t *testing.T) {
|
||||
testLogger := NewFmtMachineLogger()
|
||||
testLogger.SetDebug(true)
|
||||
|
||||
result := captureOutput(testLogger, func() { testLogger.Debug("debug") })
|
||||
|
||||
assert.Equal(t, result, "debug")
|
||||
}
|
||||
|
||||
func TestFmtInfo(t *testing.T) {
|
||||
testLogger := NewFmtMachineLogger()
|
||||
|
||||
result := captureOutput(testLogger, func() { testLogger.Info("info") })
|
||||
|
||||
assert.Equal(t, result, "info")
|
||||
}
|
||||
|
||||
func TestFmtWarn(t *testing.T) {
|
||||
testLogger := NewFmtMachineLogger()
|
||||
|
||||
result := captureOutput(testLogger, func() { testLogger.Warn("warn") })
|
||||
|
||||
assert.Equal(t, result, "warn")
|
||||
}
|
||||
|
||||
func TestFmtError(t *testing.T) {
|
||||
testLogger := NewFmtMachineLogger()
|
||||
|
||||
result := captureOutput(testLogger, func() { testLogger.Error("error") })
|
||||
|
||||
assert.Equal(t, result, "error")
|
||||
}
|
||||
|
|
@ -2,65 +2,65 @@ package log
|
|||
|
||||
import "io"
|
||||
|
||||
var logger MachineLogger
|
||||
var Logger MachineLogger
|
||||
|
||||
func init() {
|
||||
logger = NewMachineLogger()
|
||||
Logger = NewFmtMachineLogger()
|
||||
}
|
||||
|
||||
// RedirectStdOutToStdErr prevents any log from corrupting the output
|
||||
func RedirectStdOutToStdErr() {
|
||||
logger.RedirectStdOutToStdErr()
|
||||
Logger.RedirectStdOutToStdErr()
|
||||
}
|
||||
|
||||
func Debug(args ...interface{}) {
|
||||
logger.Debug(args...)
|
||||
Logger.Debug(args...)
|
||||
}
|
||||
|
||||
func Debugf(fmtString string, args ...interface{}) {
|
||||
logger.Debugf(fmtString, args...)
|
||||
Logger.Debugf(fmtString, args...)
|
||||
}
|
||||
|
||||
func Error(args ...interface{}) {
|
||||
logger.Error(args...)
|
||||
Logger.Error(args...)
|
||||
}
|
||||
|
||||
func Errorf(fmtString string, args ...interface{}) {
|
||||
logger.Errorf(fmtString, args...)
|
||||
Logger.Errorf(fmtString, args...)
|
||||
}
|
||||
|
||||
func Info(args ...interface{}) {
|
||||
logger.Info(args...)
|
||||
Logger.Info(args...)
|
||||
}
|
||||
|
||||
func Infof(fmtString string, args ...interface{}) {
|
||||
logger.Infof(fmtString, args...)
|
||||
Logger.Infof(fmtString, args...)
|
||||
}
|
||||
|
||||
func Fatal(args ...interface{}) {
|
||||
logger.Fatal(args...)
|
||||
Logger.Fatal(args...)
|
||||
}
|
||||
|
||||
func Fatalf(fmtString string, args ...interface{}) {
|
||||
logger.Fatalf(fmtString, args...)
|
||||
Logger.Fatalf(fmtString, args...)
|
||||
}
|
||||
|
||||
func Warn(args ...interface{}) {
|
||||
logger.Warn(args...)
|
||||
Logger.Warn(args...)
|
||||
}
|
||||
|
||||
func Warnf(fmtString string, args ...interface{}) {
|
||||
logger.Warnf(fmtString, args...)
|
||||
}
|
||||
|
||||
func Logger() interface{} {
|
||||
return logger
|
||||
Logger.Warnf(fmtString, args...)
|
||||
}
|
||||
|
||||
func SetDebug(debug bool) {
|
||||
logger.SetDebug(debug)
|
||||
Logger.SetDebug(debug)
|
||||
}
|
||||
|
||||
func SetOutput(out io.Writer) {
|
||||
logger.SetOutput(out)
|
||||
Logger.SetOutput(out)
|
||||
}
|
||||
|
||||
func History() []string {
|
||||
return Logger.History()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,9 +0,0 @@
|
|||
package log
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestFormatterIsSet(t *testing.T) {
|
||||
|
||||
}
|
||||
|
|
@ -3,26 +3,33 @@ package log
|
|||
import (
|
||||
"io"
|
||||
|
||||
"fmt"
|
||||
|
||||
"sync"
|
||||
|
||||
"github.com/Sirupsen/logrus"
|
||||
)
|
||||
|
||||
type LogrusMachineLogger struct {
|
||||
logger *logrus.Logger
|
||||
history []string
|
||||
historyLock sync.Locker
|
||||
logger *logrus.Logger
|
||||
}
|
||||
|
||||
func NewMachineLogger() MachineLogger {
|
||||
// NewLogrusMachineLogger creates the MachineLogger implementation used by the docker-machine
|
||||
func NewLogrusMachineLogger() MachineLogger {
|
||||
logrusLogger := logrus.New()
|
||||
logrusLogger.Level = logrus.InfoLevel
|
||||
logrusLogger.Formatter = new(MachineFormatter)
|
||||
return LogrusMachineLogger{logrusLogger}
|
||||
return &LogrusMachineLogger{[]string{}, &sync.Mutex{}, logrusLogger}
|
||||
}
|
||||
|
||||
// RedirectStdOutToStdErr prevents any log from corrupting the output
|
||||
func (ml LogrusMachineLogger) RedirectStdOutToStdErr() {
|
||||
func (ml *LogrusMachineLogger) RedirectStdOutToStdErr() {
|
||||
ml.logger.Level = logrus.ErrorLevel
|
||||
}
|
||||
|
||||
func (ml LogrusMachineLogger) SetDebug(debug bool) {
|
||||
func (ml *LogrusMachineLogger) SetDebug(debug bool) {
|
||||
if debug {
|
||||
ml.logger.Level = logrus.DebugLevel
|
||||
} else {
|
||||
|
|
@ -30,50 +37,76 @@ func (ml LogrusMachineLogger) SetDebug(debug bool) {
|
|||
}
|
||||
}
|
||||
|
||||
func (ml LogrusMachineLogger) SetOutput(out io.Writer) {
|
||||
func (ml *LogrusMachineLogger) SetOutput(out io.Writer) {
|
||||
ml.logger.Out = out
|
||||
}
|
||||
|
||||
func (ml LogrusMachineLogger) Logger() interface{} {
|
||||
func (ml *LogrusMachineLogger) Logger() *logrus.Logger {
|
||||
return ml.logger
|
||||
}
|
||||
|
||||
func (ml LogrusMachineLogger) Debug(args ...interface{}) {
|
||||
func (ml *LogrusMachineLogger) Debug(args ...interface{}) {
|
||||
ml.record(args...)
|
||||
ml.logger.Debug(args...)
|
||||
}
|
||||
|
||||
func (ml LogrusMachineLogger) Debugf(fmtString string, args ...interface{}) {
|
||||
func (ml *LogrusMachineLogger) Debugf(fmtString string, args ...interface{}) {
|
||||
ml.recordf(fmtString, args...)
|
||||
ml.logger.Debugf(fmtString, args...)
|
||||
}
|
||||
|
||||
func (ml LogrusMachineLogger) Error(args ...interface{}) {
|
||||
func (ml *LogrusMachineLogger) Error(args ...interface{}) {
|
||||
ml.record(args...)
|
||||
ml.logger.Error(args...)
|
||||
}
|
||||
|
||||
func (ml LogrusMachineLogger) Errorf(fmtString string, args ...interface{}) {
|
||||
func (ml *LogrusMachineLogger) Errorf(fmtString string, args ...interface{}) {
|
||||
ml.recordf(fmtString, args...)
|
||||
ml.logger.Errorf(fmtString, args...)
|
||||
}
|
||||
|
||||
func (ml LogrusMachineLogger) Info(args ...interface{}) {
|
||||
func (ml *LogrusMachineLogger) Info(args ...interface{}) {
|
||||
ml.record(args...)
|
||||
ml.logger.Info(args...)
|
||||
}
|
||||
|
||||
func (ml LogrusMachineLogger) Infof(fmtString string, args ...interface{}) {
|
||||
func (ml *LogrusMachineLogger) Infof(fmtString string, args ...interface{}) {
|
||||
ml.recordf(fmtString, args...)
|
||||
ml.logger.Infof(fmtString, args...)
|
||||
}
|
||||
|
||||
func (ml LogrusMachineLogger) Fatal(args ...interface{}) {
|
||||
func (ml *LogrusMachineLogger) Fatal(args ...interface{}) {
|
||||
ml.record(args...)
|
||||
ml.logger.Fatal(args...)
|
||||
}
|
||||
|
||||
func (ml LogrusMachineLogger) Fatalf(fmtString string, args ...interface{}) {
|
||||
func (ml *LogrusMachineLogger) Fatalf(fmtString string, args ...interface{}) {
|
||||
ml.recordf(fmtString, args...)
|
||||
ml.logger.Fatalf(fmtString, args...)
|
||||
}
|
||||
|
||||
func (ml LogrusMachineLogger) Warn(args ...interface{}) {
|
||||
func (ml *LogrusMachineLogger) Warn(args ...interface{}) {
|
||||
ml.record(args...)
|
||||
ml.logger.Warn(args...)
|
||||
}
|
||||
|
||||
func (ml LogrusMachineLogger) Warnf(fmtString string, args ...interface{}) {
|
||||
func (ml *LogrusMachineLogger) Warnf(fmtString string, args ...interface{}) {
|
||||
ml.recordf(fmtString, args...)
|
||||
ml.logger.Warnf(fmtString, args...)
|
||||
}
|
||||
|
||||
func (ml *LogrusMachineLogger) History() []string {
|
||||
return ml.history
|
||||
}
|
||||
|
||||
func (ml *LogrusMachineLogger) record(args ...interface{}) {
|
||||
ml.historyLock.Lock()
|
||||
defer ml.historyLock.Unlock()
|
||||
ml.history = append(ml.history, fmt.Sprint(args...))
|
||||
}
|
||||
|
||||
func (ml *LogrusMachineLogger) recordf(fmtString string, args ...interface{}) {
|
||||
ml.historyLock.Lock()
|
||||
defer ml.historyLock.Unlock()
|
||||
ml.history = append(ml.history, fmt.Sprintf(fmtString, args...))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,31 +11,31 @@ import (
|
|||
)
|
||||
|
||||
func TestDefaultLevelIsInfo(t *testing.T) {
|
||||
testLogger := NewMachineLogger()
|
||||
assert.Equal(t, testLogger.Logger().(*logrus.Logger).Level, logrus.InfoLevel)
|
||||
testLogger := NewLogrusMachineLogger().(*LogrusMachineLogger)
|
||||
assert.Equal(t, testLogger.Logger().Level, logrus.InfoLevel)
|
||||
}
|
||||
|
||||
func TestSetDebugToTrue(t *testing.T) {
|
||||
testLogger := NewMachineLogger()
|
||||
testLogger := NewLogrusMachineLogger().(*LogrusMachineLogger)
|
||||
testLogger.SetDebug(true)
|
||||
assert.Equal(t, testLogger.Logger().(*logrus.Logger).Level, logrus.DebugLevel)
|
||||
assert.Equal(t, testLogger.Logger().Level, logrus.DebugLevel)
|
||||
}
|
||||
|
||||
func TestSetDebugToFalse(t *testing.T) {
|
||||
testLogger := NewMachineLogger()
|
||||
testLogger := NewLogrusMachineLogger().(*LogrusMachineLogger)
|
||||
testLogger.SetDebug(true)
|
||||
testLogger.SetDebug(false)
|
||||
assert.Equal(t, testLogger.Logger().(*logrus.Logger).Level, logrus.InfoLevel)
|
||||
assert.Equal(t, testLogger.Logger().Level, logrus.InfoLevel)
|
||||
}
|
||||
|
||||
func TestSetSilenceOutput(t *testing.T) {
|
||||
testLogger := NewMachineLogger()
|
||||
testLogger := NewLogrusMachineLogger().(*LogrusMachineLogger)
|
||||
testLogger.RedirectStdOutToStdErr()
|
||||
assert.Equal(t, testLogger.Logger().(*logrus.Logger).Level, logrus.ErrorLevel)
|
||||
assert.Equal(t, testLogger.Logger().Level, logrus.ErrorLevel)
|
||||
}
|
||||
|
||||
func TestDebug(t *testing.T) {
|
||||
testLogger := NewMachineLogger()
|
||||
func TestDebugOutput(t *testing.T) {
|
||||
testLogger := NewLogrusMachineLogger()
|
||||
testLogger.SetDebug(true)
|
||||
|
||||
result := captureOutput(testLogger, func() { testLogger.Debug("debug") })
|
||||
|
|
@ -43,30 +43,42 @@ func TestDebug(t *testing.T) {
|
|||
assert.Equal(t, result, "debug")
|
||||
}
|
||||
|
||||
func TestInfo(t *testing.T) {
|
||||
testLogger := NewMachineLogger()
|
||||
func TestInfoOutput(t *testing.T) {
|
||||
testLogger := NewLogrusMachineLogger()
|
||||
|
||||
result := captureOutput(testLogger, func() { testLogger.Info("info") })
|
||||
|
||||
assert.Equal(t, result, "info")
|
||||
}
|
||||
|
||||
func TestWarn(t *testing.T) {
|
||||
testLogger := NewMachineLogger()
|
||||
func TestWarnOutput(t *testing.T) {
|
||||
testLogger := NewLogrusMachineLogger()
|
||||
|
||||
result := captureOutput(testLogger, func() { testLogger.Warn("warn") })
|
||||
|
||||
assert.Equal(t, result, "warn")
|
||||
}
|
||||
|
||||
func TestError(t *testing.T) {
|
||||
testLogger := NewMachineLogger()
|
||||
func TestErrorOutput(t *testing.T) {
|
||||
testLogger := NewLogrusMachineLogger()
|
||||
|
||||
result := captureOutput(testLogger, func() { testLogger.Error("error") })
|
||||
|
||||
assert.Equal(t, result, "error")
|
||||
}
|
||||
|
||||
func TestEntriesAreCollected(t *testing.T) {
|
||||
testLogger := NewLogrusMachineLogger()
|
||||
testLogger.RedirectStdOutToStdErr()
|
||||
testLogger.Debug("debug")
|
||||
testLogger.Info("info")
|
||||
testLogger.Error("error")
|
||||
assert.Equal(t, 3, len(testLogger.History()))
|
||||
assert.Equal(t, "debug", testLogger.History()[0])
|
||||
assert.Equal(t, "info", testLogger.History()[1])
|
||||
assert.Equal(t, "error", testLogger.History()[2])
|
||||
}
|
||||
|
||||
func captureOutput(testLogger MachineLogger, lambda func()) string {
|
||||
pipeReader, pipeWriter := io.Pipe()
|
||||
scanner := bufio.NewScanner(pipeReader)
|
||||
|
|
|
|||
|
|
@ -24,5 +24,5 @@ type MachineLogger interface {
|
|||
Warn(args ...interface{})
|
||||
Warnf(fmtString string, args ...interface{})
|
||||
|
||||
Logger() interface{}
|
||||
History() []string
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue