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

refact "cscli metrics" part 1 #2805

Merged
merged 2 commits into from
Feb 2, 2024
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.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@
"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 @@
}

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 @@
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

Check warning on line 322 in cmd/crowdsec-cli/metrics.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/metrics.go#L322

Added line #L322 was not covered by tests
}

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 @@
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 @@
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 {

Check warning on line 137 in cmd/crowdsec-cli/metrics_table.go

View check run for this annotation

Codecov / codecov/patch

cmd/crowdsec-cli/metrics_table.go#L137

Added line #L137 was not covered by tests
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 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
Loading