Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
aos
GitHub Repository: aos/grafana-agent
Path: blob/main/pkg/integrations/redis_exporter/redis_exporter.go
5395 views
1
// Package redis_exporter embeds https://github.com/oliver006/redis_exporter
2
package redis_exporter //nolint:golint
3
4
import (
5
"errors"
6
"fmt"
7
"os"
8
"strings"
9
"time"
10
11
"github.com/grafana/agent/pkg/integrations"
12
integrations_v2 "github.com/grafana/agent/pkg/integrations/v2"
13
"github.com/grafana/agent/pkg/integrations/v2/metricsutils"
14
15
"github.com/go-kit/log"
16
"github.com/go-kit/log/level"
17
re "github.com/oliver006/redis_exporter/exporter"
18
config_util "github.com/prometheus/common/config"
19
)
20
21
// DefaultConfig holds non-zero default options for the Config when it is
22
// unmarshaled from YAML.
23
var DefaultConfig = Config{
24
Namespace: "redis",
25
ConfigCommand: "CONFIG",
26
ConnectionTimeout: 15 * time.Second,
27
SetClientName: true,
28
CheckKeyGroupsBatchSize: 10000,
29
MaxDistinctKeyGroups: 100,
30
}
31
32
// Config controls the redis_exporter integration.
33
type Config struct {
34
IncludeExporterMetrics bool `yaml:"include_exporter_metrics"`
35
36
// exporter-specific config.
37
//
38
// The exporter binary config differs to this, but these
39
// are the only fields that are relevant to the exporter struct.
40
RedisAddr string `yaml:"redis_addr,omitempty"`
41
RedisUser string `yaml:"redis_user,omitempty"`
42
RedisPassword config_util.Secret `yaml:"redis_password,omitempty"`
43
RedisPasswordFile string `yaml:"redis_password_file,omitempty"`
44
RedisPasswordMapFile string `yaml:"redis_password_map_file,omitempty"`
45
Namespace string `yaml:"namespace,omitempty"`
46
ConfigCommand string `yaml:"config_command,omitempty"`
47
CheckKeys string `yaml:"check_keys,omitempty"`
48
CheckKeyGroups string `yaml:"check_key_groups,omitempty"`
49
CheckKeyGroupsBatchSize int64 `yaml:"check_key_groups_batch_size,omitempty"`
50
MaxDistinctKeyGroups int64 `yaml:"max_distinct_key_groups,omitempty"`
51
CheckSingleKeys string `yaml:"check_single_keys,omitempty"`
52
CheckStreams string `yaml:"check_streams,omitempty"`
53
CheckSingleStreams string `yaml:"check_single_streams,omitempty"`
54
CountKeys string `yaml:"count_keys,omitempty"`
55
ScriptPath string `yaml:"script_path,omitempty"`
56
ConnectionTimeout time.Duration `yaml:"connection_timeout,omitempty"`
57
TLSClientKeyFile string `yaml:"tls_client_key_file,omitempty"`
58
TLSClientCertFile string `yaml:"tls_client_cert_file,omitempty"`
59
TLSCaCertFile string `yaml:"tls_ca_cert_file,omitempty"`
60
SetClientName bool `yaml:"set_client_name,omitempty"`
61
IsTile38 bool `yaml:"is_tile38,omitempty"`
62
IsCluster bool `yaml:"is_cluster,omitempty"`
63
ExportClientList bool `yaml:"export_client_list,omitempty"`
64
ExportClientPort bool `yaml:"export_client_port,omitempty"`
65
RedisMetricsOnly bool `yaml:"redis_metrics_only,omitempty"`
66
PingOnConnect bool `yaml:"ping_on_connect,omitempty"`
67
InclSystemMetrics bool `yaml:"incl_system_metrics,omitempty"`
68
SkipTLSVerification bool `yaml:"skip_tls_verification,omitempty"`
69
}
70
71
// GetExporterOptions returns relevant Config properties as a redis_exporter
72
// Options struct. The redis_exporter Options struct has no yaml tags, so
73
// we marshal the yaml into Config and then create the re.Options from that.
74
func (c Config) GetExporterOptions() re.Options {
75
return re.Options{
76
User: c.RedisUser,
77
Password: string(c.RedisPassword),
78
Namespace: c.Namespace,
79
ConfigCommandName: c.ConfigCommand,
80
CheckKeys: c.CheckKeys,
81
CheckKeysBatchSize: c.CheckKeyGroupsBatchSize,
82
CheckKeyGroups: c.CheckKeyGroups,
83
CheckSingleKeys: c.CheckSingleKeys,
84
CheckStreams: c.CheckStreams,
85
CheckSingleStreams: c.CheckSingleStreams,
86
CountKeys: c.CountKeys,
87
InclSystemMetrics: c.InclSystemMetrics,
88
InclConfigMetrics: false,
89
RedactConfigMetrics: true,
90
SkipTLSVerification: c.SkipTLSVerification,
91
SetClientName: c.SetClientName,
92
IsTile38: c.IsTile38,
93
IsCluster: c.IsCluster,
94
ExportClientList: c.ExportClientList,
95
ExportClientsInclPort: c.ExportClientPort,
96
ConnectionTimeouts: c.ConnectionTimeout,
97
RedisMetricsOnly: c.RedisMetricsOnly,
98
PingOnConnect: c.PingOnConnect,
99
}
100
}
101
102
// UnmarshalYAML implements yaml.Unmarshaler for Config
103
func (c *Config) UnmarshalYAML(unmarshal func(interface{}) error) error {
104
*c = DefaultConfig
105
106
type plain Config
107
return unmarshal((*plain)(c))
108
}
109
110
// Name returns the name of the integration this config is for.
111
func (c *Config) Name() string {
112
return "redis_exporter"
113
}
114
115
// InstanceKey returns the addr of the redis server.
116
func (c *Config) InstanceKey(agentKey string) (string, error) {
117
return c.RedisAddr, nil
118
}
119
120
// NewIntegration converts the config into an integration instance.
121
func (c *Config) NewIntegration(l log.Logger) (integrations.Integration, error) {
122
return New(l, c)
123
}
124
125
func init() {
126
integrations.RegisterIntegration(&Config{})
127
integrations_v2.RegisterLegacy(&Config{}, integrations_v2.TypeMultiplex, metricsutils.NewNamedShim("redis"))
128
}
129
130
// New creates a new redis_exporter integration. The integration queries
131
// a redis instance's INFO and exposes the results as metrics.
132
func New(log log.Logger, c *Config) (integrations.Integration, error) {
133
level.Debug(log).Log("msg", "initializing redis_exporter", "config", c)
134
135
exporterConfig := c.GetExporterOptions()
136
137
if c.RedisAddr == "" {
138
return nil, errors.New("cannot create redis_exporter; redis_exporter.redis_addr is not defined")
139
}
140
141
if c.ScriptPath != "" {
142
scripts := map[string][]byte{}
143
for _, path := range strings.Split(c.ScriptPath, ",") {
144
ls, err := os.ReadFile(path)
145
if err != nil {
146
return nil, fmt.Errorf("error loading script file %s: %w", c.ScriptPath, err)
147
}
148
scripts[path] = ls
149
}
150
exporterConfig.LuaScript = scripts
151
}
152
153
//new version of the exporter takes the file paths directly, for hot-reloading support (https://github.com/oliver006/redis_exporter/pull/526)
154
155
if (c.TLSClientKeyFile != "") != (c.TLSClientCertFile != "") {
156
return nil, errors.New("TLS client key file and cert file should both be present")
157
} else if c.TLSClientKeyFile != "" && c.TLSClientCertFile != "" {
158
exporterConfig.ClientKeyFile = c.TLSClientKeyFile
159
exporterConfig.ClientCertFile = c.TLSClientCertFile
160
}
161
162
if c.TLSCaCertFile != "" {
163
exporterConfig.CaCertFile = c.TLSCaCertFile
164
}
165
166
// only one type of password file should be specified
167
if c.RedisPasswordFile != "" && c.RedisPasswordMapFile != "" {
168
return nil, errors.New("only one of redis_password_file and redis_password_map_file should be specified")
169
}
170
171
// optional password file to take precedence over password property
172
if c.RedisPasswordFile != "" {
173
password, err := os.ReadFile(c.RedisPasswordFile)
174
if err != nil {
175
return nil, fmt.Errorf("Error loading password file %s: %w", c.RedisPasswordFile, err)
176
}
177
exporterConfig.Password = strings.TrimSpace(string(password))
178
}
179
180
// optional password file containing map of redis uris to passwords. If this is specified, it will take
181
// precedence over a different password file
182
if c.RedisPasswordMapFile != "" {
183
passwordMap, err := re.LoadPwdFile(c.RedisPasswordMapFile)
184
if err != nil {
185
return nil, fmt.Errorf("error loading password map file %s: %w", c.RedisPasswordMapFile, err)
186
}
187
exporterConfig.PasswordMap = passwordMap
188
}
189
190
exporter, err := re.NewRedisExporter(
191
c.RedisAddr,
192
exporterConfig,
193
)
194
if err != nil {
195
return nil, fmt.Errorf("failed to create redis exporter: %w", err)
196
}
197
198
return integrations.NewCollectorIntegration(
199
c.Name(),
200
integrations.WithCollectors(exporter),
201
integrations.WithExporterMetricsIncluded(c.IncludeExporterMetrics),
202
), nil
203
}
204
205