Adding upstream version 1.34.4.
Signed-off-by: Daniel Baumann <daniel@debian.org>
This commit is contained in:
parent
e393c3af3f
commit
4978089aab
4963 changed files with 677545 additions and 0 deletions
214
plugins/inputs/redis_sentinel/README.md
Normal file
214
plugins/inputs/redis_sentinel/README.md
Normal file
|
@ -0,0 +1,214 @@
|
|||
# Redis Sentinel Input Plugin
|
||||
|
||||
A plugin for Redis Sentinel to monitor multiple Sentinel instances that are
|
||||
monitoring multiple Redis servers and replicas.
|
||||
|
||||
## Global configuration options <!-- @/docs/includes/plugin_config.md -->
|
||||
|
||||
In addition to the plugin-specific configuration settings, plugins support
|
||||
additional global and plugin configuration settings. These settings are used to
|
||||
modify metrics, tags, and field or create aliases and configure ordering, etc.
|
||||
See the [CONFIGURATION.md][CONFIGURATION.md] for more details.
|
||||
|
||||
[CONFIGURATION.md]: ../../../docs/CONFIGURATION.md#plugins
|
||||
|
||||
## Configuration
|
||||
|
||||
```toml @sample.conf
|
||||
# Read metrics from one or many redis-sentinel servers
|
||||
[[inputs.redis_sentinel]]
|
||||
## specify servers via a url matching:
|
||||
## [protocol://][username:password]@address[:port]
|
||||
## e.g.
|
||||
## tcp://localhost:26379
|
||||
## tcp://username:password@192.168.99.100
|
||||
## unix:///var/run/redis-sentinel.sock
|
||||
##
|
||||
## If no servers are specified, then localhost is used as the host.
|
||||
## If no port is specified, 26379 is used
|
||||
# servers = ["tcp://localhost:26379"]
|
||||
|
||||
## Optional TLS Config
|
||||
# tls_ca = "/etc/telegraf/ca.pem"
|
||||
# tls_cert = "/etc/telegraf/cert.pem"
|
||||
# tls_key = "/etc/telegraf/key.pem"
|
||||
## Use TLS but skip chain & host verification
|
||||
# insecure_skip_verify = true
|
||||
```
|
||||
|
||||
## Measurements & Fields
|
||||
|
||||
The plugin gathers the results of these commands and measurements:
|
||||
|
||||
* `sentinel masters` - `redis_sentinel_masters`
|
||||
* `sentinel sentinels` - `redis_sentinels`
|
||||
* `sentinel replicas` - `redis_replicas`
|
||||
* `info all` - `redis_sentinel`
|
||||
|
||||
The `has_quorum` field in `redis_sentinel_masters` is from calling the command
|
||||
`sentinels ckquorum`.
|
||||
|
||||
There are 5 remote network requests made for each server listed in the config.
|
||||
|
||||
## Metrics
|
||||
|
||||
* redis_sentinel_masters
|
||||
* tags:
|
||||
* host
|
||||
* master
|
||||
* port
|
||||
* source
|
||||
|
||||
* fields:
|
||||
* config_epoch (int)
|
||||
* down_after_milliseconds (int)
|
||||
* failover_timeout (int)
|
||||
* flags (string)
|
||||
* has_quorum (bool)
|
||||
* info_refresh (int)
|
||||
* ip (string)
|
||||
* last_ok_ping_reply (int)
|
||||
* last_ping_reply (int)
|
||||
* last_ping_sent (int)
|
||||
* link_pending_commands (int)
|
||||
* link_refcount (int)
|
||||
* num_other_sentinels (int)
|
||||
* num_slaves (int)
|
||||
* parallel_syncs (int)
|
||||
* port (int)
|
||||
* quorum (int)
|
||||
* role_reported (string)
|
||||
* role_reported_time (int)
|
||||
|
||||
* redis_sentinel_sentinels
|
||||
* tags:
|
||||
* host
|
||||
* master
|
||||
* port
|
||||
* sentinel_ip
|
||||
* sentinel_port
|
||||
* source
|
||||
|
||||
* fields:
|
||||
* down_after_milliseconds (int)
|
||||
* flags (string)
|
||||
* last_hello_message (int)
|
||||
* last_ok_ping_reply (int)
|
||||
* last_ping_reply (int)
|
||||
* last_ping_sent (int)
|
||||
* link_pending_commands (int)
|
||||
* link_refcount (int)
|
||||
* name (string)
|
||||
* voted_leader (string)
|
||||
* voted_leader_epoch (int)
|
||||
|
||||
* redis_sentinel_replicas
|
||||
* tags:
|
||||
* host
|
||||
* master
|
||||
* port
|
||||
* replica_ip
|
||||
* replica_port
|
||||
* source
|
||||
|
||||
* fields:
|
||||
* down_after_milliseconds (int)
|
||||
* flags (string)
|
||||
* info_refresh (int)
|
||||
* last_ok_ping_reply (int)
|
||||
* last_ping_reply (int)
|
||||
* last_ping_sent (int)
|
||||
* link_pending_commands (int)
|
||||
* link_refcount (int)
|
||||
* master_host (string)
|
||||
* master_link_down_time (int)
|
||||
* master_link_status (string)
|
||||
* master_port (int)
|
||||
* name (string)
|
||||
* role_reported (string)
|
||||
* role_reported_time (int)
|
||||
* slave_priority (int)
|
||||
* slave_repl_offset (int)
|
||||
|
||||
* redis_sentinel
|
||||
* tags:
|
||||
* host
|
||||
* port
|
||||
* source
|
||||
|
||||
* fields:
|
||||
* active_defrag_hits (int)
|
||||
* active_defrag_key_hits (int)
|
||||
* active_defrag_key_misses (int)
|
||||
* active_defrag_misses (int)
|
||||
* blocked_clients (int)
|
||||
* client_recent_max_input_buffer (int)
|
||||
* client_recent_max_output_buffer (int)
|
||||
* clients (int)
|
||||
* evicted_keys (int)
|
||||
* expired_keys (int)
|
||||
* expired_stale_perc (float)
|
||||
* expired_time_cap_reached_count (int)
|
||||
* instantaneous_input_kbps (float)
|
||||
* instantaneous_ops_per_sec (int)
|
||||
* instantaneous_output_kbps (float)
|
||||
* keyspace_hits (int)
|
||||
* keyspace_misses (int)
|
||||
* latest_fork_usec (int)
|
||||
* lru_clock (int)
|
||||
* migrate_cached_sockets (int)
|
||||
* pubsub_channels (int)
|
||||
* pubsub_patterns (int)
|
||||
* redis_version (string)
|
||||
* rejected_connections (int)
|
||||
* sentinel_masters (int)
|
||||
* sentinel_running_scripts (int)
|
||||
* sentinel_scripts_queue_length (int)
|
||||
* sentinel_simulate_failure_flags (int)
|
||||
* sentinel_tilt (int)
|
||||
* slave_expires_tracked_keys (int)
|
||||
* sync_full (int)
|
||||
* sync_partial_err (int)
|
||||
* sync_partial_ok (int)
|
||||
* total_commands_processed (int)
|
||||
* total_connections_received (int)
|
||||
* total_net_input_bytes (int)
|
||||
* total_net_output_bytes (int)
|
||||
* uptime_ns (int, nanoseconds)
|
||||
* used_cpu_sys (float)
|
||||
* used_cpu_sys_children (float)
|
||||
* used_cpu_user (float)
|
||||
* used_cpu_user_children (float)
|
||||
|
||||
## Example Output
|
||||
|
||||
An example of 2 Redis Sentinel instances monitoring a single master and
|
||||
replica. It produces:
|
||||
|
||||
### redis_sentinel_masters
|
||||
|
||||
```text
|
||||
redis_sentinel_masters,host=somehostname,master=mymaster,port=26380,source=localhost config_epoch=0i,down_after_milliseconds=30000i,failover_timeout=180000i,flags="master",has_quorum=1i,info_refresh=110i,ip="127.0.0.1",last_ok_ping_reply=819i,last_ping_reply=819i,last_ping_sent=0i,link_pending_commands=0i,link_refcount=1i,num_other_sentinels=1i,num_slaves=1i,parallel_syncs=1i,port=6379i,quorum=2i,role_reported="master",role_reported_time=311248i 1570207377000000000
|
||||
redis_sentinel_masters,host=somehostname,master=mymaster,port=26379,source=localhost config_epoch=0i,down_after_milliseconds=30000i,failover_timeout=180000i,flags="master",has_quorum=1i,info_refresh=1650i,ip="127.0.0.1",last_ok_ping_reply=1003i,last_ping_reply=1003i,last_ping_sent=0i,link_pending_commands=0i,link_refcount=1i,num_other_sentinels=1i,num_slaves=1i,parallel_syncs=1i,port=6379i,quorum=2i,role_reported="master",role_reported_time=302990i 1570207377000000000
|
||||
```
|
||||
|
||||
### redis_sentinel_sentinels
|
||||
|
||||
```text
|
||||
redis_sentinel_sentinels,host=somehostname,master=mymaster,port=26380,sentinel_ip=127.0.0.1,sentinel_port=26379,source=localhost down_after_milliseconds=30000i,flags="sentinel",last_hello_message=1337i,last_ok_ping_reply=566i,last_ping_reply=566i,last_ping_sent=0i,link_pending_commands=0i,link_refcount=1i,name="fd7444de58ecc00f2685cd89fc11ff96c72f0569",voted_leader="?",voted_leader_epoch=0i 1570207377000000000
|
||||
redis_sentinel_sentinels,host=somehostname,master=mymaster,port=26379,sentinel_ip=127.0.0.1,sentinel_port=26380,source=localhost down_after_milliseconds=30000i,flags="sentinel",last_hello_message=1510i,last_ok_ping_reply=1004i,last_ping_reply=1004i,last_ping_sent=0i,link_pending_commands=0i,link_refcount=1i,name="d06519438fe1b35692cb2ea06d57833c959f9114",voted_leader="?",voted_leader_epoch=0i 1570207377000000000
|
||||
```
|
||||
|
||||
### redis_sentinel_replicas
|
||||
|
||||
```text
|
||||
redis_sentinel_replicas,host=somehostname,master=mymaster,port=26379,replica_ip=127.0.0.1,replica_port=6380,source=localhost down_after_milliseconds=30000i,flags="slave",info_refresh=1651i,last_ok_ping_reply=1005i,last_ping_reply=1005i,last_ping_sent=0i,link_pending_commands=0i,link_refcount=1i,master_host="127.0.0.1",master_link_down_time=0i,master_link_status="ok",master_port=6379i,name="127.0.0.1:6380",role_reported="slave",role_reported_time=302983i,slave_priority=100i,slave_repl_offset=40175i 1570207377000000000
|
||||
redis_sentinel_replicas,host=somehostname,master=mymaster,port=26380,replica_ip=127.0.0.1,replica_port=6380,source=localhost down_after_milliseconds=30000i,flags="slave",info_refresh=111i,last_ok_ping_reply=821i,last_ping_reply=821i,last_ping_sent=0i,link_pending_commands=0i,link_refcount=1i,master_host="127.0.0.1",master_link_down_time=0i,master_link_status="ok",master_port=6379i,name="127.0.0.1:6380",role_reported="slave",role_reported_time=311243i,slave_priority=100i,slave_repl_offset=40441i 1570207377000000000
|
||||
```
|
||||
|
||||
### redis_sentinel
|
||||
|
||||
```text
|
||||
redis_sentinel,host=somehostname,port=26379,source=localhost active_defrag_hits=0i,active_defrag_key_hits=0i,active_defrag_key_misses=0i,active_defrag_misses=0i,blocked_clients=0i,client_recent_max_input_buffer=2i,client_recent_max_output_buffer=0i,clients=3i,evicted_keys=0i,expired_keys=0i,expired_stale_perc=0,expired_time_cap_reached_count=0i,instantaneous_input_kbps=0.01,instantaneous_ops_per_sec=0i,instantaneous_output_kbps=0,keyspace_hits=0i,keyspace_misses=0i,latest_fork_usec=0i,lru_clock=9926289i,migrate_cached_sockets=0i,pubsub_channels=0i,pubsub_patterns=0i,redis_version="5.0.5",rejected_connections=0i,sentinel_masters=1i,sentinel_running_scripts=0i,sentinel_scripts_queue_length=0i,sentinel_simulate_failure_flags=0i,sentinel_tilt=0i,slave_expires_tracked_keys=0i,sync_full=0i,sync_partial_err=0i,sync_partial_ok=0i,total_commands_processed=459i,total_connections_received=6i,total_net_input_bytes=24517i,total_net_output_bytes=14864i,uptime_ns=303000000000i,used_cpu_sys=0.404,used_cpu_sys_children=0,used_cpu_user=0.436,used_cpu_user_children=0 1570207377000000000
|
||||
redis_sentinel,host=somehostname,port=26380,source=localhost active_defrag_hits=0i,active_defrag_key_hits=0i,active_defrag_key_misses=0i,active_defrag_misses=0i,blocked_clients=0i,client_recent_max_input_buffer=2i,client_recent_max_output_buffer=0i,clients=2i,evicted_keys=0i,expired_keys=0i,expired_stale_perc=0,expired_time_cap_reached_count=0i,instantaneous_input_kbps=0.01,instantaneous_ops_per_sec=0i,instantaneous_output_kbps=0,keyspace_hits=0i,keyspace_misses=0i,latest_fork_usec=0i,lru_clock=9926289i,migrate_cached_sockets=0i,pubsub_channels=0i,pubsub_patterns=0i,redis_version="5.0.5",rejected_connections=0i,sentinel_masters=1i,sentinel_running_scripts=0i,sentinel_scripts_queue_length=0i,sentinel_simulate_failure_flags=0i,sentinel_tilt=0i,slave_expires_tracked_keys=0i,sync_full=0i,sync_partial_err=0i,sync_partial_ok=0i,total_commands_processed=442i,total_connections_received=2i,total_net_input_bytes=23861i,total_net_output_bytes=4443i,uptime_ns=312000000000i,used_cpu_sys=0.46,used_cpu_sys_children=0,used_cpu_user=0.416,used_cpu_user_children=0 1570207377000000000
|
||||
```
|
436
plugins/inputs/redis_sentinel/redis_sentinel.go
Normal file
436
plugins/inputs/redis_sentinel/redis_sentinel.go
Normal file
|
@ -0,0 +1,436 @@
|
|||
//go:generate ../../../tools/readme_config_includer/generator
|
||||
package redis_sentinel
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
_ "embed"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/url"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
|
||||
"github.com/go-redis/redis/v7"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/plugins/common/tls"
|
||||
"github.com/influxdata/telegraf/plugins/inputs"
|
||||
)
|
||||
|
||||
//go:embed sample.conf
|
||||
var sampleConfig string
|
||||
|
||||
const (
|
||||
measurementMasters = "redis_sentinel_masters"
|
||||
measurementSentinel = "redis_sentinel"
|
||||
measurementSentinels = "redis_sentinel_sentinels"
|
||||
measurementReplicas = "redis_sentinel_replicas"
|
||||
)
|
||||
|
||||
type RedisSentinel struct {
|
||||
Servers []string `toml:"servers"`
|
||||
tls.ClientConfig
|
||||
|
||||
clients []*redisSentinelClient
|
||||
}
|
||||
|
||||
type redisSentinelClient struct {
|
||||
sentinel *redis.SentinelClient
|
||||
tags map[string]string
|
||||
}
|
||||
|
||||
func (*RedisSentinel) SampleConfig() string {
|
||||
return sampleConfig
|
||||
}
|
||||
|
||||
func (r *RedisSentinel) Init() error {
|
||||
if len(r.Servers) == 0 {
|
||||
r.Servers = []string{"tcp://localhost:26379"}
|
||||
}
|
||||
|
||||
tlsConfig, err := r.ClientConfig.TLSConfig()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
r.clients = make([]*redisSentinelClient, 0, len(r.Servers))
|
||||
for _, serv := range r.Servers {
|
||||
u, err := url.Parse(serv)
|
||||
if err != nil {
|
||||
return fmt.Errorf("unable to parse to address %q: %w", serv, err)
|
||||
}
|
||||
|
||||
username := ""
|
||||
password := ""
|
||||
if u.User != nil {
|
||||
username = u.User.Username()
|
||||
pw, ok := u.User.Password()
|
||||
if ok {
|
||||
password = pw
|
||||
}
|
||||
}
|
||||
|
||||
var address string
|
||||
tags := make(map[string]string, 2)
|
||||
switch u.Scheme {
|
||||
case "tcp":
|
||||
address = u.Host
|
||||
tags["source"] = u.Hostname()
|
||||
tags["port"] = u.Port()
|
||||
case "unix":
|
||||
address = u.Path
|
||||
tags["socket"] = u.Path
|
||||
default:
|
||||
return fmt.Errorf("invalid scheme %q, expected tcp or unix", u.Scheme)
|
||||
}
|
||||
|
||||
sentinel := redis.NewSentinelClient(
|
||||
&redis.Options{
|
||||
Addr: address,
|
||||
Username: username,
|
||||
Password: password,
|
||||
Network: u.Scheme,
|
||||
PoolSize: 1,
|
||||
TLSConfig: tlsConfig,
|
||||
},
|
||||
)
|
||||
|
||||
r.clients = append(r.clients, &redisSentinelClient{
|
||||
sentinel: sentinel,
|
||||
tags: tags,
|
||||
})
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *RedisSentinel) Gather(acc telegraf.Accumulator) error {
|
||||
var wg sync.WaitGroup
|
||||
|
||||
for _, client := range r.clients {
|
||||
wg.Add(1)
|
||||
|
||||
go func(acc telegraf.Accumulator, client *redisSentinelClient) {
|
||||
defer wg.Done()
|
||||
|
||||
masters, err := client.gatherMasterStats(acc)
|
||||
acc.AddError(err)
|
||||
|
||||
for _, master := range masters {
|
||||
acc.AddError(client.gatherReplicaStats(acc, master))
|
||||
acc.AddError(client.gatherSentinelStats(acc, master))
|
||||
}
|
||||
|
||||
acc.AddError(client.gatherInfoStats(acc))
|
||||
}(acc, client)
|
||||
}
|
||||
|
||||
wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Redis list format has string key/values adjacent, so convert to a map for easier use
|
||||
func toMap(vals []interface{}) map[string]string {
|
||||
m := make(map[string]string)
|
||||
|
||||
for idx := 0; idx < len(vals)-1; idx += 2 {
|
||||
key, keyOk := vals[idx].(string)
|
||||
value, valueOk := vals[idx+1].(string)
|
||||
|
||||
if keyOk && valueOk {
|
||||
m[key] = value
|
||||
}
|
||||
}
|
||||
|
||||
return m
|
||||
}
|
||||
|
||||
func castFieldValue(value string, fieldType configFieldType) (interface{}, error) {
|
||||
var castedValue interface{}
|
||||
var err error
|
||||
|
||||
switch fieldType {
|
||||
case configFieldTypeFloat:
|
||||
castedValue, err = strconv.ParseFloat(value, 64)
|
||||
case configFieldTypeInteger:
|
||||
castedValue, err = strconv.ParseInt(value, 10, 64)
|
||||
case configFieldTypeString:
|
||||
castedValue = value
|
||||
default:
|
||||
return nil, fmt.Errorf("unsupported field type %v", fieldType)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("casting value %q failed: %w", value, err)
|
||||
}
|
||||
|
||||
return castedValue, nil
|
||||
}
|
||||
|
||||
func prepareFieldValues(fields map[string]string, typeMap map[string]configFieldType) (map[string]interface{}, error) {
|
||||
preparedFields := make(map[string]interface{})
|
||||
|
||||
for key, val := range fields {
|
||||
key = strings.ReplaceAll(key, "-", "_")
|
||||
|
||||
valType, ok := typeMap[key]
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
castedVal, err := castFieldValue(val, valType)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
preparedFields[key] = castedVal
|
||||
}
|
||||
|
||||
return preparedFields, nil
|
||||
}
|
||||
|
||||
func (client *redisSentinelClient) gatherInfoStats(acc telegraf.Accumulator) error {
|
||||
infoCmd := redis.NewStringCmd("info", "all")
|
||||
if err := client.sentinel.Process(infoCmd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
info, err := infoCmd.Result()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
rdr := strings.NewReader(info)
|
||||
infoTags, infoFields, err := convertSentinelInfoOutput(client.tags, rdr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
acc.AddFields(measurementSentinel, infoFields, infoTags)
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *redisSentinelClient) gatherMasterStats(acc telegraf.Accumulator) ([]string, error) {
|
||||
mastersCmd := redis.NewSliceCmd("sentinel", "masters")
|
||||
if err := client.sentinel.Process(mastersCmd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
masters, err := mastersCmd.Result()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Break out of the loop if one of the items comes out malformed
|
||||
// It's safe to assume that if we fail parsing one item that the rest will fail too
|
||||
// This is because we are iterating over a single server response
|
||||
masterNames := make([]string, 0, len(masters))
|
||||
for _, master := range masters {
|
||||
master, ok := master.([]interface{})
|
||||
if !ok {
|
||||
return masterNames, errors.New("unable to process master response")
|
||||
}
|
||||
|
||||
m := toMap(master)
|
||||
|
||||
masterName, ok := m["name"]
|
||||
if !ok {
|
||||
return masterNames, errors.New("unable to resolve master name")
|
||||
}
|
||||
masterNames = append(masterNames, masterName)
|
||||
|
||||
quorumCmd := redis.NewStringCmd("sentinel", "ckquorum", masterName)
|
||||
quorumErr := client.sentinel.Process(quorumCmd)
|
||||
|
||||
sentinelMastersTags, sentinelMastersFields, err := convertSentinelMastersOutput(client.tags, m, quorumErr)
|
||||
if err != nil {
|
||||
return masterNames, err
|
||||
}
|
||||
acc.AddFields(measurementMasters, sentinelMastersFields, sentinelMastersTags)
|
||||
}
|
||||
|
||||
return masterNames, nil
|
||||
}
|
||||
|
||||
func (client *redisSentinelClient) gatherReplicaStats(acc telegraf.Accumulator, masterName string) error {
|
||||
replicasCmd := redis.NewSliceCmd("sentinel", "replicas", masterName)
|
||||
if err := client.sentinel.Process(replicasCmd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
replicas, err := replicasCmd.Result()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Break out of the loop if one of the items comes out malformed
|
||||
// It's safe to assume that if we fail parsing one item that the rest will fail too
|
||||
// This is because we are iterating over a single server response
|
||||
for _, replica := range replicas {
|
||||
replica, ok := replica.([]interface{})
|
||||
if !ok {
|
||||
return errors.New("unable to process replica response")
|
||||
}
|
||||
|
||||
rm := toMap(replica)
|
||||
replicaTags, replicaFields, err := convertSentinelReplicaOutput(client.tags, masterName, rm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
acc.AddFields(measurementReplicas, replicaFields, replicaTags)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (client *redisSentinelClient) gatherSentinelStats(acc telegraf.Accumulator, masterName string) error {
|
||||
sentinelsCmd := redis.NewSliceCmd("sentinel", "sentinels", masterName)
|
||||
if err := client.sentinel.Process(sentinelsCmd); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
sentinels, err := sentinelsCmd.Result()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Break out of the loop if one of the items comes out malformed
|
||||
// It's safe to assume that if we fail parsing one item that the rest will fail too
|
||||
// This is because we are iterating over a single server response
|
||||
for _, sentinel := range sentinels {
|
||||
sentinel, ok := sentinel.([]interface{})
|
||||
if !ok {
|
||||
return errors.New("unable to process sentinel response")
|
||||
}
|
||||
|
||||
sm := toMap(sentinel)
|
||||
sentinelTags, sentinelFields, err := convertSentinelSentinelsOutput(client.tags, masterName, sm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
acc.AddFields(measurementSentinels, sentinelFields, sentinelTags)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// converts `sentinel masters <name>` output to tags and fields
|
||||
func convertSentinelMastersOutput(globalTags, master map[string]string, quorumErr error) (map[string]string, map[string]interface{}, error) {
|
||||
tags := globalTags
|
||||
|
||||
tags["master"] = master["name"]
|
||||
|
||||
fields, err := prepareFieldValues(master, measurementMastersFields)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
fields["has_quorum"] = quorumErr == nil
|
||||
|
||||
return tags, fields, nil
|
||||
}
|
||||
|
||||
// converts `sentinel sentinels <name>` output to tags and fields
|
||||
func convertSentinelSentinelsOutput(
|
||||
globalTags map[string]string,
|
||||
masterName string,
|
||||
sentinelMaster map[string]string,
|
||||
) (map[string]string, map[string]interface{}, error) {
|
||||
tags := globalTags
|
||||
|
||||
tags["sentinel_ip"] = sentinelMaster["ip"]
|
||||
tags["sentinel_port"] = sentinelMaster["port"]
|
||||
tags["master"] = masterName
|
||||
|
||||
fields, err := prepareFieldValues(sentinelMaster, measurementSentinelsFields)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return tags, fields, nil
|
||||
}
|
||||
|
||||
// converts `sentinel replicas <name>` output to tags and fields
|
||||
func convertSentinelReplicaOutput(
|
||||
globalTags map[string]string,
|
||||
masterName string,
|
||||
replica map[string]string,
|
||||
) (map[string]string, map[string]interface{}, error) {
|
||||
tags := globalTags
|
||||
|
||||
tags["replica_ip"] = replica["ip"]
|
||||
tags["replica_port"] = replica["port"]
|
||||
tags["master"] = masterName
|
||||
|
||||
fields, err := prepareFieldValues(replica, measurementReplicasFields)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
return tags, fields, nil
|
||||
}
|
||||
|
||||
// convertSentinelInfoOutput parses `INFO` command output
|
||||
// Largely copied from the Redis input plugin's gatherInfoOutput()
|
||||
func convertSentinelInfoOutput(globalTags map[string]string, rdr io.Reader) (map[string]string, map[string]interface{}, error) {
|
||||
scanner := bufio.NewScanner(rdr)
|
||||
rawFields := make(map[string]string)
|
||||
|
||||
tags := globalTags
|
||||
|
||||
for scanner.Scan() {
|
||||
line := scanner.Text()
|
||||
if len(line) == 0 {
|
||||
continue
|
||||
}
|
||||
|
||||
// Redis denotes configuration sections with a hashtag
|
||||
// Example of the section header: # Clients
|
||||
if line[0] == '#' {
|
||||
// Nothing interesting here
|
||||
continue
|
||||
}
|
||||
|
||||
parts := strings.SplitN(line, ":", 2)
|
||||
if len(parts) < 2 {
|
||||
// Not a valid configuration option
|
||||
continue
|
||||
}
|
||||
|
||||
key := strings.TrimSpace(parts[0])
|
||||
val := strings.TrimSpace(parts[1])
|
||||
|
||||
rawFields[key] = val
|
||||
}
|
||||
|
||||
fields, err := prepareFieldValues(rawFields, measurementSentinelFields)
|
||||
if err != nil {
|
||||
return nil, nil, err
|
||||
}
|
||||
|
||||
// Rename the field and convert it to nanoseconds
|
||||
secs, ok := fields["uptime_in_seconds"].(int64)
|
||||
if !ok {
|
||||
return nil, nil, fmt.Errorf("uptime type %T is not int64", fields["uptime_in_seconds"])
|
||||
}
|
||||
fields["uptime_ns"] = secs * 1000_000_000
|
||||
delete(fields, "uptime_in_seconds")
|
||||
|
||||
// Rename in order to match the "redis" input plugin
|
||||
fields["clients"] = fields["connected_clients"]
|
||||
delete(fields, "connected_clients")
|
||||
|
||||
return tags, fields, nil
|
||||
}
|
||||
|
||||
func init() {
|
||||
inputs.Add("redis_sentinel", func() telegraf.Input {
|
||||
return &RedisSentinel{}
|
||||
})
|
||||
}
|
373
plugins/inputs/redis_sentinel/redis_sentinel_test.go
Normal file
373
plugins/inputs/redis_sentinel/redis_sentinel_test.go
Normal file
|
@ -0,0 +1,373 @@
|
|||
package redis_sentinel
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"fmt"
|
||||
"os"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/docker/go-connections/nat"
|
||||
"github.com/stretchr/testify/require"
|
||||
"github.com/testcontainers/testcontainers-go/network"
|
||||
"github.com/testcontainers/testcontainers-go/wait"
|
||||
|
||||
"github.com/influxdata/telegraf"
|
||||
"github.com/influxdata/telegraf/testutil"
|
||||
)
|
||||
|
||||
const masterName = "mymaster"
|
||||
const sentinelServicePort = "26379"
|
||||
|
||||
func TestRedisSentinelConnectIntegration(t *testing.T) {
|
||||
if testing.Short() {
|
||||
t.Skip("Skipping integration test in short mode")
|
||||
}
|
||||
|
||||
net, err := network.New(t.Context())
|
||||
require.NoError(t, err)
|
||||
defer func() {
|
||||
require.NoError(t, net.Remove(t.Context()), "terminating network failed")
|
||||
}()
|
||||
|
||||
redis := createRedisContainer(net.Name)
|
||||
err = redis.Start()
|
||||
require.NoError(t, err, "failed to start container")
|
||||
defer redis.Terminate()
|
||||
|
||||
firstSentinel := createSentinelContainer(redis.Name, net.Name, wait.ForAll(
|
||||
wait.ForLog("+monitor master"),
|
||||
wait.ForListeningPort(nat.Port(sentinelServicePort)),
|
||||
))
|
||||
err = firstSentinel.Start()
|
||||
require.NoError(t, err, "failed to start container")
|
||||
defer firstSentinel.Terminate()
|
||||
|
||||
secondSentinel := createSentinelContainer(redis.Name, net.Name, wait.ForAll(
|
||||
wait.ForLog("+sentinel sentinel"),
|
||||
wait.ForListeningPort(nat.Port(sentinelServicePort)),
|
||||
))
|
||||
err = secondSentinel.Start()
|
||||
require.NoError(t, err, "failed to start container")
|
||||
defer secondSentinel.Terminate()
|
||||
|
||||
addr := fmt.Sprintf("tcp://%s:%s", secondSentinel.Address, secondSentinel.Ports[sentinelServicePort])
|
||||
|
||||
r := &RedisSentinel{
|
||||
Servers: []string{addr},
|
||||
}
|
||||
err = r.Init()
|
||||
require.NoError(t, err, "failed to run Init function")
|
||||
|
||||
var acc testutil.Accumulator
|
||||
|
||||
err = acc.GatherError(r.Gather)
|
||||
require.NoError(t, err)
|
||||
|
||||
require.True(t, acc.HasMeasurement("redis_sentinel_masters"), "redis_sentinel_masters measurement is missing")
|
||||
require.True(t, acc.HasMeasurement("redis_sentinel_sentinels"), "redis_sentinel_sentinels measurement is missing")
|
||||
require.True(t, acc.HasMeasurement("redis_sentinel"), "redis_sentinel measurement is missing")
|
||||
}
|
||||
|
||||
func TestRedisSentinelMasters(t *testing.T) {
|
||||
now := time.Now()
|
||||
|
||||
globalTags := map[string]string{
|
||||
"port": "6379",
|
||||
"source": "redis.io",
|
||||
}
|
||||
|
||||
expectedTags := map[string]string{
|
||||
"port": "6379",
|
||||
"source": "redis.io",
|
||||
"master": masterName,
|
||||
}
|
||||
|
||||
// has_quorum is a custom field
|
||||
expectedFields := map[string]interface{}{
|
||||
"config_epoch": 0,
|
||||
"down_after_milliseconds": 30000,
|
||||
"failover_timeout": 180000,
|
||||
"flags": "master",
|
||||
"info_refresh": 8819,
|
||||
"ip": "127.0.0.1",
|
||||
"last_ok_ping_reply": 174,
|
||||
"last_ping_reply": 174,
|
||||
"last_ping_sent": 0,
|
||||
"link_pending_commands": 0,
|
||||
"link_refcount": 1,
|
||||
"num_other_sentinels": 1,
|
||||
"num_slaves": 0,
|
||||
"parallel_syncs": 1,
|
||||
"port": 6379,
|
||||
"quorum": 2,
|
||||
"role_reported": "master",
|
||||
"role_reported_time": 83138826,
|
||||
"has_quorum": true,
|
||||
}
|
||||
|
||||
expectedMetrics := []telegraf.Metric{
|
||||
testutil.MustMetric(measurementMasters, expectedTags, expectedFields, now),
|
||||
}
|
||||
|
||||
sentinelMastersOutput := map[string]string{
|
||||
"config_epoch": "0",
|
||||
"down_after_milliseconds": "30000",
|
||||
"failover_timeout": "180000",
|
||||
"flags": "master",
|
||||
"info_refresh": "8819",
|
||||
"ip": "127.0.0.1",
|
||||
"last_ok_ping_reply": "174",
|
||||
"last_ping_reply": "174",
|
||||
"last_ping_sent": "0",
|
||||
"link_pending_commands": "0",
|
||||
"link_refcount": "1",
|
||||
"name": "mymaster",
|
||||
"num_other_sentinels": "1",
|
||||
"num_slaves": "0",
|
||||
"parallel_syncs": "1",
|
||||
"port": "6379",
|
||||
"quorum": "2",
|
||||
"role_reported": "master",
|
||||
"role_reported_time": "83138826",
|
||||
"runid": "ff3dadd1cfea3043de4d25711d93f01a564562f7",
|
||||
}
|
||||
|
||||
sentinelTags, sentinelFields, sentinelErr := convertSentinelMastersOutput(globalTags, sentinelMastersOutput, nil)
|
||||
require.NoErrorf(t, sentinelErr, "failed converting output: %v", sentinelErr)
|
||||
|
||||
actualMetrics := []telegraf.Metric{
|
||||
testutil.MustMetric(measurementMasters, sentinelTags, sentinelFields, now),
|
||||
}
|
||||
|
||||
testutil.RequireMetricsEqual(t, expectedMetrics, actualMetrics, testutil.IgnoreTime())
|
||||
}
|
||||
|
||||
func TestRedisSentinels(t *testing.T) {
|
||||
now := time.Now()
|
||||
|
||||
globalTags := make(map[string]string)
|
||||
|
||||
expectedTags := map[string]string{
|
||||
"sentinel_ip": "127.0.0.1",
|
||||
"sentinel_port": "26380",
|
||||
"master": masterName,
|
||||
}
|
||||
expectedFields := map[string]interface{}{
|
||||
"name": "adfd343f6b6ecc77e2b9636de6d9f28d4b827521",
|
||||
"flags": "sentinel",
|
||||
"link_pending_commands": 0,
|
||||
"link_refcount": 1,
|
||||
"last_ping_sent": 0,
|
||||
"last_ok_ping_reply": 516,
|
||||
"last_ping_reply": 516,
|
||||
"down_after_milliseconds": 30000,
|
||||
"last_hello_message": 1905,
|
||||
"voted_leader": "?",
|
||||
"voted_leader_epoch": 0,
|
||||
}
|
||||
|
||||
expectedMetrics := []telegraf.Metric{
|
||||
testutil.MustMetric(measurementSentinels, expectedTags, expectedFields, now),
|
||||
}
|
||||
|
||||
sentinelsOutput := map[string]string{
|
||||
"name": "adfd343f6b6ecc77e2b9636de6d9f28d4b827521",
|
||||
"ip": "127.0.0.1",
|
||||
"port": "26380",
|
||||
"runid": "adfd343f6b6ecc77e2b9636de6d9f28d4b827521",
|
||||
"flags": "sentinel",
|
||||
"link_pending_commands": "0",
|
||||
"link_refcount": "1",
|
||||
"last_ping_sent": "0",
|
||||
"last_ok_ping_reply": "516",
|
||||
"last_ping_reply": "516",
|
||||
"down_after_milliseconds": "30000",
|
||||
"last_hello_message": "1905",
|
||||
"voted_leader": "?",
|
||||
"voted_leader_epoch": "0",
|
||||
}
|
||||
|
||||
sentinelTags, sentinelFields, sentinelErr := convertSentinelSentinelsOutput(globalTags, masterName, sentinelsOutput)
|
||||
require.NoErrorf(t, sentinelErr, "failed converting output: %v", sentinelErr)
|
||||
|
||||
actualMetrics := []telegraf.Metric{
|
||||
testutil.MustMetric(measurementSentinels, sentinelTags, sentinelFields, now),
|
||||
}
|
||||
|
||||
testutil.RequireMetricsEqual(t, expectedMetrics, actualMetrics)
|
||||
}
|
||||
|
||||
func TestRedisSentinelReplicas(t *testing.T) {
|
||||
now := time.Now()
|
||||
|
||||
globalTags := make(map[string]string)
|
||||
|
||||
expectedTags := map[string]string{
|
||||
"replica_ip": "127.0.0.1",
|
||||
"replica_port": "6380",
|
||||
"master": masterName,
|
||||
}
|
||||
expectedFields := map[string]interface{}{
|
||||
"down_after_milliseconds": 30000,
|
||||
"flags": "slave",
|
||||
"info_refresh": 8476,
|
||||
"last_ok_ping_reply": 987,
|
||||
"last_ping_reply": 987,
|
||||
"last_ping_sent": 0,
|
||||
"link_pending_commands": 0,
|
||||
"link_refcount": 1,
|
||||
"master_host": "127.0.0.1",
|
||||
"master_link_down_time": 0,
|
||||
"master_link_status": "ok",
|
||||
"master_port": 6379,
|
||||
"name": "127.0.0.1:6380",
|
||||
"role_reported": "slave",
|
||||
"role_reported_time": 10267432,
|
||||
"slave_priority": 100,
|
||||
"slave_repl_offset": 1392400,
|
||||
}
|
||||
|
||||
expectedMetrics := []telegraf.Metric{
|
||||
testutil.MustMetric(measurementReplicas, expectedTags, expectedFields, now),
|
||||
}
|
||||
|
||||
replicasOutput := map[string]string{
|
||||
"down_after_milliseconds": "30000",
|
||||
"flags": "slave",
|
||||
"info_refresh": "8476",
|
||||
"ip": "127.0.0.1",
|
||||
"last_ok_ping_reply": "987",
|
||||
"last_ping_reply": "987",
|
||||
"last_ping_sent": "0",
|
||||
"link_pending_commands": "0",
|
||||
"link_refcount": "1",
|
||||
"master_host": "127.0.0.1",
|
||||
"master_link_down_time": "0",
|
||||
"master_link_status": "ok",
|
||||
"master_port": "6379",
|
||||
"name": "127.0.0.1:6380",
|
||||
"port": "6380",
|
||||
"role_reported": "slave",
|
||||
"role_reported_time": "10267432",
|
||||
"runid": "70e07dad9e450e2d35f1b75338e0a5341b59d710",
|
||||
"slave_priority": "100",
|
||||
"slave_repl_offset": "1392400",
|
||||
}
|
||||
|
||||
sentinelTags, sentinelFields, sentinelErr := convertSentinelReplicaOutput(globalTags, masterName, replicasOutput)
|
||||
require.NoErrorf(t, sentinelErr, "failed converting output: %v", sentinelErr)
|
||||
|
||||
actualMetrics := []telegraf.Metric{
|
||||
testutil.MustMetric(measurementReplicas, sentinelTags, sentinelFields, now),
|
||||
}
|
||||
|
||||
testutil.RequireMetricsEqual(t, expectedMetrics, actualMetrics)
|
||||
}
|
||||
|
||||
func TestRedisSentinelInfoAll(t *testing.T) {
|
||||
now := time.Now()
|
||||
|
||||
globalTags := map[string]string{
|
||||
"port": "6379",
|
||||
"source": "redis.io",
|
||||
}
|
||||
|
||||
expectedTags := map[string]string{
|
||||
"port": "6379",
|
||||
"source": "redis.io",
|
||||
}
|
||||
|
||||
expectedFields := map[string]interface{}{
|
||||
"lru_clock": int64(15585808),
|
||||
"uptime_ns": int64(901000000000),
|
||||
"redis_version": "5.0.5",
|
||||
|
||||
"clients": int64(2),
|
||||
"client_recent_max_input_buffer": int64(2),
|
||||
"client_recent_max_output_buffer": int64(0),
|
||||
"blocked_clients": int64(0),
|
||||
|
||||
"used_cpu_sys": float64(0.786872),
|
||||
"used_cpu_user": float64(0.939455),
|
||||
"used_cpu_sys_children": float64(0.000000),
|
||||
"used_cpu_user_children": float64(0.000000),
|
||||
|
||||
"total_connections_received": int64(2),
|
||||
"total_commands_processed": int64(6),
|
||||
"instantaneous_ops_per_sec": int64(0),
|
||||
"total_net_input_bytes": int64(124),
|
||||
"total_net_output_bytes": int64(10148),
|
||||
"instantaneous_input_kbps": float64(0.00),
|
||||
"instantaneous_output_kbps": float64(0.00),
|
||||
"rejected_connections": int64(0),
|
||||
"sync_full": int64(0),
|
||||
"sync_partial_ok": int64(0),
|
||||
"sync_partial_err": int64(0),
|
||||
"expired_keys": int64(0),
|
||||
"expired_stale_perc": float64(0.00),
|
||||
"expired_time_cap_reached_count": int64(0),
|
||||
"evicted_keys": int64(0),
|
||||
"keyspace_hits": int64(0),
|
||||
"keyspace_misses": int64(0),
|
||||
"pubsub_channels": int64(0),
|
||||
"pubsub_patterns": int64(0),
|
||||
"latest_fork_usec": int64(0),
|
||||
"migrate_cached_sockets": int64(0),
|
||||
"slave_expires_tracked_keys": int64(0),
|
||||
"active_defrag_hits": int64(0),
|
||||
"active_defrag_misses": int64(0),
|
||||
"active_defrag_key_hits": int64(0),
|
||||
"active_defrag_key_misses": int64(0),
|
||||
|
||||
"sentinel_masters": int64(2),
|
||||
"sentinel_running_scripts": int64(0),
|
||||
"sentinel_scripts_queue_length": int64(0),
|
||||
"sentinel_simulate_failure_flags": int64(0),
|
||||
"sentinel_tilt": int64(0),
|
||||
}
|
||||
|
||||
expectedMetrics := []telegraf.Metric{
|
||||
testutil.MustMetric(measurementSentinel, expectedTags, expectedFields, now),
|
||||
}
|
||||
|
||||
sentinelInfoResponse, err := os.ReadFile("testdata/sentinel.info.response")
|
||||
require.NoErrorf(t, err, "could not init fixture: %v", err)
|
||||
|
||||
rdr := bufio.NewReader(bytes.NewReader(sentinelInfoResponse))
|
||||
|
||||
sentinelTags, sentinelFields, sentinelErr := convertSentinelInfoOutput(globalTags, rdr)
|
||||
require.NoErrorf(t, sentinelErr, "failed converting output: %v", sentinelErr)
|
||||
|
||||
actualMetrics := []telegraf.Metric{
|
||||
testutil.MustMetric(measurementSentinel, sentinelTags, sentinelFields, now),
|
||||
}
|
||||
|
||||
testutil.RequireMetricsEqual(t, expectedMetrics, actualMetrics)
|
||||
}
|
||||
|
||||
func createRedisContainer(networkName string) testutil.Container {
|
||||
return testutil.Container{
|
||||
Image: "redis:7.0-alpine",
|
||||
Name: "telegraf-test-redis-sentinel-redis",
|
||||
Networks: []string{networkName},
|
||||
ExposedPorts: []string{"6379"},
|
||||
WaitingFor: wait.ForAll(
|
||||
wait.ForLog("Ready to accept connections"),
|
||||
wait.ForListeningPort(nat.Port("6379")),
|
||||
),
|
||||
}
|
||||
}
|
||||
|
||||
func createSentinelContainer(redisAddress, networkName string, waitingFor wait.Strategy) testutil.Container {
|
||||
return testutil.Container{
|
||||
Image: "bitnami/redis-sentinel:7.0",
|
||||
ExposedPorts: []string{sentinelServicePort},
|
||||
Networks: []string{networkName},
|
||||
Env: map[string]string{
|
||||
"REDIS_MASTER_HOST": redisAddress,
|
||||
},
|
||||
WaitingFor: waitingFor,
|
||||
}
|
||||
}
|
113
plugins/inputs/redis_sentinel/redis_sentinel_types.go
Normal file
113
plugins/inputs/redis_sentinel/redis_sentinel_types.go
Normal file
|
@ -0,0 +1,113 @@
|
|||
package redis_sentinel
|
||||
|
||||
type configFieldType int32
|
||||
|
||||
const (
|
||||
configFieldTypeInteger configFieldType = iota
|
||||
configFieldTypeString
|
||||
configFieldTypeFloat
|
||||
)
|
||||
|
||||
// Supported fields for "redis_sentinel_masters"
|
||||
var measurementMastersFields = map[string]configFieldType{
|
||||
"config_epoch": configFieldTypeInteger,
|
||||
"down_after_milliseconds": configFieldTypeInteger,
|
||||
"failover_timeout": configFieldTypeInteger,
|
||||
"flags": configFieldTypeString,
|
||||
"info_refresh": configFieldTypeInteger,
|
||||
"ip": configFieldTypeString,
|
||||
"last_ok_ping_reply": configFieldTypeInteger,
|
||||
"last_ping_reply": configFieldTypeInteger,
|
||||
"last_ping_sent": configFieldTypeInteger,
|
||||
"link_pending_commands": configFieldTypeInteger,
|
||||
"link_refcount": configFieldTypeInteger,
|
||||
"num_other_sentinels": configFieldTypeInteger,
|
||||
"num_slaves": configFieldTypeInteger,
|
||||
"parallel_syncs": configFieldTypeInteger,
|
||||
"port": configFieldTypeInteger,
|
||||
"quorum": configFieldTypeInteger,
|
||||
"role_reported": configFieldTypeString,
|
||||
"role_reported_time": configFieldTypeInteger,
|
||||
}
|
||||
|
||||
// Supported fields for "redis_sentinel"
|
||||
var measurementSentinelFields = map[string]configFieldType{
|
||||
"active_defrag_hits": configFieldTypeInteger,
|
||||
"active_defrag_key_hits": configFieldTypeInteger,
|
||||
"active_defrag_key_misses": configFieldTypeInteger,
|
||||
"active_defrag_misses": configFieldTypeInteger,
|
||||
"blocked_clients": configFieldTypeInteger,
|
||||
"client_recent_max_input_buffer": configFieldTypeInteger,
|
||||
"client_recent_max_output_buffer": configFieldTypeInteger,
|
||||
"connected_clients": configFieldTypeInteger, // Renamed to "clients"
|
||||
"evicted_keys": configFieldTypeInteger,
|
||||
"expired_keys": configFieldTypeInteger,
|
||||
"expired_stale_perc": configFieldTypeFloat,
|
||||
"expired_time_cap_reached_count": configFieldTypeInteger,
|
||||
"instantaneous_input_kbps": configFieldTypeFloat,
|
||||
"instantaneous_ops_per_sec": configFieldTypeInteger,
|
||||
"instantaneous_output_kbps": configFieldTypeFloat,
|
||||
"keyspace_hits": configFieldTypeInteger,
|
||||
"keyspace_misses": configFieldTypeInteger,
|
||||
"latest_fork_usec": configFieldTypeInteger,
|
||||
"lru_clock": configFieldTypeInteger,
|
||||
"migrate_cached_sockets": configFieldTypeInteger,
|
||||
"pubsub_channels": configFieldTypeInteger,
|
||||
"pubsub_patterns": configFieldTypeInteger,
|
||||
"redis_version": configFieldTypeString,
|
||||
"rejected_connections": configFieldTypeInteger,
|
||||
"sentinel_masters": configFieldTypeInteger,
|
||||
"sentinel_running_scripts": configFieldTypeInteger,
|
||||
"sentinel_scripts_queue_length": configFieldTypeInteger,
|
||||
"sentinel_simulate_failure_flags": configFieldTypeInteger,
|
||||
"sentinel_tilt": configFieldTypeInteger,
|
||||
"slave_expires_tracked_keys": configFieldTypeInteger,
|
||||
"sync_full": configFieldTypeInteger,
|
||||
"sync_partial_err": configFieldTypeInteger,
|
||||
"sync_partial_ok": configFieldTypeInteger,
|
||||
"total_commands_processed": configFieldTypeInteger,
|
||||
"total_connections_received": configFieldTypeInteger,
|
||||
"total_net_input_bytes": configFieldTypeInteger,
|
||||
"total_net_output_bytes": configFieldTypeInteger,
|
||||
"uptime_in_seconds": configFieldTypeInteger, // Renamed to "uptime_ns"
|
||||
"used_cpu_sys": configFieldTypeFloat,
|
||||
"used_cpu_sys_children": configFieldTypeFloat,
|
||||
"used_cpu_user": configFieldTypeFloat,
|
||||
"used_cpu_user_children": configFieldTypeFloat,
|
||||
}
|
||||
|
||||
// Supported fields for "redis_sentinel_sentinels"
|
||||
var measurementSentinelsFields = map[string]configFieldType{
|
||||
"down_after_milliseconds": configFieldTypeInteger,
|
||||
"flags": configFieldTypeString,
|
||||
"last_hello_message": configFieldTypeInteger,
|
||||
"last_ok_ping_reply": configFieldTypeInteger,
|
||||
"last_ping_reply": configFieldTypeInteger,
|
||||
"last_ping_sent": configFieldTypeInteger,
|
||||
"link_pending_commands": configFieldTypeInteger,
|
||||
"link_refcount": configFieldTypeInteger,
|
||||
"name": configFieldTypeString,
|
||||
"voted_leader": configFieldTypeString,
|
||||
"voted_leader_epoch": configFieldTypeInteger,
|
||||
}
|
||||
|
||||
// Supported fields for "redis_sentinel_replicas"
|
||||
var measurementReplicasFields = map[string]configFieldType{
|
||||
"down_after_milliseconds": configFieldTypeInteger,
|
||||
"flags": configFieldTypeString,
|
||||
"info_refresh": configFieldTypeInteger,
|
||||
"last_ok_ping_reply": configFieldTypeInteger,
|
||||
"last_ping_reply": configFieldTypeInteger,
|
||||
"last_ping_sent": configFieldTypeInteger,
|
||||
"link_pending_commands": configFieldTypeInteger,
|
||||
"link_refcount": configFieldTypeInteger,
|
||||
"master_host": configFieldTypeString,
|
||||
"master_link_down_time": configFieldTypeInteger,
|
||||
"master_link_status": configFieldTypeString,
|
||||
"master_port": configFieldTypeInteger,
|
||||
"name": configFieldTypeString,
|
||||
"role_reported": configFieldTypeString,
|
||||
"role_reported_time": configFieldTypeInteger,
|
||||
"slave_priority": configFieldTypeInteger,
|
||||
"slave_repl_offset": configFieldTypeInteger,
|
||||
}
|
19
plugins/inputs/redis_sentinel/sample.conf
Normal file
19
plugins/inputs/redis_sentinel/sample.conf
Normal file
|
@ -0,0 +1,19 @@
|
|||
# Read metrics from one or many redis-sentinel servers
|
||||
[[inputs.redis_sentinel]]
|
||||
## specify servers via a url matching:
|
||||
## [protocol://][username:password]@address[:port]
|
||||
## e.g.
|
||||
## tcp://localhost:26379
|
||||
## tcp://username:password@192.168.99.100
|
||||
## unix:///var/run/redis-sentinel.sock
|
||||
##
|
||||
## If no servers are specified, then localhost is used as the host.
|
||||
## If no port is specified, 26379 is used
|
||||
# servers = ["tcp://localhost:26379"]
|
||||
|
||||
## Optional TLS Config
|
||||
# tls_ca = "/etc/telegraf/ca.pem"
|
||||
# tls_cert = "/etc/telegraf/cert.pem"
|
||||
# tls_key = "/etc/telegraf/key.pem"
|
||||
## Use TLS but skip chain & host verification
|
||||
# insecure_skip_verify = true
|
71
plugins/inputs/redis_sentinel/testdata/sentinel.info.response
vendored
Normal file
71
plugins/inputs/redis_sentinel/testdata/sentinel.info.response
vendored
Normal file
|
@ -0,0 +1,71 @@
|
|||
# Server
|
||||
redis_version:5.0.5
|
||||
redis_git_sha1:00000000
|
||||
redis_git_dirty:0
|
||||
redis_build_id:78473e0efb96880a
|
||||
redis_mode:sentinel
|
||||
os:Linux 5.1.3-arch1-1-ARCH x86_64
|
||||
arch_bits:64
|
||||
multiplexing_api:epoll
|
||||
atomicvar_api:atomic-builtin
|
||||
gcc_version:8.3.0
|
||||
process_id:2837
|
||||
run_id:ecbbb2ca0035a532b03748fbec9f3f8ca1967536
|
||||
tcp_port:26379
|
||||
uptime_in_seconds:901
|
||||
uptime_in_days:0
|
||||
hz:10
|
||||
configured_hz:10
|
||||
lru_clock:15585808
|
||||
executable:/home/adam/redis-sentinel
|
||||
config_file:/home/adam/rs1.conf
|
||||
|
||||
# Clients
|
||||
connected_clients:2
|
||||
client_recent_max_input_buffer:2
|
||||
client_recent_max_output_buffer:0
|
||||
blocked_clients:0
|
||||
|
||||
# CPU
|
||||
used_cpu_sys:0.786872
|
||||
used_cpu_user:0.939455
|
||||
used_cpu_sys_children:0.000000
|
||||
used_cpu_user_children:0.000000
|
||||
|
||||
# Stats
|
||||
total_connections_received:2
|
||||
total_commands_processed:6
|
||||
instantaneous_ops_per_sec:0
|
||||
total_net_input_bytes:124
|
||||
total_net_output_bytes:10148
|
||||
instantaneous_input_kbps:0.00
|
||||
instantaneous_output_kbps:0.00
|
||||
rejected_connections:0
|
||||
sync_full:0
|
||||
sync_partial_ok:0
|
||||
sync_partial_err:0
|
||||
expired_keys:0
|
||||
expired_stale_perc:0.00
|
||||
expired_time_cap_reached_count:0
|
||||
evicted_keys:0
|
||||
keyspace_hits:0
|
||||
keyspace_misses:0
|
||||
pubsub_channels:0
|
||||
pubsub_patterns:0
|
||||
latest_fork_usec:0
|
||||
migrate_cached_sockets:0
|
||||
slave_expires_tracked_keys:0
|
||||
active_defrag_hits:0
|
||||
active_defrag_misses:0
|
||||
active_defrag_key_hits:0
|
||||
active_defrag_key_misses:0
|
||||
|
||||
# Sentinel
|
||||
sentinel_masters:2
|
||||
sentinel_tilt:0
|
||||
sentinel_running_scripts:0
|
||||
sentinel_scripts_queue_length:0
|
||||
sentinel_simulate_failure_flags:0
|
||||
master0:name=myothermaster,status=ok,address=127.0.0.1:6380,slaves=1,sentinels=2
|
||||
master0:name=myothermaster,status=ok,address=127.0.0.1:6381,slaves=1,sentinels=2
|
||||
master1:name=mymaster,status=ok,address=127.0.0.1:6379,slaves=1,sentinels=1
|
Loading…
Add table
Add a link
Reference in a new issue