|
@@ -6,6 +6,7 @@ import (
|
|
|
v1 "github.com/crowdsecurity/crowdsec/pkg/apiserver/controllers/v1"
|
|
|
"github.com/crowdsecurity/crowdsec/pkg/csconfig"
|
|
|
"github.com/crowdsecurity/crowdsec/pkg/cwversion"
|
|
|
+ "github.com/crowdsecurity/crowdsec/pkg/database"
|
|
|
leaky "github.com/crowdsecurity/crowdsec/pkg/leakybucket"
|
|
|
"github.com/crowdsecurity/crowdsec/pkg/parser"
|
|
|
"github.com/crowdsecurity/crowdsec/pkg/types"
|
|
@@ -62,6 +63,81 @@ var globalCsInfo = prometheus.NewGauge(
|
|
|
},
|
|
|
)
|
|
|
|
|
|
+var globalActiveDecisions = prometheus.NewGaugeVec(
|
|
|
+ prometheus.GaugeOpts{
|
|
|
+ Name: "cs_active_decisions",
|
|
|
+ Help: "Number of active decisions.",
|
|
|
+ },
|
|
|
+ []string{"reason", "origin", "action"},
|
|
|
+)
|
|
|
+
|
|
|
+var globalAlerts = prometheus.NewGaugeVec(
|
|
|
+ prometheus.GaugeOpts{
|
|
|
+ Name: "cs_alerts",
|
|
|
+ Help: "Number of alerts (excluding CAPI).",
|
|
|
+ },
|
|
|
+ []string{"reason"},
|
|
|
+)
|
|
|
+
|
|
|
+var globalParsingHistogram = prometheus.NewHistogramVec(
|
|
|
+ prometheus.HistogramOpts{
|
|
|
+ Help: "Time spent parsing a line",
|
|
|
+ Name: "cs_parsing_time_seconds",
|
|
|
+ Buckets: []float64{0.0005, 0.001, 0.0015, 0.002, 0.0025, 0.003, 0.004, 0.005, 0.0075, 0.01},
|
|
|
+ },
|
|
|
+ []string{"type", "source"},
|
|
|
+)
|
|
|
+
|
|
|
+var globalPourHistogram = prometheus.NewHistogramVec(
|
|
|
+ prometheus.HistogramOpts{
|
|
|
+ Name: "cs_bucket_pour_seconds",
|
|
|
+ Help: "Time spent pouring an event to buckets.",
|
|
|
+ Buckets: []float64{0.001, 0.002, 0.005, 0.01, 0.015, 0.02, 0.03, 0.04, 0.05},
|
|
|
+ },
|
|
|
+ []string{"type", "source"},
|
|
|
+)
|
|
|
+
|
|
|
+func computeDynamicMetrics(next http.Handler, dbClient *database.Client) http.HandlerFunc {
|
|
|
+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
|
+ if dbClient == nil {
|
|
|
+ next.ServeHTTP(w, r)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ decisionsFilters := make(map[string][]string, 0)
|
|
|
+ decisions, err := dbClient.QueryDecisionCountByScenario(decisionsFilters)
|
|
|
+ if err != nil {
|
|
|
+ log.Errorf("Error querying decisions for metrics: %v", err)
|
|
|
+ next.ServeHTTP(w, r)
|
|
|
+ return
|
|
|
+ }
|
|
|
+ globalActiveDecisions.Reset()
|
|
|
+ for _, d := range decisions {
|
|
|
+ globalActiveDecisions.With(prometheus.Labels{"reason": d.Scenario, "origin": d.Origin, "action": d.Type}).Set(float64(d.Count))
|
|
|
+ }
|
|
|
+
|
|
|
+ globalAlerts.Reset()
|
|
|
+
|
|
|
+ alertsFilter := map[string][]string{
|
|
|
+ "include_capi": {"false"},
|
|
|
+ }
|
|
|
+
|
|
|
+ alerts, err := dbClient.AlertsCountPerScenario(alertsFilter)
|
|
|
+
|
|
|
+ if err != nil {
|
|
|
+ log.Errorf("Error querying alerts for metrics: %v", err)
|
|
|
+ next.ServeHTTP(w, r)
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ for k, v := range alerts {
|
|
|
+ globalAlerts.With(prometheus.Labels{"reason": k}).Set(float64(v))
|
|
|
+ }
|
|
|
+
|
|
|
+ next.ServeHTTP(w, r)
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
func registerPrometheus(config *csconfig.PrometheusCfg) {
|
|
|
if !config.Enabled {
|
|
|
return
|
|
@@ -75,13 +151,12 @@ func registerPrometheus(config *csconfig.PrometheusCfg) {
|
|
|
config.ListenPort = 6060
|
|
|
}
|
|
|
|
|
|
- defer types.CatchPanic("crowdsec/registerPrometheus")
|
|
|
/*Registering prometheus*/
|
|
|
/*If in aggregated mode, do not register events associated to a source, keeps cardinality low*/
|
|
|
if config.Level == "aggregated" {
|
|
|
log.Infof("Loading aggregated prometheus collectors")
|
|
|
prometheus.MustRegister(globalParserHits, globalParserHitsOk, globalParserHitsKo,
|
|
|
- globalCsInfo,
|
|
|
+ globalCsInfo, globalParsingHistogram, globalPourHistogram,
|
|
|
leaky.BucketsUnderflow, leaky.BucketsCanceled, leaky.BucketsInstanciation, leaky.BucketsOverflow,
|
|
|
v1.LapiRouteHits,
|
|
|
leaky.BucketsCurrentCount)
|
|
@@ -89,12 +164,22 @@ func registerPrometheus(config *csconfig.PrometheusCfg) {
|
|
|
log.Infof("Loading prometheus collectors")
|
|
|
prometheus.MustRegister(globalParserHits, globalParserHitsOk, globalParserHitsKo,
|
|
|
parser.NodesHits, parser.NodesHitsOk, parser.NodesHitsKo,
|
|
|
- globalCsInfo,
|
|
|
- v1.LapiRouteHits, v1.LapiMachineHits, v1.LapiBouncerHits, v1.LapiNilDecisions, v1.LapiNonNilDecisions,
|
|
|
- leaky.BucketsPour, leaky.BucketsUnderflow, leaky.BucketsCanceled, leaky.BucketsInstanciation, leaky.BucketsOverflow, leaky.BucketsCurrentCount)
|
|
|
+ globalCsInfo, globalParsingHistogram, globalPourHistogram,
|
|
|
+ v1.LapiRouteHits, v1.LapiMachineHits, v1.LapiBouncerHits, v1.LapiNilDecisions, v1.LapiNonNilDecisions, v1.LapiResponseTime,
|
|
|
+ leaky.BucketsPour, leaky.BucketsUnderflow, leaky.BucketsCanceled, leaky.BucketsInstanciation, leaky.BucketsOverflow, leaky.BucketsCurrentCount,
|
|
|
+ globalActiveDecisions, globalAlerts)
|
|
|
|
|
|
}
|
|
|
- http.Handle("/metrics", promhttp.Handler())
|
|
|
+}
|
|
|
+
|
|
|
+func servePrometheus(config *csconfig.PrometheusCfg, dbClient *database.Client) {
|
|
|
+ if !config.Enabled {
|
|
|
+ return
|
|
|
+ }
|
|
|
+
|
|
|
+ defer types.CatchPanic("crowdsec/servePrometheus")
|
|
|
+
|
|
|
+ http.Handle("/metrics", computeDynamicMetrics(promhttp.Handler(), dbClient))
|
|
|
if err := http.ListenAndServe(fmt.Sprintf("%s:%d", config.ListenAddr, config.ListenPort), nil); err != nil {
|
|
|
log.Warningf("prometheus: %s", err)
|
|
|
}
|