Skip to content

Commit

Permalink
refact "cscli metrics" par 1 (#2805)
Browse files Browse the repository at this point in the history
  • Loading branch information
mmetc authored Feb 2, 2024
1 parent 4160bb8 commit 5ff8a03
Show file tree
Hide file tree
Showing 4 changed files with 50 additions and 44 deletions.
2 changes: 1 addition & 1 deletion cmd/crowdsec-cli/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ It is meant to allow you to manage bans, parsers/scenarios/etc, api and generall
cmd.AddCommand(NewCLIVersion().NewCommand())
cmd.AddCommand(NewConfigCmd())
cmd.AddCommand(NewCLIHub(getconfig).NewCommand())
cmd.AddCommand(NewMetricsCmd())
cmd.AddCommand(NewCLIMetrics(getconfig).NewCommand())
cmd.AddCommand(NewCLIDashboard(getconfig).NewCommand())
cmd.AddCommand(NewCLIDecisions(getconfig).NewCommand())
cmd.AddCommand(NewCLIAlerts().NewCommand())
Expand Down
68 changes: 37 additions & 31 deletions cmd/crowdsec-cli/metrics.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,8 +19,19 @@ import (
"github.com/crowdsecurity/go-cs-lib/trace"
)

type cliMetrics struct {
cfg configGetter
}

func NewCLIMetrics(getconfig configGetter) *cliMetrics {
return &cliMetrics{
cfg: getconfig,
}
}


// FormatPrometheusMetrics is a complete rip from prom2json
func FormatPrometheusMetrics(out io.Writer, url string, formatType string) error {
func FormatPrometheusMetrics(out io.Writer, url string, formatType string, noUnit bool) error {
mfChan := make(chan *dto.MetricFamily, 1024)
errChan := make(chan error, 1)

Expand Down Expand Up @@ -256,18 +267,18 @@ func FormatPrometheusMetrics(out io.Writer, url string, formatType string) error
}

if formatType == "human" {
acquisStatsTable(out, acquis_stats)
bucketStatsTable(out, buckets_stats)
parserStatsTable(out, parsers_stats)
acquisStatsTable(out, acquis_stats, noUnit)
bucketStatsTable(out, buckets_stats, noUnit)
parserStatsTable(out, parsers_stats, noUnit)
lapiStatsTable(out, lapi_stats)
lapiMachineStatsTable(out, lapi_machine_stats)
lapiBouncerStatsTable(out, lapi_bouncer_stats)
lapiDecisionStatsTable(out, lapi_decisions_stats)
decisionStatsTable(out, decisions_stats)
alertStatsTable(out, alerts_stats)
stashStatsTable(out, stash_stats)
appsecMetricsToTable(out, appsec_engine_stats)
appsecRulesToTable(out, appsec_rule_stats)
appsecMetricsToTable(out, appsec_engine_stats, noUnit)
appsecRulesToTable(out, appsec_rule_stats, noUnit)
return nil
}

Expand Down Expand Up @@ -304,52 +315,47 @@ func FormatPrometheusMetrics(out io.Writer, url string, formatType string) error
return nil
}

var noUnit bool

func runMetrics(cmd *cobra.Command, args []string) error {
flags := cmd.Flags()

url, err := flags.GetString("url")
if err != nil {
return err
}
func (cli *cliMetrics) run(url string, noUnit bool) error {
cfg := cli.cfg()

if url != "" {
csConfig.Cscli.PrometheusUrl = url
}

noUnit, err = flags.GetBool("no-unit")
if err != nil {
return err
cfg.Cscli.PrometheusUrl = url
}

if csConfig.Prometheus == nil {
if cfg.Prometheus == nil {
return fmt.Errorf("prometheus section missing, can't show metrics")
}

if !csConfig.Prometheus.Enabled {
if !cfg.Prometheus.Enabled {
return fmt.Errorf("prometheus is not enabled, can't show metrics")
}

if err = FormatPrometheusMetrics(color.Output, csConfig.Cscli.PrometheusUrl, csConfig.Cscli.Output); err != nil {
if err := FormatPrometheusMetrics(color.Output, cfg.Cscli.PrometheusUrl, cfg.Cscli.Output, noUnit); err != nil {
return err
}
return nil
}

func NewMetricsCmd() *cobra.Command {
cmdMetrics := &cobra.Command{
func (cli *cliMetrics) NewCommand() *cobra.Command {
var (
url string
noUnit bool
)

cmd := &cobra.Command{
Use: "metrics",
Short: "Display crowdsec prometheus metrics.",
Long: `Fetch metrics from the prometheus server and display them in a human-friendly way`,
Args: cobra.ExactArgs(0),
DisableAutoGenTag: true,
RunE: runMetrics,
RunE: func(cmd *cobra.Command, args []string) error {
return cli.run(url, noUnit)
},
}

flags := cmdMetrics.PersistentFlags()
flags.StringP("url", "u", "", "Prometheus url (http://<ip>:<port>/metrics)")
flags.Bool("no-unit", false, "Show the real number instead of formatted with units")
flags := cmd.Flags()
flags.StringVarP(&url, "url", "u", "", "Prometheus url (http://<ip>:<port>/metrics)")
flags.BoolVar(&noUnit, "no-unit", false, "Show the real number instead of formatted with units")

return cmdMetrics
return cmd
}
22 changes: 11 additions & 11 deletions cmd/crowdsec-cli/metrics_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ func lapiMetricsToTable(t *table.Table, stats map[string]map[string]map[string]i
return numRows
}

func metricsToTable(t *table.Table, stats map[string]map[string]int, keys []string) (int, error) {
func metricsToTable(t *table.Table, stats map[string]map[string]int, keys []string, noUnit bool) (int, error) {
if t == nil {
return 0, fmt.Errorf("nil table")
}
Expand Down Expand Up @@ -81,60 +81,60 @@ func metricsToTable(t *table.Table, stats map[string]map[string]int, keys []stri
return numRows, nil
}

func bucketStatsTable(out io.Writer, stats map[string]map[string]int) {
func bucketStatsTable(out io.Writer, stats map[string]map[string]int, noUnit bool) {
t := newTable(out)
t.SetRowLines(false)
t.SetHeaders("Bucket", "Current Count", "Overflows", "Instantiated", "Poured", "Expired")
t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft)

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

if numRows, err := metricsToTable(t, stats, keys); err != nil {
if numRows, err := metricsToTable(t, stats, keys, noUnit); err != nil {
log.Warningf("while collecting bucket stats: %s", err)
} else if numRows > 0 {
renderTableTitle(out, "\nBucket Metrics:")
t.Render()
}
}

func acquisStatsTable(out io.Writer, stats map[string]map[string]int) {
func acquisStatsTable(out io.Writer, stats map[string]map[string]int, noUnit bool) {
t := newTable(out)
t.SetRowLines(false)
t.SetHeaders("Source", "Lines read", "Lines parsed", "Lines unparsed", "Lines poured to bucket")
t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft)

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

if numRows, err := metricsToTable(t, stats, keys); err != nil {
if numRows, err := metricsToTable(t, stats, keys, noUnit); err != nil {
log.Warningf("while collecting acquis stats: %s", err)
} else if numRows > 0 {
renderTableTitle(out, "\nAcquisition Metrics:")
t.Render()
}
}

func appsecMetricsToTable(out io.Writer, metrics map[string]map[string]int) {
func appsecMetricsToTable(out io.Writer, metrics map[string]map[string]int, noUnit bool) {
t := newTable(out)
t.SetRowLines(false)
t.SetHeaders("Appsec Engine", "Processed", "Blocked")
t.SetAlignment(table.AlignLeft, table.AlignLeft)
keys := []string{"processed", "blocked"}
if numRows, err := metricsToTable(t, metrics, keys); err != nil {
if numRows, err := metricsToTable(t, metrics, keys, noUnit); err != nil {
log.Warningf("while collecting appsec stats: %s", err)
} else if numRows > 0 {
renderTableTitle(out, "\nAppsec Metrics:")
t.Render()
}
}

func appsecRulesToTable(out io.Writer, metrics map[string]map[string]map[string]int) {
func appsecRulesToTable(out io.Writer, metrics map[string]map[string]map[string]int, noUnit bool) {
for appsecEngine, appsecEngineRulesStats := range metrics {
t := newTable(out)
t.SetRowLines(false)
t.SetHeaders("Rule ID", "Triggered")
t.SetAlignment(table.AlignLeft, table.AlignLeft)
keys := []string{"triggered"}
if numRows, err := metricsToTable(t, appsecEngineRulesStats, keys); err != nil {
if numRows, err := metricsToTable(t, appsecEngineRulesStats, keys, noUnit); err != nil {
log.Warningf("while collecting appsec rules stats: %s", err)
} else if numRows > 0 {
renderTableTitle(out, fmt.Sprintf("\nAppsec '%s' Rules Metrics:", appsecEngine))
Expand All @@ -144,15 +144,15 @@ func appsecRulesToTable(out io.Writer, metrics map[string]map[string]map[string]

}

func parserStatsTable(out io.Writer, stats map[string]map[string]int) {
func parserStatsTable(out io.Writer, stats map[string]map[string]int, noUnit bool) {
t := newTable(out)
t.SetRowLines(false)
t.SetHeaders("Parsers", "Hits", "Parsed", "Unparsed")
t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft)

keys := []string{"hits", "parsed", "unparsed"}

if numRows, err := metricsToTable(t, stats, keys); err != nil {
if numRows, err := metricsToTable(t, stats, keys, noUnit); err != nil {
log.Warningf("while collecting parsers stats: %s", err)
} else if numRows > 0 {
renderTableTitle(out, "\nParser Metrics:")
Expand Down
2 changes: 1 addition & 1 deletion cmd/crowdsec-cli/support.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ func collectMetrics() ([]byte, []byte, error) {
}

humanMetrics := bytes.NewBuffer(nil)
err := FormatPrometheusMetrics(humanMetrics, csConfig.Cscli.PrometheusUrl, "human")
err := FormatPrometheusMetrics(humanMetrics, csConfig.Cscli.PrometheusUrl, "human", false)

if err != nil {
return nil, nil, fmt.Errorf("could not fetch promtheus metrics: %s", err)
Expand Down

0 comments on commit 5ff8a03

Please sign in to comment.