• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

oliver006 / redis_exporter / 15504448026

07 Jun 2025 04:56AM UTC coverage: 84.301% (-0.1%) from 84.444%
15504448026

Pull #997

github

web-flow
Merge 85a8b2560 into b21204e0a
Pull Request #997: Add all Go runtime metrics

0 of 6 new or added lines in 1 file covered. (0.0%)

1 existing line in 1 file now uncovered.

2497 of 2962 relevant lines covered (84.3%)

12911.07 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/main.go
1
package main
2

3
import (
4
        "context"
5
        "errors"
6
        "flag"
7
        "net/http"
8
        "os"
9
        "os/signal"
10
        "runtime"
11
        "strconv"
12
        "strings"
13
        "syscall"
14
        "time"
15

16
        "github.com/prometheus/client_golang/prometheus"
17
        "github.com/prometheus/client_golang/prometheus/collectors"
18
        log "github.com/sirupsen/logrus"
19

20
        "github.com/oliver006/redis_exporter/exporter"
21
)
22

23
var (
24
        /*
25
                BuildVersion, BuildDate, BuildCommitSha are filled in by the build script
26
        */
27
        BuildVersion   = "<<< filled in by build >>>"
28
        BuildDate      = "<<< filled in by build >>>"
29
        BuildCommitSha = "<<< filled in by build >>>"
30
)
31

32
func getEnv(key string, defaultVal string) string {
×
33
        if envVal, ok := os.LookupEnv(key); ok {
×
34
                return envVal
×
35
        }
×
36
        return defaultVal
×
37
}
38

39
func getEnvBool(key string, defaultVal bool) bool {
×
40
        if envVal, ok := os.LookupEnv(key); ok {
×
41
                envBool, err := strconv.ParseBool(envVal)
×
42
                if err == nil {
×
43
                        return envBool
×
44
                }
×
45
        }
46
        return defaultVal
×
47
}
48

49
func getEnvInt64(key string, defaultVal int64) int64 {
×
50
        if envVal, ok := os.LookupEnv(key); ok {
×
51
                envInt64, err := strconv.ParseInt(envVal, 10, 64)
×
52
                if err == nil {
×
53
                        return envInt64
×
54
                }
×
55
        }
56
        return defaultVal
×
57
}
58

59
func main() {
×
60
        var (
×
61
                redisAddr                      = flag.String("redis.addr", getEnv("REDIS_ADDR", "redis://localhost:6379"), "Address of the Redis instance to scrape")
×
62
                redisUser                      = flag.String("redis.user", getEnv("REDIS_USER", ""), "User name to use for authentication (Redis ACL for Redis 6.0 and newer)")
×
63
                redisPwd                       = flag.String("redis.password", getEnv("REDIS_PASSWORD", ""), "Password of the Redis instance to scrape")
×
64
                redisPwdFile                   = flag.String("redis.password-file", getEnv("REDIS_PASSWORD_FILE", ""), "Password file of the Redis instance to scrape")
×
65
                namespace                      = flag.String("namespace", getEnv("REDIS_EXPORTER_NAMESPACE", "redis"), "Namespace for metrics")
×
66
                checkKeys                      = flag.String("check-keys", getEnv("REDIS_EXPORTER_CHECK_KEYS", ""), "Comma separated list of key-patterns to export value and length/size, searched for with SCAN")
×
67
                checkSingleKeys                = flag.String("check-single-keys", getEnv("REDIS_EXPORTER_CHECK_SINGLE_KEYS", ""), "Comma separated list of single keys to export value and length/size")
×
68
                checkKeyGroups                 = flag.String("check-key-groups", getEnv("REDIS_EXPORTER_CHECK_KEY_GROUPS", ""), "Comma separated list of lua regex for grouping keys")
×
69
                checkStreams                   = flag.String("check-streams", getEnv("REDIS_EXPORTER_CHECK_STREAMS", ""), "Comma separated list of stream-patterns to export info about streams, groups and consumers, searched for with SCAN")
×
70
                checkSingleStreams             = flag.String("check-single-streams", getEnv("REDIS_EXPORTER_CHECK_SINGLE_STREAMS", ""), "Comma separated list of single streams to export info about streams, groups and consumers")
×
71
                streamsExcludeConsumerMetrics  = flag.Bool("streams-exclude-consumer-metrics", getEnvBool("REDIS_EXPORTER_STREAMS_EXCLUDE_CONSUMER_METRICS", false), "Don't collect per consumer metrics for streams (decreases cardinality)")
×
72
                countKeys                      = flag.String("count-keys", getEnv("REDIS_EXPORTER_COUNT_KEYS", ""), "Comma separated list of patterns to count (eg: 'db0=production_*,db3=sessions:*'), searched for with SCAN")
×
73
                checkKeysBatchSize             = flag.Int64("check-keys-batch-size", getEnvInt64("REDIS_EXPORTER_CHECK_KEYS_BATCH_SIZE", 1000), "Approximate number of keys to process in each execution, larger value speeds up scanning.\nWARNING: Still Redis is a single-threaded app, huge COUNT can affect production environment.")
×
74
                scriptPath                     = flag.String("script", getEnv("REDIS_EXPORTER_SCRIPT", ""), "Comma separated list of path(s) to Redis Lua script(s) for gathering extra metrics")
×
75
                listenAddress                  = flag.String("web.listen-address", getEnv("REDIS_EXPORTER_WEB_LISTEN_ADDRESS", ":9121"), "Address to listen on for web interface and telemetry.")
×
76
                metricPath                     = flag.String("web.telemetry-path", getEnv("REDIS_EXPORTER_WEB_TELEMETRY_PATH", "/metrics"), "Path under which to expose metrics.")
×
77
                logFormat                      = flag.String("log-format", getEnv("REDIS_EXPORTER_LOG_FORMAT", "txt"), "Log format, valid options are txt and json")
×
78
                configCommand                  = flag.String("config-command", getEnv("REDIS_EXPORTER_CONFIG_COMMAND", "CONFIG"), "What to use for the CONFIG command, set to \"-\" to skip config metrics extraction")
×
79
                connectionTimeout              = flag.String("connection-timeout", getEnv("REDIS_EXPORTER_CONNECTION_TIMEOUT", "15s"), "Timeout for connection to Redis instance")
×
80
                tlsClientKeyFile               = flag.String("tls-client-key-file", getEnv("REDIS_EXPORTER_TLS_CLIENT_KEY_FILE", ""), "Name of the client key file (including full path) if the server requires TLS client authentication")
×
81
                tlsClientCertFile              = flag.String("tls-client-cert-file", getEnv("REDIS_EXPORTER_TLS_CLIENT_CERT_FILE", ""), "Name of the client certificate file (including full path) if the server requires TLS client authentication")
×
82
                tlsCaCertFile                  = flag.String("tls-ca-cert-file", getEnv("REDIS_EXPORTER_TLS_CA_CERT_FILE", ""), "Name of the CA certificate file (including full path) if the server requires TLS client authentication")
×
83
                tlsServerKeyFile               = flag.String("tls-server-key-file", getEnv("REDIS_EXPORTER_TLS_SERVER_KEY_FILE", ""), "Name of the server key file (including full path) if the web interface and telemetry should use TLS")
×
84
                tlsServerCertFile              = flag.String("tls-server-cert-file", getEnv("REDIS_EXPORTER_TLS_SERVER_CERT_FILE", ""), "Name of the server certificate file (including full path) if the web interface and telemetry should use TLS")
×
85
                tlsServerCaCertFile            = flag.String("tls-server-ca-cert-file", getEnv("REDIS_EXPORTER_TLS_SERVER_CA_CERT_FILE", ""), "Name of the CA certificate file (including full path) if the web interface and telemetry should require TLS client authentication")
×
86
                tlsServerMinVersion            = flag.String("tls-server-min-version", getEnv("REDIS_EXPORTER_TLS_SERVER_MIN_VERSION", "TLS1.2"), "Minimum TLS version that is acceptable by the web interface and telemetry when using TLS")
×
87
                maxDistinctKeyGroups           = flag.Int64("max-distinct-key-groups", getEnvInt64("REDIS_EXPORTER_MAX_DISTINCT_KEY_GROUPS", 100), "The maximum number of distinct key groups with the most memory utilization to present as distinct metrics per database, the leftover key groups will be aggregated in the 'overflow' bucket")
×
88
                isDebug                        = flag.Bool("debug", getEnvBool("REDIS_EXPORTER_DEBUG", false), "Output verbose debug information")
×
89
                setClientName                  = flag.Bool("set-client-name", getEnvBool("REDIS_EXPORTER_SET_CLIENT_NAME", true), "Whether to set client name to redis_exporter")
×
90
                isTile38                       = flag.Bool("is-tile38", getEnvBool("REDIS_EXPORTER_IS_TILE38", false), "Whether to scrape Tile38 specific metrics")
×
91
                isCluster                      = flag.Bool("is-cluster", getEnvBool("REDIS_EXPORTER_IS_CLUSTER", false), "Whether this is a redis cluster (Enable this if you need to fetch key level data on a Redis Cluster).")
×
92
                exportClientList               = flag.Bool("export-client-list", getEnvBool("REDIS_EXPORTER_EXPORT_CLIENT_LIST", false), "Whether to scrape Client List specific metrics")
×
93
                exportClientPort               = flag.Bool("export-client-port", getEnvBool("REDIS_EXPORTER_EXPORT_CLIENT_PORT", false), "Whether to include the client's port when exporting the client list. Warning: including the port increases the number of metrics generated and will make your Prometheus server take up more memory")
×
94
                showVersion                    = flag.Bool("version", false, "Show version information and exit")
×
95
                redisMetricsOnly               = flag.Bool("redis-only-metrics", getEnvBool("REDIS_EXPORTER_REDIS_ONLY_METRICS", false), "Whether to also export go runtime metrics")
×
96
                pingOnConnect                  = flag.Bool("ping-on-connect", getEnvBool("REDIS_EXPORTER_PING_ON_CONNECT", false), "Whether to ping the redis instance after connecting")
×
97
                inclConfigMetrics              = flag.Bool("include-config-metrics", getEnvBool("REDIS_EXPORTER_INCL_CONFIG_METRICS", false), "Whether to include all config settings as metrics")
×
98
                inclModulesMetrics             = flag.Bool("include-modules-metrics", getEnvBool("REDIS_EXPORTER_INCL_MODULES_METRICS", false), "Whether to collect Redis Modules metrics")
×
99
                disableExportingKeyValues      = flag.Bool("disable-exporting-key-values", getEnvBool("REDIS_EXPORTER_DISABLE_EXPORTING_KEY_VALUES", false), "Whether to disable values of keys stored in redis as labels or not when using check-keys/check-single-key")
×
100
                excludeLatencyHistogramMetrics = flag.Bool("exclude-latency-histogram-metrics", getEnvBool("REDIS_EXPORTER_EXCLUDE_LATENCY_HISTOGRAM_METRICS", false), "Do not try to collect latency histogram metrics")
×
101
                redactConfigMetrics            = flag.Bool("redact-config-metrics", getEnvBool("REDIS_EXPORTER_REDACT_CONFIG_METRICS", true), "Whether to redact config settings that include potentially sensitive information like passwords")
×
102
                inclSystemMetrics              = flag.Bool("include-system-metrics", getEnvBool("REDIS_EXPORTER_INCL_SYSTEM_METRICS", false), "Whether to include system metrics like e.g. redis_total_system_memory_bytes")
×
103
                skipTLSVerification            = flag.Bool("skip-tls-verification", getEnvBool("REDIS_EXPORTER_SKIP_TLS_VERIFICATION", false), "Whether to to skip TLS verification")
×
104
                skipCheckKeysForRoleMaster     = flag.Bool("skip-checkkeys-for-role-master", getEnvBool("REDIS_EXPORTER_SKIP_CHECKKEYS_FOR_ROLE_MASTER", false), "Whether to skip gathering the check-keys metrics (size, val) when the instance is of type master (reduce load on master nodes)")
×
105
                basicAuthUsername              = flag.String("basic-auth-username", getEnv("REDIS_EXPORTER_BASIC_AUTH_USERNAME", ""), "Username for basic authentication")
×
106
                basicAuthPassword              = flag.String("basic-auth-password", getEnv("REDIS_EXPORTER_BASIC_AUTH_PASSWORD", ""), "Password for basic authentication")
×
107
                inclMetricsForEmptyDatabases   = flag.Bool("include-metrics-for-empty-databases", getEnvBool("REDIS_EXPORTER_INCL_METRICS_FOR_EMPTY_DATABASES", true), "Whether to emit db metrics (like db_keys) for empty databases")
×
108
        )
×
109
        flag.Parse()
×
110

×
111
        switch *logFormat {
×
112
        case "json":
×
113
                log.SetFormatter(&log.JSONFormatter{})
×
114
        default:
×
115
                log.SetFormatter(&log.TextFormatter{})
×
116
        }
117
        if *showVersion {
×
118
                log.SetOutput(os.Stdout)
×
119
        }
×
120
        log.Printf("Redis Metrics Exporter %s    build date: %s    sha1: %s    Go: %s    GOOS: %s    GOARCH: %s",
×
121
                BuildVersion, BuildDate, BuildCommitSha,
×
122
                runtime.Version(),
×
123
                runtime.GOOS,
×
124
                runtime.GOARCH,
×
125
        )
×
126
        if *showVersion {
×
127
                return
×
128
        }
×
129
        if *isDebug {
×
130
                log.SetLevel(log.DebugLevel)
×
131
                log.Debugln("Enabling debug output")
×
132
        } else {
×
133
                log.SetLevel(log.InfoLevel)
×
134
        }
×
135

136
        to, err := time.ParseDuration(*connectionTimeout)
×
137
        if err != nil {
×
138
                log.Fatalf("Couldn't parse connection timeout duration, err: %s", err)
×
139
        }
×
140

141
        passwordMap := make(map[string]string)
×
142
        if *redisPwd == "" && *redisPwdFile != "" {
×
143
                passwordMap, err = exporter.LoadPwdFile(*redisPwdFile)
×
144
                if err != nil {
×
145
                        log.Fatalf("Error loading redis passwords from file %s, err: %s", *redisPwdFile, err)
×
146
                }
×
147
        }
148

149
        var ls map[string][]byte
×
150
        if *scriptPath != "" {
×
151
                scripts := strings.Split(*scriptPath, ",")
×
152
                ls = make(map[string][]byte, len(scripts))
×
153
                for _, script := range scripts {
×
154
                        if ls[script], err = os.ReadFile(script); err != nil {
×
155
                                log.Fatalf("Error loading script file %s    err: %s", script, err)
×
156
                        }
×
157
                }
158
        }
159

160
        registry := prometheus.NewRegistry()
×
161
        if !*redisMetricsOnly {
×
NEW
162
                registry.MustRegister(
×
NEW
163
                        // expose process metrics like CPU, Memory, file descriptor usage etc.
×
NEW
164
                        collectors.NewProcessCollector(collectors.ProcessCollectorOpts{}),
×
NEW
165
                        // expose all Go runtime metrics like GC stats, memory stats etc.
×
NEW
166
                        collectors.NewGoCollector(collectors.WithGoCollectorRuntimeMetrics(collectors.MetricsAll)),
×
NEW
167
                )
×
UNCOV
168
        }
×
169

170
        exp, err := exporter.NewRedisExporter(
×
171
                *redisAddr,
×
172
                exporter.Options{
×
173
                        User:                           *redisUser,
×
174
                        Password:                       *redisPwd,
×
175
                        PasswordMap:                    passwordMap,
×
176
                        Namespace:                      *namespace,
×
177
                        ConfigCommandName:              *configCommand,
×
178
                        CheckKeys:                      *checkKeys,
×
179
                        CheckSingleKeys:                *checkSingleKeys,
×
180
                        CheckKeysBatchSize:             *checkKeysBatchSize,
×
181
                        CheckKeyGroups:                 *checkKeyGroups,
×
182
                        MaxDistinctKeyGroups:           *maxDistinctKeyGroups,
×
183
                        CheckStreams:                   *checkStreams,
×
184
                        CheckSingleStreams:             *checkSingleStreams,
×
185
                        StreamsExcludeConsumerMetrics:  *streamsExcludeConsumerMetrics,
×
186
                        CountKeys:                      *countKeys,
×
187
                        LuaScript:                      ls,
×
188
                        InclSystemMetrics:              *inclSystemMetrics,
×
189
                        InclConfigMetrics:              *inclConfigMetrics,
×
190
                        DisableExportingKeyValues:      *disableExportingKeyValues,
×
191
                        ExcludeLatencyHistogramMetrics: *excludeLatencyHistogramMetrics,
×
192
                        RedactConfigMetrics:            *redactConfigMetrics,
×
193
                        SetClientName:                  *setClientName,
×
194
                        IsTile38:                       *isTile38,
×
195
                        IsCluster:                      *isCluster,
×
196
                        InclModulesMetrics:             *inclModulesMetrics,
×
197
                        ExportClientList:               *exportClientList,
×
198
                        ExportClientsInclPort:          *exportClientPort,
×
199
                        SkipCheckKeysForRoleMaster:     *skipCheckKeysForRoleMaster,
×
200
                        SkipTLSVerification:            *skipTLSVerification,
×
201
                        ClientCertFile:                 *tlsClientCertFile,
×
202
                        ClientKeyFile:                  *tlsClientKeyFile,
×
203
                        CaCertFile:                     *tlsCaCertFile,
×
204
                        ConnectionTimeouts:             to,
×
205
                        MetricsPath:                    *metricPath,
×
206
                        RedisMetricsOnly:               *redisMetricsOnly,
×
207
                        PingOnConnect:                  *pingOnConnect,
×
208
                        RedisPwdFile:                   *redisPwdFile,
×
209
                        Registry:                       registry,
×
210
                        BuildInfo: exporter.BuildInfo{
×
211
                                Version:   BuildVersion,
×
212
                                CommitSha: BuildCommitSha,
×
213
                                Date:      BuildDate,
×
214
                        },
×
215
                        BasicAuthUsername:            *basicAuthUsername,
×
216
                        BasicAuthPassword:            *basicAuthPassword,
×
217
                        InclMetricsForEmptyDatabases: *inclMetricsForEmptyDatabases,
×
218
                },
×
219
        )
×
220
        if err != nil {
×
221
                log.Fatal(err)
×
222
        }
×
223

224
        // Verify that initial client keypair and CA are accepted
225
        if (*tlsClientCertFile != "") != (*tlsClientKeyFile != "") {
×
226
                log.Fatal("TLS client key file and cert file should both be present")
×
227
        }
×
228
        _, err = exp.CreateClientTLSConfig()
×
229
        if err != nil {
×
230
                log.Fatal(err)
×
231
        }
×
232

233
        log.Infof("Providing metrics at %s%s", *listenAddress, *metricPath)
×
234
        log.Debugf("Configured redis addr: %#v", *redisAddr)
×
235
        server := &http.Server{
×
236
                Addr:    *listenAddress,
×
237
                Handler: exp,
×
238
        }
×
239
        go func() {
×
240
                if *tlsServerCertFile != "" && *tlsServerKeyFile != "" {
×
241
                        log.Debugf("Bind as TLS using cert %s and key %s", *tlsServerCertFile, *tlsServerKeyFile)
×
242

×
243
                        tlsConfig, err := exp.CreateServerTLSConfig(*tlsServerCertFile, *tlsServerKeyFile, *tlsServerCaCertFile, *tlsServerMinVersion)
×
244
                        if err != nil {
×
245
                                log.Fatal(err)
×
246
                        }
×
247
                        server.TLSConfig = tlsConfig
×
248
                        if err := server.ListenAndServeTLS("", ""); err != nil && !errors.Is(err, http.ErrServerClosed) {
×
249
                                log.Fatalf("TLS Server error: %v", err)
×
250
                        }
×
251
                } else {
×
252
                        if err := server.ListenAndServe(); err != nil && !errors.Is(err, http.ErrServerClosed) {
×
253
                                log.Fatalf("Server error: %v", err)
×
254
                        }
×
255
                }
256
        }()
257

258
        // graceful shutdown
259
        quit := make(chan os.Signal, 1)
×
260
        signal.Notify(quit, syscall.SIGINT, syscall.SIGTERM)
×
261
        _quit := <-quit
×
262
        log.Infof("Received %s signal, exiting", _quit.String())
×
263
        // Create a context with a timeout
×
264
        ctx, cancel := context.WithTimeout(context.Background(), 10*time.Second)
×
265
        defer cancel()
×
266

×
267
        // Shutdown the HTTP server gracefully
×
268
        if err := server.Shutdown(ctx); err != nil {
×
269
                log.Fatalf("Server shutdown failed: %v", err)
×
270
        }
×
271
        log.Infof("Server shut down gracefully")
×
272
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc