From 69e346b97cb408e3072f12e975234061f371b376 Mon Sep 17 00:00:00 2001 From: marco Date: Thu, 1 Feb 2024 10:07:08 +0100 Subject: [PATCH 1/2] refact "cscli metrics" --- cmd/crowdsec-cli/bouncers.go | 5 +-- cmd/crowdsec-cli/main.go | 4 +- cmd/crowdsec-cli/metrics.go | 68 +++++++++++++++++-------------- cmd/crowdsec-cli/metrics_table.go | 22 +++++----- cmd/crowdsec-cli/support.go | 2 +- 5 files changed, 54 insertions(+), 47 deletions(-) diff --git a/cmd/crowdsec-cli/bouncers.go b/cmd/crowdsec-cli/bouncers.go index 410827b3159..d2685901ebb 100644 --- a/cmd/crowdsec-cli/bouncers.go +++ b/cmd/crowdsec-cli/bouncers.go @@ -16,7 +16,6 @@ import ( "github.com/crowdsecurity/crowdsec/cmd/crowdsec-cli/require" middlewares "github.com/crowdsecurity/crowdsec/pkg/apiserver/middlewares/v1" - "github.com/crowdsecurity/crowdsec/pkg/csconfig" "github.com/crowdsecurity/crowdsec/pkg/database" "github.com/crowdsecurity/crowdsec/pkg/types" ) @@ -38,10 +37,10 @@ func askYesNo(message string, defaultAnswer bool) (bool, error) { type cliBouncers struct { db *database.Client - cfg func() *csconfig.Config + cfg configGetter } -func NewCLIBouncers(getconfig func() *csconfig.Config) *cliBouncers { +func NewCLIBouncers(getconfig configGetter) *cliBouncers { return &cliBouncers{ cfg: getconfig, } diff --git a/cmd/crowdsec-cli/main.go b/cmd/crowdsec-cli/main.go index fda4cddc2bc..a1d4fe823f5 100644 --- a/cmd/crowdsec-cli/main.go +++ b/cmd/crowdsec-cli/main.go @@ -29,6 +29,8 @@ var mergedConfig string // flagBranch overrides the value in csConfig.Cscli.HubBranch var flagBranch = "" +type configGetter func() *csconfig.Config + func initConfig() { var err error @@ -191,7 +193,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().NewCommand()) - cmd.AddCommand(NewMetricsCmd()) + cmd.AddCommand(NewCLIMetrics(getconfig).NewCommand()) cmd.AddCommand(NewCLIDashboard().NewCommand()) cmd.AddCommand(NewCLIDecisions().NewCommand()) cmd.AddCommand(NewCLIAlerts().NewCommand()) diff --git a/cmd/crowdsec-cli/metrics.go b/cmd/crowdsec-cli/metrics.go index 5b24dc84c91..902d4f0f445 100644 --- a/cmd/crowdsec-cli/metrics.go +++ b/cmd/crowdsec-cli/metrics.go @@ -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) @@ -256,9 +267,9 @@ 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) @@ -266,8 +277,8 @@ func FormatPrometheusMetrics(out io.Writer, url string, formatType string) error 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 } @@ -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://:/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://:/metrics)") + flags.BoolVar(&noUnit, "no-unit", false, "Show the real number instead of formatted with units") - return cmdMetrics + return cmd } diff --git a/cmd/crowdsec-cli/metrics_table.go b/cmd/crowdsec-cli/metrics_table.go index 80b9cb6e435..287333b5f3c 100644 --- a/cmd/crowdsec-cli/metrics_table.go +++ b/cmd/crowdsec-cli/metrics_table.go @@ -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") } @@ -81,7 +81,7 @@ 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") @@ -89,7 +89,7 @@ func bucketStatsTable(out io.Writer, stats map[string]map[string]int) { 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:") @@ -97,7 +97,7 @@ func bucketStatsTable(out io.Writer, stats map[string]map[string]int) { } } -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") @@ -105,7 +105,7 @@ func acquisStatsTable(out io.Writer, stats map[string]map[string]int) { 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:") @@ -113,13 +113,13 @@ func acquisStatsTable(out io.Writer, stats map[string]map[string]int) { } } -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:") @@ -127,14 +127,14 @@ func appsecMetricsToTable(out io.Writer, metrics map[string]map[string]int) { } } -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)) @@ -144,7 +144,7 @@ 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") @@ -152,7 +152,7 @@ func parserStatsTable(out io.Writer, stats map[string]map[string]int) { 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:") diff --git a/cmd/crowdsec-cli/support.go b/cmd/crowdsec-cli/support.go index 47768e7c2ee..abbe845b660 100644 --- a/cmd/crowdsec-cli/support.go +++ b/cmd/crowdsec-cli/support.go @@ -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) From ece71c9d13b0f691fb67422a29cf2a116be9262b Mon Sep 17 00:00:00 2001 From: marco Date: Thu, 1 Feb 2024 11:27:29 +0100 Subject: [PATCH 2/2] refact "cscli metrics" part 2 --- cmd/crowdsec-cli/metrics.go | 243 ++++++++++++++++-------------- cmd/crowdsec-cli/metrics_table.go | 60 ++++---- 2 files changed, 155 insertions(+), 148 deletions(-) diff --git a/cmd/crowdsec-cli/metrics.go b/cmd/crowdsec-cli/metrics.go index 902d4f0f445..ad255e847db 100644 --- a/cmd/crowdsec-cli/metrics.go +++ b/cmd/crowdsec-cli/metrics.go @@ -19,6 +19,27 @@ import ( "github.com/crowdsecurity/go-cs-lib/trace" ) +type ( + statAcquis map[string]map[string]int + statParser map[string]map[string]int + statBucket map[string]map[string]int + statLapi map[string]map[string]int + statLapiMachine map[string]map[string]map[string]int + statLapiBouncer map[string]map[string]map[string]int + statLapiDecision map[string]struct { + NonEmpty int + Empty int + } + statDecision map[string]map[string]map[string]int + statAppsecEngine map[string]map[string]int + statAppsecRule map[string]map[string]map[string]int + statAlert map[string]int + statStash map[string]struct { + Type string + Count int + } +) + type cliMetrics struct { cfg configGetter } @@ -29,7 +50,6 @@ func NewCLIMetrics(getconfig configGetter) *cliMetrics { } } - // FormatPrometheusMetrics is a complete rip from prom2json func FormatPrometheusMetrics(out io.Writer, url string, formatType string, noUnit bool) error { mfChan := make(chan *dto.MetricFamily, 1024) @@ -63,24 +83,19 @@ func FormatPrometheusMetrics(out io.Writer, url string, formatType string, noUni log.Debugf("Finished reading prometheus output, %d entries", len(result)) /*walk*/ - lapi_decisions_stats := map[string]struct { - NonEmpty int - Empty int - }{} - acquis_stats := map[string]map[string]int{} - parsers_stats := map[string]map[string]int{} - buckets_stats := map[string]map[string]int{} - lapi_stats := map[string]map[string]int{} - lapi_machine_stats := map[string]map[string]map[string]int{} - lapi_bouncer_stats := map[string]map[string]map[string]int{} - decisions_stats := map[string]map[string]map[string]int{} - appsec_engine_stats := map[string]map[string]int{} - appsec_rule_stats := map[string]map[string]map[string]int{} - alerts_stats := map[string]int{} - stash_stats := map[string]struct { - Type string - Count int - }{} + + mAcquis := statAcquis{} + mParser := statParser{} + mBucket := statBucket{} + mLapi := statLapi{} + mLapiMachine := statLapiMachine{} + mLapiBouncer := statLapiBouncer{} + mLapiDecision := statLapiDecision{} + mDecision := statDecision{} + mAppsecEngine := statAppsecEngine{} + mAppsecRule := statAppsecRule{} + mAlert := statAlert{} + mStash := statStash{} for idx, fam := range result { if !strings.HasPrefix(fam.Name, "cs_") { @@ -127,138 +142,138 @@ func FormatPrometheusMetrics(out io.Writer, url string, formatType string, noUni switch fam.Name { /*buckets*/ case "cs_bucket_created_total": - if _, ok := buckets_stats[name]; !ok { - buckets_stats[name] = make(map[string]int) + if _, ok := mBucket[name]; !ok { + mBucket[name] = make(map[string]int) } - buckets_stats[name]["instantiation"] += ival + mBucket[name]["instantiation"] += ival case "cs_buckets": - if _, ok := buckets_stats[name]; !ok { - buckets_stats[name] = make(map[string]int) + if _, ok := mBucket[name]; !ok { + mBucket[name] = make(map[string]int) } - buckets_stats[name]["curr_count"] += ival + mBucket[name]["curr_count"] += ival case "cs_bucket_overflowed_total": - if _, ok := buckets_stats[name]; !ok { - buckets_stats[name] = make(map[string]int) + if _, ok := mBucket[name]; !ok { + mBucket[name] = make(map[string]int) } - buckets_stats[name]["overflow"] += ival + mBucket[name]["overflow"] += ival case "cs_bucket_poured_total": - if _, ok := buckets_stats[name]; !ok { - buckets_stats[name] = make(map[string]int) + if _, ok := mBucket[name]; !ok { + mBucket[name] = make(map[string]int) } - if _, ok := acquis_stats[source]; !ok { - acquis_stats[source] = make(map[string]int) + if _, ok := mAcquis[source]; !ok { + mAcquis[source] = make(map[string]int) } - buckets_stats[name]["pour"] += ival - acquis_stats[source]["pour"] += ival + mBucket[name]["pour"] += ival + mAcquis[source]["pour"] += ival case "cs_bucket_underflowed_total": - if _, ok := buckets_stats[name]; !ok { - buckets_stats[name] = make(map[string]int) + if _, ok := mBucket[name]; !ok { + mBucket[name] = make(map[string]int) } - buckets_stats[name]["underflow"] += ival + mBucket[name]["underflow"] += ival /*acquis*/ case "cs_parser_hits_total": - if _, ok := acquis_stats[source]; !ok { - acquis_stats[source] = make(map[string]int) + if _, ok := mAcquis[source]; !ok { + mAcquis[source] = make(map[string]int) } - acquis_stats[source]["reads"] += ival + mAcquis[source]["reads"] += ival case "cs_parser_hits_ok_total": - if _, ok := acquis_stats[source]; !ok { - acquis_stats[source] = make(map[string]int) + if _, ok := mAcquis[source]; !ok { + mAcquis[source] = make(map[string]int) } - acquis_stats[source]["parsed"] += ival + mAcquis[source]["parsed"] += ival case "cs_parser_hits_ko_total": - if _, ok := acquis_stats[source]; !ok { - acquis_stats[source] = make(map[string]int) + if _, ok := mAcquis[source]; !ok { + mAcquis[source] = make(map[string]int) } - acquis_stats[source]["unparsed"] += ival + mAcquis[source]["unparsed"] += ival case "cs_node_hits_total": - if _, ok := parsers_stats[name]; !ok { - parsers_stats[name] = make(map[string]int) + if _, ok := mParser[name]; !ok { + mParser[name] = make(map[string]int) } - parsers_stats[name]["hits"] += ival + mParser[name]["hits"] += ival case "cs_node_hits_ok_total": - if _, ok := parsers_stats[name]; !ok { - parsers_stats[name] = make(map[string]int) + if _, ok := mParser[name]; !ok { + mParser[name] = make(map[string]int) } - parsers_stats[name]["parsed"] += ival + mParser[name]["parsed"] += ival case "cs_node_hits_ko_total": - if _, ok := parsers_stats[name]; !ok { - parsers_stats[name] = make(map[string]int) + if _, ok := mParser[name]; !ok { + mParser[name] = make(map[string]int) } - parsers_stats[name]["unparsed"] += ival + mParser[name]["unparsed"] += ival case "cs_lapi_route_requests_total": - if _, ok := lapi_stats[route]; !ok { - lapi_stats[route] = make(map[string]int) + if _, ok := mLapi[route]; !ok { + mLapi[route] = make(map[string]int) } - lapi_stats[route][method] += ival + mLapi[route][method] += ival case "cs_lapi_machine_requests_total": - if _, ok := lapi_machine_stats[machine]; !ok { - lapi_machine_stats[machine] = make(map[string]map[string]int) + if _, ok := mLapiMachine[machine]; !ok { + mLapiMachine[machine] = make(map[string]map[string]int) } - if _, ok := lapi_machine_stats[machine][route]; !ok { - lapi_machine_stats[machine][route] = make(map[string]int) + if _, ok := mLapiMachine[machine][route]; !ok { + mLapiMachine[machine][route] = make(map[string]int) } - lapi_machine_stats[machine][route][method] += ival + mLapiMachine[machine][route][method] += ival case "cs_lapi_bouncer_requests_total": - if _, ok := lapi_bouncer_stats[bouncer]; !ok { - lapi_bouncer_stats[bouncer] = make(map[string]map[string]int) + if _, ok := mLapiBouncer[bouncer]; !ok { + mLapiBouncer[bouncer] = make(map[string]map[string]int) } - if _, ok := lapi_bouncer_stats[bouncer][route]; !ok { - lapi_bouncer_stats[bouncer][route] = make(map[string]int) + if _, ok := mLapiBouncer[bouncer][route]; !ok { + mLapiBouncer[bouncer][route] = make(map[string]int) } - lapi_bouncer_stats[bouncer][route][method] += ival + mLapiBouncer[bouncer][route][method] += ival case "cs_lapi_decisions_ko_total", "cs_lapi_decisions_ok_total": - if _, ok := lapi_decisions_stats[bouncer]; !ok { - lapi_decisions_stats[bouncer] = struct { + if _, ok := mLapiDecision[bouncer]; !ok { + mLapiDecision[bouncer] = struct { NonEmpty int Empty int }{} } - x := lapi_decisions_stats[bouncer] + x := mLapiDecision[bouncer] if fam.Name == "cs_lapi_decisions_ko_total" { x.Empty += ival } else if fam.Name == "cs_lapi_decisions_ok_total" { x.NonEmpty += ival } - lapi_decisions_stats[bouncer] = x + mLapiDecision[bouncer] = x case "cs_active_decisions": - if _, ok := decisions_stats[reason]; !ok { - decisions_stats[reason] = make(map[string]map[string]int) + if _, ok := mDecision[reason]; !ok { + mDecision[reason] = make(map[string]map[string]int) } - if _, ok := decisions_stats[reason][origin]; !ok { - decisions_stats[reason][origin] = make(map[string]int) + if _, ok := mDecision[reason][origin]; !ok { + mDecision[reason][origin] = make(map[string]int) } - decisions_stats[reason][origin][action] += ival + mDecision[reason][origin][action] += ival case "cs_alerts": - /*if _, ok := alerts_stats[scenario]; !ok { - alerts_stats[scenario] = make(map[string]int) + /*if _, ok := mAlert[scenario]; !ok { + mAlert[scenario] = make(map[string]int) }*/ - alerts_stats[reason] += ival + mAlert[reason] += ival case "cs_cache_size": - stash_stats[name] = struct { + mStash[name] = struct { Type string Count int }{Type: mtype, Count: ival} case "cs_appsec_reqs_total": - if _, ok := appsec_engine_stats[metric.Labels["appsec_engine"]]; !ok { - appsec_engine_stats[metric.Labels["appsec_engine"]] = make(map[string]int, 0) + if _, ok := mAppsecEngine[metric.Labels["appsec_engine"]]; !ok { + mAppsecEngine[metric.Labels["appsec_engine"]] = make(map[string]int, 0) } - appsec_engine_stats[metric.Labels["appsec_engine"]]["processed"] = ival + mAppsecEngine[metric.Labels["appsec_engine"]]["processed"] = ival case "cs_appsec_block_total": - if _, ok := appsec_engine_stats[metric.Labels["appsec_engine"]]; !ok { - appsec_engine_stats[metric.Labels["appsec_engine"]] = make(map[string]int, 0) + if _, ok := mAppsecEngine[metric.Labels["appsec_engine"]]; !ok { + mAppsecEngine[metric.Labels["appsec_engine"]] = make(map[string]int, 0) } - appsec_engine_stats[metric.Labels["appsec_engine"]]["blocked"] = ival + mAppsecEngine[metric.Labels["appsec_engine"]]["blocked"] = ival case "cs_appsec_rule_hits": appsecEngine := metric.Labels["appsec_engine"] ruleID := metric.Labels["rule_name"] - if _, ok := appsec_rule_stats[appsecEngine]; !ok { - appsec_rule_stats[appsecEngine] = make(map[string]map[string]int, 0) + if _, ok := mAppsecRule[appsecEngine]; !ok { + mAppsecRule[appsecEngine] = make(map[string]map[string]int, 0) } - if _, ok := appsec_rule_stats[appsecEngine][ruleID]; !ok { - appsec_rule_stats[appsecEngine][ruleID] = make(map[string]int, 0) + if _, ok := mAppsecRule[appsecEngine][ruleID]; !ok { + mAppsecRule[appsecEngine][ruleID] = make(map[string]int, 0) } - appsec_rule_stats[appsecEngine][ruleID]["triggered"] = ival + mAppsecRule[appsecEngine][ruleID]["triggered"] = ival default: log.Debugf("unknown: %+v", fam.Name) continue @@ -267,33 +282,33 @@ func FormatPrometheusMetrics(out io.Writer, url string, formatType string, noUni } if formatType == "human" { - 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, noUnit) - appsecRulesToTable(out, appsec_rule_stats, noUnit) + mAcquis.table(out, noUnit) + mBucket.table(out, noUnit) + mParser.table(out, noUnit) + mLapi.table(out) + mLapiMachine.table(out) + mLapiBouncer.table(out) + mLapiDecision.table(out) + mDecision.table(out) + mAlert.table(out) + mStash.table(out) + mAppsecEngine.table(out, noUnit) + mAppsecRule.table(out, noUnit) return nil } stats := make(map[string]any) - stats["acquisition"] = acquis_stats - stats["buckets"] = buckets_stats - stats["parsers"] = parsers_stats - stats["lapi"] = lapi_stats - stats["lapi_machine"] = lapi_machine_stats - stats["lapi_bouncer"] = lapi_bouncer_stats - stats["lapi_decisions"] = lapi_decisions_stats - stats["decisions"] = decisions_stats - stats["alerts"] = alerts_stats - stats["stash"] = stash_stats + stats["acquisition"] = mAcquis + stats["buckets"] = mBucket + stats["parsers"] = mParser + stats["lapi"] = mLapi + stats["lapi_machine"] = mLapiMachine + stats["lapi_bouncer"] = mLapiBouncer + stats["lapi_decisions"] = mLapiDecision + stats["decisions"] = mDecision + stats["alerts"] = mAlert + stats["stash"] = mStash switch formatType { case "json": diff --git a/cmd/crowdsec-cli/metrics_table.go b/cmd/crowdsec-cli/metrics_table.go index 287333b5f3c..835277aa4ee 100644 --- a/cmd/crowdsec-cli/metrics_table.go +++ b/cmd/crowdsec-cli/metrics_table.go @@ -81,7 +81,7 @@ 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, noUnit bool) { +func (s statBucket) table(out io.Writer, noUnit bool) { t := newTable(out) t.SetRowLines(false) t.SetHeaders("Bucket", "Current Count", "Overflows", "Instantiated", "Poured", "Expired") @@ -89,7 +89,7 @@ func bucketStatsTable(out io.Writer, stats map[string]map[string]int, noUnit boo keys := []string{"curr_count", "overflow", "instantiation", "pour", "underflow"} - if numRows, err := metricsToTable(t, stats, keys, noUnit); err != nil { + if numRows, err := metricsToTable(t, s, keys, noUnit); err != nil { log.Warningf("while collecting bucket stats: %s", err) } else if numRows > 0 { renderTableTitle(out, "\nBucket Metrics:") @@ -97,7 +97,7 @@ func bucketStatsTable(out io.Writer, stats map[string]map[string]int, noUnit boo } } -func acquisStatsTable(out io.Writer, stats map[string]map[string]int, noUnit bool) { +func (s statAcquis) table(out io.Writer, noUnit bool) { t := newTable(out) t.SetRowLines(false) t.SetHeaders("Source", "Lines read", "Lines parsed", "Lines unparsed", "Lines poured to bucket") @@ -105,7 +105,7 @@ func acquisStatsTable(out io.Writer, stats map[string]map[string]int, noUnit boo keys := []string{"reads", "parsed", "unparsed", "pour"} - if numRows, err := metricsToTable(t, stats, keys, noUnit); err != nil { + if numRows, err := metricsToTable(t, s, keys, noUnit); err != nil { log.Warningf("while collecting acquis stats: %s", err) } else if numRows > 0 { renderTableTitle(out, "\nAcquisition Metrics:") @@ -113,13 +113,13 @@ func acquisStatsTable(out io.Writer, stats map[string]map[string]int, noUnit boo } } -func appsecMetricsToTable(out io.Writer, metrics map[string]map[string]int, noUnit bool) { +func (s statAppsecEngine) table(out io.Writer, 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, noUnit); err != nil { + if numRows, err := metricsToTable(t, s, keys, noUnit); err != nil { log.Warningf("while collecting appsec stats: %s", err) } else if numRows > 0 { renderTableTitle(out, "\nAppsec Metrics:") @@ -127,8 +127,8 @@ func appsecMetricsToTable(out io.Writer, metrics map[string]map[string]int, noUn } } -func appsecRulesToTable(out io.Writer, metrics map[string]map[string]map[string]int, noUnit bool) { - for appsecEngine, appsecEngineRulesStats := range metrics { +func (s statAppsecRule) table(out io.Writer, noUnit bool) { + for appsecEngine, appsecEngineRulesStats := range s { t := newTable(out) t.SetRowLines(false) t.SetHeaders("Rule ID", "Triggered") @@ -144,7 +144,7 @@ func appsecRulesToTable(out io.Writer, metrics map[string]map[string]map[string] } -func parserStatsTable(out io.Writer, stats map[string]map[string]int, noUnit bool) { +func (s statParser) table(out io.Writer, noUnit bool) { t := newTable(out) t.SetRowLines(false) t.SetHeaders("Parsers", "Hits", "Parsed", "Unparsed") @@ -152,7 +152,7 @@ func parserStatsTable(out io.Writer, stats map[string]map[string]int, noUnit boo keys := []string{"hits", "parsed", "unparsed"} - if numRows, err := metricsToTable(t, stats, keys, noUnit); err != nil { + if numRows, err := metricsToTable(t, s, keys, noUnit); err != nil { log.Warningf("while collecting parsers stats: %s", err) } else if numRows > 0 { renderTableTitle(out, "\nParser Metrics:") @@ -160,11 +160,7 @@ func parserStatsTable(out io.Writer, stats map[string]map[string]int, noUnit boo } } -func stashStatsTable(out io.Writer, stats map[string]struct { - Type string - Count int -}) { - +func (s statStash) table(out io.Writer) { t := newTable(out) t.SetRowLines(false) t.SetHeaders("Name", "Type", "Items") @@ -172,14 +168,14 @@ func stashStatsTable(out io.Writer, stats map[string]struct { // unfortunately, we can't reuse metricsToTable as the structure is too different :/ sortedKeys := []string{} - for k := range stats { + for k := range s { sortedKeys = append(sortedKeys, k) } sort.Strings(sortedKeys) numRows := 0 for _, alabel := range sortedKeys { - astats := stats[alabel] + astats := s[alabel] row := []string{ alabel, @@ -195,7 +191,7 @@ func stashStatsTable(out io.Writer, stats map[string]struct { } } -func lapiStatsTable(out io.Writer, stats map[string]map[string]int) { +func (s statLapi) table(out io.Writer) { t := newTable(out) t.SetRowLines(false) t.SetHeaders("Route", "Method", "Hits") @@ -203,14 +199,14 @@ func lapiStatsTable(out io.Writer, stats map[string]map[string]int) { // unfortunately, we can't reuse metricsToTable as the structure is too different :/ sortedKeys := []string{} - for k := range stats { + for k := range s { sortedKeys = append(sortedKeys, k) } sort.Strings(sortedKeys) numRows := 0 for _, alabel := range sortedKeys { - astats := stats[alabel] + astats := s[alabel] subKeys := []string{} for skey := range astats { @@ -235,13 +231,13 @@ func lapiStatsTable(out io.Writer, stats map[string]map[string]int) { } } -func lapiMachineStatsTable(out io.Writer, stats map[string]map[string]map[string]int) { +func (s statLapiMachine) table(out io.Writer) { t := newTable(out) t.SetRowLines(false) t.SetHeaders("Machine", "Route", "Method", "Hits") t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft) - numRows := lapiMetricsToTable(t, stats) + numRows := lapiMetricsToTable(t, s) if numRows > 0 { renderTableTitle(out, "\nLocal API Machines Metrics:") @@ -249,13 +245,13 @@ func lapiMachineStatsTable(out io.Writer, stats map[string]map[string]map[string } } -func lapiBouncerStatsTable(out io.Writer, stats map[string]map[string]map[string]int) { +func (s statLapiBouncer) table(out io.Writer) { t := newTable(out) t.SetRowLines(false) t.SetHeaders("Bouncer", "Route", "Method", "Hits") t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft) - numRows := lapiMetricsToTable(t, stats) + numRows := lapiMetricsToTable(t, s) if numRows > 0 { renderTableTitle(out, "\nLocal API Bouncers Metrics:") @@ -263,18 +259,14 @@ func lapiBouncerStatsTable(out io.Writer, stats map[string]map[string]map[string } } -func lapiDecisionStatsTable(out io.Writer, stats map[string]struct { - NonEmpty int - Empty int -}, -) { +func (s statLapiDecision) table(out io.Writer) { t := newTable(out) t.SetRowLines(false) t.SetHeaders("Bouncer", "Empty answers", "Non-empty answers") t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft) numRows := 0 - for bouncer, hits := range stats { + for bouncer, hits := range s { t.AddRow( bouncer, fmt.Sprintf("%d", hits.Empty), @@ -289,14 +281,14 @@ func lapiDecisionStatsTable(out io.Writer, stats map[string]struct { } } -func decisionStatsTable(out io.Writer, stats map[string]map[string]map[string]int) { +func (s statDecision) table(out io.Writer) { t := newTable(out) t.SetRowLines(false) t.SetHeaders("Reason", "Origin", "Action", "Count") t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft) numRows := 0 - for reason, origins := range stats { + for reason, origins := range s { for origin, actions := range origins { for action, hits := range actions { t.AddRow( @@ -316,14 +308,14 @@ func decisionStatsTable(out io.Writer, stats map[string]map[string]map[string]in } } -func alertStatsTable(out io.Writer, stats map[string]int) { +func (s statAlert) table(out io.Writer) { t := newTable(out) t.SetRowLines(false) t.SetHeaders("Reason", "Count") t.SetAlignment(table.AlignLeft, table.AlignLeft) numRows := 0 - for scenario, hits := range stats { + for scenario, hits := range s { t.AddRow( scenario, fmt.Sprintf("%d", hits),