diff --git a/CHANGELOG.md b/CHANGELOG.md index a9b8ea52b1..b6f9b1cea6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ We use *breaking :warning:* to mark changes that are not backward compatible (re ### Added +- [#4228](https://github.com/thanos-io/thanos/pull/4228) Tools `thanos bucket inspect`: Add flag `--output` to provide output method (table,csv,tsv). - [#4680](https://github.com/thanos-io/thanos/pull/4680) Query: add `exemplar.partial-response` flag to control partial response. - [#4679](https://github.com/thanos-io/thanos/pull/4679) Added `enable-feature` flag to enable negative offsets and @ modifier, similar to Prometheus. - [#4696](https://github.com/thanos-io/thanos/pull/4696) Query: add cache name to tracing spans. diff --git a/cmd/thanos/tools_bucket.go b/cmd/thanos/tools_bucket.go index 1ad00b34a1..1b630fb697 100644 --- a/cmd/thanos/tools_bucket.go +++ b/cmd/thanos/tools_bucket.go @@ -6,8 +6,10 @@ package main import ( "context" "crypto/rand" + "encoding/csv" "encoding/json" "fmt" + "io" "io/ioutil" "net/http" "os" @@ -72,6 +74,15 @@ var ( }, } inspectColumns = []string{"ULID", "FROM", "UNTIL", "RANGE", "UNTIL-DOWN", "#SERIES", "#SAMPLES", "#CHUNKS", "COMP-LEVEL", "COMP-FAILED", "LABELS", "RESOLUTION", "SOURCE"} + outputTypes = []string{"table", "tsv", "csv"} +) + +type outputType string + +const ( + TABLE outputType = "table" + CSV outputType = "csv" + TSV outputType = "tsv" ) type bucketRewriteConfig struct { @@ -445,6 +456,8 @@ func registerBucketInspect(app extkingpin.AppClause, objStoreConfig *extflag.Pat tbc := &bucketInspectConfig{} tbc.registerBucketInspectFlag(cmd) + output := cmd.Flag("output", "Output format for result. Currently supports table, cvs, tsv.").Default("table").Enum(outputTypes...) + cmd.Setup(func(g *run.Group, logger log.Logger, reg *prometheus.Registry, _ opentracing.Tracer, _ <-chan struct{}, _ bool) error { // Parse selector. @@ -487,7 +500,17 @@ func registerBucketInspect(app extkingpin.AppClause, objStoreConfig *extflag.Pat blockMetas = append(blockMetas, meta) } - return printTable(blockMetas, selectorLabels, tbc.sortBy) + var opPrinter tablePrinter + op := outputType(*output) + switch op { + case TABLE: + opPrinter = printTable + case TSV: + opPrinter = printTSV + case CSV: + opPrinter = printCSV + } + return printBlockData(blockMetas, selectorLabels, tbc.sortBy, opPrinter) }) } @@ -804,7 +827,56 @@ func registerBucketCleanup(app extkingpin.AppClause, objStoreConfig *extflag.Pat }) } -func printTable(blockMetas []*metadata.Meta, selectorLabels labels.Labels, sortBy []string) error { +type tablePrinter func(w io.Writer, t Table) error + +func printTable(w io.Writer, t Table) error { + table := tablewriter.NewWriter(w) + table.SetHeader(t.Header) + table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false}) + table.SetCenterSeparator("|") + table.SetAutoWrapText(false) + table.SetReflowDuringAutoWrap(false) + table.SetAlignment(tablewriter.ALIGN_LEFT) + table.AppendBulk(t.Lines) + table.Render() + return nil +} + +func printCSV(w io.Writer, t Table) error { + csv := csv.NewWriter(w) + err := csv.Write(t.Header) + if err != nil { + return err + } + err = csv.WriteAll(t.Lines) + if err != nil { + return err + } + csv.Flush() + return nil +} + +func newTSVWriter(w io.Writer) *csv.Writer { + writer := csv.NewWriter(w) + writer.Comma = rune('\t') + return writer +} + +func printTSV(w io.Writer, t Table) error { + tsv := newTSVWriter(w) + err := tsv.Write(t.Header) + if err != nil { + return err + } + err = tsv.WriteAll(t.Lines) + if err != nil { + return err + } + tsv.Flush() + return nil +} + +func printBlockData(blockMetas []*metadata.Meta, selectorLabels labels.Labels, sortBy []string, printer tablePrinter) error { header := inspectColumns var lines [][]string @@ -856,17 +928,10 @@ func printTable(blockMetas []*metadata.Meta, selectorLabels labels.Labels, sortB t := Table{Header: header, Lines: lines, SortIndices: sortByColNum} sort.Sort(t) - - table := tablewriter.NewWriter(os.Stdout) - table.SetHeader(t.Header) - table.SetBorders(tablewriter.Border{Left: true, Top: false, Right: true, Bottom: false}) - table.SetCenterSeparator("|") - table.SetAutoWrapText(false) - table.SetReflowDuringAutoWrap(false) - table.SetAlignment(tablewriter.ALIGN_LEFT) - table.AppendBulk(t.Lines) - table.Render() - + err := printer(os.Stdout, t) + if err != nil { + return errors.Errorf("unable to write output.") + } return nil } diff --git a/docs/components/tools.md b/docs/components/tools.md index b000316a54..fe4f42b632 100644 --- a/docs/components/tools.md +++ b/docs/components/tools.md @@ -462,6 +462,8 @@ Flags: Path to YAML file that contains object store configuration. See format details: https://thanos.io/tip/thanos/storage.md/#configuration + --output=table Output format for result. Currently supports table, + cvs, tsv. -l, --selector==\"\" ... Selects blocks based on label, e.g. '-l key1=\"value1\" -l key2=\"value2\"'. All key value