metrics.go 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  1. package main
  2. import (
  3. "fmt"
  4. "net/http"
  5. "time"
  6. "github.com/prometheus/client_golang/prometheus"
  7. "github.com/prometheus/client_golang/prometheus/promhttp"
  8. log "github.com/sirupsen/logrus"
  9. "github.com/crowdsecurity/go-cs-lib/trace"
  10. "github.com/crowdsecurity/go-cs-lib/version"
  11. v1 "github.com/crowdsecurity/crowdsec/pkg/apiserver/controllers/v1"
  12. "github.com/crowdsecurity/crowdsec/pkg/cache"
  13. "github.com/crowdsecurity/crowdsec/pkg/csconfig"
  14. "github.com/crowdsecurity/crowdsec/pkg/database"
  15. "github.com/crowdsecurity/crowdsec/pkg/exprhelpers"
  16. leaky "github.com/crowdsecurity/crowdsec/pkg/leakybucket"
  17. "github.com/crowdsecurity/crowdsec/pkg/parser"
  18. )
  19. /*prometheus*/
  20. var globalParserHits = prometheus.NewCounterVec(
  21. prometheus.CounterOpts{
  22. Name: "cs_parser_hits_total",
  23. Help: "Total events entered the parser.",
  24. },
  25. []string{"source", "type"},
  26. )
  27. var globalParserHitsOk = prometheus.NewCounterVec(
  28. prometheus.CounterOpts{
  29. Name: "cs_parser_hits_ok_total",
  30. Help: "Total events were successfully parsed.",
  31. },
  32. []string{"source", "type"},
  33. )
  34. var globalParserHitsKo = prometheus.NewCounterVec(
  35. prometheus.CounterOpts{
  36. Name: "cs_parser_hits_ko_total",
  37. Help: "Total events were unsuccessfully parsed.",
  38. },
  39. []string{"source", "type"},
  40. )
  41. var globalBucketPourKo = prometheus.NewCounter(
  42. prometheus.CounterOpts{
  43. Name: "cs_bucket_pour_ko_total",
  44. Help: "Total events were not poured in a bucket.",
  45. },
  46. )
  47. var globalBucketPourOk = prometheus.NewCounter(
  48. prometheus.CounterOpts{
  49. Name: "cs_bucket_pour_ok_total",
  50. Help: "Total events were poured in at least one bucket.",
  51. },
  52. )
  53. var globalCsInfo = prometheus.NewGauge(
  54. prometheus.GaugeOpts{
  55. Name: "cs_info",
  56. Help: "Information about Crowdsec.",
  57. ConstLabels: prometheus.Labels{"version": version.String()},
  58. },
  59. )
  60. var globalActiveDecisions = prometheus.NewGaugeVec(
  61. prometheus.GaugeOpts{
  62. Name: "cs_active_decisions",
  63. Help: "Number of active decisions.",
  64. },
  65. []string{"reason", "origin", "action"},
  66. )
  67. var globalAlerts = prometheus.NewGaugeVec(
  68. prometheus.GaugeOpts{
  69. Name: "cs_alerts",
  70. Help: "Number of alerts (excluding CAPI).",
  71. },
  72. []string{"reason"},
  73. )
  74. var globalParsingHistogram = prometheus.NewHistogramVec(
  75. prometheus.HistogramOpts{
  76. Help: "Time spent parsing a line",
  77. Name: "cs_parsing_time_seconds",
  78. Buckets: []float64{0.0005, 0.001, 0.0015, 0.002, 0.0025, 0.003, 0.004, 0.005, 0.0075, 0.01},
  79. },
  80. []string{"type", "source"},
  81. )
  82. var globalPourHistogram = prometheus.NewHistogramVec(
  83. prometheus.HistogramOpts{
  84. Name: "cs_bucket_pour_seconds",
  85. Help: "Time spent pouring an event to buckets.",
  86. Buckets: []float64{0.001, 0.002, 0.005, 0.01, 0.015, 0.02, 0.03, 0.04, 0.05},
  87. },
  88. []string{"type", "source"},
  89. )
  90. func computeDynamicMetrics(next http.Handler, dbClient *database.Client) http.HandlerFunc {
  91. return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
  92. //update cache metrics (stash)
  93. cache.UpdateCacheMetrics()
  94. //update cache metrics (regexp)
  95. exprhelpers.UpdateRegexpCacheMetrics()
  96. //decision metrics are only relevant for LAPI
  97. if dbClient == nil {
  98. next.ServeHTTP(w, r)
  99. return
  100. }
  101. decisionsFilters := make(map[string][]string, 0)
  102. decisions, err := dbClient.QueryDecisionCountByScenario(decisionsFilters)
  103. if err != nil {
  104. log.Errorf("Error querying decisions for metrics: %v", err)
  105. next.ServeHTTP(w, r)
  106. return
  107. }
  108. globalActiveDecisions.Reset()
  109. for _, d := range decisions {
  110. globalActiveDecisions.With(prometheus.Labels{"reason": d.Scenario, "origin": d.Origin, "action": d.Type}).Set(float64(d.Count))
  111. }
  112. globalAlerts.Reset()
  113. alertsFilter := map[string][]string{
  114. "include_capi": {"false"},
  115. }
  116. alerts, err := dbClient.AlertsCountPerScenario(alertsFilter)
  117. if err != nil {
  118. log.Errorf("Error querying alerts for metrics: %v", err)
  119. next.ServeHTTP(w, r)
  120. return
  121. }
  122. for k, v := range alerts {
  123. globalAlerts.With(prometheus.Labels{"reason": k}).Set(float64(v))
  124. }
  125. next.ServeHTTP(w, r)
  126. })
  127. }
  128. func registerPrometheus(config *csconfig.PrometheusCfg) {
  129. if !config.Enabled {
  130. return
  131. }
  132. // Registering prometheus
  133. // If in aggregated mode, do not register events associated with a source, to keep the cardinality low
  134. if config.Level == "aggregated" {
  135. log.Infof("Loading aggregated prometheus collectors")
  136. prometheus.MustRegister(globalParserHits, globalParserHitsOk, globalParserHitsKo,
  137. globalCsInfo, globalParsingHistogram, globalPourHistogram,
  138. leaky.BucketsUnderflow, leaky.BucketsCanceled, leaky.BucketsInstantiation, leaky.BucketsOverflow,
  139. v1.LapiRouteHits,
  140. leaky.BucketsCurrentCount,
  141. cache.CacheMetrics, exprhelpers.RegexpCacheMetrics,
  142. )
  143. } else {
  144. log.Infof("Loading prometheus collectors")
  145. prometheus.MustRegister(globalParserHits, globalParserHitsOk, globalParserHitsKo,
  146. parser.NodesHits, parser.NodesHitsOk, parser.NodesHitsKo,
  147. globalCsInfo, globalParsingHistogram, globalPourHistogram,
  148. v1.LapiRouteHits, v1.LapiMachineHits, v1.LapiBouncerHits, v1.LapiNilDecisions, v1.LapiNonNilDecisions, v1.LapiResponseTime,
  149. leaky.BucketsPour, leaky.BucketsUnderflow, leaky.BucketsCanceled, leaky.BucketsInstantiation, leaky.BucketsOverflow, leaky.BucketsCurrentCount,
  150. globalActiveDecisions, globalAlerts,
  151. cache.CacheMetrics, exprhelpers.RegexpCacheMetrics,
  152. )
  153. }
  154. }
  155. func servePrometheus(config *csconfig.PrometheusCfg, dbClient *database.Client, apiReady chan bool, agentReady chan bool) {
  156. if !config.Enabled {
  157. return
  158. }
  159. defer trace.CatchPanic("crowdsec/servePrometheus")
  160. http.Handle("/metrics", computeDynamicMetrics(promhttp.Handler(), dbClient))
  161. <-apiReady
  162. <-agentReady
  163. log.Debugf("serving metrics after %s ms", time.Since(crowdsecT0))
  164. if err := http.ListenAndServe(fmt.Sprintf("%s:%d", config.ListenAddr, config.ListenPort), nil); err != nil {
  165. log.Warningf("prometheus: %s", err)
  166. }
  167. }