Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Snapshots #83

Merged
merged 9 commits into from
Sep 29, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ See [rare.zdyn.net](https://rare.zdyn.net) or the [docs/ folder](docs/) for the
* Aggregating and realtime summary (Don't have to wait for all data to be scanned)
* Multi-threaded reading, parsing, and aggregation (It's fast)
* Color-coded outputs (optionally)
* Pipe support (stdin for reading, stdout will disable color) eg. `tail -f | rare ...`
* Pipe support (stdin for reading, stdout will disable realtime) eg. `tail -f | rare ... > out`

Take a look at [examples](docs/usage/examples.md) to see more of what *rare* does.

Expand Down
6 changes: 3 additions & 3 deletions cmd/analyze.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func humanf(arg interface{}) string {
return color.Wrap(color.BrightWhite, humanize.Hf(arg))
}

func writeAggrOutput(writer *multiterm.TermWriter, aggr *aggregation.MatchNumerical, extra bool, quantiles []float64) int {
func writeAggrOutput(writer multiterm.MultilineTerm, aggr *aggregation.MatchNumerical, extra bool, quantiles []float64) int {
writer.WriteForLinef(0, "Samples: %v", color.Wrap(color.BrightWhite, humanize.Hi(aggr.Count())))
writer.WriteForLinef(1, "Mean: %v", humanf(aggr.Mean()))
writer.WriteForLinef(2, "StdDev: %v", humanf(aggr.StdDev()))
Expand Down Expand Up @@ -59,8 +59,7 @@ func analyzeFunction(c *cli.Context) error {
}

aggr := aggregation.NewNumericalAggregator(&config)
writer := multiterm.New()
defer multiterm.ResetCursor()
writer := helpers.BuildVTermFromArguments(c)
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should this defer writer.close (and all other agg's?) Is it safer?


batcher := helpers.BuildBatcherFromArguments(c)
ext := helpers.BuildExtractorFromArguments(c, batcher)
Expand Down Expand Up @@ -102,6 +101,7 @@ func analyzeCommand() *cli.Command {
Usage: "Adds a quantile to the output set. Requires --extra",
Value: cli.NewStringSlice("90", "99", "99.9"),
},
helpers.SnapshotFlag,
},
})
}
5 changes: 3 additions & 2 deletions cmd/bargraph.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package cmd
import (
"rare/cmd/helpers"
"rare/pkg/aggregation"
"rare/pkg/multiterm"
"rare/pkg/multiterm/termrenderers"

"github.com/urfave/cli/v2"
Expand All @@ -20,8 +19,9 @@ func bargraphFunction(c *cli.Context) error {
sortName = c.String(helpers.DefaultSortFlag.Name)
)

vt := helpers.BuildVTermFromArguments(c)
counter := aggregation.NewSubKeyCounter()
writer := termrenderers.NewBarGraph(multiterm.New())
writer := termrenderers.NewBarGraph(vt)
writer.Stacked = stacked

batcher := helpers.BuildBatcherFromArguments(c)
Expand Down Expand Up @@ -63,6 +63,7 @@ func bargraphCommand() *cli.Command {
Usage: "Display bargraph as stacked",
},
helpers.DefaultSortFlag,
helpers.SnapshotFlag,
},
})
}
4 changes: 3 additions & 1 deletion cmd/heatmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ func heatmapFunction(c *cli.Context) error {
rowSorter := helpers.BuildSorterOrFail(sortRows)
colSorter := helpers.BuildSorterOrFail(sortCols)

writer := termrenderers.NewHeatmap(multiterm.New(), numRows, numCols)
vt := helpers.BuildVTermFromArguments(c)
writer := termrenderers.NewHeatmap(vt, numRows, numCols)

writer.FixedMin = minFixed
writer.FixedMax = maxFixed
Expand Down Expand Up @@ -96,6 +97,7 @@ func heatmapCommand() *cli.Command {
Usage: helpers.DefaultSortFlag.Usage,
Value: helpers.DefaultSortFlag.Value,
},
helpers.SnapshotFlag,
},
})
}
25 changes: 25 additions & 0 deletions cmd/helpers/output.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package helpers

import (
"rare/pkg/multiterm"
"rare/pkg/multiterm/termstate"

"github.com/urfave/cli/v2"
)

var SnapshotFlag = &cli.BoolFlag{
Name: "snapshot",
Usage: "In aggregators that support it, only output final results, and not progressive updates. Will enable automatically when piping output",
}

func BuildVTerm(forceSnapshot bool) multiterm.MultilineTerm {
if forceSnapshot || termstate.IsPipedOutput() {
return multiterm.NewBufferedTerm()
}
return multiterm.New()
}

func BuildVTermFromArguments(c *cli.Context) multiterm.MultilineTerm {
snapshot := c.Bool(SnapshotFlag.Name)
return BuildVTerm(snapshot)
}
27 changes: 27 additions & 0 deletions cmd/helpers/output_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
package helpers

import (
"testing"

"github.com/stretchr/testify/assert"
"github.com/urfave/cli/v2"
)

func TestBuildVTerm(t *testing.T) {
assert.NotNil(t, BuildVTerm(false))
assert.NotNil(t, BuildVTerm(true))
}

func TestBuildVTermFromArgs(t *testing.T) {
app := cli.NewApp()
app.Flags = []cli.Flag{
&cli.BoolFlag{
Name: "snapshot",
},
}
app.Action = func(ctx *cli.Context) error {
BuildVTermFromArguments(ctx)
return nil
}
app.Run([]string{"", "--snapshot"})
}
4 changes: 0 additions & 4 deletions cmd/helpers/updatingAggregator.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package helpers

import (
"fmt"
"os"
"os/signal"
"rare/pkg/aggregation"
"rare/pkg/extractor"
"rare/pkg/logger"
"rare/pkg/multiterm"
"sync"
"time"
)
Expand All @@ -18,7 +16,6 @@ import (
// writeOutput - triggered after a delay, only if there's an update
// The two functions are guaranteed to never happen at the same time
func RunAggregationLoop(ext *extractor.Extractor, aggregator aggregation.Aggregator, writeOutput func()) {
defer multiterm.ResetCursor()
logger.DeferLogs()

// Updater sync variables
Expand Down Expand Up @@ -62,5 +59,4 @@ PROCESSING_LOOP:
outputDone <- true

writeOutput()
fmt.Println()
}
6 changes: 5 additions & 1 deletion cmd/histo.go
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,9 @@ func histoFunction(c *cli.Context) error {
sortName = c.String(helpers.DefaultSortFlag.Name)
)

vt := helpers.BuildVTermFromArguments(c)
counter := aggregation.NewCounter()
writer := termrenderers.NewHistogram(multiterm.New(), topItems)
writer := termrenderers.NewHistogram(vt, topItems)
writer.ShowBar = c.Bool("bars") || extra
writer.ShowPercentage = c.Bool("percentage") || extra

Expand All @@ -56,6 +57,8 @@ func histoFunction(c *cli.Context) error {
writer.WriteFooter(1, batcher.StatusString())
})

// Not deferred because of the `all` below to print out before it
// when in snapshot mode
writer.Close()

if all {
Expand Down Expand Up @@ -118,6 +121,7 @@ func histogramCommand() *cli.Command {
Value: 0,
},
helpers.DefaultSortFlagWithDefault("value"),
helpers.SnapshotFlag,
},
})
}
4 changes: 2 additions & 2 deletions cmd/histo_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ func TestHistogram(t *testing.T) {
}

func TestHistogramRender(t *testing.T) {
out, eout, err := testCommandCapture(histogramCommand(), `-m "(\d+)" -e "{bucket {1} 10}" testdata/log.txt`)
out, eout, err := testCommandCapture(histogramCommand(), `--snapshot -m "(\d+)" -e "{bucket {1} 10}" testdata/log.txt`)
assert.NoError(t, err)
assert.Contains(t, out, "Matched: 3 / 6 (Groups: 2)")
assert.Equal(t, out, "0 2 \n20 1 \n\n\n\nMatched: 3 / 6 (Groups: 2)\n96 B (0 B/s) \n")
assert.Equal(t, "", eout)
}
5 changes: 3 additions & 2 deletions cmd/tabulate.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ import (
"rare/pkg/color"
"rare/pkg/expressions"
"rare/pkg/humanize"
"rare/pkg/multiterm"
"rare/pkg/multiterm/termrenderers"

"github.com/urfave/cli/v2"
Expand All @@ -32,7 +31,8 @@ func tabulateFunction(c *cli.Context) error {
)

counter := aggregation.NewTable(delim)
writer := termrenderers.NewTable(multiterm.New(), numCols+2, numRows+2)
vt := helpers.BuildVTermFromArguments(c)
writer := termrenderers.NewTable(vt, numCols+2, numRows+2)

batcher := helpers.BuildBatcherFromArguments(c)
ext := helpers.BuildExtractorFromArguments(c, batcher)
Expand Down Expand Up @@ -148,6 +148,7 @@ func tabulateCommand() *cli.Command {
Usage: helpers.DefaultSortFlag.Usage,
Value: "value",
},
helpers.SnapshotFlag,
},
})
}
2 changes: 1 addition & 1 deletion docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ Supports various CLI-based graphing and metric formats (filter (grep-like), hist
* Aggregating and realtime summary (Don't have to wait for all data to be scanned)
* Multi-threaded reading, parsing, and aggregation (It's fast)
* Color-coded outputs (optionally)
* Pipe support (stdin for reading, stdout will disable color) eg. `tail -f | rare ...`
* Pipe support (stdin for reading, stdout will disable realtime) eg. `tail -f | rare ... > out`

Take a look at [examples](usage/examples.md) to see more of what *rare* does.

Expand Down
8 changes: 3 additions & 5 deletions pkg/color/coloring.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package color
import (
"fmt"
"io"
"os"
"rare/pkg/multiterm/termstate"
"strings"
"unicode/utf8"
)
Expand Down Expand Up @@ -56,10 +56,8 @@ var Enabled = true
var GroupColors = [...]ColorCode{Red, Green, Yellow, Blue, Magenta, Cyan, BrightRed, BrightGreen, BrightYellow, BrightBlue, BrightMagenta, BrightCyan}

func init() {
if fi, err := os.Stdout.Stat(); err == nil {
if (fi.Mode() & os.ModeCharDevice) == 0 {
Enabled = false
}
if termstate.IsPipedOutput() {
Enabled = false
}
}

Expand Down
19 changes: 19 additions & 0 deletions pkg/multiterm/bufferedterm.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package multiterm

import "os"

type BufferedTerm struct {
*VirtualTerm
}

// NewBufferedTerm writes on Close
func NewBufferedTerm() *BufferedTerm {
return &BufferedTerm{
NewVirtualTerm(),
}
}

func (s *BufferedTerm) Close() {
s.WriteToOutput(os.Stdout)
s.VirtualTerm.Close()
}
11 changes: 11 additions & 0 deletions pkg/multiterm/bufferedterm_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package multiterm

import (
"testing"
)

func TestBufferedTerm(t *testing.T) {
vt := NewBufferedTerm()
vt.WriteForLine(0, "hello")
vt.Close()
}
4 changes: 0 additions & 4 deletions pkg/multiterm/cursor.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,3 @@ func showCursor() {
func eraseRemainingLine() {
fmt.Print(escape("[0K"))
}

func ResetCursor() {
showCursor()
}
1 change: 1 addition & 0 deletions pkg/multiterm/intf.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@ package multiterm

type MultilineTerm interface {
WriteForLine(line int, s string)
WriteForLinef(line int, format string, args ...interface{})
Close()
}
20 changes: 2 additions & 18 deletions pkg/multiterm/linetrim.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ package multiterm

import (
"io"
"os"

"golang.org/x/term"
"rare/pkg/multiterm/termstate"
)

var AutoTrim = true
Expand All @@ -13,22 +11,8 @@ const defaultRows, defaultCols = 24, 80

var computedRows, computedCols = 0, 0

func getTermRowsCols() (rows, cols int, ok bool) {
fd := int(os.Stdout.Fd())
if !term.IsTerminal(fd) {
return 0, 0, false
}

cols, rows, err := term.GetSize(fd)
if err != nil {
return 0, 0, false
}

return rows, cols, true
}

func init() {
if rows, cols, ok := getTermRowsCols(); ok {
if rows, cols, ok := termstate.GetTermRowsCols(); ok {
computedRows, computedCols = rows, cols
} else {
AutoTrim = false
Expand Down
1 change: 1 addition & 0 deletions pkg/multiterm/multiterm.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ func (s *TermWriter) WriteForLine(line int, text string) {

func (s *TermWriter) Close() {
s.goTo(s.maxLine)
fmt.Println() // Put cursor after last line
if s.cursorHidden {
showCursor()
}
Expand Down
2 changes: 2 additions & 0 deletions pkg/multiterm/multiterm_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,6 @@ func TestBasicMultiterm(t *testing.T) {
mt.WriteForLine(0, "Hello")
mt.WriteForLine(1, "you")
mt.WriteForLine(10, "There")
mt.WriteForLinef(5, "This is %s", "Test")
mt.Close()
}
32 changes: 32 additions & 0 deletions pkg/multiterm/termstate/term.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package termstate

import (
"os"

"golang.org/x/term"
)

// Returns 'true' if output is being piped (Not char device)
func IsPipedOutput() bool {
if fi, err := os.Stdout.Stat(); err == nil {
if (fi.Mode() & os.ModeCharDevice) == 0 {
return true
}
}
return false
}

// Gets size of the terminal
func GetTermRowsCols() (rows, cols int, ok bool) {
fd := int(os.Stdout.Fd())
if !term.IsTerminal(fd) {
return 0, 0, false
}

cols, rows, err := term.GetSize(fd)
if err != nil {
return 0, 0, false
}

return rows, cols, true
}