github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/cmd/juju/debuglog.go (about) 1 // Copyright 2013, 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package main 5 6 import ( 7 "fmt" 8 "io" 9 10 "github.com/juju/errors" 11 "github.com/juju/loggo" 12 "launchpad.net/gnuflag" 13 14 "github.com/juju/juju/cmd" 15 "github.com/juju/juju/cmd/envcmd" 16 "github.com/juju/juju/juju" 17 "github.com/juju/juju/state/api" 18 ) 19 20 type DebugLogCommand struct { 21 envcmd.EnvCommandBase 22 23 level string 24 params api.DebugLogParams 25 } 26 27 var DefaultLogLocation = "/var/log/juju/all-machines.log" 28 29 // defaultLineCount is the default number of lines to 30 // display, from the end of the consolidated log. 31 const defaultLineCount = 10 32 33 const debuglogDoc = ` 34 Stream the consolidated debug log file. This file contains the log messages 35 from all nodes in the environment. 36 ` 37 38 func (c *DebugLogCommand) Info() *cmd.Info { 39 return &cmd.Info{ 40 Name: "debug-log", 41 Purpose: "display the consolidated log file", 42 Doc: debuglogDoc, 43 } 44 } 45 46 func (c *DebugLogCommand) SetFlags(f *gnuflag.FlagSet) { 47 f.Var(cmd.NewAppendStringsValue(&c.params.IncludeEntity), "i", "only show log messages for these entities") 48 f.Var(cmd.NewAppendStringsValue(&c.params.IncludeEntity), "include", "only show log messages for these entities") 49 f.Var(cmd.NewAppendStringsValue(&c.params.ExcludeEntity), "x", "do not show log messages for these entities") 50 f.Var(cmd.NewAppendStringsValue(&c.params.ExcludeEntity), "exclude", "do not show log messages for these entities") 51 f.Var(cmd.NewAppendStringsValue(&c.params.IncludeModule), "include-module", "only show log messages for these logging modules") 52 f.Var(cmd.NewAppendStringsValue(&c.params.ExcludeModule), "exclude-module", "do not show log messages for these logging modules") 53 54 f.StringVar(&c.level, "l", "", "log level to show, one of [TRACE, DEBUG, INFO, WARNING, ERROR]") 55 f.StringVar(&c.level, "level", "", "") 56 57 f.UintVar(&c.params.Backlog, "n", defaultLineCount, "go back this many lines from the end before starting to filter") 58 f.UintVar(&c.params.Backlog, "lines", defaultLineCount, "") 59 f.UintVar(&c.params.Limit, "limit", 0, "show at most this many lines") 60 f.BoolVar(&c.params.Replay, "replay", false, "start filtering from the start") 61 } 62 63 func (c *DebugLogCommand) Init(args []string) error { 64 if c.level != "" { 65 level, ok := loggo.ParseLevel(c.level) 66 if !ok || level < loggo.TRACE || level > loggo.ERROR { 67 return fmt.Errorf("level value %q is not one of %q, %q, %q, %q, %q", 68 c.level, loggo.TRACE, loggo.DEBUG, loggo.INFO, loggo.WARNING, loggo.ERROR) 69 } 70 c.params.Level = level 71 } 72 return cmd.CheckEmpty(args) 73 } 74 75 type DebugLogAPI interface { 76 WatchDebugLog(params api.DebugLogParams) (io.ReadCloser, error) 77 Close() error 78 } 79 80 var getDebugLogAPI = func(envName string) (DebugLogAPI, error) { 81 return juju.NewAPIClientFromName(envName) 82 } 83 84 // Run retrieves the debug log via the API. 85 func (c *DebugLogCommand) Run(ctx *cmd.Context) (err error) { 86 client, err := getDebugLogAPI(c.EnvName) 87 if err != nil { 88 return err 89 } 90 defer client.Close() 91 debugLog, err := client.WatchDebugLog(c.params) 92 if err != nil { 93 if errors.IsNotSupported(err) { 94 return c.watchDebugLog1dot18(ctx) 95 } 96 return err 97 } 98 defer debugLog.Close() 99 _, err = io.Copy(ctx.Stdout, debugLog) 100 return err 101 } 102 103 var runSSHCommand = func(sshCmd *SSHCommand, ctx *cmd.Context) error { 104 return sshCmd.Run(ctx) 105 } 106 107 // watchDebugLog1dot18 runs in case of an older API server and uses ssh 108 // but with server-side grep. 109 func (c *DebugLogCommand) watchDebugLog1dot18(ctx *cmd.Context) error { 110 ctx.Infof("Server does not support new stream log, falling back to tail") 111 ctx.Verbosef("filters are not supported with tail") 112 sshCmd := &SSHCommand{} 113 tailCmd := fmt.Sprintf("tail -n -%d -f %s", c.params.Backlog, DefaultLogLocation) 114 // If the api doesn't support WatchDebugLog, then it won't be running in 115 // HA either, so machine 0 is where it is all at. 116 args := []string{"0", tailCmd} 117 err := sshCmd.Init(args) 118 if err != nil { 119 return err 120 } 121 sshCmd.EnvName = c.EnvName 122 return runSSHCommand(sshCmd, ctx) 123 }