diff --git a/app/app.go b/app/app.go index 9bacdda238..c74be2c51d 100644 --- a/app/app.go +++ b/app/app.go @@ -26,10 +26,29 @@ type App struct { // program will exit. func (a *App) Start() error { a.Logger.Debug().Logf("Starting up App...") + a.Metrics.Register("config_hash", "gauge") + a.Metrics.Register("rule_config_hash", "gauge") a.IncomingRouter.SetVersion(a.Version) a.PeerRouter.SetVersion(a.Version) + a.Config.RegisterReloadCallback(func(configHash, rulesHash string) { + if a.Logger != nil { + a.Logger.Warn().WithFields(map[string]interface{}{ + "configHash": configHash, + "rulesHash": rulesHash, + }).Logf("configuration change was detected and the configuration was reloaded.") + + cfgMetric := config.ConfigHashMetrics(configHash) + ruleMetric := config.ConfigHashMetrics(rulesHash) + + a.Metrics.Gauge("config_hash", cfgMetric) + a.Metrics.Gauge("rule_config_hash", ruleMetric) + + } + + }) + // launch our main routers to listen for incoming event traffic from both peers // and external sources a.IncomingRouter.LnS("incoming") diff --git a/cmd/refinery/main.go b/cmd/refinery/main.go index 389e16fed9..506e663fc8 100644 --- a/cmd/refinery/main.go +++ b/cmd/refinery/main.go @@ -96,12 +96,6 @@ func main() { fmt.Println("Config and Rules validated successfully.") os.Exit(0) } - c.RegisterReloadCallback(func() { - if a.Logger != nil { - a.Logger.Info().Logf("configuration change was detected and the configuration was reloaded") - } - }) - // get desired implementation for each dependency to inject lgr := logger.GetLoggerImplementation(c) collector := collect.GetCollectorImplementation(c) diff --git a/collect/collect.go b/collect/collect.go index 06845fc35e..0f987105c4 100644 --- a/collect/collect.go +++ b/collect/collect.go @@ -141,7 +141,7 @@ func (i *InMemCollector) Start() error { } // sendReloadSignal will trigger the collector reloading its config, eventually. -func (i *InMemCollector) sendReloadSignal() { +func (i *InMemCollector) sendReloadSignal(cfgHash, ruleHash string) { // non-blocking insert of the signal here so we don't leak goroutines select { case i.reload <- struct{}{}: diff --git a/config/config.go b/config/config.go index 8419256932..7fe1b0971a 100644 --- a/config/config.go +++ b/config/config.go @@ -21,7 +21,7 @@ type Config interface { // consumers of configuration set config values on startup, they should // check their values haven't changed and re-start anything that needs // restarting with the new values. - RegisterReloadCallback(callback func()) + RegisterReloadCallback(callback ConfigReloadCallback) // GetListenAddr returns the address and port on which to listen for // incoming events @@ -191,6 +191,8 @@ type Config interface { GetParentIdFieldNames() []string } +type ConfigReloadCallback func(configHash, ruleCfgHash string) + type ConfigMetadata struct { Type string `json:"type"` ID string `json:"id"` diff --git a/config/configLoadHelpers.go b/config/configLoadHelpers.go index 14f7602ba4..b4e39b4dd7 100644 --- a/config/configLoadHelpers.go +++ b/config/configLoadHelpers.go @@ -12,6 +12,7 @@ import ( "os" "path/filepath" "reflect" + "strconv" "github.com/creasty/defaults" "github.com/pelletier/go-toml/v2" @@ -240,3 +241,21 @@ func readConfigInto(dest any, location string, opts *CmdEnv) (string, error) { return hash, nil } + +// ConfigHashMetrics takes a config hash and returns a integer value for use in metrics. +// The value is the last 4 characters of the config hash, converted to an integer. +// If the config hash is too short, or if there is an error converting the hash to an integer, +// it returns 0. +func ConfigHashMetrics(hash string) int64 { + // get last 4 characters of config hash + if len(hash) < 4 { + return 0 + } + suffix := hash[len(hash)-4:] + CfgDecimal, err := strconv.ParseInt(suffix, 16, 64) + if err != nil { + return 0 + } + + return CfgDecimal +} diff --git a/config/configLoadHelpers_test.go b/config/configLoadHelpers_test.go index c029e1ee13..8a8a65ca59 100644 --- a/config/configLoadHelpers_test.go +++ b/config/configLoadHelpers_test.go @@ -6,6 +6,8 @@ import ( "strings" "testing" "time" + + "github.com/stretchr/testify/require" ) func Test_formatFromFilename(t *testing.T) { @@ -127,3 +129,21 @@ func Test_loadMemsize(t *testing.T) { }) } } + +func Test_ConfigHashMetrics(t *testing.T) { + testcases := []struct { + name string + hash string + expected int64 + }{ + {name: "valid hash", hash: "7f1237f7db723f4e874a7a8269081a77", expected: 6775}, + {name: "invalid length", hash: "1a8", expected: 0}, + } + + for _, tc := range testcases { + t.Run(tc.name, func(t *testing.T) { + result := ConfigHashMetrics(tc.hash) + require.Equal(t, tc.expected, result) + }) + } +} diff --git a/config/config_test.go b/config/config_test.go index 138525b0d5..6a25730e1f 100644 --- a/config/config_test.go +++ b/config/config_test.go @@ -212,7 +212,7 @@ func TestReload(t *testing.T) { ch := make(chan interface{}, 1) - c.RegisterReloadCallback(func() { + c.RegisterReloadCallback(func(cfgHash, ruleHash string) { close(ch) }) diff --git a/config/file_config.go b/config/file_config.go index b46b7d7a1b..c687e030a5 100644 --- a/config/file_config.go +++ b/config/file_config.go @@ -38,7 +38,7 @@ type fileConfig struct { rulesConfig *V2SamplerConfig rulesHash string opts *CmdEnv - callbacks []func() + callbacks []ConfigReloadCallback errorCallback func(error) done chan struct{} ticker *time.Ticker @@ -412,7 +412,7 @@ func NewConfig(opts *CmdEnv, errorCallback func(error)) (Config, error) { os.Exit(0) } - cfg.callbacks = make([]func(), 0) + cfg.callbacks = make([]ConfigReloadCallback, 0) cfg.errorCallback = errorCallback if cfg.mainConfig.General.ConfigReloadInterval > 0 { @@ -451,8 +451,9 @@ func (f *fileConfig) monitor() { f.rulesConfig = cfg.rulesConfig f.rulesHash = cfg.rulesHash f.mux.Unlock() // can't defer -- routine never ends, and callbacks will deadlock + for _, cb := range f.callbacks { - cb() + cb(cfg.mainHash, cfg.rulesHash) } } } @@ -469,7 +470,7 @@ func (f *fileConfig) Stop() { } } -func (f *fileConfig) RegisterReloadCallback(cb func()) { +func (f *fileConfig) RegisterReloadCallback(cb ConfigReloadCallback) { f.mux.Lock() defer f.mux.Unlock() diff --git a/config/mock.go b/config/mock.go index 9ea645fc2a..ec3c72660c 100644 --- a/config/mock.go +++ b/config/mock.go @@ -9,7 +9,7 @@ import ( // MockConfig will respond with whatever config it's set to do during // initialization type MockConfig struct { - Callbacks []func() + Callbacks []ConfigReloadCallback IsAPIKeyValidFunc func(string) bool GetCollectorTypeErr error GetCollectorTypeVal string @@ -99,11 +99,11 @@ func (m *MockConfig) ReloadConfig() { defer m.Mux.RUnlock() for _, callback := range m.Callbacks { - callback() + callback("", "") } } -func (m *MockConfig) RegisterReloadCallback(callback func()) { +func (m *MockConfig) RegisterReloadCallback(callback ConfigReloadCallback) { m.Mux.Lock() m.Callbacks = append(m.Callbacks, callback) m.Mux.Unlock() diff --git a/logger/honeycomb.go b/logger/honeycomb.go index 829fd04ef8..a80eca70fb 100644 --- a/logger/honeycomb.go +++ b/logger/honeycomb.go @@ -122,7 +122,7 @@ func (h *HoneycombLogger) readResponses() { } } -func (h *HoneycombLogger) reloadBuilder() { +func (h *HoneycombLogger) reloadBuilder(cfgHash, ruleHash string) { h.Debug().Logf("reloading config for Honeycomb logger") // preserve log level h.level = h.Config.GetLoggerLevel() diff --git a/metrics/legacy.go b/metrics/legacy.go index b5806c80ba..8df829d355 100644 --- a/metrics/legacy.go +++ b/metrics/legacy.go @@ -92,7 +92,7 @@ func (h *LegacyMetrics) Start() error { return nil } -func (h *LegacyMetrics) reloadBuilder() { +func (h *LegacyMetrics) reloadBuilder(cfgHash, ruleHash string) { h.Logger.Debug().Logf("reloading config for honeycomb metrics reporter") mc := h.Config.GetLegacyMetricsConfig() h.libhClient.Close() diff --git a/transmit/transmit.go b/transmit/transmit.go index b98ef658c0..f59c8efc76 100644 --- a/transmit/transmit.go +++ b/transmit/transmit.go @@ -83,7 +83,7 @@ func (d *DefaultTransmission) Start() error { return nil } -func (d *DefaultTransmission) reloadTransmissionBuilder() { +func (d *DefaultTransmission) reloadTransmissionBuilder(cfgHash, ruleHash string) { d.Logger.Debug().Logf("reloading transmission config") upstreamAPI, err := d.Config.GetHoneycombAPI() if err != nil {