Skip to content

Commit

Permalink
refact cscli metrics: fix lines between tables, skip wrapper api (#3137)
Browse files Browse the repository at this point in the history
* fix empty line between metrics tables

* refact metrics tables: use go-pretty api directly

* lint
  • Loading branch information
mmetc authored Jul 17, 2024
1 parent 189fb9c commit 8d96ddd
Show file tree
Hide file tree
Showing 20 changed files with 164 additions and 196 deletions.
4 changes: 2 additions & 2 deletions cmd/crowdsec-cli/bouncers.go
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ func (cli *cliBouncers) listHuman(out io.Writer, bouncers ent.Bouncers) {
t.AppendRow(table.Row{b.Name, b.IPAddress, revoked, lastPull, b.Type, b.Version, b.AuthType})
}

fmt.Fprintln(out, t.Render())
io.WriteString(out, t.Render() + "\n")
}

// bouncerInfo contains only the data we want for inspect/list
Expand Down Expand Up @@ -475,7 +475,7 @@ func (cli *cliBouncers) inspectHuman(out io.Writer, bouncer *ent.Bouncer) {
t.AppendRow(table.Row{"Feature Flags", ff})
}

fmt.Fprintln(out, t.Render())
io.WriteString(out, t.Render() + "\n")
}

func (cli *cliBouncers) inspect(bouncer *ent.Bouncer) error {
Expand Down
17 changes: 9 additions & 8 deletions cmd/crowdsec-cli/climetrics/list.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ package climetrics
import (
"encoding/json"
"fmt"
"io"

"github.com/fatih/color"
"github.com/spf13/cobra"

"github.com/jedib0t/go-pretty/v6/table"
"github.com/jedib0t/go-pretty/v6/text"
"github.com/spf13/cobra"

"github.com/crowdsecurity/go-cs-lib/maptools"

Expand Down Expand Up @@ -43,27 +43,28 @@ func (cli *cliMetrics) list() error {
t.AppendHeader(table.Row{"Type", "Title", "Description"})
t.SetColumnConfigs([]table.ColumnConfig{
{
Name: "Type",
Name: "Type",
AlignHeader: text.AlignCenter,
},
{
Name: "Title",
Name: "Title",
AlignHeader: text.AlignCenter,
},
{
Name: "Description",
AlignHeader: text.AlignCenter,
WidthMax: 60,
Name: "Description",
AlignHeader: text.AlignCenter,
WidthMax: 60,
WidthMaxEnforcer: text.WrapSoft,
},
})

t.Style().Options.SeparateRows = true

for _, metric := range allMetrics {
t.AppendRow(table.Row{metric.Type, metric.Title, metric.Description})
}

fmt.Fprintln(out, t.Render())
io.WriteString(out, t.Render() + "\n")
case "json":
x, err := json.MarshalIndent(allMetrics, "", " ")
if err != nil {
Expand Down
13 changes: 6 additions & 7 deletions cmd/crowdsec-cli/climetrics/statacquis.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package climetrics
import (
"io"

"github.com/jedib0t/go-pretty/v6/text"
"github.com/jedib0t/go-pretty/v6/table"
log "github.com/sirupsen/logrus"

"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/cstable"
Expand All @@ -28,18 +28,17 @@ func (s statAcquis) Process(source, metric string, val int) {
}

func (s statAcquis) Table(out io.Writer, wantColor string, noUnit bool, showEmpty bool) {
t := cstable.New(out, wantColor)
t.SetRowLines(false)
t.SetHeaders("Source", "Lines read", "Lines parsed", "Lines unparsed", "Lines poured to bucket", "Lines whitelisted")
t.SetAlignment(text.AlignLeft, text.AlignLeft, text.AlignLeft, text.AlignLeft, text.AlignLeft)
t := cstable.New(out, wantColor).Writer
t.AppendHeader(table.Row{"Source", "Lines read", "Lines parsed", "Lines unparsed", "Lines poured to bucket", "Lines whitelisted"})

keys := []string{"reads", "parsed", "unparsed", "pour", "whitelisted"}

if numRows, err := metricsToTable(t, s, keys, noUnit); err != nil {
log.Warningf("while collecting acquis stats: %s", err)
} else if numRows > 0 || showEmpty {
title, _ := s.Description()
cstable.RenderTitle(out, "\n"+title+":")
t.Render()
io.WriteString(out, title + ":\n")
io.WriteString(out, t.Render() + "\n")
io.WriteString(out, "\n")
}
}
18 changes: 9 additions & 9 deletions cmd/crowdsec-cli/climetrics/statalert.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"io"
"strconv"

"github.com/jedib0t/go-pretty/v6/text"
"github.com/jedib0t/go-pretty/v6/table"

"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/cstable"
)
Expand All @@ -21,25 +21,25 @@ func (s statAlert) Process(reason string, val int) {
}

func (s statAlert) Table(out io.Writer, wantColor string, noUnit bool, showEmpty bool) {
t := cstable.New(out, wantColor)
t.SetRowLines(false)
t.SetHeaders("Reason", "Count")
t.SetAlignment(text.AlignLeft, text.AlignLeft)
t := cstable.New(out, wantColor).Writer
t.AppendHeader(table.Row{"Reason", "Count"})

numRows := 0

// TODO: sort keys
for scenario, hits := range s {
t.AddRow(
t.AppendRow(table.Row{
scenario,
strconv.Itoa(hits),
)
})

numRows++
}

if numRows > 0 || showEmpty {
title, _ := s.Description()
cstable.RenderTitle(out, "\n"+title+":")
t.Render()
io.WriteString(out, title + ":\n")
io.WriteString(out, t.Render() + "\n")
io.WriteString(out, "\n")
}
}
13 changes: 6 additions & 7 deletions cmd/crowdsec-cli/climetrics/statappsecengine.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package climetrics
import (
"io"

"github.com/jedib0t/go-pretty/v6/text"
"github.com/jedib0t/go-pretty/v6/table"
log "github.com/sirupsen/logrus"

"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/cstable"
Expand All @@ -25,18 +25,17 @@ func (s statAppsecEngine) Process(appsecEngine, metric string, val int) {
}

func (s statAppsecEngine) Table(out io.Writer, wantColor string, noUnit bool, showEmpty bool) {
t := cstable.New(out, wantColor)
t.SetRowLines(false)
t.SetHeaders("Appsec Engine", "Processed", "Blocked")
t.SetAlignment(text.AlignLeft, text.AlignLeft)
t := cstable.New(out, wantColor).Writer
t.AppendHeader(table.Row{"Appsec Engine", "Processed", "Blocked"})

keys := []string{"processed", "blocked"}

if numRows, err := metricsToTable(t, s, keys, noUnit); err != nil {
log.Warningf("while collecting appsec stats: %s", err)
} else if numRows > 0 || showEmpty {
title, _ := s.Description()
cstable.RenderTitle(out, "\n"+title+":")
t.Render()
io.WriteString(out, title + ":\n")
io.WriteString(out, t.Render() + "\n")
io.WriteString(out, "\n")
}
}
14 changes: 7 additions & 7 deletions cmd/crowdsec-cli/climetrics/statappsecrule.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import (
"fmt"
"io"

"github.com/jedib0t/go-pretty/v6/text"
"github.com/jedib0t/go-pretty/v6/table"
log "github.com/sirupsen/logrus"

"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/cstable"
Expand All @@ -30,19 +30,19 @@ func (s statAppsecRule) Process(appsecEngine, appsecRule string, metric string,
}

func (s statAppsecRule) Table(out io.Writer, wantColor string, noUnit bool, showEmpty bool) {
// TODO: sort keys
for appsecEngine, appsecEngineRulesStats := range s {
t := cstable.New(out, wantColor)
t.SetRowLines(false)
t.SetHeaders("Rule ID", "Triggered")
t.SetAlignment(text.AlignLeft, text.AlignLeft)
t := cstable.New(out, wantColor).Writer
t.AppendHeader(table.Row{"Rule ID", "Triggered"})

keys := []string{"triggered"}

if numRows, err := metricsToTable(t, appsecEngineRulesStats, keys, noUnit); err != nil {
log.Warningf("while collecting appsec rules stats: %s", err)
} else if numRows > 0 || showEmpty {
cstable.RenderTitle(out, fmt.Sprintf("\nAppsec '%s' Rules Metrics:", appsecEngine))
t.Render()
io.WriteString(out, fmt.Sprintf("Appsec '%s' Rules Metrics:\n", appsecEngine))
io.WriteString(out, t.Render() + "\n")
io.WriteString(out, "\n")
}
}
}
43 changes: 24 additions & 19 deletions cmd/crowdsec-cli/climetrics/statbouncer.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,8 +34,8 @@ type statBouncer struct {
oldestTS map[string]*time.Time
// we keep de-normalized metrics so we can iterate
// over them multiple times and keep the aggregation code simple
rawMetrics []bouncerMetricItem
aggregated map[string]map[string]map[string]map[string]int64
rawMetrics []bouncerMetricItem
aggregated map[string]map[string]map[string]map[string]int64
aggregatedAllOrigin map[string]map[string]map[string]int64
}

Expand All @@ -57,6 +57,7 @@ func (s *statBouncer) Description() (string, string) {
func warnOnce(warningsLogged map[string]bool, msg string) {
if _, ok := warningsLogged[msg]; !ok {
log.Warningf(msg)

warningsLogged[msg] = true
}
}
Expand Down Expand Up @@ -200,6 +201,7 @@ func (s *statBouncer) aggregate() {
// bouncerTable displays a table of metrics for a single bouncer
func (s *statBouncer) bouncerTable(out io.Writer, bouncerName string, wantColor string, noUnit bool) {
columns := make(map[string]map[string]bool)

for _, item := range s.rawMetrics {
if item.bouncerName != bouncerName {
continue
Expand All @@ -208,6 +210,7 @@ func (s *statBouncer) bouncerTable(out io.Writer, bouncerName string, wantColor
if _, ok := columns[item.name]; !ok {
columns[item.name] = make(map[string]bool)
}

columns[item.name][item.unit] = true
}

Expand All @@ -225,16 +228,16 @@ func (s *statBouncer) bouncerTable(out io.Writer, bouncerName string, wantColor
colNum := 1

colCfg := []table.ColumnConfig{{
Number:colNum,
AlignHeader:
text.AlignLeft,
Align: text.AlignLeft,
Number: colNum,
AlignHeader: text.AlignLeft,
Align: text.AlignLeft,
AlignFooter: text.AlignRight,
}}

for _, name := range maptools.SortedKeys(columns) {
for _, unit := range maptools.SortedKeys(columns[name]) {
colNum += 1

header1 = append(header1, name)

// we don't add "s" to random words
Expand All @@ -244,11 +247,11 @@ func (s *statBouncer) bouncerTable(out io.Writer, bouncerName string, wantColor

header2 = append(header2, unit)
colCfg = append(colCfg, table.ColumnConfig{
Number: colNum,
Number: colNum,
AlignHeader: text.AlignCenter,
Align: text.AlignRight,
AlignFooter: text.AlignRight},
)
Align: text.AlignRight,
AlignFooter: text.AlignRight,
})
}
}

Expand Down Expand Up @@ -277,16 +280,20 @@ func (s *statBouncer) bouncerTable(out io.Writer, bouncerName string, wantColor
}

row := table.Row{origin}

for _, name := range maptools.SortedKeys(columns) {
for _, unit := range maptools.SortedKeys(columns[name]) {
valStr := "-"

val, ok := metrics[name][unit]
if ok {
valStr = formatNumber(val, !noUnit)
}

row = append(row, valStr)
}
}

t.AppendRow(row)

numRows += 1
Expand All @@ -299,6 +306,7 @@ func (s *statBouncer) bouncerTable(out io.Writer, bouncerName string, wantColor
}

footer := table.Row{"Total"}

for _, name := range maptools.SortedKeys(columns) {
for _, unit := range maptools.SortedKeys(columns[name]) {
footer = append(footer, formatNumber(totals[name][unit], !noUnit))
Expand All @@ -309,16 +317,19 @@ func (s *statBouncer) bouncerTable(out io.Writer, bouncerName string, wantColor

title, _ := s.Description()
title = fmt.Sprintf("%s (%s)", title, bouncerName)

if s.oldestTS != nil {
// if we change this to .Local() beware of tests
title = fmt.Sprintf("%s since %s", title, s.oldestTS[bouncerName].String())
}

title += ":"

// don't use SetTitle() because it draws the title inside table box
// TODO: newline position wrt other stat tables
cstable.RenderTitle(out, title)
fmt.Fprintln(out, t.Render())
io.WriteString(out, title+"\n")
io.WriteString(out, t.Render() + "\n")
// empty line between tables
io.WriteString(out, "\n")
}

// Table displays a table of metrics for each bouncer
Expand All @@ -328,13 +339,7 @@ func (s *statBouncer) Table(out io.Writer, wantColor string, noUnit bool, _ bool
bouncerNames[item.bouncerName] = true
}

nl := false
for _, bouncerName := range maptools.SortedKeys(bouncerNames) {
if nl {
// empty line between tables
fmt.Fprintln(out)
}
s.bouncerTable(out, bouncerName, wantColor, noUnit)
nl = true
}
}
13 changes: 6 additions & 7 deletions cmd/crowdsec-cli/climetrics/statbucket.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package climetrics
import (
"io"

"github.com/jedib0t/go-pretty/v6/text"
"github.com/jedib0t/go-pretty/v6/table"
log "github.com/sirupsen/logrus"

"github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/cstable"
Expand All @@ -26,18 +26,17 @@ func (s statBucket) Process(bucket, metric string, val int) {
}

func (s statBucket) Table(out io.Writer, wantColor string, noUnit bool, showEmpty bool) {
t := cstable.New(out, wantColor)
t.SetRowLines(false)
t.SetHeaders("Scenario", "Current Count", "Overflows", "Instantiated", "Poured", "Expired")
t.SetAlignment(text.AlignLeft, text.AlignLeft, text.AlignLeft, text.AlignLeft, text.AlignLeft, text.AlignLeft)
t := cstable.New(out, wantColor).Writer
t.AppendHeader(table.Row{"Scenario", "Current Count", "Overflows", "Instantiated", "Poured", "Expired"})

keys := []string{"curr_count", "overflow", "instantiation", "pour", "underflow"}

if numRows, err := metricsToTable(t, s, keys, noUnit); err != nil {
log.Warningf("while collecting scenario stats: %s", err)
} else if numRows > 0 || showEmpty {
title, _ := s.Description()
cstable.RenderTitle(out, "\n"+title+":")
t.Render()
io.WriteString(out, title + ":\n")
io.WriteString(out, t.Render() + "\n")
io.WriteString(out, "\n")
}
}
Loading

0 comments on commit 8d96ddd

Please sign in to comment.