From f5ddbcbcdef575471be4b1fe6c500b821cb0827d Mon Sep 17 00:00:00 2001 From: emile Date: Thu, 24 Sep 2015 14:32:37 +0200 Subject: [PATCH] Logrus logging backend https://github.com/EmileVauge/traefik/issues/15 --- Godeps/Godeps.json | 14 ++++----- adapters.go | 16 +++++----- consul.go | 19 ++++++------ docker.go | 11 ++++--- docs/index.md | 10 +----- file.go | 1 + marathon.go | 9 +++--- middlewares/cbreaker.go | 3 +- traefik.go | 69 ++++++++++++++++++----------------------- traefik.sample.toml | 8 +---- web.go | 21 +++++++------ 11 files changed, 83 insertions(+), 98 deletions(-) diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index eb598a003..dc9563a3e 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -1,9 +1,6 @@ { "ImportPath": "github.com/emilevauge/traefik", - "GoVersion": "go1.5", - "Packages": [ - "./..." - ], + "GoVersion": "go1.4.2", "Deps": [ { "ImportPath": "github.com/BurntSushi/toml", @@ -13,6 +10,11 @@ "ImportPath": "github.com/BurntSushi/ty", "Rev": "6add9cd6ad42d389d6ead1dde60b4ad71e46fd74" }, + { + "ImportPath": "github.com/Sirupsen/logrus", + "Comment": "v0.8.7", + "Rev": "418b41d23a1bf978c06faea5313ba194650ac088" + }, { "ImportPath": "github.com/alecthomas/template", "Rev": "b867cc6ab45cece8143cfcc6fc9c77cf3f2c23c0" @@ -95,10 +97,6 @@ "ImportPath": "github.com/mailgun/timetools", "Rev": "fd192d755b00c968d312d23f521eb0cdc6f66bd0" }, - { - "ImportPath": "github.com/op/go-logging", - "Rev": "e8d5414f0947014548c2334044a0fac13187dfee" - }, { "ImportPath": "github.com/thoas/stats", "Rev": "54ed61c2b47e263ae2f01b86837b0c4bd1da28e8" diff --git a/adapters.go b/adapters.go index a22207ab9..bbe6dc650 100644 --- a/adapters.go +++ b/adapters.go @@ -2,25 +2,27 @@ Copyright */ package main + import ( - "net/http" - "github.com/mailgun/oxy/utils" + log "github.com/Sirupsen/logrus" "github.com/gorilla/mux" + "github.com/mailgun/oxy/utils" + "net/http" ) -type OxyLogger struct{ +type OxyLogger struct { } func (oxylogger *OxyLogger) Infof(format string, args ...interface{}) { - log.Debug(format, args...) + log.Debugf(format, args...) } func (oxylogger *OxyLogger) Warningf(format string, args ...interface{}) { - log.Warning(format, args...) + log.Warningf(format, args...) } func (oxylogger *OxyLogger) Errorf(format string, args ...interface{}) { - log.Error(format, args...) + log.Errorf(format, args...) } type ErrorHandler struct { @@ -40,4 +42,4 @@ func LoadDefaultConfig(gloablConfiguration *GlobalConfiguration) *mux.Router { router := mux.NewRouter() router.NotFoundHandler = http.HandlerFunc(notFoundHandler) return router -} \ No newline at end of file +} diff --git a/consul.go b/consul.go index c6b085d82..3355a6e8a 100644 --- a/consul.go +++ b/consul.go @@ -1,16 +1,16 @@ package main import ( - "github.com/hashicorp/consul/api" - "text/template" "bytes" "github.com/BurntSushi/toml" - "strings" "github.com/BurntSushi/ty/fun" + log "github.com/Sirupsen/logrus" + "github.com/hashicorp/consul/api" "net/http" + "strings" + "text/template" ) - type Key struct { Value string } @@ -22,6 +22,7 @@ type ConsulProvider struct { Filename string consulClient *api.Client } + var kvClient *api.KV var ConsulFuncMap = template.FuncMap{ @@ -33,7 +34,7 @@ var ConsulFuncMap = template.FuncMap{ return nil } keysPairs = fun.Filter(func(key string) bool { - if (key == joinedKeys) { + if key == joinedKeys { return false } return true @@ -51,7 +52,7 @@ var ConsulFuncMap = template.FuncMap{ }, "Last": func(key string) string { splittedKey := strings.Split(key, "/") - return splittedKey[len(splittedKey) -2] + return splittedKey[len(splittedKey)-2] }, } @@ -64,7 +65,7 @@ func NewConsulProvider() *ConsulProvider { return consulProvider } -func (provider *ConsulProvider) Provide(configurationChan chan <- *Configuration) { +func (provider *ConsulProvider) Provide(configurationChan chan<- *Configuration) { config := &api.Config{ Address: provider.Endpoint, Scheme: "http", @@ -106,10 +107,10 @@ func (provider *ConsulProvider) loadConsulConfig() *Configuration { kvClient = provider.consulClient.KV() servicesName, _, _ := provider.consulClient.Catalog().Services(nil) - for serviceName, _ := range servicesName { + for serviceName := range servicesName { catalogServices, _, _ := provider.consulClient.Catalog().Service(serviceName, "", nil) for _, catalogService := range catalogServices { - services= append(services, catalogService) + services = append(services, catalogService) } } diff --git a/docker.go b/docker.go index 919fc3eb5..daec7d56e 100644 --- a/docker.go +++ b/docker.go @@ -5,6 +5,7 @@ import ( "errors" "github.com/BurntSushi/toml" "github.com/BurntSushi/ty/fun" + log "github.com/Sirupsen/logrus" "github.com/cenkalti/backoff" "github.com/fsouza/go-dockerclient" "strconv" @@ -85,7 +86,7 @@ func (provider *DockerProvider) Provide(configurationChan chan<- *Configuration) // log.Fatalf("Docker connection error") } if event.Status == "start" || event.Status == "die" { - log.Debug("Docker event receveived %+v", event) + log.Debugf("Docker event receveived %+v", event) configuration := provider.loadDockerConfig(dockerClient) if configuration != nil { configurationChan <- configuration @@ -94,7 +95,7 @@ func (provider *DockerProvider) Provide(configurationChan chan<- *Configuration) } } notify := func(err error, time time.Duration) { - log.Error("Docker connection error %+v, retrying in %s", err, time) + log.Errorf("Docker connection error %+v, retrying in %s", err, time) } err := backoff.RetryNotify(operation, backoff.NewExponentialBackOff(), notify) if err != nil { @@ -123,16 +124,16 @@ func (provider *DockerProvider) loadDockerConfig(dockerClient *docker.Client) *C // filter containers filteredContainers := fun.Filter(func(container docker.Container) bool { if len(container.NetworkSettings.Ports) == 0 { - log.Debug("Filtering container without port %s", container.Name) + log.Debugf("Filtering container without port %s", container.Name) return false } _, err := strconv.Atoi(container.Config.Labels["traefik.port"]) if len(container.NetworkSettings.Ports) > 1 && err != nil { - log.Debug("Filtering container with more than 1 port and no traefik.port label %s", container.Name) + log.Debugf("Filtering container with more than 1 port and no traefik.port label %s", container.Name) return false } if container.Config.Labels["traefik.enable"] == "false" { - log.Debug("Filtering disabled container %s", container.Name) + log.Debugf("Filtering disabled container %s", container.Name) return false } return true diff --git a/docs/index.md b/docs/index.md index 55fb8ada1..beb9c1f63 100644 --- a/docs/index.md +++ b/docs/index.md @@ -69,18 +69,12 @@ A circuit breaker can also be applied to a backend, preventing high loads on fai # graceTimeOut = 10 # Traefik logs file +# If not defined, logs to stdout # # Optional # # traefikLogsFile = "log/traefik.log" -# Traefik log to standard output -# -# Optional -# Default: true -# -# traefikLogsStdout = true - # Access logs file # # Optional @@ -114,7 +108,6 @@ Like any other reverse proxy, Træfɪk can be configured with a file. You have t port = ":80" graceTimeOut = 10 logLevel = "DEBUG" -traefikLogsStdout = true [file] @@ -153,7 +146,6 @@ traefikLogsStdout = true port = ":80" graceTimeOut = 10 logLevel = "DEBUG" -traefikLogsStdout = true [file] filename = "rules.toml" diff --git a/file.go b/file.go index 876a95b92..722a726fd 100644 --- a/file.go +++ b/file.go @@ -2,6 +2,7 @@ package main import ( "github.com/BurntSushi/toml" + log "github.com/Sirupsen/logrus" "gopkg.in/fsnotify.v1" "os" "path/filepath" diff --git a/marathon.go b/marathon.go index b9b13608b..1caad18b1 100644 --- a/marathon.go +++ b/marathon.go @@ -4,6 +4,7 @@ import ( "bytes" "github.com/BurntSushi/toml" "github.com/BurntSushi/ty/fun" + log "github.com/Sirupsen/logrus" "github.com/gambol99/go-marathon" "strconv" "strings" @@ -62,14 +63,14 @@ func (provider *MarathonProvider) Provide(configurationChan chan<- *Configuratio config.URL = provider.Endpoint config.EventsInterface = provider.NetworkInterface if client, err := marathon.NewClient(config); err != nil { - log.Error("Failed to create a client for marathon, error: %s", err) + log.Errorf("Failed to create a client for marathon, error: %s", err) return } else { provider.marathonClient = client update := make(marathon.EventsChannel, 5) if provider.Watch { if err := client.AddEventsListener(update, marathon.EVENTS_APPLICATIONS); err != nil { - log.Error("Failed to register for subscriptions, %s", err) + log.Errorf("Failed to register for subscriptions, %s", err) } else { go func() { for { @@ -94,13 +95,13 @@ func (provider *MarathonProvider) loadMarathonConfig() *Configuration { applications, err := provider.marathonClient.Applications(nil) if err != nil { - log.Error("Failed to create a client for marathon, error: %s", err) + log.Errorf("Failed to create a client for marathon, error: %s", err) return nil } tasks, err := provider.marathonClient.AllTasks() if err != nil { - log.Error("Failed to create a client for marathon, error: %s", err) + log.Errorf("Failed to create a client for marathon, error: %s", err) return nil } diff --git a/middlewares/cbreaker.go b/middlewares/cbreaker.go index a9956f13f..d58b98550 100644 --- a/middlewares/cbreaker.go +++ b/middlewares/cbreaker.go @@ -2,9 +2,10 @@ Copyright */ package middlewares + import ( - "net/http" "github.com/mailgun/oxy/cbreaker" + "net/http" ) type CircuitBreaker struct { diff --git a/traefik.go b/traefik.go index 820ef2367..f1303aea9 100644 --- a/traefik.go +++ b/traefik.go @@ -2,34 +2,34 @@ package main import ( "github.com/BurntSushi/toml" + log "github.com/Sirupsen/logrus" "github.com/codegangsta/negroni" "github.com/emilevauge/traefik/middlewares" "github.com/gorilla/mux" + "github.com/mailgun/oxy/cbreaker" "github.com/mailgun/oxy/forward" "github.com/mailgun/oxy/roundrobin" - "github.com/op/go-logging" "github.com/thoas/stats" "github.com/tylerb/graceful" "github.com/unrolled/render" "gopkg.in/alecthomas/kingpin.v2" + fmtlog "log" + "net" "net/http" "net/url" "os" "os/signal" "reflect" + "strings" "syscall" "time" - fmtlog "log" - "github.com/mailgun/oxy/cbreaker" - "net" ) var ( globalConfigFile = kingpin.Arg("conf", "Main configration file.").Default("traefik.toml").String() currentConfiguration = new(Configuration) metrics = stats.New() - log = logging.MustGetLogger("traefik") - oxyLogger = &OxyLogger{} + oxyLogger = &OxyLogger{} templatesRenderer = render.New(render.Options{ Directory: "templates", Asset: Asset, @@ -45,7 +45,6 @@ func main() { var configurationChan = make(chan *Configuration, 10) defer close(configurationChan) var providers = []Provider{} - var format = logging.MustStringFormatter("%{color}%{time:15:04:05.000} %{shortfile:20.20s} %{level:8.8s} %{id:03x} ▶%{color:reset} %{message}") var sigs = make(chan os.Signal, 1) signal.Notify(sigs, syscall.SIGINT, syscall.SIGTERM) @@ -56,11 +55,11 @@ func main() { defer loggerMiddleware.Close() // logging - backends := []logging.Backend{} - level, err := logging.LogLevel(gloablConfiguration.LogLevel) + level, err := log.ParseLevel(strings.ToLower(gloablConfiguration.LogLevel)) if err != nil { log.Fatal("Error getting level", err) } + log.SetLevel(level) if len(gloablConfiguration.TraefikLogsFile) > 0 { fi, err := os.OpenFile(gloablConfiguration.TraefikLogsFile, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0666) @@ -68,18 +67,12 @@ func main() { if err != nil { log.Fatal("Error opening file", err) } else { - logBackend := logging.NewLogBackend(fi, "", 0) - logBackendFormatter := logging.NewBackendFormatter(logBackend, logging.GlogFormatter) - backends = append(backends, logBackendFormatter) + log.SetOutput(fi) + log.SetFormatter(&log.TextFormatter{DisableColors: true, FullTimestamp: true, DisableSorting: true}) } + } else { + log.SetFormatter(&log.TextFormatter{FullTimestamp: true, DisableSorting: true}) } - if gloablConfiguration.TraefikLogsStdout { - logBackend := logging.NewLogBackend(os.Stdout, "", 0) - logBackendFormatter := logging.NewBackendFormatter(logBackend, format) - backends = append(backends, logBackendFormatter) - } - logging.SetBackend(backends...) - logging.SetLevel(level, "traefik") configurationRouter = LoadDefaultConfig(gloablConfiguration) @@ -87,19 +80,19 @@ func main() { go func() { for { configuration := <-configurationChan - log.Info("Configuration receveived %+v", configuration) + log.Infof("Configuration receveived %+v", configuration) if configuration == nil { log.Info("Skipping empty configuration") } else if reflect.DeepEqual(currentConfiguration, configuration) { log.Info("Skipping same configuration") } else { newConfigurationRouter, err := LoadConfig(configuration, gloablConfiguration) - if (err == nil ){ + if err == nil { currentConfiguration = configuration configurationRouter = newConfigurationRouter srv.Stop(time.Duration(gloablConfiguration.GraceTimeOut) * time.Second) time.Sleep(3 * time.Second) - }else{ + } else { log.Error("Error loading new configuration, aborted ", err) } } @@ -129,7 +122,7 @@ func main() { // start providers for _, provider := range providers { - log.Notice("Starting provider %v %+v", reflect.TypeOf(provider), provider) + log.Infof("Starting provider %v %+v", reflect.TypeOf(provider), provider) currentProvider := provider go func() { currentProvider.Provide(configurationChan) @@ -139,7 +132,7 @@ func main() { goAway := false go func() { sig := <-sigs - log.Notice("I have to go... %+v", sig) + log.Infof("I have to go... %+v", sig) goAway = true srv.Stop(time.Duration(gloablConfiguration.GraceTimeOut) * time.Second) }() @@ -170,7 +163,7 @@ func main() { go func() { if len(gloablConfiguration.CertFile) > 0 && len(gloablConfiguration.KeyFile) > 0 { err := srv.ListenAndServeTLS(gloablConfiguration.CertFile, gloablConfiguration.KeyFile) - if (err!= nil ){ + if err != nil { netOpError, ok := err.(*net.OpError) if ok && netOpError.Err.Error() != "use of closed network connection" { log.Fatal("Error creating server: ", err) @@ -178,7 +171,7 @@ func main() { } } else { err := srv.ListenAndServe() - if (err!= nil ){ + if err != nil { netOpError, ok := err.(*net.OpError) if ok && netOpError.Err.Error() != "use of closed network connection" { log.Fatal("Error creating server: ", err) @@ -186,9 +179,9 @@ func main() { } } }() - log.Notice("Started") + log.Info("Started") <-srv.StopChan() - log.Notice("Stopped") + log.Info("Stopped") } } @@ -197,31 +190,31 @@ func LoadConfig(configuration *Configuration, gloablConfiguration *GlobalConfigu router.NotFoundHandler = http.HandlerFunc(notFoundHandler) backends := map[string]http.Handler{} for frontendName, frontend := range configuration.Frontends { - log.Debug("Creating frontend %s", frontendName) + log.Debugf("Creating frontend %s", frontendName) fwd, _ := forward.New(forward.Logger(oxyLogger)) newRoute := router.NewRoute().Name(frontendName) for routeName, route := range frontend.Routes { - log.Debug("Creating route %s %s:%s", routeName, route.Rule, route.Value) + log.Debugf("Creating route %s %s:%s", routeName, route.Rule, route.Value) newRouteReflect := Invoke(newRoute, route.Rule, route.Value) newRoute = newRouteReflect[0].Interface().(*mux.Route) } if backends[frontend.Backend] == nil { - log.Debug("Creating backend %s", frontend.Backend) + log.Debugf("Creating backend %s", frontend.Backend) lb, _ := roundrobin.New(fwd) rb, _ := roundrobin.NewRebalancer(lb, roundrobin.RebalancerLogger(oxyLogger)) for serverName, server := range configuration.Backends[frontend.Backend].Servers { - if url, err := url.Parse(server.Url); err != nil{ + if url, err := url.Parse(server.Url); err != nil { return nil, err - }else{ - log.Debug("Creating server %s %s", serverName, url.String()) + } else { + log.Debugf("Creating server %s %s", serverName, url.String()) rb.UpsertServer(url, roundrobin.Weight(server.Weight)) } } backends[frontend.Backend] = rb } else { - log.Debug("Reusing backend %s", frontend.Backend) + log.Debugf("Reusing backend %s", frontend.Backend) } -// stream.New(backends[frontend.Backend], stream.Retry("IsNetworkError() && Attempts() <= " + strconv.Itoa(gloablConfiguration.Replay)), stream.Logger(oxyLogger)) + // stream.New(backends[frontend.Backend], stream.Retry("IsNetworkError() && Attempts() <= " + strconv.Itoa(gloablConfiguration.Replay)), stream.Logger(oxyLogger)) var negroni = negroni.New() negroni.Use(middlewares.NewCircuitBreaker(backends[frontend.Backend], cbreaker.Logger(oxyLogger))) newRoute.Handler(negroni) @@ -246,6 +239,6 @@ func LoadFileConfig(file string) *GlobalConfiguration { if _, err := toml.DecodeFile(file, configuration); err != nil { log.Fatal("Error reading file ", err) } - log.Debug("Global configuration loaded %+v", configuration) + log.Debugf("Global configuration loaded %+v", configuration) return configuration -} \ No newline at end of file +} diff --git a/traefik.sample.toml b/traefik.sample.toml index ef3566dc7..341bc9645 100644 --- a/traefik.sample.toml +++ b/traefik.sample.toml @@ -18,18 +18,12 @@ # graceTimeOut = 10 # Traefik logs file +# If not defined, logs to stdout # # Optional # # traefikLogsFile = "log/traefik.log" -# Traefik log to standard output -# -# Optional -# Default: true -# -# traefikLogsStdout = true - # Access logs file # # Optional diff --git a/web.go b/web.go index bb516dce3..58541ed3c 100644 --- a/web.go +++ b/web.go @@ -3,6 +3,7 @@ package main import ( "encoding/json" "fmt" + log "github.com/Sirupsen/logrus" "github.com/elazarl/go-bindata-assetfs" "github.com/gorilla/mux" "io/ioutil" @@ -10,7 +11,7 @@ import ( ) type WebProvider struct { - Address string + Address string CertFile, KeyFile string } @@ -32,7 +33,7 @@ func (provider *WebProvider) Provide(configurationChan chan<- *Configuration) { configurationChan <- configuration GetConfigHandler(rw, r) } else { - log.Error("Error parsing configuration %+v\n", err) + log.Errorf("Error parsing configuration %+v", err) http.Error(rw, fmt.Sprintf("%+v", err), http.StatusBadRequest) } })) @@ -46,13 +47,13 @@ func (provider *WebProvider) Provide(configurationChan chan<- *Configuration) { go func() { if len(provider.CertFile) > 0 && len(provider.KeyFile) > 0 { - err := http.ListenAndServeTLS(provider.Address,provider.CertFile, provider.KeyFile, systemRouter) - if (err!= nil ){ + err := http.ListenAndServeTLS(provider.Address, provider.CertFile, provider.KeyFile, systemRouter) + if err != nil { log.Fatal("Error creating server: ", err) } } else { err := http.ListenAndServe(provider.Address, systemRouter) - if (err!= nil ){ + if err != nil { log.Fatal("Error creating server: ", err) } } @@ -80,7 +81,7 @@ func GetBackendHandler(rw http.ResponseWriter, r *http.Request) { id := vars["backend"] if backend, ok := currentConfiguration.Backends[id]; ok { templatesRenderer.JSON(rw, http.StatusOK, backend) - }else{ + } else { http.NotFound(rw, r) } } @@ -94,7 +95,7 @@ func GetFrontendHandler(rw http.ResponseWriter, r *http.Request) { id := vars["frontend"] if frontend, ok := currentConfiguration.Frontends[id]; ok { templatesRenderer.JSON(rw, http.StatusOK, frontend) - }else{ + } else { http.NotFound(rw, r) } } @@ -104,7 +105,7 @@ func GetServersHandler(rw http.ResponseWriter, r *http.Request) { backend := vars["backend"] if backend, ok := currentConfiguration.Backends[backend]; ok { templatesRenderer.JSON(rw, http.StatusOK, backend.Servers) - }else{ + } else { http.NotFound(rw, r) } } @@ -116,10 +117,10 @@ func GetServerHandler(rw http.ResponseWriter, r *http.Request) { if backend, ok := currentConfiguration.Backends[backend]; ok { if server, ok := backend.Servers[server]; ok { templatesRenderer.JSON(rw, http.StatusOK, server) - }else{ + } else { http.NotFound(rw, r) } - }else{ + } else { http.NotFound(rw, r) } }