Skip to content

Commit 43f6a7a

Browse files
author
zhongming Fan
committed
feat: use env replace config file params
1 parent 33ca18e commit 43f6a7a

File tree

6 files changed

+141
-22
lines changed

6 files changed

+141
-22
lines changed

cmd/sql_exporter/main.go

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ var (
3737
metricsPath = flag.String("web.metrics-path", "/metrics", "Path under which to expose metrics")
3838
enableReload = flag.Bool("web.enable-reload", false, "Enable reload collector data handler")
3939
webConfigFile = flag.String("web.config.file", "", "[EXPERIMENTAL] TLS/BasicAuth configuration file path")
40-
configFile = flag.String("config.file", "sql_exporter.yml", "SQL Exporter configuration file path")
40+
configFile = flag.String("config.file", "", "SQL Exporter configuration file path")
41+
collectorFile = flag.String("collector.file", "", "SQL Exporter collector file path")
4142
logFormatJSON = flag.Bool("log.json", false, "[DEPRECATED] Set log output format to JSON")
4243
logFormat = flag.String("log.format", "logfmt", "Set log output format")
4344
logLevel = flag.String("log.level", "info", "Set log level")
@@ -77,7 +78,7 @@ func main() {
7778
}
7879

7980
klog.Warningf("Starting SQL exporter %s %s", version.Info(), version.BuildContext())
80-
exporter, err := sql_exporter.NewExporter(*configFile)
81+
exporter, err := sql_exporter.NewExporter(*configFile, *collectorFile)
8182
if err != nil {
8283
klog.Fatalf("Error creating exporter: %s", err)
8384
}
@@ -86,7 +87,7 @@ func main() {
8687
startScrapeErrorsDropTicker(exporter, exporter.Config().Globals.ScrapeErrorDropInterval)
8788

8889
// Start signal handler to reload collector and target data.
89-
signalHandler(exporter, *configFile)
90+
signalHandler(exporter, *configFile, *collectorFile)
9091

9192
// Setup and start webserver.
9293
http.HandleFunc("/healthz", func(w http.ResponseWriter, r *http.Request) { http.Error(w, "OK", http.StatusOK) })
@@ -97,7 +98,7 @@ func main() {
9798
http.Handle("/sql_exporter_metrics", promhttp.HandlerFor(prometheus.DefaultGatherer, promhttp.HandlerOpts{}))
9899
// Expose refresh handler to reload collectors and targets
99100
if *enableReload {
100-
http.HandleFunc("/reload", reloadHandler(exporter, *configFile))
101+
http.HandleFunc("/reload", reloadHandler(exporter, *configFile, *collectorFile))
101102
}
102103

103104
server := &http.Server{Addr: *listenAddress, ReadHeaderTimeout: httpReadHeaderTimeout}
@@ -110,9 +111,9 @@ func main() {
110111
}
111112

112113
// reloadHandler returns a handler that reloads collector and target data.
113-
func reloadHandler(e sql_exporter.Exporter, configFile string) func(http.ResponseWriter, *http.Request) {
114+
func reloadHandler(e sql_exporter.Exporter, configFile string, collectorFile string) func(http.ResponseWriter, *http.Request) {
114115
return func(w http.ResponseWriter, r *http.Request) {
115-
if err := sql_exporter.Reload(e, &configFile); err != nil {
116+
if err := sql_exporter.Reload(e, &configFile, &collectorFile); err != nil {
116117
klog.Error(err)
117118
http.Error(w, err.Error(), http.StatusInternalServerError)
118119
return
@@ -122,12 +123,12 @@ func reloadHandler(e sql_exporter.Exporter, configFile string) func(http.Respons
122123
}
123124

124125
// signalHandler listens for SIGHUP signals and reloads the collector and target data.
125-
func signalHandler(e sql_exporter.Exporter, configFile string) {
126+
func signalHandler(e sql_exporter.Exporter, configFile string, collectorFile string) {
126127
c := make(chan os.Signal, 1)
127128
signal.Notify(c, syscall.SIGHUP)
128129
go func() {
129130
for range c {
130-
if err := sql_exporter.Reload(e, &configFile); err != nil {
131+
if err := sql_exporter.Reload(e, &configFile, &collectorFile); err != nil {
131132
klog.Error(err)
132133
}
133134
}

config/config.go

Lines changed: 56 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,12 @@ package config
22

33
import (
44
"context"
5+
"embed"
56
"fmt"
7+
"github.com/prometheus/common/model"
68
"os"
79
"path/filepath"
10+
"time"
811

912
"github.com/sethvargo/go-envconfig"
1013
"gopkg.in/yaml.v3"
@@ -19,27 +22,43 @@ const MaxInt32 int = 1<<31 - 1
1922
const (
2023
EnvPrefix string = "SQLEXPORTER_"
2124

22-
EnvConfigFile string = EnvPrefix + "CONFIG"
23-
EnvDebug string = EnvPrefix + "DEBUG"
25+
EnvConfigFile string = EnvPrefix + "CONFIG"
26+
EnvDebug string = EnvPrefix + "DEBUG"
27+
EmbedSqlExporterConfigFile string = "sql_exporter_embed.yml"
2428
)
2529

2630
var (
2731
EnablePing bool
2832
IgnoreMissingVals bool
2933
DsnOverride string
3034
TargetLabel string
31-
kingbaseDatabaseMode = os.Getenv("KINGBASE_DATABASE_MODE")
35+
kingbaseDatabaseMode = os.Getenv("KINGBASE_DATABASE_MODE")
36+
useEmbedConfig bool = true // Use embedded config by default
37+
//go:embed sql_exporter_embed.yml
38+
sqlExporterConfig embed.FS
3239
)
3340

3441
// Load attempts to parse the given config file and return a Config object.
35-
func Load(configFile string) (*Config, error) {
36-
klog.Infof("Loading configuration from %s", configFile)
37-
buf, err := os.ReadFile(configFile)
42+
func Load(configFile string, collectorFile string) (*Config, error) {
43+
source := configFile
44+
var readFn func(string) ([]byte, error)
45+
if source == "" {
46+
useEmbedConfig = true
47+
source = EmbedSqlExporterConfigFile
48+
readFn = sqlExporterConfig.ReadFile
49+
klog.Infof("Loading configuration from embed file: %s", source)
50+
} else {
51+
useEmbedConfig = false
52+
readFn = os.ReadFile
53+
klog.Infof("Loading configuration from: %s", source)
54+
}
55+
56+
buf, err := readFn(source)
3857
if err != nil {
3958
return nil, err
4059
}
4160

42-
c := Config{configFile: configFile}
61+
c := Config{configFile: configFile, collectorFile: collectorFile}
4362
err = yaml.Unmarshal(buf, &c)
4463
if err != nil {
4564
return nil, err
@@ -64,8 +83,9 @@ type Config struct {
6483
Jobs []*JobConfig `yaml:"jobs,omitempty"`
6584
Collectors []*CollectorConfig `yaml:"collectors,omitempty"`
6685

67-
configFile string
68-
86+
configFile string
87+
collectorFile string
88+
useEmbedConfig bool
6989
// Catches all undefined fields and must be empty after parsing.
7090
XXX map[string]any `yaml:",inline" json:"-"`
7191
}
@@ -76,6 +96,12 @@ func (c *Config) UnmarshalYAML(unmarshal func(any) error) error {
7696
if err := c.unmarshalConfig(unmarshal); err != nil {
7797
return err
7898
}
99+
100+
// Apply environment overrides.
101+
if useEmbedConfig {
102+
c.applyEnvOverrides(c.collectorFile)
103+
}
104+
79105
// Populate global defaults.
80106
if err := c.populateGlobalDefaults(); err != nil {
81107
return err
@@ -177,7 +203,12 @@ func (c *Config) YAML() ([]byte, error) {
177203

178204
// loadCollectorFiles resolves all collector file globs to files and loads the collectors they define.
179205
func (c *Config) loadCollectorFiles() error {
180-
baseDir := filepath.Dir(c.configFile)
206+
var baseDir string
207+
if useEmbedConfig {
208+
baseDir = "."
209+
} else {
210+
baseDir = filepath.Dir(c.configFile)
211+
}
181212

182213
// pg模式下写死采集sql文件
183214
if kingbaseDatabaseMode == "pg" {
@@ -219,3 +250,18 @@ func (c *Config) loadCollectorFiles() error {
219250

220251
return nil
221252
}
253+
254+
func (c *Config) applyEnvOverrides(collectorFile string) {
255+
// sql采集指标文件
256+
c.CollectorFiles = []string{collectorFile}
257+
// sql采集名称
258+
c.Target.CollectorRefs = []string{os.Getenv("COLLECTOR_REFS")}
259+
260+
// 应用配置
261+
SetDurationFromEnv("SCRAPE_TIMEOUT_OFFSET", "500ms", func(d model.Duration) { c.Globals.TimeoutOffset = d })
262+
SetDurationFromEnv("MIN_INTERVAL", "0s", func(d model.Duration) { c.Globals.MinInterval = d })
263+
SetIntFromEnv("MAX_CONNECTIONS", "3", func(i int) { c.Globals.MaxConns = i })
264+
SetIntFromEnv("MAX_IDLE_CONNECTIONS", "3", func(i int) { c.Globals.MaxIdleConns = i })
265+
SetTimeDurationFromEnv("MAX_CONNECTION_LIFETIME", "5m", func(d time.Duration) { c.Globals.MaxConnLifetime = d })
266+
SetDurationFromEnv("SCRAPE_TIMEOUT", "10s", func(d model.Duration) { c.Globals.ScrapeTimeout = d })
267+
}

config/sql_exporter_embed.yml

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
# Global defaults.
2+
global:
3+
# Subtracted from Prometheus' scrape_timeout to give us some headroom and prevent Prometheus from timing out first.
4+
scrape_timeout_offset: 500ms
5+
# Minimum interval between collector runs: by default (0s) collectors are executed on every scrape.
6+
min_interval: 0s
7+
# Maximum number of open connections to any one target. Metric queries will run concurrently on multiple connections,
8+
# as will concurrent scrapes.
9+
max_connections: 3
10+
# Maximum number of idle connections to any one target. Unless you use very long collection intervals, this should
11+
# always be the same as max_connections.
12+
max_idle_connections: 3
13+
# Maximum number of maximum amount of time a connection may be reused. Expired connections may be closed lazily before reuse.
14+
# If 0, connections are not closed due to a connection's age.
15+
max_connection_lifetime: 5m
16+
17+
# The target to monitor and the collectors to execute on it.
18+
target:
19+
# Data source name always has a URI schema that matches the driver name. In some cases (e.g. MySQL)
20+
# the schema gets dropped or replaced to match the driver expected DSN format.
21+
data_source_name: 'dm://sysdba:sysdba001@127.0.0.1:5236'
22+
23+
# Collectors (referenced by name) to execute on the target.
24+
# Glob patterns are supported (see <https://pkg.go.dev/path/filepath#Match> for syntax).
25+
collectors: [demo]
26+
27+
# Collector files specifies a list of globs. One collector definition is read from each matching file.
28+
# Glob patterns are supported (see <https://pkg.go.dev/path/filepath#Match> for syntax).
29+
collector_files:
30+
- "demo.collector.yml"

config/util.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,12 @@ package config
22

33
import (
44
"fmt"
5+
"github.com/prometheus/common/model"
6+
"os"
57
"path/filepath"
8+
"strconv"
69
"strings"
10+
"time"
711

812
"k8s.io/klog/v2"
913
)
@@ -69,3 +73,41 @@ func checkOverflow(m map[string]any, ctx string) error {
6973
}
7074
return nil
7175
}
76+
77+
// GetEnvWithDefault 从环境变量获取值,如果为空则使用默认值
78+
func GetEnvWithDefault(key, defaultVal string) string {
79+
if val := os.Getenv(key); val != "" {
80+
return val
81+
}
82+
return defaultVal
83+
}
84+
85+
// SetDurationFromEnv 从环境变量设置 model.Duration 类型值
86+
func SetDurationFromEnv(key, defaultVal string, setter func(model.Duration)) {
87+
val := GetEnvWithDefault(key, defaultVal)
88+
if parsed, err := model.ParseDuration(val); err == nil {
89+
setter(parsed)
90+
} else {
91+
klog.Warningf("Invalid %s: %v", key, err)
92+
}
93+
}
94+
95+
// SetIntFromEnv 从环境变量设置整数类型值
96+
func SetIntFromEnv(key, defaultVal string, setter func(int)) {
97+
val := GetEnvWithDefault(key, defaultVal)
98+
if parsed, err := strconv.Atoi(val); err == nil {
99+
setter(parsed)
100+
} else {
101+
klog.Warningf("Invalid %s: %v", key, err)
102+
}
103+
}
104+
105+
// SetTimeDurationFromEnv 从环境变量设置 time.Duration 类型值
106+
func SetTimeDurationFromEnv(key, defaultVal string, setter func(time.Duration)) {
107+
val := GetEnvWithDefault(key, defaultVal)
108+
if parsed, err := time.ParseDuration(val); err == nil {
109+
setter(parsed)
110+
} else {
111+
klog.Warningf("Invalid %s: %v", key, err)
112+
}
113+
}

exporter.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ type exporter struct {
7373
}
7474

7575
// NewExporter returns a new Exporter with the provided config.
76-
func NewExporter(configFile string) (Exporter, error) {
77-
c, err := config.Load(configFile)
76+
func NewExporter(configFile string, collectorFile string) (Exporter, error) {
77+
c, err := config.Load(configFile, collectorFile)
7878
if err != nil {
7979
return nil, err
8080
}

reload.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,10 @@ import (
88
)
99

1010
// Reload function is used to reload the exporter configuration without restarting the exporter
11-
func Reload(e Exporter, configFile *string) error {
11+
func Reload(e Exporter, configFile *string, collectorFile *string) error {
1212
klog.Warning("Reloading collectors has started...")
1313
klog.Warning("Connections will not be changed upon the restart of the exporter")
14-
configNext, err := cfg.Load(*configFile)
14+
configNext, err := cfg.Load(*configFile, *collectorFile)
1515
if err != nil {
1616
klog.Errorf("Error reading config file - %v", err)
1717
return err

0 commit comments

Comments
 (0)