@@ -172,10 +172,22 @@ func (e *Exporter) ScrapeHandler(w http.ResponseWriter, r *http.Request) {
172172 return
173173 }
174174
175- // todo: allow passing check-keys?
176-
177175 start := time .Now ()
178- exp , _ := NewRedisExporter (target , e .options )
176+
177+ // todo: this needs a test
178+ checkKeys := r .URL .Query ().Get ("check-keys" )
179+ checkSingleKey := r .URL .Query ().Get ("check-single-keys" )
180+
181+ opts := e .options
182+ opts .CheckKeys = checkKeys
183+ opts .CheckSingleKeys = checkSingleKey
184+
185+ exp , err := NewRedisExporter (target , opts )
186+ if err != nil {
187+ http .Error (w , "NewRedisExporter() err: err" , 400 )
188+ e .targetScrapeRequestErrors .Inc ()
189+ return
190+ }
179191 registry := prometheus .NewRegistry ()
180192 registry .MustRegister (exp )
181193 h := promhttp .HandlerFor (registry , promhttp.HandlerOpts {})
@@ -268,11 +280,10 @@ func NewRedisExporter(redisURI string, opts Options) (*Exporter, error) {
268280 metricMapGauges ["total_system_memory" ] = "total_system_memory_bytes"
269281 }
270282
271- l := []string {}
272283 e .metricDescriptions = map [string ]* prometheus.Desc {}
273- e .metricDescriptions ["up" ] = newMetricDescr (opts .Namespace , "up" , "Information about the Redis instance" , l )
284+ e .metricDescriptions ["up" ] = newMetricDescr (opts .Namespace , "up" , "Information about the Redis instance" , nil )
274285 e .metricDescriptions ["instance_info" ] = newMetricDescr (opts .Namespace , "instance_info" , "Information about the Redis instance" , []string {"role" , "redis_version" , "redis_build_id" , "redis_mode" , "os" })
275- e .metricDescriptions ["last_scrape_duration" ] = newMetricDescr (opts .Namespace , "exporter_last_scrape_duration_seconds" , "The last scrape duration" , l )
286+ e .metricDescriptions ["last_scrape_duration" ] = newMetricDescr (opts .Namespace , "exporter_last_scrape_duration_seconds" , "The last scrape duration" , nil )
276287 e .metricDescriptions ["scrape_error" ] = newMetricDescr (opts .Namespace , "exporter_last_scrape_error" , "The last scrape error status." , []string {"err" })
277288
278289 e .metricDescriptions ["script_values" ] = newMetricDescr (opts .Namespace , "script_value" , "Values returned by the collect script" , []string {"key" })
@@ -281,17 +292,17 @@ func NewRedisExporter(redisURI string, opts Options) (*Exporter, error) {
281292
282293 e .metricDescriptions ["commands_total" ] = newMetricDescr (opts .Namespace , "commands_total" , `Total number of calls per command` , []string {"cmd" })
283294 e .metricDescriptions ["commands_duration_seconds_total" ] = newMetricDescr (opts .Namespace , "commands_duration_seconds_total" , `Total amount of time in seconds spent per command` , []string {"cmd" })
284- e .metricDescriptions ["slowlog_length" ] = newMetricDescr (opts .Namespace , "slowlog_length" , `Total slowlog` , l )
285- e .metricDescriptions ["slowlog_last_id" ] = newMetricDescr (opts .Namespace , "slowlog_last_id" , `Last id of slowlog` , l )
286- e .metricDescriptions ["last_slow_execution_duration_seconds" ] = newMetricDescr (opts .Namespace , "last_slow_execution_duration_seconds" , `The amount of time needed for last slow execution, in seconds` , l )
295+ e .metricDescriptions ["slowlog_length" ] = newMetricDescr (opts .Namespace , "slowlog_length" , `Total slowlog` , nil )
296+ e .metricDescriptions ["slowlog_last_id" ] = newMetricDescr (opts .Namespace , "slowlog_last_id" , `Last id of slowlog` , nil )
297+ e .metricDescriptions ["last_slow_execution_duration_seconds" ] = newMetricDescr (opts .Namespace , "last_slow_execution_duration_seconds" , `The amount of time needed for last slow execution, in seconds` , nil )
287298
288299 e .metricDescriptions ["latency_spike_last" ] = newMetricDescr (opts .Namespace , "latency_spike_last" , `When the latency spike last occurred` , []string {"event_name" })
289300 e .metricDescriptions ["latency_spike_seconds" ] = newMetricDescr (opts .Namespace , "latency_spike_seconds" , `Length of the last latency spike in seconds` , []string {"event_name" })
290301
291302 e .metricDescriptions ["slave_info" ] = newMetricDescr (opts .Namespace , "slave_info" , "Information about the Redis slave" , []string {"master_host" , "master_port" , "read_only" })
292303
293- e .metricDescriptions ["start_time_seconds" ] = newMetricDescr (opts .Namespace , "start_time_seconds" , "Start time of the Redis instance since unix epoch in seconds." , l )
294- e .metricDescriptions ["master_link_up" ] = newMetricDescr (opts .Namespace , "master_link_up" , "Master link status on Redis slave" , l )
304+ e .metricDescriptions ["start_time_seconds" ] = newMetricDescr (opts .Namespace , "start_time_seconds" , "Start time of the Redis instance since unix epoch in seconds." , nil )
305+ e .metricDescriptions ["master_link_up" ] = newMetricDescr (opts .Namespace , "master_link_up" , "Master link status on Redis slave" , nil )
295306 e .metricDescriptions ["connected_slave_offset" ] = newMetricDescr (opts .Namespace , "connected_slave_offset" , "Offset of connected slave" , []string {"slave_ip" , "slave_port" , "slave_state" })
296307 e .metricDescriptions ["connected_slave_lag_seconds" ] = newMetricDescr (opts .Namespace , "connected_slave_lag_seconds" , "Lag of connected slave" , []string {"slave_ip" , "slave_port" , "slave_state" })
297308
@@ -309,11 +320,11 @@ func (e *Exporter) Describe(ch chan<- *prometheus.Desc) {
309320 }
310321
311322 for _ , v := range metricMapGauges {
312- ch <- newMetricDescr (e .options .Namespace , v , v + " metric" , [] string {} )
323+ ch <- newMetricDescr (e .options .Namespace , v , v + " metric" , nil )
313324 }
314325
315326 for _ , v := range metricMapCounters {
316- ch <- newMetricDescr (e .options .Namespace , v , v + " metric" , [] string {} )
327+ ch <- newMetricDescr (e .options .Namespace , v , v + " metric" , nil )
317328 }
318329
319330 ch <- e .totalScrapes .Desc ()
@@ -485,16 +496,16 @@ func (e *Exporter) registerGaugeValue(ch chan<- prometheus.Metric, metric string
485496 e .registerMetricValue (ch , metric , val , prometheus .GaugeValue , labels ... )
486497}
487498
488- func (e * Exporter ) registerMetricValue (ch chan <- prometheus.Metric , metric string , val float64 , valType prometheus.ValueType , labels ... string ) {
499+ func (e * Exporter ) registerMetricValue (ch chan <- prometheus.Metric , metric string , val float64 , valType prometheus.ValueType , labelValues ... string ) {
489500 descr := e .metricDescriptions [metric ]
490501 if descr == nil {
491- descr = newMetricDescr (e .options .Namespace , metric , metric + " metric" , [] string {} )
502+ descr = newMetricDescr (e .options .Namespace , metric , metric + " metric" , nil )
492503 }
493504
494- ch <- prometheus .MustNewConstMetric (descr , valType , val , labels ... )
505+ ch <- prometheus .MustNewConstMetric (descr , valType , val , labelValues ... )
495506}
496507
497- func (e * Exporter ) extractTile38Metrics (ch chan <- prometheus.Metric , info []string ) error {
508+ func (e * Exporter ) extractTile38Metrics (ch chan <- prometheus.Metric , info []string ) {
498509 for i := 0 ; i < len (info ); i += 2 {
499510 log .Debugf ("tile38: %s:%s" , info [i ], info [i + 1 ])
500511
@@ -507,8 +518,6 @@ func (e *Exporter) extractTile38Metrics(ch chan<- prometheus.Metric, info []stri
507518
508519 e .parseAndRegisterMetric (ch , fieldKey , fieldValue )
509520 }
510-
511- return nil
512521}
513522
514523func (e * Exporter ) handleMetricsCommandStats (ch chan <- prometheus.Metric , fieldKey string , fieldValue string ) {
@@ -699,6 +708,89 @@ func (e *Exporter) extractClusterInfoMetrics(ch chan<- prometheus.Metric, info s
699708 return nil
700709}
701710
711+ func (e * Exporter ) extractCheckKeyMetrics (ch chan <- prometheus.Metric , c redis.Conn ) {
712+ log .Debugf ("e.singleKeys: %#v" , e .singleKeys )
713+ allKeys := append ([]dbKeyPair {}, e .singleKeys ... )
714+
715+ log .Debugf ("e.keys: %#v" , e .keys )
716+ scannedKeys , err := getKeysFromPatterns (c , e .keys )
717+ if err != nil {
718+ log .Errorf ("Error expanding key patterns: %#v" , err )
719+ } else {
720+ allKeys = append (allKeys , scannedKeys ... )
721+ }
722+
723+ log .Debugf ("allKeys: %#v" , allKeys )
724+ for _ , k := range allKeys {
725+ if _ , err := doRedisCmd (c , "SELECT" , k .db ); err != nil {
726+ log .Debugf ("Couldn't select database %#v when getting key info." , k .db )
727+ continue
728+ }
729+
730+ info , err := getKeyInfo (c , k .key )
731+ if err != nil {
732+ switch err {
733+ case errNotFound :
734+ log .Debugf ("Key '%s' not found when trying to get type and size." , k .key )
735+ default :
736+ log .Error (err )
737+ }
738+ continue
739+ }
740+ dbLabel := "db" + k .db
741+ e .registerGaugeValue (ch , "key_sizes" , info .size , dbLabel , k .key )
742+
743+ // Only record value metric if value is float-y
744+ if val , err := redis .Float64 (c .Do ("GET" , k .key )); err == nil {
745+ e .registerGaugeValue (ch , "key_values" , val , dbLabel , k .key )
746+ }
747+ }
748+ }
749+
750+ func (e * Exporter ) extractLuaScriptMetrics (ch chan <- prometheus.Metric , c redis.Conn ) {
751+ if e .LuaScript == nil || len (e .LuaScript ) == 0 {
752+ return
753+ }
754+
755+ log .Debug ("Evaluating e.LuaScript" )
756+ kv , err := redis .StringMap (doRedisCmd (c , "EVAL" , e .LuaScript , 0 , 0 ))
757+ if err != nil {
758+ log .Errorf ("LuaScript error: %v" , err )
759+ return
760+ }
761+
762+ if kv != nil {
763+ for key , stringVal := range kv {
764+ if val , err := strconv .ParseFloat (stringVal , 64 ); err == nil {
765+ e .registerGaugeValue (ch , "script_values" , val , key )
766+ }
767+ }
768+ }
769+ }
770+
771+ func (e * Exporter ) extractSlowLogMetrics (ch chan <- prometheus.Metric , c redis.Conn ) {
772+ if reply , err := c .Do ("SLOWLOG" , "LEN" ); err == nil {
773+ e .registerGaugeValue (ch , "slowlog_length" , float64 (reply .(int64 )))
774+ }
775+
776+ if values , err := redis .Values (c .Do ("SLOWLOG" , "GET" , "1" )); err == nil {
777+ var slowlogLastID int64
778+ var lastSlowExecutionDurationSeconds float64
779+
780+ if len (values ) > 0 {
781+ if values , err = redis .Values (values [0 ], err ); err == nil && len (values ) > 0 {
782+ slowlogLastID = values [0 ].(int64 )
783+ if len (values ) > 2 {
784+ lastSlowExecutionDurationSeconds = float64 (values [2 ].(int64 )) / 1e6
785+ }
786+ }
787+ }
788+
789+ e .registerGaugeValue (ch , "slowlog_last_id" , float64 (slowlogLastID ))
790+ e .registerGaugeValue (ch , "last_slow_execution_duration_seconds" , lastSlowExecutionDurationSeconds )
791+ }
792+ }
793+
702794func (e * Exporter ) parseAndRegisterMetric (ch chan <- prometheus.Metric , fieldKey , fieldValue string ) error {
703795 orgMetricName := sanitizeMetricName (fieldKey )
704796 metricName := orgMetricName
@@ -762,7 +854,7 @@ func getKeyInfo(c redis.Conn, key string) (info keyInfo, err error) {
762854 return info , errNotFound
763855 case "string" :
764856 if size , err := redis .Int64 (c .Do ("PFCOUNT" , key )); err == nil {
765- info . keyType = " hyperloglog"
857+ // hyperloglog
766858 info .size = float64 (size )
767859 } else if size , err := redis .Int64 (c .Do ("STRLEN" , key )); err == nil {
768860 info .size = float64 (size )
@@ -880,12 +972,11 @@ func (e *Exporter) scrapeRedisHost(ch chan<- prometheus.Metric) error {
880972 log .Debugf ("aborting for addr: %s - redis err: %s" , e .redisAddr , err )
881973 return err
882974 }
883-
884975 defer c .Close ()
976+
885977 log .Debugf ("connected to: %s" , e .redisAddr )
886978
887979 dbCount := 0
888-
889980 if config , err := redis .Strings (c .Do (e .options .ConfigCommandName , "GET" , "*" )); err == nil {
890981 dbCount , err = e .extractConfigMetrics (ch , config )
891982 if err != nil {
@@ -904,9 +995,8 @@ func (e *Exporter) scrapeRedisHost(ch chan<- prometheus.Metric) error {
904995 return err
905996 }
906997 }
907- isClusterEnabled := strings .Contains (infoAll , "cluster_enabled:1" )
908998
909- if isClusterEnabled {
999+ if strings . Contains ( infoAll , "cluster_enabled:1" ) {
9101000 if clusterInfo , err := redis .String (doRedisCmd (c , "CLUSTER" , "INFO" )); err == nil {
9111001 e .extractClusterInfoMetrics (ch , clusterInfo )
9121002
@@ -946,77 +1036,11 @@ func (e *Exporter) scrapeRedisHost(ch chan<- prometheus.Metric) error {
9461036 }
9471037 }
9481038
949- log .Debugf ("e.singleKeys: %#v" , e .singleKeys )
950- allKeys := append ([]dbKeyPair {}, e .singleKeys ... )
1039+ e .extractCheckKeyMetrics (ch , c )
9511040
952- log .Debugf ("e.keys: %#v" , e .keys )
953- scannedKeys , err := getKeysFromPatterns (c , e .keys )
954- if err != nil {
955- log .Errorf ("Error expanding key patterns: %#v" , err )
956- } else {
957- allKeys = append (allKeys , scannedKeys ... )
958- }
959-
960- log .Debugf ("allKeys: %#v" , allKeys )
961- for _ , k := range allKeys {
962- if _ , err := doRedisCmd (c , "SELECT" , k .db ); err != nil {
963- log .Debugf ("Couldn't select database %#v when getting key info." , k .db )
964- continue
965- }
1041+ e .extractLuaScriptMetrics (ch , c )
9661042
967- info , err := getKeyInfo (c , k .key )
968- if err != nil {
969- switch err {
970- case errNotFound :
971- log .Debugf ("Key '%s' not found when trying to get type and size." , k .key )
972- default :
973- log .Error (err )
974- }
975- continue
976- }
977- dbLabel := "db" + k .db
978- e .registerGaugeValue (ch , "key_sizes" , info .size , dbLabel , k .key )
979-
980- // Only record value metric if value is float-y
981- if val , err := redis .Float64 (c .Do ("GET" , k .key )); err == nil {
982- e .registerGaugeValue (ch , "key_values" , val , dbLabel , k .key )
983- }
984- }
985-
986- if e .LuaScript != nil && len (e .LuaScript ) > 0 {
987- log .Debug ("e.script" )
988- kv , err := redis .StringMap (doRedisCmd (c , "EVAL" , e .LuaScript , 0 , 0 ))
989- if err != nil {
990- log .Errorf ("Collect script error: %v" , err )
991- } else if kv != nil {
992- for key , stringVal := range kv {
993- if val , err := strconv .ParseFloat (stringVal , 64 ); err == nil {
994- e .registerGaugeValue (ch , "script_values" , val , key )
995- }
996- }
997- }
998- }
999-
1000- if reply , err := c .Do ("SLOWLOG" , "LEN" ); err == nil {
1001- e .registerGaugeValue (ch , "slowlog_length" , float64 (reply .(int64 )))
1002- }
1003-
1004- if values , err := redis .Values (c .Do ("SLOWLOG" , "GET" , "1" )); err == nil {
1005- var slowlogLastID int64
1006- var lastSlowExecutionDurationSeconds float64
1007-
1008- if len (values ) > 0 {
1009- if values , err = redis .Values (values [0 ], err ); err == nil && len (values ) > 0 {
1010- slowlogLastID = values [0 ].(int64 )
1011- if len (values ) > 2 {
1012- lastSlowExecutionDurationSeconds = float64 (values [2 ].(int64 )) / 1e6
1013- }
1014- }
1015- }
1016-
1017- e .registerGaugeValue (ch , "slowlog_last_id" , float64 (slowlogLastID ))
1018- e .registerGaugeValue (ch , "last_slow_execution_duration_seconds" , lastSlowExecutionDurationSeconds )
1019- }
1043+ e .extractSlowLogMetrics (ch , c )
10201044
10211045 log .Debugf ("scrapeRedisHost() done" )
10221046 return nil
0 commit comments