summaryrefslogtreecommitdiffstats
path: root/client
diff options
context:
space:
mode:
authorMartin Polden <mpolden@mpolden.no>2021-09-28 14:59:57 +0200
committerMartin Polden <mpolden@mpolden.no>2021-09-29 08:53:14 +0200
commit7e9e07995ce83e148cd62d0eb7dbecdad8bcac77 (patch)
tree7da52ef275949082109224255fe6f4a62dd86852 /client
parent04d529f9b7c6ec02ececf667f441c7de2247d3ee (diff)
Implement vespa log command
Diffstat (limited to 'client')
-rw-r--r--client/go/cmd/log.go97
-rw-r--r--client/go/cmd/log_test.go27
2 files changed, 124 insertions, 0 deletions
diff --git a/client/go/cmd/log.go b/client/go/cmd/log.go
new file mode 100644
index 00000000000..acfeef0e71f
--- /dev/null
+++ b/client/go/cmd/log.go
@@ -0,0 +1,97 @@
+package cmd
+
+import (
+ "fmt"
+ "time"
+
+ "github.com/spf13/cobra"
+ "github.com/vespa-engine/vespa/client/go/vespa"
+)
+
+var (
+ fromArg string
+ toArg string
+ levelArg string
+ followArg bool
+ dequoteArg bool
+)
+
+func init() {
+ rootCmd.AddCommand(logCmd)
+ logCmd.Flags().StringVarP(&fromArg, "from", "F", "", "Include logs since this timestamp (RFC3339 format)")
+ logCmd.Flags().StringVarP(&toArg, "to", "T", "", "Include logs until this timestamp (RFC3339 format)")
+ logCmd.Flags().StringVarP(&levelArg, "level", "l", "debug", `The maximum log level to show. Must be "error", "warning", "info" or "debug"`)
+ logCmd.Flags().BoolVarP(&followArg, "follow", "f", false, "Follow logs")
+ logCmd.Flags().BoolVarP(&dequoteArg, "nldequote", "n", true, "Dequote LF and TAB characters in log messages")
+}
+
+var logCmd = &cobra.Command{
+ Use: "log [relative-period]",
+ Short: "Show the Vespa log",
+ Long: `Show the Vespa log.
+
+The logs shown can be limited to a relative or fixed period. All timestamps are shown in UTC.
+`,
+ Example: `$ vespa log 1h
+$ vespa log --nldequote=false 10m
+$ vespa log --from 2021-08-25T15:00:00Z --to 2021-08-26T02:00:00Z
+$ vespa log --follow`,
+ DisableAutoGenTag: true,
+ Args: cobra.MaximumNArgs(1),
+ Run: func(cmd *cobra.Command, args []string) {
+ target := getTarget()
+ options := vespa.LogOptions{
+ Level: vespa.LogLevel(levelArg),
+ Follow: followArg,
+ Writer: stdout,
+ Dequote: dequoteArg,
+ }
+ if options.Follow {
+ if fromArg != "" || toArg != "" || len(args) > 0 {
+ fatalErr(fmt.Errorf("cannot combine --from/--to or relative time with --follow"), "Could not follow logs")
+ }
+ options.From = time.Now().Add(-5 * time.Minute)
+ } else {
+ from, to, err := parsePeriod(args)
+ if err != nil {
+ fatalErr(err, "Invalid period")
+ return
+ }
+ options.From = from
+ options.To = to
+ }
+ if err := target.PrintLog(options); err != nil {
+ fatalErr(err, "Could not retrieve logs")
+ }
+ },
+}
+
+func parsePeriod(args []string) (time.Time, time.Time, error) {
+ if len(args) == 1 {
+ if fromArg != "" || toArg != "" {
+ return time.Time{}, time.Time{}, fmt.Errorf("cannot combine --from/--to with relative value: %s", args[0])
+ }
+ d, err := time.ParseDuration(args[0])
+ if err != nil {
+ return time.Time{}, time.Time{}, err
+ }
+ if d > 0 {
+ d = -d
+ }
+ to := time.Now()
+ from := to.Add(d)
+ return from, to, nil
+ }
+ from, err := time.Parse(time.RFC3339, fromArg)
+ if err != nil {
+ return time.Time{}, time.Time{}, err
+ }
+ to, err := time.Parse(time.RFC3339, toArg)
+ if err != nil {
+ return time.Time{}, time.Time{}, err
+ }
+ if !to.After(from) {
+ return time.Time{}, time.Time{}, fmt.Errorf("--to must specify a time after --from")
+ }
+ return from, to, nil
+}
diff --git a/client/go/cmd/log_test.go b/client/go/cmd/log_test.go
new file mode 100644
index 00000000000..a4660001ec4
--- /dev/null
+++ b/client/go/cmd/log_test.go
@@ -0,0 +1,27 @@
+package cmd
+
+import (
+ "path/filepath"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+)
+
+func TestLog(t *testing.T) {
+ homeDir := filepath.Join(t.TempDir(), ".vespa")
+ pkgDir := mockApplicationPackage(t, false)
+ httpClient := &mockHttpClient{}
+ httpClient.NextResponse(200, `1632738690.905535 host1a.dev.aws-us-east-1c 806/53 logserver-container Container.com.yahoo.container.jdisc.ConfiguredApplication info Switching to the latest deployed set of configurations and components. Application config generation: 52532`)
+ execute(command{homeDir: homeDir, args: []string{"config", "set", "application", "t1.a1.i1"}}, t, httpClient)
+ execute(command{homeDir: homeDir, args: []string{"config", "set", "target", "cloud"}}, t, httpClient)
+ execute(command{homeDir: homeDir, args: []string{"api-key"}}, t, httpClient)
+ execute(command{homeDir: homeDir, args: []string{"cert", pkgDir}}, t, httpClient)
+
+ out, _ := execute(command{homeDir: homeDir, args: []string{"log", "--from", "2021-09-27T10:00:00Z", "--to", "2021-09-27T11:00:00Z"}}, t, httpClient)
+
+ expected := "[2021-09-27 10:31:30.905535] host1a.dev.aws-us-east-1c info logserver-container Container.com.yahoo.container.jdisc.ConfiguredApplication Switching to the latest deployed set of configurations and components. Application config generation: 52532\n"
+ assert.Equal(t, expected, out)
+
+ out, _ = execute(command{homeDir: homeDir, args: []string{"log", "--from", "2021-09-27T13:12:49Z", "--to", "2021-09-27T13:15:00", "1h"}}, t, httpClient)
+ assert.Equal(t, "Error: Invalid period\ncannot combine --from/--to with relative value: 1h\n", out)
+}