2022-10-07 09:05:35 +00:00
|
|
|
|
package main
|
|
|
|
|
|
|
|
|
|
import (
|
2024-02-15 13:34:12 +00:00
|
|
|
|
"errors"
|
2022-10-07 09:05:35 +00:00
|
|
|
|
"fmt"
|
|
|
|
|
"io"
|
|
|
|
|
"sort"
|
2024-02-07 10:10:25 +00:00
|
|
|
|
"strconv"
|
2022-10-07 09:05:35 +00:00
|
|
|
|
|
|
|
|
|
"github.com/aquasecurity/table"
|
|
|
|
|
log "github.com/sirupsen/logrus"
|
2024-02-06 09:07:05 +00:00
|
|
|
|
|
|
|
|
|
"github.com/crowdsecurity/go-cs-lib/maptools"
|
2022-10-07 09:05:35 +00:00
|
|
|
|
)
|
|
|
|
|
|
2024-02-07 10:10:25 +00:00
|
|
|
|
// ErrNilTable means a nil pointer was passed instead of a table instance. This is a programming error.
|
2024-02-15 13:34:12 +00:00
|
|
|
|
var ErrNilTable = errors.New("nil table")
|
2024-02-07 10:10:25 +00:00
|
|
|
|
|
2022-10-07 09:05:35 +00:00
|
|
|
|
func lapiMetricsToTable(t *table.Table, stats map[string]map[string]map[string]int) int {
|
|
|
|
|
// stats: machine -> route -> method -> count
|
|
|
|
|
// sort keys to keep consistent order when printing
|
|
|
|
|
machineKeys := []string{}
|
|
|
|
|
for k := range stats {
|
|
|
|
|
machineKeys = append(machineKeys, k)
|
|
|
|
|
}
|
2024-02-07 10:10:25 +00:00
|
|
|
|
|
2022-10-07 09:05:35 +00:00
|
|
|
|
sort.Strings(machineKeys)
|
|
|
|
|
|
|
|
|
|
numRows := 0
|
2024-02-07 10:10:25 +00:00
|
|
|
|
|
2022-10-07 09:05:35 +00:00
|
|
|
|
for _, machine := range machineKeys {
|
|
|
|
|
// oneRow: route -> method -> count
|
|
|
|
|
machineRow := stats[machine]
|
|
|
|
|
for routeName, route := range machineRow {
|
|
|
|
|
for methodName, count := range route {
|
|
|
|
|
row := []string{
|
|
|
|
|
machine,
|
|
|
|
|
routeName,
|
|
|
|
|
methodName,
|
|
|
|
|
}
|
|
|
|
|
if count != 0 {
|
2024-02-07 10:10:25 +00:00
|
|
|
|
row = append(row, strconv.Itoa(count))
|
2022-10-07 09:05:35 +00:00
|
|
|
|
} else {
|
|
|
|
|
row = append(row, "-")
|
|
|
|
|
}
|
2024-02-07 10:10:25 +00:00
|
|
|
|
|
2022-10-07 09:05:35 +00:00
|
|
|
|
t.AddRow(row...)
|
2024-02-15 13:34:12 +00:00
|
|
|
|
|
2022-10-07 09:05:35 +00:00
|
|
|
|
numRows++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-02-07 10:10:25 +00:00
|
|
|
|
|
2022-10-07 09:05:35 +00:00
|
|
|
|
return numRows
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 17:04:17 +00:00
|
|
|
|
func wlMetricsToTable(t *table.Table, stats map[string]map[string]map[string]int, noUnit bool) (int, error) {
|
|
|
|
|
if t == nil {
|
2024-02-07 10:10:25 +00:00
|
|
|
|
return 0, ErrNilTable
|
2024-02-06 17:04:17 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
numRows := 0
|
|
|
|
|
|
|
|
|
|
for _, name := range maptools.SortedKeys(stats) {
|
|
|
|
|
for _, reason := range maptools.SortedKeys(stats[name]) {
|
2024-02-07 10:10:25 +00:00
|
|
|
|
row := []string{
|
|
|
|
|
name,
|
|
|
|
|
reason,
|
|
|
|
|
"-",
|
|
|
|
|
"-",
|
|
|
|
|
}
|
2024-02-06 17:04:17 +00:00
|
|
|
|
|
|
|
|
|
for _, action := range maptools.SortedKeys(stats[name][reason]) {
|
|
|
|
|
value := stats[name][reason][action]
|
2024-02-07 10:10:25 +00:00
|
|
|
|
|
|
|
|
|
switch action {
|
|
|
|
|
case "whitelisted":
|
|
|
|
|
row[3] = strconv.Itoa(value)
|
|
|
|
|
case "hits":
|
|
|
|
|
row[2] = strconv.Itoa(value)
|
|
|
|
|
default:
|
2024-02-06 17:04:17 +00:00
|
|
|
|
log.Debugf("unexpected counter '%s' for whitelists = %d", action, value)
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-02-07 10:10:25 +00:00
|
|
|
|
|
2024-02-06 17:04:17 +00:00
|
|
|
|
t.AddRow(row...)
|
2024-02-15 13:34:12 +00:00
|
|
|
|
|
2024-02-06 17:04:17 +00:00
|
|
|
|
numRows++
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-02-07 10:10:25 +00:00
|
|
|
|
|
2024-02-06 17:04:17 +00:00
|
|
|
|
return numRows, nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-02 08:45:03 +00:00
|
|
|
|
func metricsToTable(t *table.Table, stats map[string]map[string]int, keys []string, noUnit bool) (int, error) {
|
2022-10-07 09:05:35 +00:00
|
|
|
|
if t == nil {
|
2024-02-07 10:10:25 +00:00
|
|
|
|
return 0, ErrNilTable
|
2022-10-07 09:05:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
numRows := 0
|
2024-02-06 09:07:05 +00:00
|
|
|
|
|
|
|
|
|
for _, alabel := range maptools.SortedKeys(stats) {
|
2022-10-07 09:05:35 +00:00
|
|
|
|
astats, ok := stats[alabel]
|
|
|
|
|
if !ok {
|
|
|
|
|
continue
|
|
|
|
|
}
|
2024-02-07 10:10:25 +00:00
|
|
|
|
|
2022-10-07 09:05:35 +00:00
|
|
|
|
row := []string{
|
|
|
|
|
alabel,
|
|
|
|
|
}
|
2024-02-07 10:10:25 +00:00
|
|
|
|
|
2022-10-07 09:05:35 +00:00
|
|
|
|
for _, sl := range keys {
|
|
|
|
|
if v, ok := astats[sl]; ok && v != 0 {
|
2024-02-07 10:10:25 +00:00
|
|
|
|
numberToShow := strconv.Itoa(v)
|
2022-10-07 09:05:35 +00:00
|
|
|
|
if !noUnit {
|
|
|
|
|
numberToShow = formatNumber(v)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
row = append(row, numberToShow)
|
|
|
|
|
} else {
|
|
|
|
|
row = append(row, "-")
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-02-07 10:10:25 +00:00
|
|
|
|
|
2022-10-07 09:05:35 +00:00
|
|
|
|
t.AddRow(row...)
|
2024-02-15 13:34:12 +00:00
|
|
|
|
|
2022-10-13 13:49:41 +00:00
|
|
|
|
numRows++
|
2022-10-07 09:05:35 +00:00
|
|
|
|
}
|
2024-02-07 10:10:25 +00:00
|
|
|
|
|
2022-10-07 09:05:35 +00:00
|
|
|
|
return numRows, nil
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 09:07:05 +00:00
|
|
|
|
func (s statBucket) Description() (string, string) {
|
2024-02-15 13:34:12 +00:00
|
|
|
|
return "Scenario Metrics",
|
2024-02-07 10:10:25 +00:00
|
|
|
|
`Measure events in different scenarios. Current count is the number of buckets during metrics collection. ` +
|
|
|
|
|
`Overflows are past event-producing buckets, while Expired are the ones that didn’t receive enough events to Overflow.`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s statBucket) Process(bucket, metric string, val int) {
|
|
|
|
|
if _, ok := s[bucket]; !ok {
|
|
|
|
|
s[bucket] = make(map[string]int)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s[bucket][metric] += val
|
2024-02-06 09:07:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s statBucket) Table(out io.Writer, noUnit bool, showEmpty bool) {
|
2022-10-07 09:05:35 +00:00
|
|
|
|
t := newTable(out)
|
|
|
|
|
t.SetRowLines(false)
|
2024-02-15 13:34:12 +00:00
|
|
|
|
t.SetHeaders("Scenario", "Current Count", "Overflows", "Instantiated", "Poured", "Expired")
|
2022-10-07 09:05:35 +00:00
|
|
|
|
t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft)
|
|
|
|
|
|
|
|
|
|
keys := []string{"curr_count", "overflow", "instantiation", "pour", "underflow"}
|
|
|
|
|
|
2024-02-02 09:40:55 +00:00
|
|
|
|
if numRows, err := metricsToTable(t, s, keys, noUnit); err != nil {
|
2024-02-15 13:34:12 +00:00
|
|
|
|
log.Warningf("while collecting scenario stats: %s", err)
|
2024-02-06 09:07:05 +00:00
|
|
|
|
} else if numRows > 0 || showEmpty {
|
|
|
|
|
title, _ := s.Description()
|
2024-02-06 17:04:17 +00:00
|
|
|
|
renderTableTitle(out, "\n"+title+":")
|
2022-10-07 09:05:35 +00:00
|
|
|
|
t.Render()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 09:07:05 +00:00
|
|
|
|
func (s statAcquis) Description() (string, string) {
|
|
|
|
|
return "Acquisition Metrics",
|
2024-02-07 10:10:25 +00:00
|
|
|
|
`Measures the lines read, parsed, and unparsed per datasource. ` +
|
|
|
|
|
`Zero read lines indicate a misconfigured or inactive datasource. ` +
|
|
|
|
|
`Zero parsed lines mean the parser(s) failed. ` +
|
|
|
|
|
`Non-zero parsed lines are fine as crowdsec selects relevant lines.`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s statAcquis) Process(source, metric string, val int) {
|
|
|
|
|
if _, ok := s[source]; !ok {
|
|
|
|
|
s[source] = make(map[string]int)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s[source][metric] += val
|
2024-02-06 09:07:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s statAcquis) Table(out io.Writer, noUnit bool, showEmpty bool) {
|
2022-10-07 09:05:35 +00:00
|
|
|
|
t := newTable(out)
|
|
|
|
|
t.SetRowLines(false)
|
2024-02-06 17:04:17 +00:00
|
|
|
|
t.SetHeaders("Source", "Lines read", "Lines parsed", "Lines unparsed", "Lines poured to bucket", "Lines whitelisted")
|
2022-10-07 09:05:35 +00:00
|
|
|
|
t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft)
|
|
|
|
|
|
2024-02-06 17:04:17 +00:00
|
|
|
|
keys := []string{"reads", "parsed", "unparsed", "pour", "whitelisted"}
|
2022-10-07 09:05:35 +00:00
|
|
|
|
|
2024-02-02 09:40:55 +00:00
|
|
|
|
if numRows, err := metricsToTable(t, s, keys, noUnit); err != nil {
|
2022-10-07 09:05:35 +00:00
|
|
|
|
log.Warningf("while collecting acquis stats: %s", err)
|
2024-02-06 09:07:05 +00:00
|
|
|
|
} else if numRows > 0 || showEmpty {
|
|
|
|
|
title, _ := s.Description()
|
2024-02-06 17:04:17 +00:00
|
|
|
|
renderTableTitle(out, "\n"+title+":")
|
2022-10-07 09:05:35 +00:00
|
|
|
|
t.Render()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 09:07:05 +00:00
|
|
|
|
func (s statAppsecEngine) Description() (string, string) {
|
|
|
|
|
return "Appsec Metrics",
|
|
|
|
|
`Measures the number of parsed and blocked requests by the AppSec Component.`
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-07 10:10:25 +00:00
|
|
|
|
func (s statAppsecEngine) Process(appsecEngine, metric string, val int) {
|
|
|
|
|
if _, ok := s[appsecEngine]; !ok {
|
|
|
|
|
s[appsecEngine] = make(map[string]int)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s[appsecEngine][metric] += val
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 09:07:05 +00:00
|
|
|
|
func (s statAppsecEngine) Table(out io.Writer, noUnit bool, showEmpty bool) {
|
2023-12-07 11:21:04 +00:00
|
|
|
|
t := newTable(out)
|
|
|
|
|
t.SetRowLines(false)
|
|
|
|
|
t.SetHeaders("Appsec Engine", "Processed", "Blocked")
|
|
|
|
|
t.SetAlignment(table.AlignLeft, table.AlignLeft)
|
2024-02-07 10:10:25 +00:00
|
|
|
|
|
2023-12-07 11:21:04 +00:00
|
|
|
|
keys := []string{"processed", "blocked"}
|
2024-02-07 10:10:25 +00:00
|
|
|
|
|
2024-02-02 09:40:55 +00:00
|
|
|
|
if numRows, err := metricsToTable(t, s, keys, noUnit); err != nil {
|
2023-12-07 11:21:04 +00:00
|
|
|
|
log.Warningf("while collecting appsec stats: %s", err)
|
2024-02-06 09:07:05 +00:00
|
|
|
|
} else if numRows > 0 || showEmpty {
|
|
|
|
|
title, _ := s.Description()
|
2024-02-06 17:04:17 +00:00
|
|
|
|
renderTableTitle(out, "\n"+title+":")
|
2023-12-07 11:21:04 +00:00
|
|
|
|
t.Render()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 09:07:05 +00:00
|
|
|
|
func (s statAppsecRule) Description() (string, string) {
|
|
|
|
|
return "Appsec Rule Metrics",
|
|
|
|
|
`Provides “per AppSec Component” information about the number of matches for loaded AppSec Rules.`
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-07 10:10:25 +00:00
|
|
|
|
func (s statAppsecRule) Process(appsecEngine, appsecRule string, metric string, val int) {
|
|
|
|
|
if _, ok := s[appsecEngine]; !ok {
|
|
|
|
|
s[appsecEngine] = make(map[string]map[string]int)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if _, ok := s[appsecEngine][appsecRule]; !ok {
|
|
|
|
|
s[appsecEngine][appsecRule] = make(map[string]int)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s[appsecEngine][appsecRule][metric] += val
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 09:07:05 +00:00
|
|
|
|
func (s statAppsecRule) Table(out io.Writer, noUnit bool, showEmpty bool) {
|
2024-02-02 09:40:55 +00:00
|
|
|
|
for appsecEngine, appsecEngineRulesStats := range s {
|
2023-12-07 11:21:04 +00:00
|
|
|
|
t := newTable(out)
|
|
|
|
|
t.SetRowLines(false)
|
|
|
|
|
t.SetHeaders("Rule ID", "Triggered")
|
|
|
|
|
t.SetAlignment(table.AlignLeft, table.AlignLeft)
|
2024-02-07 10:10:25 +00:00
|
|
|
|
|
2023-12-07 11:21:04 +00:00
|
|
|
|
keys := []string{"triggered"}
|
2024-02-07 10:10:25 +00:00
|
|
|
|
|
2024-02-02 08:45:03 +00:00
|
|
|
|
if numRows, err := metricsToTable(t, appsecEngineRulesStats, keys, noUnit); err != nil {
|
2023-12-07 11:21:04 +00:00
|
|
|
|
log.Warningf("while collecting appsec rules stats: %s", err)
|
2024-02-06 17:04:17 +00:00
|
|
|
|
} else if numRows > 0 || showEmpty {
|
2023-12-07 11:21:04 +00:00
|
|
|
|
renderTableTitle(out, fmt.Sprintf("\nAppsec '%s' Rules Metrics:", appsecEngine))
|
|
|
|
|
t.Render()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 17:04:17 +00:00
|
|
|
|
func (s statWhitelist) Description() (string, string) {
|
|
|
|
|
return "Whitelist Metrics",
|
|
|
|
|
`Tracks the number of events processed and possibly whitelisted by each parser whitelist.`
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-07 10:10:25 +00:00
|
|
|
|
func (s statWhitelist) Process(whitelist, reason, metric string, val int) {
|
|
|
|
|
if _, ok := s[whitelist]; !ok {
|
|
|
|
|
s[whitelist] = make(map[string]map[string]int)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if _, ok := s[whitelist][reason]; !ok {
|
|
|
|
|
s[whitelist][reason] = make(map[string]int)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s[whitelist][reason][metric] += val
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 17:04:17 +00:00
|
|
|
|
func (s statWhitelist) Table(out io.Writer, noUnit bool, showEmpty bool) {
|
|
|
|
|
t := newTable(out)
|
|
|
|
|
t.SetRowLines(false)
|
|
|
|
|
t.SetHeaders("Whitelist", "Reason", "Hits", "Whitelisted")
|
|
|
|
|
t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft)
|
|
|
|
|
|
|
|
|
|
if numRows, err := wlMetricsToTable(t, s, noUnit); err != nil {
|
|
|
|
|
log.Warningf("while collecting parsers stats: %s", err)
|
|
|
|
|
} else if numRows > 0 || showEmpty {
|
|
|
|
|
title, _ := s.Description()
|
|
|
|
|
renderTableTitle(out, "\n"+title+":")
|
|
|
|
|
t.Render()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 09:07:05 +00:00
|
|
|
|
func (s statParser) Description() (string, string) {
|
|
|
|
|
return "Parser Metrics",
|
2024-02-07 10:10:25 +00:00
|
|
|
|
`Tracks the number of events processed by each parser and indicates success of failure. ` +
|
|
|
|
|
`Zero parsed lines means the parer(s) failed. ` +
|
|
|
|
|
`Non-zero unparsed lines are fine as crowdsec select relevant lines.`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s statParser) Process(parser, metric string, val int) {
|
|
|
|
|
if _, ok := s[parser]; !ok {
|
|
|
|
|
s[parser] = make(map[string]int)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s[parser][metric] += val
|
2024-02-06 09:07:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s statParser) Table(out io.Writer, noUnit bool, showEmpty bool) {
|
2022-10-07 09:05:35 +00:00
|
|
|
|
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"}
|
|
|
|
|
|
2024-02-02 09:40:55 +00:00
|
|
|
|
if numRows, err := metricsToTable(t, s, keys, noUnit); err != nil {
|
2023-12-07 11:21:04 +00:00
|
|
|
|
log.Warningf("while collecting parsers stats: %s", err)
|
2024-02-06 09:07:05 +00:00
|
|
|
|
} else if numRows > 0 || showEmpty {
|
|
|
|
|
title, _ := s.Description()
|
2024-02-06 17:04:17 +00:00
|
|
|
|
renderTableTitle(out, "\n"+title+":")
|
2022-10-07 09:05:35 +00:00
|
|
|
|
t.Render()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 09:07:05 +00:00
|
|
|
|
func (s statStash) Description() (string, string) {
|
|
|
|
|
return "Parser Stash Metrics",
|
|
|
|
|
`Tracks the status of stashes that might be created by various parsers and scenarios.`
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-07 10:10:25 +00:00
|
|
|
|
func (s statStash) Process(name, mtype string, val int) {
|
|
|
|
|
s[name] = struct {
|
|
|
|
|
Type string
|
|
|
|
|
Count int
|
|
|
|
|
}{
|
|
|
|
|
Type: mtype,
|
|
|
|
|
Count: val,
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 09:07:05 +00:00
|
|
|
|
func (s statStash) Table(out io.Writer, noUnit bool, showEmpty bool) {
|
2023-01-11 14:01:02 +00:00
|
|
|
|
t := newTable(out)
|
|
|
|
|
t.SetRowLines(false)
|
|
|
|
|
t.SetHeaders("Name", "Type", "Items")
|
|
|
|
|
t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft)
|
|
|
|
|
|
|
|
|
|
// unfortunately, we can't reuse metricsToTable as the structure is too different :/
|
|
|
|
|
numRows := 0
|
2024-02-06 09:07:05 +00:00
|
|
|
|
|
|
|
|
|
for _, alabel := range maptools.SortedKeys(s) {
|
2024-02-02 09:40:55 +00:00
|
|
|
|
astats := s[alabel]
|
2023-01-11 14:01:02 +00:00
|
|
|
|
|
|
|
|
|
row := []string{
|
|
|
|
|
alabel,
|
|
|
|
|
astats.Type,
|
2024-02-07 10:10:25 +00:00
|
|
|
|
strconv.Itoa(astats.Count),
|
2023-01-11 14:01:02 +00:00
|
|
|
|
}
|
|
|
|
|
t.AddRow(row...)
|
2024-02-15 13:34:12 +00:00
|
|
|
|
|
2023-01-11 14:01:02 +00:00
|
|
|
|
numRows++
|
|
|
|
|
}
|
2024-02-07 10:10:25 +00:00
|
|
|
|
|
2024-02-06 09:07:05 +00:00
|
|
|
|
if numRows > 0 || showEmpty {
|
|
|
|
|
title, _ := s.Description()
|
2024-02-06 17:04:17 +00:00
|
|
|
|
renderTableTitle(out, "\n"+title+":")
|
2023-01-11 14:01:02 +00:00
|
|
|
|
t.Render()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 09:07:05 +00:00
|
|
|
|
func (s statLapi) Description() (string, string) {
|
|
|
|
|
return "Local API Metrics",
|
|
|
|
|
`Monitors the requests made to local API routes.`
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-07 10:10:25 +00:00
|
|
|
|
func (s statLapi) Process(route, method string, val int) {
|
|
|
|
|
if _, ok := s[route]; !ok {
|
|
|
|
|
s[route] = make(map[string]int)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s[route][method] += val
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 09:07:05 +00:00
|
|
|
|
func (s statLapi) Table(out io.Writer, noUnit bool, showEmpty bool) {
|
2022-10-07 09:05:35 +00:00
|
|
|
|
t := newTable(out)
|
|
|
|
|
t.SetRowLines(false)
|
|
|
|
|
t.SetHeaders("Route", "Method", "Hits")
|
|
|
|
|
t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft)
|
|
|
|
|
|
|
|
|
|
// unfortunately, we can't reuse metricsToTable as the structure is too different :/
|
|
|
|
|
numRows := 0
|
2024-02-06 09:07:05 +00:00
|
|
|
|
|
|
|
|
|
for _, alabel := range maptools.SortedKeys(s) {
|
2024-02-02 09:40:55 +00:00
|
|
|
|
astats := s[alabel]
|
2022-10-07 09:05:35 +00:00
|
|
|
|
|
|
|
|
|
subKeys := []string{}
|
|
|
|
|
for skey := range astats {
|
|
|
|
|
subKeys = append(subKeys, skey)
|
|
|
|
|
}
|
2024-02-07 10:10:25 +00:00
|
|
|
|
|
2022-10-07 09:05:35 +00:00
|
|
|
|
sort.Strings(subKeys)
|
|
|
|
|
|
|
|
|
|
for _, sl := range subKeys {
|
|
|
|
|
row := []string{
|
|
|
|
|
alabel,
|
|
|
|
|
sl,
|
2024-02-07 10:10:25 +00:00
|
|
|
|
strconv.Itoa(astats[sl]),
|
2022-10-07 09:05:35 +00:00
|
|
|
|
}
|
2024-02-15 13:34:12 +00:00
|
|
|
|
|
2022-10-07 09:05:35 +00:00
|
|
|
|
t.AddRow(row...)
|
2024-02-15 13:34:12 +00:00
|
|
|
|
|
2022-10-07 09:05:35 +00:00
|
|
|
|
numRows++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 09:07:05 +00:00
|
|
|
|
if numRows > 0 || showEmpty {
|
|
|
|
|
title, _ := s.Description()
|
2024-02-06 17:04:17 +00:00
|
|
|
|
renderTableTitle(out, "\n"+title+":")
|
2022-10-07 09:05:35 +00:00
|
|
|
|
t.Render()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 09:07:05 +00:00
|
|
|
|
func (s statLapiMachine) Description() (string, string) {
|
|
|
|
|
return "Local API Machines Metrics",
|
|
|
|
|
`Tracks the number of calls to the local API from each registered machine.`
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-07 10:10:25 +00:00
|
|
|
|
func (s statLapiMachine) Process(machine, route, method string, val int) {
|
|
|
|
|
if _, ok := s[machine]; !ok {
|
|
|
|
|
s[machine] = make(map[string]map[string]int)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if _, ok := s[machine][route]; !ok {
|
|
|
|
|
s[machine][route] = make(map[string]int)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s[machine][route][method] += val
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 09:07:05 +00:00
|
|
|
|
func (s statLapiMachine) Table(out io.Writer, noUnit bool, showEmpty bool) {
|
2022-10-07 09:05:35 +00:00
|
|
|
|
t := newTable(out)
|
|
|
|
|
t.SetRowLines(false)
|
|
|
|
|
t.SetHeaders("Machine", "Route", "Method", "Hits")
|
|
|
|
|
t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft)
|
|
|
|
|
|
2024-02-02 09:40:55 +00:00
|
|
|
|
numRows := lapiMetricsToTable(t, s)
|
2022-10-07 09:05:35 +00:00
|
|
|
|
|
2024-02-06 17:04:17 +00:00
|
|
|
|
if numRows > 0 || showEmpty {
|
2024-02-06 09:07:05 +00:00
|
|
|
|
title, _ := s.Description()
|
2024-02-06 17:04:17 +00:00
|
|
|
|
renderTableTitle(out, "\n"+title+":")
|
2022-10-07 09:05:35 +00:00
|
|
|
|
t.Render()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 09:07:05 +00:00
|
|
|
|
func (s statLapiBouncer) Description() (string, string) {
|
|
|
|
|
return "Local API Bouncers Metrics",
|
|
|
|
|
`Tracks total hits to remediation component related API routes.`
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-07 10:10:25 +00:00
|
|
|
|
func (s statLapiBouncer) Process(bouncer, route, method string, val int) {
|
|
|
|
|
if _, ok := s[bouncer]; !ok {
|
|
|
|
|
s[bouncer] = make(map[string]map[string]int)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if _, ok := s[bouncer][route]; !ok {
|
|
|
|
|
s[bouncer][route] = make(map[string]int)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s[bouncer][route][method] += val
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 09:07:05 +00:00
|
|
|
|
func (s statLapiBouncer) Table(out io.Writer, noUnit bool, showEmpty bool) {
|
2022-10-07 09:05:35 +00:00
|
|
|
|
t := newTable(out)
|
|
|
|
|
t.SetRowLines(false)
|
|
|
|
|
t.SetHeaders("Bouncer", "Route", "Method", "Hits")
|
|
|
|
|
t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft)
|
|
|
|
|
|
2024-02-02 09:40:55 +00:00
|
|
|
|
numRows := lapiMetricsToTable(t, s)
|
2022-10-07 09:05:35 +00:00
|
|
|
|
|
2024-02-06 09:07:05 +00:00
|
|
|
|
if numRows > 0 || showEmpty {
|
|
|
|
|
title, _ := s.Description()
|
2024-02-06 17:04:17 +00:00
|
|
|
|
renderTableTitle(out, "\n"+title+":")
|
2022-10-07 09:05:35 +00:00
|
|
|
|
t.Render()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 09:07:05 +00:00
|
|
|
|
func (s statLapiDecision) Description() (string, string) {
|
|
|
|
|
return "Local API Bouncers Decisions",
|
|
|
|
|
`Tracks the number of empty/non-empty answers from LAPI to bouncers that are working in "live" mode.`
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-07 10:10:25 +00:00
|
|
|
|
func (s statLapiDecision) Process(bouncer, fam string, val int) {
|
|
|
|
|
if _, ok := s[bouncer]; !ok {
|
|
|
|
|
s[bouncer] = struct {
|
|
|
|
|
NonEmpty int
|
|
|
|
|
Empty int
|
|
|
|
|
}{}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
x := s[bouncer]
|
|
|
|
|
|
|
|
|
|
switch fam {
|
|
|
|
|
case "cs_lapi_decisions_ko_total":
|
|
|
|
|
x.Empty += val
|
|
|
|
|
case "cs_lapi_decisions_ok_total":
|
|
|
|
|
x.NonEmpty += val
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s[bouncer] = x
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 09:07:05 +00:00
|
|
|
|
func (s statLapiDecision) Table(out io.Writer, noUnit bool, showEmpty bool) {
|
2022-10-07 09:05:35 +00:00
|
|
|
|
t := newTable(out)
|
|
|
|
|
t.SetRowLines(false)
|
|
|
|
|
t.SetHeaders("Bouncer", "Empty answers", "Non-empty answers")
|
|
|
|
|
t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft)
|
|
|
|
|
|
|
|
|
|
numRows := 0
|
2024-02-07 10:10:25 +00:00
|
|
|
|
|
2024-02-02 09:40:55 +00:00
|
|
|
|
for bouncer, hits := range s {
|
2022-10-07 09:05:35 +00:00
|
|
|
|
t.AddRow(
|
|
|
|
|
bouncer,
|
2024-02-07 10:10:25 +00:00
|
|
|
|
strconv.Itoa(hits.Empty),
|
|
|
|
|
strconv.Itoa(hits.NonEmpty),
|
2022-10-07 09:05:35 +00:00
|
|
|
|
)
|
2024-02-15 13:34:12 +00:00
|
|
|
|
|
2022-10-07 09:05:35 +00:00
|
|
|
|
numRows++
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 17:04:17 +00:00
|
|
|
|
if numRows > 0 || showEmpty {
|
2024-02-06 09:07:05 +00:00
|
|
|
|
title, _ := s.Description()
|
2024-02-06 17:04:17 +00:00
|
|
|
|
renderTableTitle(out, "\n"+title+":")
|
2022-10-07 09:05:35 +00:00
|
|
|
|
t.Render()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 09:07:05 +00:00
|
|
|
|
func (s statDecision) Description() (string, string) {
|
|
|
|
|
return "Local API Decisions",
|
2024-02-07 10:10:25 +00:00
|
|
|
|
`Provides information about all currently active decisions. ` +
|
|
|
|
|
`Includes both local (crowdsec) and global decisions (CAPI), and lists subscriptions (lists).`
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s statDecision) Process(reason, origin, action string, val int) {
|
|
|
|
|
if _, ok := s[reason]; !ok {
|
|
|
|
|
s[reason] = make(map[string]map[string]int)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if _, ok := s[reason][origin]; !ok {
|
|
|
|
|
s[reason][origin] = make(map[string]int)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
s[reason][origin][action] += val
|
2024-02-06 09:07:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (s statDecision) Table(out io.Writer, noUnit bool, showEmpty bool) {
|
2022-10-07 09:05:35 +00:00
|
|
|
|
t := newTable(out)
|
|
|
|
|
t.SetRowLines(false)
|
|
|
|
|
t.SetHeaders("Reason", "Origin", "Action", "Count")
|
|
|
|
|
t.SetAlignment(table.AlignLeft, table.AlignLeft, table.AlignLeft, table.AlignLeft)
|
|
|
|
|
|
|
|
|
|
numRows := 0
|
2024-02-07 10:10:25 +00:00
|
|
|
|
|
2024-02-02 09:40:55 +00:00
|
|
|
|
for reason, origins := range s {
|
2022-10-07 09:05:35 +00:00
|
|
|
|
for origin, actions := range origins {
|
|
|
|
|
for action, hits := range actions {
|
|
|
|
|
t.AddRow(
|
|
|
|
|
reason,
|
|
|
|
|
origin,
|
|
|
|
|
action,
|
2024-02-07 10:10:25 +00:00
|
|
|
|
strconv.Itoa(hits),
|
2022-10-07 09:05:35 +00:00
|
|
|
|
)
|
2024-02-15 13:34:12 +00:00
|
|
|
|
|
2022-10-07 09:05:35 +00:00
|
|
|
|
numRows++
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 17:04:17 +00:00
|
|
|
|
if numRows > 0 || showEmpty {
|
2024-02-06 09:07:05 +00:00
|
|
|
|
title, _ := s.Description()
|
2024-02-06 17:04:17 +00:00
|
|
|
|
renderTableTitle(out, "\n"+title+":")
|
2022-10-07 09:05:35 +00:00
|
|
|
|
t.Render()
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 09:07:05 +00:00
|
|
|
|
func (s statAlert) Description() (string, string) {
|
|
|
|
|
return "Local API Alerts",
|
|
|
|
|
`Tracks the total number of past and present alerts for the installed scenarios.`
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-07 10:10:25 +00:00
|
|
|
|
func (s statAlert) Process(reason string, val int) {
|
|
|
|
|
s[reason] += val
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 09:07:05 +00:00
|
|
|
|
func (s statAlert) Table(out io.Writer, noUnit bool, showEmpty bool) {
|
2022-10-07 09:05:35 +00:00
|
|
|
|
t := newTable(out)
|
|
|
|
|
t.SetRowLines(false)
|
|
|
|
|
t.SetHeaders("Reason", "Count")
|
|
|
|
|
t.SetAlignment(table.AlignLeft, table.AlignLeft)
|
|
|
|
|
|
|
|
|
|
numRows := 0
|
2024-02-07 10:10:25 +00:00
|
|
|
|
|
2024-02-02 09:40:55 +00:00
|
|
|
|
for scenario, hits := range s {
|
2022-10-07 09:05:35 +00:00
|
|
|
|
t.AddRow(
|
|
|
|
|
scenario,
|
2024-02-07 10:10:25 +00:00
|
|
|
|
strconv.Itoa(hits),
|
2022-10-07 09:05:35 +00:00
|
|
|
|
)
|
2024-02-15 13:34:12 +00:00
|
|
|
|
|
2022-10-07 09:05:35 +00:00
|
|
|
|
numRows++
|
|
|
|
|
}
|
|
|
|
|
|
2024-02-06 17:04:17 +00:00
|
|
|
|
if numRows > 0 || showEmpty {
|
2024-02-06 09:07:05 +00:00
|
|
|
|
title, _ := s.Description()
|
2024-02-06 17:04:17 +00:00
|
|
|
|
renderTableTitle(out, "\n"+title+":")
|
2022-10-07 09:05:35 +00:00
|
|
|
|
t.Render()
|
|
|
|
|
}
|
|
|
|
|
}
|