diff --git a/config/config.go b/config/config.go index 965b52312..ad98d857e 100644 --- a/config/config.go +++ b/config/config.go @@ -56,6 +56,7 @@ type Proxy struct { TLSHeader string TLSHeaderValue string GZIPContentTypes *regexp.Regexp + LogRoutes string } type Runtime struct { diff --git a/config/default.go b/config/default.go index e81720b97..fc5060a4a 100644 --- a/config/default.go +++ b/config/default.go @@ -25,6 +25,7 @@ var defaultConfig = &Config{ DialTimeout: 30 * time.Second, FlushInterval: time.Second, LocalIP: LocalIPString(), + LogRoutes: "delta", }, Registry: Registry{ Backend: "consul", diff --git a/config/load.go b/config/load.go index 99444df6b..269a7564b 100644 --- a/config/load.go +++ b/config/load.go @@ -131,6 +131,7 @@ func load(cmdline, environ, envprefix []string, props *properties.Properties) (c f.DurationVar(&readTimeout, "proxy.readtimeout", defaultValues.ReadTimeout, "read timeout for incoming requests") f.DurationVar(&writeTimeout, "proxy.writetimeout", defaultValues.WriteTimeout, "write timeout for outgoing responses") f.DurationVar(&cfg.Proxy.FlushInterval, "proxy.flushinterval", defaultConfig.Proxy.FlushInterval, "flush interval for streaming responses") + f.StringVar(&cfg.Proxy.LogRoutes, "proxy.log.routes", defaultConfig.Proxy.LogRoutes, "log format of routing table updates") f.StringVar(&cfg.Metrics.Target, "metrics.target", defaultConfig.Metrics.Target, "metrics backend") f.StringVar(&cfg.Metrics.Prefix, "metrics.prefix", defaultConfig.Metrics.Prefix, "prefix for reported metrics") f.StringVar(&cfg.Metrics.Names, "metrics.names", defaultConfig.Metrics.Names, "route metric name template") diff --git a/config/load_test.go b/config/load_test.go index a5bae3c40..e80029ef3 100644 --- a/config/load_test.go +++ b/config/load_test.go @@ -288,6 +288,13 @@ func TestLoad(t *testing.T) { return cfg }, }, + { + args: []string{"-proxy.log.routes", "foobar"}, + cfg: func(cfg *Config) *Config { + cfg.Proxy.LogRoutes = "foobar" + return cfg + }, + }, { args: []string{"-registry.backend", "value"}, cfg: func(cfg *Config) *Config { diff --git a/fabio.properties b/fabio.properties index f7993215f..79a40520a 100644 --- a/fabio.properties +++ b/fabio.properties @@ -371,6 +371,19 @@ # proxy.gzip.contenttype = +# proxy.log.routes configures the log output format of routing table updates. +# +# Changes to the routing table are written to the standard log. This option +# configures the output format: +# +# delta: additions and deletions +# all: complete routing table +# +# The default is +# +# proxy.log.routes = delta + + # registry.backend configures which backend is used. # Supported backends are: consul, static, file # diff --git a/main.go b/main.go index 4ca6a422c..0f364eff9 100644 --- a/main.go +++ b/main.go @@ -1,6 +1,7 @@ package main import ( + "bytes" "encoding/json" "fmt" "log" @@ -9,6 +10,7 @@ import ( "os" "runtime" "runtime/debug" + "strings" "github.com/eBay/fabio/admin" "github.com/eBay/fabio/config" @@ -20,6 +22,7 @@ import ( "github.com/eBay/fabio/registry/file" "github.com/eBay/fabio/registry/static" "github.com/eBay/fabio/route" + dmp "github.com/sergi/go-diff/diffmatchpatch" ) // version contains the version number @@ -59,7 +62,7 @@ func main() { initRuntime(cfg) initBackend(cfg) - go watchBackend() + go watchBackend(cfg) startAdmin(cfg) // create proxies after metrics since they use the metrics registry. @@ -161,7 +164,7 @@ func initBackend(cfg *config.Config) { } } -func watchBackend() { +func watchBackend(cfg *config.Config) { var ( last string svccfg string @@ -190,12 +193,47 @@ func watchBackend() { continue } route.SetTable(t) - log.Printf("[INFO] Updated config to\n%s", t) - + logRoutes(last, next, cfg.Proxy.LogRoutes) last = next } } +func logRoutes(last, next, format string) { + fmtDiff := func(diffs []dmp.Diff) string { + var b bytes.Buffer + for _, d := range diffs { + t := strings.TrimSpace(d.Text) + if t == "" { + continue + } + switch d.Type { + case dmp.DiffDelete: + b.WriteString("- ") + b.WriteString(strings.Replace(t, "\n", "\n- ", -1)) + case dmp.DiffInsert: + b.WriteString("+ ") + b.WriteString(strings.Replace(t, "\n", "\n+ ", -1)) + } + } + return b.String() + } + + const defFormat = "delta" + switch format { + case "delta": + if delta := fmtDiff(dmp.New().DiffMain(last, next, true)); delta != "" { + log.Printf("[INFO] Config updates\n%s", delta) + } + + case "all": + log.Printf("[INFO] Updated config to\n%s", next) + + default: + log.Printf("[WARN] Invalid route format %q. Defaulting to %q", format, defFormat) + logRoutes(last, next, defFormat) + } +} + func toJSON(v interface{}) string { data, err := json.MarshalIndent(v, "", " ") if err != nil {