Path: blob/main/pkg/integrations/redis_exporter/redis_exporter.go
5395 views
// Package redis_exporter embeds https://github.com/oliver006/redis_exporter1package redis_exporter //nolint:golint23import (4"errors"5"fmt"6"os"7"strings"8"time"910"github.com/grafana/agent/pkg/integrations"11integrations_v2 "github.com/grafana/agent/pkg/integrations/v2"12"github.com/grafana/agent/pkg/integrations/v2/metricsutils"1314"github.com/go-kit/log"15"github.com/go-kit/log/level"16re "github.com/oliver006/redis_exporter/exporter"17config_util "github.com/prometheus/common/config"18)1920// DefaultConfig holds non-zero default options for the Config when it is21// unmarshaled from YAML.22var DefaultConfig = Config{23Namespace: "redis",24ConfigCommand: "CONFIG",25ConnectionTimeout: 15 * time.Second,26SetClientName: true,27CheckKeyGroupsBatchSize: 10000,28MaxDistinctKeyGroups: 100,29}3031// Config controls the redis_exporter integration.32type Config struct {33IncludeExporterMetrics bool `yaml:"include_exporter_metrics"`3435// exporter-specific config.36//37// The exporter binary config differs to this, but these38// are the only fields that are relevant to the exporter struct.39RedisAddr string `yaml:"redis_addr,omitempty"`40RedisUser string `yaml:"redis_user,omitempty"`41RedisPassword config_util.Secret `yaml:"redis_password,omitempty"`42RedisPasswordFile string `yaml:"redis_password_file,omitempty"`43RedisPasswordMapFile string `yaml:"redis_password_map_file,omitempty"`44Namespace string `yaml:"namespace,omitempty"`45ConfigCommand string `yaml:"config_command,omitempty"`46CheckKeys string `yaml:"check_keys,omitempty"`47CheckKeyGroups string `yaml:"check_key_groups,omitempty"`48CheckKeyGroupsBatchSize int64 `yaml:"check_key_groups_batch_size,omitempty"`49MaxDistinctKeyGroups int64 `yaml:"max_distinct_key_groups,omitempty"`50CheckSingleKeys string `yaml:"check_single_keys,omitempty"`51CheckStreams string `yaml:"check_streams,omitempty"`52CheckSingleStreams string `yaml:"check_single_streams,omitempty"`53CountKeys string `yaml:"count_keys,omitempty"`54ScriptPath string `yaml:"script_path,omitempty"`55ConnectionTimeout time.Duration `yaml:"connection_timeout,omitempty"`56TLSClientKeyFile string `yaml:"tls_client_key_file,omitempty"`57TLSClientCertFile string `yaml:"tls_client_cert_file,omitempty"`58TLSCaCertFile string `yaml:"tls_ca_cert_file,omitempty"`59SetClientName bool `yaml:"set_client_name,omitempty"`60IsTile38 bool `yaml:"is_tile38,omitempty"`61IsCluster bool `yaml:"is_cluster,omitempty"`62ExportClientList bool `yaml:"export_client_list,omitempty"`63ExportClientPort bool `yaml:"export_client_port,omitempty"`64RedisMetricsOnly bool `yaml:"redis_metrics_only,omitempty"`65PingOnConnect bool `yaml:"ping_on_connect,omitempty"`66InclSystemMetrics bool `yaml:"incl_system_metrics,omitempty"`67SkipTLSVerification bool `yaml:"skip_tls_verification,omitempty"`68}6970// GetExporterOptions returns relevant Config properties as a redis_exporter71// Options struct. The redis_exporter Options struct has no yaml tags, so72// we marshal the yaml into Config and then create the re.Options from that.73func (c Config) GetExporterOptions() re.Options {74return re.Options{75User: c.RedisUser,76Password: string(c.RedisPassword),77Namespace: c.Namespace,78ConfigCommandName: c.ConfigCommand,79CheckKeys: c.CheckKeys,80CheckKeysBatchSize: c.CheckKeyGroupsBatchSize,81CheckKeyGroups: c.CheckKeyGroups,82CheckSingleKeys: c.CheckSingleKeys,83CheckStreams: c.CheckStreams,84CheckSingleStreams: c.CheckSingleStreams,85CountKeys: c.CountKeys,86InclSystemMetrics: c.InclSystemMetrics,87InclConfigMetrics: false,88RedactConfigMetrics: true,89SkipTLSVerification: c.SkipTLSVerification,90SetClientName: c.SetClientName,91IsTile38: c.IsTile38,92IsCluster: c.IsCluster,93ExportClientList: c.ExportClientList,94ExportClientsInclPort: c.ExportClientPort,95ConnectionTimeouts: c.ConnectionTimeout,96RedisMetricsOnly: c.RedisMetricsOnly,97PingOnConnect: c.PingOnConnect,98}99}100101// UnmarshalYAML implements yaml.Unmarshaler for Config102func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {103*c = DefaultConfig104105type plain Config106return unmarshal((*plain)(c))107}108109// Name returns the name of the integration this config is for.110func (c *Config) Name() string {111return "redis_exporter"112}113114// InstanceKey returns the addr of the redis server.115func (c *Config) InstanceKey(agentKey string) (string, error) {116return c.RedisAddr, nil117}118119// NewIntegration converts the config into an integration instance.120func (c *Config) NewIntegration(l log.Logger) (integrations.Integration, error) {121return New(l, c)122}123124func init() {125integrations.RegisterIntegration(&Config{})126integrations_v2.RegisterLegacy(&Config{}, integrations_v2.TypeMultiplex, metricsutils.NewNamedShim("redis"))127}128129// New creates a new redis_exporter integration. The integration queries130// a redis instance's INFO and exposes the results as metrics.131func New(log log.Logger, c *Config) (integrations.Integration, error) {132level.Debug(log).Log("msg", "initializing redis_exporter", "config", c)133134exporterConfig := c.GetExporterOptions()135136if c.RedisAddr == "" {137return nil, errors.New("cannot create redis_exporter; redis_exporter.redis_addr is not defined")138}139140if c.ScriptPath != "" {141scripts := map[string][]byte{}142for _, path := range strings.Split(c.ScriptPath, ",") {143ls, err := os.ReadFile(path)144if err != nil {145return nil, fmt.Errorf("error loading script file %s: %w", c.ScriptPath, err)146}147scripts[path] = ls148}149exporterConfig.LuaScript = scripts150}151152//new version of the exporter takes the file paths directly, for hot-reloading support (https://github.com/oliver006/redis_exporter/pull/526)153154if (c.TLSClientKeyFile != "") != (c.TLSClientCertFile != "") {155return nil, errors.New("TLS client key file and cert file should both be present")156} else if c.TLSClientKeyFile != "" && c.TLSClientCertFile != "" {157exporterConfig.ClientKeyFile = c.TLSClientKeyFile158exporterConfig.ClientCertFile = c.TLSClientCertFile159}160161if c.TLSCaCertFile != "" {162exporterConfig.CaCertFile = c.TLSCaCertFile163}164165// only one type of password file should be specified166if c.RedisPasswordFile != "" && c.RedisPasswordMapFile != "" {167return nil, errors.New("only one of redis_password_file and redis_password_map_file should be specified")168}169170// optional password file to take precedence over password property171if c.RedisPasswordFile != "" {172password, err := os.ReadFile(c.RedisPasswordFile)173if err != nil {174return nil, fmt.Errorf("Error loading password file %s: %w", c.RedisPasswordFile, err)175}176exporterConfig.Password = strings.TrimSpace(string(password))177}178179// optional password file containing map of redis uris to passwords. If this is specified, it will take180// precedence over a different password file181if c.RedisPasswordMapFile != "" {182passwordMap, err := re.LoadPwdFile(c.RedisPasswordMapFile)183if err != nil {184return nil, fmt.Errorf("error loading password map file %s: %w", c.RedisPasswordMapFile, err)185}186exporterConfig.PasswordMap = passwordMap187}188189exporter, err := re.NewRedisExporter(190c.RedisAddr,191exporterConfig,192)193if err != nil {194return nil, fmt.Errorf("failed to create redis exporter: %w", err)195}196197return integrations.NewCollectorIntegration(198c.Name(),199integrations.WithCollectors(exporter),200integrations.WithExporterMetricsIncluded(c.IncludeExporterMetrics),201), nil202}203204205