diff --git a/istioctl/cmd/root.go b/istioctl/cmd/root.go index 2bf3e49fe77d..406502d3fc36 100644 --- a/istioctl/cmd/root.go +++ b/istioctl/cmd/root.go @@ -264,7 +264,7 @@ debug and diagnose their Istio mesh. hideInheritedFlags(upgradeCmd, "namespace", "istioNamespace") rootCmd.AddCommand(upgradeCmd) - bugReportCmd := bugreport.Cmd() + bugReportCmd := bugreport.Cmd(loggingOptions) hideInheritedFlags(bugReportCmd, "namespace", "istioNamespace") rootCmd.AddCommand(bugReportCmd) diff --git a/tools/bug-report/main.go b/tools/bug-report/main.go index 6f6d02d3d9ca..26e8c523d976 100644 --- a/tools/bug-report/main.go +++ b/tools/bug-report/main.go @@ -19,10 +19,11 @@ import ( "os" "istio.io/istio/tools/bug-report/pkg/bugreport" + "istio.io/pkg/log" ) func main() { - if err := bugreport.Cmd().Execute(); err != nil { + if err := bugreport.Cmd(log.DefaultOptions()).Execute(); err != nil { fmt.Println(err) os.Exit(-1) } diff --git a/tools/bug-report/pkg/archive/archive.go b/tools/bug-report/pkg/archive/archive.go index 59a02ae38d06..e57f2ced454b 100644 --- a/tools/bug-report/pkg/archive/archive.go +++ b/tools/bug-report/pkg/archive/archive.go @@ -36,6 +36,10 @@ var ( initDir sync.Once ) +func OutputRootDir(rootDir string) string { + return getRootDir(rootDir) +} + func ProxyLogPath(rootDir, namespace, pod string) string { dir := filepath.Join(getRootDir(rootDir), proxyLogsPathSubdir, namespace) return filepath.Join(dir, pod+".log") diff --git a/tools/bug-report/pkg/bugreport/bugreport.go b/tools/bug-report/pkg/bugreport/bugreport.go index 7a705346292b..22a2dbba5138 100644 --- a/tools/bug-report/pkg/bugreport/bugreport.go +++ b/tools/bug-report/pkg/bugreport/bugreport.go @@ -55,7 +55,7 @@ var ( ) // Cmd returns a cobra command for bug-report. -func Cmd() *cobra.Command { +func Cmd(logOpts *log.Options) *cobra.Command { rootCmd := &cobra.Command{ Use: "bug-report", Short: "Cluster information and log capture support tool.", @@ -63,7 +63,7 @@ func Cmd() *cobra.Command { Long: "This command selectively captures cluster information and logs into an archive to help " + "diagnose problems. It optionally uploads the archive to a GCS bucket.", RunE: func(cmd *cobra.Command, args []string) error { - return runBugReportCommand(cmd) + return runBugReportCommand(cmd, logOpts) }, } rootCmd.AddCommand(version.CobraCommand()) @@ -83,7 +83,10 @@ var ( lock = sync.RWMutex{} ) -func runBugReportCommand(_ *cobra.Command) error { +func runBugReportCommand(_ *cobra.Command, logOpts *log.Options) error { + if err := configLogs(logOpts); err != nil { + return err + } config, err := parseConfig() if err != nil { return err @@ -108,7 +111,7 @@ func runBugReportCommand(_ *cobra.Command) error { return err } - log.Infof("Fetching logs for the following containers:\n\n%s\n", strings.Join(paths, "\n")) + logAndPrintf("Fetching proxy logs for the following containers:\n\n%s\n", strings.Join(paths, "\n")) gatherInfo(client, config, resources, paths) if len(gErrors) != 0 { @@ -124,6 +127,24 @@ func runBugReportCommand(_ *cobra.Command) error { } writeFile(archive.ProxyLogPath(tempDir, namespace, pod), text) } + + outDir, err := os.Getwd() + if err != nil { + log.Errorf("using ./ to write archive: %s", err.Error()) + outDir = "." + } + outPath := filepath.Join(outDir, "bug-report.tgz") + logAndPrintf("Creating archive at %s.\n", outPath) + + tempRoot := archive.OutputRootDir(tempDir) + if err := archive.Create(tempRoot, outPath); err != nil { + return err + } + logAndPrintf("Cleaning up temporary files in %s.\n", tempRoot) + if err := os.RemoveAll(tempRoot); err != nil { + return err + } + logAndPrintf("Done.\n") return nil } @@ -141,6 +162,7 @@ func gatherInfo(client kube.ExtendedClient, config *config.BugReportConfig, reso Client: client, DryRun: config.DryRun, } + logAndPrintf("\nFetching Istio control plane information from cluster.\n\n") getFromCluster(content.GetK8sResources, params, clusterDir, &mandatoryWg) getFromCluster(content.GetCRs, params, clusterDir, &mandatoryWg) getFromCluster(content.GetEvents, params, clusterDir, &mandatoryWg) @@ -303,3 +325,25 @@ func BuildClientsFromConfig(kubeConfig []byte) (kube.Client, error) { } return clients, nil } + +func logAndPrintf(format string, a ...interface{}) { + fmt.Printf(format, a...) + log.Infof(format, a...) +} + +func configLogs(opt *log.Options) error { + logDir := filepath.Join(archive.OutputRootDir(tempDir), "bug-report.log") + mkdirOrExit(logDir) + f, err := os.Create(logDir) + if err != nil { + return err + } + f.Close() + op := []string{logDir} + opt2 := *opt + opt2.OutputPaths = op + opt2.ErrorOutputPaths = op + opt2.SetOutputLevel("default", log.InfoLevel) + + return log.Configure(&opt2) +}