Repository: traefik/traefik (language: go)

Issue:
support log rotation within traefik binary
### Do you want to request a *feature* or report a *bug*?

Feature

### What did you expect to see?

Traefik currently supports log rotation by handling SIGUSR1 correctly. This is certainly a very standard way of handling this. However, configuring this in kubernetes is non-trivial. K8s 1.10 landed support for shared [process spaces ](https://kubernetes.io/docs/tasks/configure-pod-container/share-process-namespace/). In order to use this, i would need to create a logging sidecar that handled rotation and configuration of that rotation... also I need to send those logs upstream, etc. Additionally, I deploy traefik with the [helm chart](https://github.com/kubernetes/charts/tree/master/stable/traefik). So now I need to add support to configure arbitrary sidecars, k8s version detection, etc. Life would be easier if I could configure rotation directly as configuration in traefik. This way the side car is just concerned with log shipping instead of also handling rotation.

https://github.com/natefinch/lumberjack
```go
log.SetOutput(&lumberjack.Logger{
    Filename:   "/var/log/myapp/foo.log",
    MaxSize:    500, // megabytes
    MaxBackups: 3,
    MaxAge:     28, //days
    Compress:   true, // disabled by default
})
```




Discussion / hints:
Would the traefik team accept an MR that accomplished this goal?
I have a few of concerns about this:
- If you are using kubernetes, and are opting to write logs to files, you may want to review your reasoning to do so.

- Logging to stdout is always preferrable, and is supported by a bunch of well-used tools such as fluent, logstash etc.

- When you have multiple replicas, managing log files gets nasty, and the solution is essentially to run one of the previously mentioned tools, which work without log files...

Is there a particular reason that you _want_ log files in a k8s environment?
there are a couple of reasons I want to log to a file vs stdout/stderr. 
* logging to a file I can clearly configure the output type (eg json) and feed that to a structured logger. I can trust that every entry in the log file will be JSON and  nothing else.
* events like panics are completely separated from structured output like JSON
* log files act as a buffer to pushing logs upstream. If upstream log injestion is down, the local log files act at a configurable buffer

I'm not following your statement about "managing log files get nasty". The use of lumberjack makes logfile management very easy. Also, the log files (in my environment) are put into a tmp volume. It doesn't matter how many replicas there are so I don't see how that is relevant. 

The MR to accomplish this would be very small. Exposing the lumberjack parameters in configuration would be all that is necessary along with the initialization of the log output.
Hey @daniel-garcia,

Here is a bit more clarification on my previous statement:

when I say `When you have multiple replicas, managing log files gets nasty`, if you are using traefik in k8s, and you have 3+ replicas, each has their own log file, that each has to be rotated. What happens if one of the pods doesn't rotate at the right time? what happens if load balancing is uneven, and you have one log file that fills up, and gets rotated 3x faster than any of the other pods? What happens if your pod fails a liveliness check, and the pod gets deleted and recreated, and you haven't triggered a log "push"? These are all things that we have to consider.

When you say `Also, the log files (in my environment) are put into a tmp volume. It doesn't matter how many replicas there are so I don't see how that is relevant` Kubernetes file storage is absolutely something that is of great concern, expecially when adding features. Are you using persistent storage for your log files? If not, your volume could get deleted and erased on pod crash. If you are using persistent storage, then you would have to configure each pod to log using a unique path/filename, since there are multiple pods writing to the same volume. This is where you could run into issues.

I am wary about approving feature addition that may work in one use case, but for the majority are against best practices or design goals.

Just my 2 cents
@dtomcej 

> What happens if one of the pods doesn't rotate at the right time?

With lumberjack, for example, rotation is handled by by the library and is triggered by a set of configured values. The rotation "policy" is defined by the configuration of luberjack. Rotation is not necessarily triggered by at a specific time but by the policy. For example, when the file reaches 100 MB, it is rotated. If it is 28 days old, it is rotated, etc.

> What happens if load balancing is uneven, and you have one log file that fills up, and gets rotated 3x faster than any of the other pods?

The logging behavior of pods are independent from one another. It is the job of the sidecar to take care of shipping logs.

> What happens if your pod fails a liveliness check, and the pod gets deleted and recreated, and you haven't triggered a log "push"? 

Liveness probes are defined as part of the [container lifetime](https://kubernetes.io/docs/tasks/configure-pod-container/configure-liveness-readiness-probes/), not the pod lifetime. If a "container" fails a liveness probe, it is restarted. This does not mean the logging sidecar would get restarted. 

> Are you using persistent storage for your log files?

That is not dictated by this feature at all. The logging sidecar could be using a tmpfs volume, a pvc, a host volume, etc and it really is not a concern of traefik how "persistent" or "not persistent" its log file is. Its persistent is dictated by the use case and defined by end users.

I'm quite surprise about your opposition to adding this despite traefik supporting this already via SIGUSR1 see https://github.com/containous/traefik/issues/1696 .  That MR https://github.com/containous/traefik/pull/1761 implements log file rotation by handling SIGUSR1 and having users use a program like logrotate. This could be done in kubernetes today, btw. The current supported file rotation will not work on Windows (that's not necessary for this discussion but worth mentioning). This feature request is about making something that is already possible with traefik (log rotation) easier to do.

Perhaps @emilevauge should weigh in as he approved the first log rotation support. 
Hi @daniel-garcia 
I'm not against adding native log rotate into Traefik. Supporting both "standard" `SIGUSR1` signals and internal implementation seems doable.
I have some concerns with https://github.com/natefinch/lumberjack though, which has been inactive since September 2017, and may have few [bugs](https://github.com/natefinch/lumberjack/issues/56) [waiting](https://github.com/natefinch/lumberjack/pull/57) for some fixes. WDYT ?
Hey @daniel-garcia 

I would like to start off by apologizing, I re-read my replies from yesterday, and they do come across quite negatively. For that I apologize.

Second, I do actually agree with the feature addition for log rotation and configurability. I think those are features that, to quote a colleague "Are features that should be supported". As @emilevauge noted, we may want to look at different options to achieve this, as potentially that project might not be ideal, but either way, I do agree with the introduction of those features.

Third, you are correct on your lifecycle points. You are obviously familiar and comfortable with k8s, and are obviously capable of handling the "gotchas" of file based logging by shifting the responsibility to your sidecar in your use case. A lot of users may not be as savvy, and we want to be careful promoting features that can be "gotchas" to new users.

For these reasons, I would 👍  the proposal, however decouple your use case (k8s) from the feature request, and would just propose it as a feature for the logging module. That way, _if_ users wanted to implement it in k8s (as you will), and they are willing to address the drawbacks (either via sidecar etc), then they are more than welcome to it.

To quote another colleague:
```
"log everything to stout and have your central log agent aggregate everything" approach
 comes with its own set of issues that a more decentralized solution may be able to handle better.
 Thus, I think we should only need to make sure that both models are feasible with Traefik.
```
And I agree with this as well.

For these reasons, I would like to apologize for my negativity so far in this issue, and give the feature request a 👍 from me.
Hi @dtomcej,

Sorry for the delayed response. I've been on holiday. 

I'm fine with not using the luberjack library, @emilevauge . It was just an example. I'll work up a PR in a couple of days as a proof of concept so we can have some code to discuss.
Hey there @dtomcej!

I'm going to implement the extraVolumes/extraMounts for the traefik helm chart as well as a sidecar which will have sidecar for log rotation. 

My 50 cents for having the rotation built-in are those who use Elastic filebeat, and I do personally use it. _It can only read traefik logs from the file_.
Logging to stdout - yeah awesome sure. But in my case I will have to pass log files between pods via host volume and the rotation could be handy though not required... Sidecar will do the job...

PS. nginx does not have log rotation built-in, however traefik could have :)
@dtomcej Hi there

> "log everything to stout and have your central log agent aggregate everything" approach
 comes with its own set of issues that a more decentralized solution may be able to handle better.
 Thus, I think we should only need to make sure that both models are feasible with Traefik.

Awesome and we all feel that the above is right and correct. However in the real world scenario tools such as filebeat expect files to read the input from https://www.elastic.co/guide/en/beats/filebeat/current/filebeat-module-traefik.html#configuring-traefik-module. I'm not expert in different log consumption tooling, though it seems that quite a number of tools expect files :( This is definitely jet legged.

Recently I've added to traefik k8s chart, `extraVolumes` and `extraVolumeMounts` https://github.com/helm/charts/pull/8723, well now it's possible to share files between filebeat and traefik pods. I don't like it too:(

The next step is log rotation it would be no problem to use host's or a side car logrotate to send `USR1` signal to traefik. It would be simple if it was the same PID namespace, but:

- if logrotate was on the host we would need that `docker kill -s` magic with the correct pod container id, consequently **this involves further probably heavy chart mangling**
- if a sidecar container in the same pod - we need to send the signal to another container, with k8s 1.12 it will be easy to support since the pods containers can share the same PID namespaces. **Pretty simple chart modifications**. Though it's a version restriction.
- If traefik supported built-in rotation - it would be the **simplest** chart modification.

The approach taken by me is a [derived traefik container](https://github.com/plasmops/docker-traefik/blob/master/Dockerfile). It's straightforward but it brings external resources (configmap for logrotate config) which is an extra complexity...
Final thoughts: **I also go for stdout**, it's better to use filebeat docker input + ES ingest rather than filebeat traefik module (it doesn't work as expected :)
Hello everyone!
well I have a similar problem. I have linux and windows hosts running traefik. Both should be able to handle rotating logs. Due to the lag of the SIGUSR1 Signal in windows this is not possible at all.

What I would like to see:
A possibility to reload/restart the service e.g. with the service pid, so the logfiles are not locked and can be rotated.
I know that nginx did a similar thing by saving his pid. So a script is able to send the reload command to a running instance of nginx. 
This might also work for traefik?!

Best regards!
Hello, any updates on this issue?
We have a filebeat sidecar logging architecture and would like to have this feature supported natively.
If I understand correctly, a PR with this functionality would be accepted, but I'd like to know if anyone else did any work or analysis on this issue before.
I am no longer using traefik so have no time to contribute a pr. 

Fix patch:
diff --git a/.golangci.yml b/.golangci.yml
index 662de50573..632ca1e2c8 100644
--- a/.golangci.yml
+++ b/.golangci.yml
@@ -162,7 +162,7 @@ issues:
       text: "Function 'buildConstructor' has too many statements"
       linters:
         - funlen
-    - path: pkg/tracing/haystack/logger.go
+    - path: pkg/logs/haystack.go
       linters:
         - goprintffuncname
     - path: pkg/tracing/tracing.go
diff --git a/cmd/traefik/logger.go b/cmd/traefik/logger.go
new file mode 100644
index 0000000000..0c20d2bf0d
--- /dev/null
+++ b/cmd/traefik/logger.go
@@ -0,0 +1,90 @@
+package main
+
+import (
+	"io"
+	stdlog "log"
+	"os"
+	"strings"
+	"time"
+
+	"github.com/natefinch/lumberjack"
+	"github.com/rs/zerolog"
+	"github.com/rs/zerolog/log"
+	"github.com/sirupsen/logrus"
+	"github.com/traefik/traefik/v2/pkg/config/static"
+	"github.com/traefik/traefik/v2/pkg/logs"
+)
+
+func init() {
+	// hide the first logs before the setup of the logger.
+	zerolog.SetGlobalLevel(zerolog.ErrorLevel)
+}
+
+func setupLogger(staticConfiguration *static.Configuration) {
+	// configure log format
+	w := getLogWriter(staticConfiguration)
+
+	// configure log level
+	logLevel := getLogLevel(staticConfiguration)
+
+	zerolog.SetGlobalLevel(logLevel)
+
+	// create logger
+	logCtx := zerolog.New(w).With().Timestamp()
+	if logLevel <= zerolog.DebugLevel {
+		logCtx = logCtx.Caller()
+	}
+
+	log.Logger = logCtx.Logger()
+	zerolog.DefaultContextLogger = &log.Logger
+
+	// Global logrus replacement (related to lib like go-rancher-metadata, docker, etc.)
+	logrus.StandardLogger().Out = logs.NoLevel(log.Logger, zerolog.DebugLevel)
+
+	// configure default standard log.
+	stdlog.SetFlags(stdlog.Lshortfile | stdlog.LstdFlags)
+	stdlog.SetOutput(logs.NoLevel(log.Logger, zerolog.DebugLevel))
+}
+
+func getLogWriter(staticConfiguration *static.Configuration) io.Writer {
+	var w io.Writer = os.Stderr
+
+	if staticConfiguration.Log != nil && len(staticConfiguration.Log.FilePath) > 0 {
+		_, _ = os.Create(staticConfiguration.Log.FilePath)
+		w = &lumberjack.Logger{
+			Filename:   staticConfiguration.Log.FilePath,
+			MaxSize:    staticConfiguration.Log.MaxSize,
+			MaxBackups: staticConfiguration.Log.MaxBackups,
+			MaxAge:     staticConfiguration.Log.MaxAge,
+			Compress:   true,
+		}
+	}
+
+	if staticConfiguration.Log == nil || staticConfiguration.Log.Format != "json" {
+		w = zerolog.ConsoleWriter{
+			Out:        w,
+			TimeFormat: time.RFC3339,
+			NoColor:    staticConfiguration.Log != nil && (staticConfiguration.Log.NoColor || len(staticConfiguration.Log.FilePath) > 0),
+		}
+	}
+
+	return w
+}
+
+func getLogLevel(staticConfiguration *static.Configuration) zerolog.Level {
+	levelStr := "error"
+	if staticConfiguration.Log != nil && staticConfiguration.Log.Level != "" {
+		levelStr = strings.ToLower(staticConfiguration.Log.Level)
+	}
+
+	logLevel, err := zerolog.ParseLevel(strings.ToLower(levelStr))
+	if err != nil {
+		log.Error().Err(err).
+			Str("logLevel", levelStr).
+			Msg("Unspecified or invalid log level, setting the level to default (ERROR)...")
+
+		logLevel = zerolog.ErrorLevel
+	}
+
+	return logLevel
+}
diff --git a/cmd/traefik/traefik.go b/cmd/traefik/traefik.go
index db4148e5a7..15fe3f1f1b 100644
--- a/cmd/traefik/traefik.go
+++ b/cmd/traefik/traefik.go
@@ -9,7 +9,6 @@ import (
 	"net/http"
 	"os"
 	"os/signal"
-	"path/filepath"
 	"sort"
 	"strings"
 	"syscall"
@@ -18,6 +17,7 @@ import (
 	"github.com/coreos/go-systemd/daemon"
 	"github.com/go-acme/lego/v4/challenge"
 	gokitmetrics "github.com/go-kit/kit/metrics"
+	"github.com/rs/zerolog/log"
 	"github.com/sirupsen/logrus"
 	"github.com/spiffe/go-spiffe/v2/workloadapi"
 	"github.com/traefik/paerser/cli"
@@ -29,7 +29,7 @@ import (
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
 	"github.com/traefik/traefik/v2/pkg/config/runtime"
 	"github.com/traefik/traefik/v2/pkg/config/static"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/metrics"
 	"github.com/traefik/traefik/v2/pkg/middlewares/accesslog"
 	"github.com/traefik/traefik/v2/pkg/provider/acme"
@@ -46,7 +46,6 @@ import (
 	"github.com/traefik/traefik/v2/pkg/tracing/jaeger"
 	"github.com/traefik/traefik/v2/pkg/types"
 	"github.com/traefik/traefik/v2/pkg/version"
-	"github.com/vulcand/oxy/roundrobin"
 )
 
 func main() {
@@ -88,27 +87,24 @@ Complete documentation is available at https://traefik.io`,
 }
 
 func runCmd(staticConfiguration *static.Configuration) error {
-	configureLogging(staticConfiguration)
+	setupLogger(staticConfiguration)
 
 	http.DefaultTransport.(*http.Transport).Proxy = http.ProxyFromEnvironment
 
-	if err := roundrobin.SetDefaultWeight(0); err != nil {
-		log.WithoutContext().Errorf("Could not set round robin default weight: %v", err)
-	}
-
 	staticConfiguration.SetEffectiveConfiguration()
 	if err := staticConfiguration.ValidateConfiguration(); err != nil {
 		return err
 	}
 
-	log.WithoutContext().Infof("Traefik version %s built on %s", version.Version, version.BuildDate)
+	log.Info().Str("version", version.Version).
+		Msgf("Traefik version %s built on %s", version.Version, version.BuildDate)
 
 	jsonConf, err := json.Marshal(staticConfiguration)
 	if err != nil {
-		log.WithoutContext().Errorf("Could not marshal static configuration: %v", err)
-		log.WithoutContext().Debugf("Static configuration loaded [struct] %#v", staticConfiguration)
+		log.Error().Err(err).Msg("Could not marshal static configuration")
+		log.Debug().Interface("staticConfiguration", staticConfiguration).Msg("Static configuration loaded [struct]")
 	} else {
-		log.WithoutContext().Debugf("Static configuration loaded %s", string(jsonConf))
+		log.Debug().RawJSON("staticConfiguration", jsonConf).Msg("Static configuration loaded [json]")
 	}
 
 	if staticConfiguration.Global.CheckNewVersion {
@@ -133,16 +129,16 @@ func runCmd(staticConfiguration *static.Configuration) error {
 
 	sent, err := daemon.SdNotify(false, "READY=1")
 	if !sent && err != nil {
-		log.WithoutContext().Errorf("Failed to notify: %v", err)
+		log.Error().Err(err).Msg("Failed to notify")
 	}
 
 	t, err := daemon.SdWatchdogEnabled(false)
 	if err != nil {
-		log.WithoutContext().Errorf("Could not enable Watchdog: %v", err)
+		log.Error().Err(err).Msg("Could not enable Watchdog")
 	} else if t != 0 {
 		// Send a ping each half time given
 		t /= 2
-		log.WithoutContext().Infof("Watchdog activated with timer duration %s", t)
+		log.Info().Msgf("Watchdog activated with timer duration %s", t)
 		safe.Go(func() {
 			tick := time.Tick(t)
 			for range tick {
@@ -153,17 +149,17 @@ func runCmd(staticConfiguration *static.Configuration) error {
 
 				if staticConfiguration.Ping == nil || errHealthCheck == nil {
 					if ok, _ := daemon.SdNotify(false, "WATCHDOG=1"); !ok {
-						log.WithoutContext().Error("Fail to tick watchdog")
+						log.Error().Msg("Fail to tick watchdog")
 					}
 				} else {
-					log.WithoutContext().Error(errHealthCheck)
+					log.Error().Err(errHealthCheck).Send()
 				}
 			}
 		})
 	}
 
 	svr.Wait()
-	log.WithoutContext().Info("Shutting down")
+	log.Info().Msg("Shutting down")
 	return nil
 }
 
@@ -209,14 +205,14 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
 	}
 
 	if staticConfiguration.Pilot != nil {
-		log.WithoutContext().Warn("Traefik Pilot has been removed.")
+		log.Warn().Msg("Traefik Pilot has been removed.")
 	}
 
 	// Plugins
 
 	pluginBuilder, err := createPluginBuilder(staticConfiguration)
 	if err != nil {
-		log.WithoutContext().WithError(err).Error("Plugins are disabled because an error has occurred.")
+		log.Error().Err(err).Msg("Plugins are disabled because an error has occurred.")
 	}
 
 	// Providers plugins
@@ -259,9 +255,8 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
 
 	var spiffeX509Source *workloadapi.X509Source
 	if staticConfiguration.Spiffe != nil && staticConfiguration.Spiffe.WorkloadAPIAddr != "" {
-		log.WithoutContext().
-			WithField("workloadAPIAddr", staticConfiguration.Spiffe.WorkloadAPIAddr).
-			Info("Waiting on SPIFFE SVID delivery")
+		log.Info().Str("workloadAPIAddr", staticConfiguration.Spiffe.WorkloadAPIAddr).
+			Msg("Waiting on SPIFFE SVID delivery")
 
 		spiffeX509Source, err = workloadapi.NewX509Source(
 			ctx,
@@ -274,8 +269,7 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
 		if err != nil {
 			return nil, fmt.Errorf("unable to create SPIFFE x509 source: %w", err)
 		}
-
-		log.WithoutContext().Info("Successfully obtained SPIFFE SVID.")
+		log.Info().Msg("Successfully obtained SPIFFE SVID.")
 	}
 
 	roundTripperManager := service.NewRoundTripperManager(spiffeX509Source)
@@ -365,7 +359,8 @@ func setupServer(staticConfiguration *static.Configuration) (*server.Server, err
 				// "traefik-hub" is an allowed certificate resolver name in a Traefik Hub Experimental feature context.
 				// It is used to activate its own certificate resolution, even though it is not a "classical" traefik certificate resolver.
 				(staticConfiguration.Hub == nil || rt.TLS.CertResolver != "traefik-hub") {
-				log.WithoutContext().Errorf("Router %s uses a non-existent certificate resolver: %s", rtName, rt.TLS.CertResolver)
+				log.Error().Err(err).Str(logs.RouterName, rtName).Str("certificateResolver", rt.TLS.CertResolver).
+					Msg("Router uses a non-existent certificate resolver")
 			}
 		}
 	})
@@ -411,7 +406,7 @@ func getDefaultsEntrypoints(staticConfiguration *static.Configuration) []string
 		protocol, err := cfg.GetProtocol()
 		if err != nil {
 			// Should never happen because Traefik should not start if protocol is invalid.
-			log.WithoutContext().Errorf("Invalid protocol: %v", err)
+			log.Error().Err(err).Msg("Invalid protocol")
 		}
 
 		if protocol != "udp" && name != static.DefaultInternalEntryPointName {
@@ -457,7 +452,7 @@ func initACMEProvider(c *static.Configuration, providerAggregator *aggregator.Pr
 		}
 
 		if err := providerAggregator.AddProvider(p); err != nil {
-			log.WithoutContext().Errorf("The ACME resolver %q is skipped from the resolvers list because: %v", name, err)
+			log.Error().Err(err).Str("resolver", name).Msg("The ACME resolve is skipped from the resolvers list")
 			continue
 		}
 
@@ -482,7 +477,7 @@ func initTailscaleProviders(cfg *static.Configuration, providerAggregator *aggre
 		tsProvider := &tailscale.Provider{ResolverName: name}
 
 		if err := providerAggregator.AddProvider(tsProvider); err != nil {
-			log.WithoutContext().Errorf("Unable to create Tailscale provider %s: %v", name, err)
+			log.Error().Err(err).Str(logs.ProviderName, name).Msg("Unable to create Tailscale provider")
 			continue
 		}
 
@@ -500,41 +495,46 @@ func registerMetricClients(metricsConfig *types.Metrics) []metrics.Registry {
 	var registries []metrics.Registry
 
 	if metricsConfig.Prometheus != nil {
-		ctx := log.With(context.Background(), log.Str(log.MetricsProviderName, "prometheus"))
-		prometheusRegister := metrics.RegisterPrometheus(ctx, metricsConfig.Prometheus)
+		logger := log.With().Str(logs.MetricsProviderName, "prometheus").Logger()
+
+		prometheusRegister := metrics.RegisterPrometheus(logger.WithContext(context.Background()), metricsConfig.Prometheus)
 		if prometheusRegister != nil {
 			registries = append(registries, prometheusRegister)
-			log.FromContext(ctx).Debug("Configured Prometheus metrics")
+			logger.Debug().Msg("Configured Prometheus metrics")
 		}
 	}
 
 	if metricsConfig.Datadog != nil {
-		ctx := log.With(context.Background(), log.Str(log.MetricsProviderName, "datadog"))
-		registries = append(registries, metrics.RegisterDatadog(ctx, metricsConfig.Datadog))
-		log.FromContext(ctx).Debugf("Configured Datadog metrics: pushing to %s once every %s",
+		logger := log.With().Str(logs.MetricsProviderName, "datadog").Logger()
+
+		registries = append(registries, metrics.RegisterDatadog(logger.WithContext(context.Background()), metricsConfig.Datadog))
+		logger.Debug().Msgf("Configured Datadog metrics: pushing to %s once every %s",
 			metricsConfig.Datadog.Address, metricsConfig.Datadog.PushInterval)
 	}
 
 	if metricsConfig.StatsD != nil {
-		ctx := log.With(context.Background(), log.Str(log.MetricsProviderName, "statsd"))
-		registries = append(registries, metrics.RegisterStatsd(ctx, metricsConfig.StatsD))
-		log.FromContext(ctx).Debugf("Configured StatsD metrics: pushing to %s once every %s",
+		logger := log.With().Str(logs.MetricsProviderName, "statsd").Logger()
+
+		registries = append(registries, metrics.RegisterStatsd(logger.WithContext(context.Background()), metricsConfig.StatsD))
+		logger.Debug().Msgf("Configured StatsD metrics: pushing to %s once every %s",
 			metricsConfig.StatsD.Address, metricsConfig.StatsD.PushInterval)
 	}
 
 	if metricsConfig.InfluxDB != nil {
-		ctx := log.With(context.Background(), log.Str(log.MetricsProviderName, "influxdb"))
-		registries = append(registries, metrics.RegisterInfluxDB(ctx, metricsConfig.InfluxDB))
-		log.FromContext(ctx).Debugf("Configured InfluxDB metrics: pushing to %s once every %s",
+		logger := log.With().Str(logs.MetricsProviderName, "influxdb").Logger()
+
+		registries = append(registries, metrics.RegisterInfluxDB(logger.WithContext(context.Background()), metricsConfig.InfluxDB))
+		logger.Debug().Msgf("Configured InfluxDB metrics: pushing to %s once every %s",
 			metricsConfig.InfluxDB.Address, metricsConfig.InfluxDB.PushInterval)
 	}
 
 	if metricsConfig.InfluxDB2 != nil {
-		ctx := log.With(context.Background(), log.Str(log.MetricsProviderName, "influxdb2"))
-		influxDB2Register := metrics.RegisterInfluxDB2(ctx, metricsConfig.InfluxDB2)
+		logger := log.With().Str(logs.MetricsProviderName, "influxdb2").Logger()
+
+		influxDB2Register := metrics.RegisterInfluxDB2(logger.WithContext(context.Background()), metricsConfig.InfluxDB2)
 		if influxDB2Register != nil {
 			registries = append(registries, influxDB2Register)
-			log.FromContext(ctx).Debugf("Configured InfluxDB v2 metrics: pushing to %s (%s org/%s bucket) once every %s",
+			logger.Debug().Msgf("Configured InfluxDB v2 metrics: pushing to %s (%s org/%s bucket) once every %s",
 				metricsConfig.InfluxDB2.Address, metricsConfig.InfluxDB2.Org, metricsConfig.InfluxDB2.Bucket, metricsConfig.InfluxDB2.PushInterval)
 		}
 	}
@@ -563,7 +563,7 @@ func setupAccessLog(conf *types.AccessLog) *accesslog.Handler {
 
 	accessLoggerMiddleware, err := accesslog.NewHandler(conf)
 	if err != nil {
-		log.WithoutContext().Warnf("Unable to create access logger: %v", err)
+		log.Warn().Err(err).Msg("Unable to create access logger")
 		return nil
 	}
 
@@ -583,7 +583,7 @@ func setupTracing(conf *static.Tracing) *tracing.Tracing {
 
 	if conf.Zipkin != nil {
 		if backend != nil {
-			log.WithoutContext().Error("Multiple tracing backend are not supported: cannot create Zipkin backend.")
+			log.Error().Msg("Multiple tracing backend are not supported: cannot create Zipkin backend.")
 		} else {
 			backend = conf.Zipkin
 		}
@@ -591,7 +591,7 @@ func setupTracing(conf *static.Tracing) *tracing.Tracing {
 
 	if conf.Datadog != nil {
 		if backend != nil {
-			log.WithoutContext().Error("Multiple tracing backend are not supported: cannot create Datadog backend.")
+			log.Error().Msg("Multiple tracing backend are not supported: cannot create Datadog backend.")
 		} else {
 			backend = conf.Datadog
 		}
@@ -599,7 +599,7 @@ func setupTracing(conf *static.Tracing) *tracing.Tracing {
 
 	if conf.Instana != nil {
 		if backend != nil {
-			log.WithoutContext().Error("Multiple tracing backend are not supported: cannot create Instana backend.")
+			log.Error().Msg("Multiple tracing backend are not supported: cannot create Instana backend.")
 		} else {
 			backend = conf.Instana
 		}
@@ -607,7 +607,7 @@ func setupTracing(conf *static.Tracing) *tracing.Tracing {
 
 	if conf.Haystack != nil {
 		if backend != nil {
-			log.WithoutContext().Error("Multiple tracing backend are not supported: cannot create Haystack backend.")
+			log.Error().Msg("Multiple tracing backend are not supported: cannot create Haystack backend.")
 		} else {
 			backend = conf.Haystack
 		}
@@ -615,14 +615,14 @@ func setupTracing(conf *static.Tracing) *tracing.Tracing {
 
 	if conf.Elastic != nil {
 		if backend != nil {
-			log.WithoutContext().Error("Multiple tracing backend are not supported: cannot create Elastic backend.")
+			log.Error().Msg("Multiple tracing backend are not supported: cannot create Elastic backend.")
 		} else {
 			backend = conf.Elastic
 		}
 	}
 
 	if backend == nil {
-		log.WithoutContext().Debug("Could not initialize tracing, using Jaeger by default")
+		log.Debug().Msg("Could not initialize tracing, using Jaeger by default")
 		defaultBackend := &jaeger.Config{}
 		defaultBackend.SetDefaults()
 		backend = defaultBackend
@@ -630,65 +630,12 @@ func setupTracing(conf *static.Tracing) *tracing.Tracing {
 
 	tracer, err := tracing.NewTracing(conf.ServiceName, conf.SpanNameLimit, backend)
 	if err != nil {
-		log.WithoutContext().Warnf("Unable to create tracer: %v", err)
+		log.Warn().Err(err).Msg("Unable to create tracer")
 		return nil
 	}
 	return tracer
 }
 
-func configureLogging(staticConfiguration *static.Configuration) {
-	// configure default log flags
-	stdlog.SetFlags(stdlog.Lshortfile | stdlog.LstdFlags)
-
-	// configure log level
-	// an explicitly defined log level always has precedence. if none is
-	// given and debug mode is disabled, the default is ERROR, and DEBUG
-	// otherwise.
-	levelStr := "error"
-	if staticConfiguration.Log != nil && staticConfiguration.Log.Level != "" {
-		levelStr = strings.ToLower(staticConfiguration.Log.Level)
-	}
-
-	level, err := logrus.ParseLevel(levelStr)
-	if err != nil {
-		log.WithoutContext().Errorf("Error getting level: %v", err)
-	}
-	log.SetLevel(level)
-
-	var logFile string
-	if staticConfiguration.Log != nil && len(staticConfiguration.Log.FilePath) > 0 {
-		logFile = staticConfiguration.Log.FilePath
-	}
-
-	// configure log format
-	var formatter logrus.Formatter
-	if staticConfiguration.Log != nil && staticConfiguration.Log.Format == "json" {
-		formatter = &logrus.JSONFormatter{}
-	} else {
-		disableColors := len(logFile) > 0
-		formatter = &logrus.TextFormatter{DisableColors: disableColors, FullTimestamp: true, DisableSorting: true}
-	}
-	log.SetFormatter(formatter)
-
-	if len(logFile) > 0 {
-		dir := filepath.Dir(logFile)
-
-		if err := os.MkdirAll(dir, 0o755); err != nil {
-			log.WithoutContext().Errorf("Failed to create log path %s: %s", dir, err)
-		}
-
-		err = log.OpenFile(logFile)
-		logrus.RegisterExitHandler(func() {
-			if err := log.CloseFile(); err != nil {
-				log.WithoutContext().Errorf("Error while closing log: %v", err)
-			}
-		})
-		if err != nil {
-			log.WithoutContext().Errorf("Error while opening log file %s: %v", logFile, err)
-		}
-	}
-}
-
 func checkNewVersion() {
 	ticker := time.Tick(24 * time.Hour)
 	safe.Go(func() {
@@ -699,16 +646,16 @@ func checkNewVersion() {
 }
 
 func stats(staticConfiguration *static.Configuration) {
-	logger := log.WithoutContext()
+	logger := log.Info()
 
 	if staticConfiguration.Global.SendAnonymousUsage {
-		logger.Info(`Stats collection is enabled.`)
-		logger.Info(`Many thanks for contributing to Traefik's improvement by allowing us to receive anonymous information from your configuration.`)
-		logger.Info(`Help us improve Traefik by leaving this feature on :)`)
-		logger.Info(`More details on: https://doc.traefik.io/traefik/contributing/data-collection/`)
+		logger.Msg(`Stats collection is enabled.`)
+		logger.Msg(`Many thanks for contributing to Traefik's improvement by allowing us to receive anonymous information from your configuration.`)
+		logger.Msg(`Help us improve Traefik by leaving this feature on :)`)
+		logger.Msg(`More details on: https://doc.traefik.io/traefik/contributing/data-collection/`)
 		collect(staticConfiguration)
 	} else {
-		logger.Info(`
+		logger.Msg(`
 Stats collection is disabled.
 Help us improve Traefik by turning this feature on :)
 More details on: https://doc.traefik.io/traefik/contributing/data-collection/
@@ -721,7 +668,7 @@ func collect(staticConfiguration *static.Configuration) {
 	safe.Go(func() {
 		for time.Sleep(10 * time.Minute); ; <-ticker {
 			if err := collector.Collect(staticConfiguration); err != nil {
-				log.WithoutContext().Debug(err)
+				log.Debug().Err(err).Send()
 			}
 		}
 	})
diff --git a/docs/content/observability/logs.md b/docs/content/observability/logs.md
index e1beea2a54..806d37b054 100644
--- a/docs/content/observability/logs.md
+++ b/docs/content/observability/logs.md
@@ -64,7 +64,7 @@ log:
 
 #### `level`
 
-By default, the `level` is set to `ERROR`. Alternative logging levels are `DEBUG`, `PANIC`, `FATAL`, `ERROR`, `WARN`, and `INFO`.
+By default, the `level` is set to `ERROR`. Alternative logging levels are `TRACE`, `DEBUG`, `PANIC`, `FATAL`, `ERROR`, `WARN`, and `INFO`.
 
 ```yaml tab="File (YAML)"
 log:
@@ -80,10 +80,101 @@ log:
 --log.level=DEBUG
 ```
 
+#### `noColor`
+
+When using the 'common' format, disables the colorized output.
+
+```yaml tab="File (YAML)"
+log:
+  noColor: true
+```
+
+```toml tab="File (TOML)"
+[log]
+  noColor = true
+```
+
+```bash tab="CLI"
+--log.nocolor=true
+```
+
 ## Log Rotation
 
-Traefik will close and reopen its log files, assuming they're configured, on receipt of a USR1 signal.
-This allows the logs to be rotated and processed by an external program, such as `logrotate`.
+The rotation of the log files can be configured with the following options.
+
+### `maxSize`
+
+`maxSize` is the maximum size in megabytes of the log file before it gets rotated.
+It defaults to 100 megabytes.
+
+```yaml tab="File (YAML)"
+log:
+  maxSize: 1
+```
+
+```toml tab="File (TOML)"
+[log]
+  maxSize = 1
+```
+
+```bash tab="CLI"
+--log.maxsize=1
+```
+
+### `maxBackups`
+
+`maxBackups` is the maximum number of old log files to retain.
+The default is to retain all old log files (though `maxAge` may still cause them to get deleted).
+
+```yaml tab="File (YAML)"
+log:
+  maxBackups: 3
+```
+
+```toml tab="File (TOML)"
+[log]
+  maxBackups = 3
+```
+
+```bash tab="CLI"
+--log.maxbackups=3
+```
+
+### `maxAge`
+
+`maxAge` is the maximum number of days to retain old log files based on the timestamp encoded in their filename.
+Note that a day is defined as 24 hours and may not exactly correspond to calendar days due to daylight savings, leap seconds, etc.
+The default is not to remove old log files based on age.
+
+```yaml tab="File (YAML)"
+log:
+  maxAge: 3
+```
+
+```toml tab="File (TOML)"
+[log]
+  maxAge = 3
+```
+
+```bash tab="CLI"
+--log.maxage=3
+```
+
+### `compress`
+
+`compress` determines if the rotated log files should be compressed using gzip.
+The default is not to perform compression.
+
+```yaml tab="File (YAML)"
+log:
+  compress: 3
+```
+
+```toml tab="File (TOML)"
+[log]
+  compress = 3
+```
 
-!!! warning
-    This does not work on Windows due to the lack of USR signals.
+```bash tab="CLI"
+--log.compress=3
+```
diff --git a/docs/content/reference/static-configuration/cli-ref.md b/docs/content/reference/static-configuration/cli-ref.md
index eb7831f52b..88935c8cd6 100644
--- a/docs/content/reference/static-configuration/cli-ref.md
+++ b/docs/content/reference/static-configuration/cli-ref.md
@@ -246,6 +246,9 @@ The TLS key for Traefik Proxy as a TLS client.
 `--log`:  
 Traefik log settings. (Default: ```false```)
 
+`--log.compress`:  
+Determines if the rotated log files should be compressed using gzip. (Default: ```false```)
+
 `--log.filepath`:  
 Traefik log file path. Stdout is used when omitted or empty.
 
@@ -255,6 +258,18 @@ Traefik log format: json | common (Default: ```common```)
 `--log.level`:  
 Log level set to traefik logs. (Default: ```ERROR```)
 
+`--log.maxage`:  
+Maximum number of days to retain old log files based on the timestamp encoded in their filename. (Default: ```0```)
+
+`--log.maxbackups`:  
+Maximum number of old log files to retain. (Default: ```0```)
+
+`--log.maxsize`:  
+Maximum size in megabytes of the log file before it gets rotated. (Default: ```0```)
+
+`--log.nocolor`:  
+When using the 'common' format, disables the colorized output. (Default: ```false```)
+
 `--metrics.datadog`:  
 Datadog metrics exporter type. (Default: ```false```)
 
diff --git a/docs/content/reference/static-configuration/env-ref.md b/docs/content/reference/static-configuration/env-ref.md
index e23990497e..0ea3aee87b 100644
--- a/docs/content/reference/static-configuration/env-ref.md
+++ b/docs/content/reference/static-configuration/env-ref.md
@@ -246,6 +246,9 @@ The TLS key for Traefik Proxy as a TLS client.
 `TRAEFIK_LOG`:  
 Traefik log settings. (Default: ```false```)
 
+`TRAEFIK_LOG_COMPRESS`:  
+Determines if the rotated log files should be compressed using gzip. (Default: ```false```)
+
 `TRAEFIK_LOG_FILEPATH`:  
 Traefik log file path. Stdout is used when omitted or empty.
 
@@ -255,6 +258,18 @@ Traefik log format: json | common (Default: ```common```)
 `TRAEFIK_LOG_LEVEL`:  
 Log level set to traefik logs. (Default: ```ERROR```)
 
+`TRAEFIK_LOG_MAXAGE`:  
+Maximum number of days to retain old log files based on the timestamp encoded in their filename. (Default: ```0```)
+
+`TRAEFIK_LOG_MAXBACKUPS`:  
+Maximum number of old log files to retain. (Default: ```0```)
+
+`TRAEFIK_LOG_MAXSIZE`:  
+Maximum size in megabytes of the log file before it gets rotated. (Default: ```0```)
+
+`TRAEFIK_LOG_NOCOLOR`:  
+When using the 'common' format, disables the colorized output. (Default: ```false```)
+
 `TRAEFIK_METRICS_DATADOG`:  
 Datadog metrics exporter type. (Default: ```false```)
 
diff --git a/docs/content/reference/static-configuration/file.toml b/docs/content/reference/static-configuration/file.toml
index 4f8f847f2a..4782c6cc9f 100644
--- a/docs/content/reference/static-configuration/file.toml
+++ b/docs/content/reference/static-configuration/file.toml
@@ -326,8 +326,13 @@
 
 [log]
   level = "foobar"
-  filePath = "foobar"
   format = "foobar"
+  noColor = true
+  filePath = "foobar"
+  maxSize = 42
+  maxBackups = 42
+  maxAge = 42
+  compress = true
 
 [accessLog]
   filePath = "foobar"
diff --git a/docs/content/reference/static-configuration/file.yaml b/docs/content/reference/static-configuration/file.yaml
index d668d7ac3e..55bfbf131b 100644
--- a/docs/content/reference/static-configuration/file.yaml
+++ b/docs/content/reference/static-configuration/file.yaml
@@ -352,8 +352,13 @@ ping:
   terminatingStatusCode: 42
 log:
   level: foobar
-  filePath: foobar
   format: foobar
+  noColor: true
+  filePath: foobar
+  maxSize: 42
+  maxBackups: 42
+  maxAge: 42
+  compress: true
 accessLog:
   filePath: foobar
   format: foobar
diff --git a/go.mod b/go.mod
index 8c15c0e843..b6ca40a0a8 100644
--- a/go.mod
+++ b/go.mod
@@ -49,6 +49,7 @@ require (
 	github.com/mitchellh/copystructure v1.0.0
 	github.com/mitchellh/hashstructure v1.0.0
 	github.com/mitchellh/mapstructure v1.5.0
+	github.com/natefinch/lumberjack v0.0.0-20201021141957-47ffae23317c
 	github.com/opentracing/opentracing-go v1.2.0
 	github.com/openzipkin-contrib/zipkin-go-opentracing v0.4.5
 	github.com/openzipkin/zipkin-go v0.2.2
@@ -58,9 +59,10 @@ require (
 	github.com/prometheus/client_golang v1.12.2-0.20220704083116-e8f91604d835
 	github.com/prometheus/client_model v0.2.0
 	github.com/rancher/go-rancher-metadata v0.0.0-20200311180630-7f4c936a06ac
-	github.com/sirupsen/logrus v1.8.1
+	github.com/rs/zerolog v1.28.0
+	github.com/sirupsen/logrus v1.9.0
 	github.com/spiffe/go-spiffe/v2 v2.1.1
-	github.com/stretchr/testify v1.8.0
+	github.com/stretchr/testify v1.8.1
 	github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154
 	github.com/tailscale/tscert v0.0.0-20220316030059-54bbcb9f74e2
 	github.com/traefik/paerser v0.1.9
@@ -70,15 +72,15 @@ require (
 	github.com/unrolled/render v1.0.2
 	github.com/unrolled/secure v1.0.9
 	github.com/vdemeester/shakers v0.1.0
-	github.com/vulcand/oxy v1.4.1
+	github.com/vulcand/oxy/v2 v2.0.0-20221121151423-d5cb734e4467
 	github.com/vulcand/predicate v1.2.0
 	go.elastic.co/apm v1.13.1
 	go.elastic.co/apm/module/apmot v1.13.1
-	golang.org/x/mod v0.4.2
-	golang.org/x/net v0.0.0-20220927171203-f486391704dc
-	golang.org/x/text v0.3.7
+	golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4
+	golang.org/x/net v0.1.0
+	golang.org/x/text v0.4.0
 	golang.org/x/time v0.0.0-20220224211638-0e9765cccd65
-	golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2
+	golang.org/x/tools v0.1.12
 	google.golang.org/grpc v1.46.0
 	gopkg.in/DataDog/dd-trace-go.v1 v1.38.1
 	gopkg.in/fsnotify.v1 v1.4.7
@@ -141,7 +143,7 @@ require (
 	github.com/containerd/continuity v0.1.0 // indirect
 	github.com/containerd/typeurl v1.0.2 // indirect
 	github.com/coreos/go-semver v0.3.0 // indirect
-	github.com/coreos/go-systemd/v22 v22.3.2 // indirect
+	github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534 // indirect
 	github.com/cpu/goacmedns v0.1.1 // indirect
 	github.com/deepmap/oapi-codegen v1.9.1 // indirect
 	github.com/desertbit/timer v0.0.0-20180107155436-c41aec40b27f // indirect
@@ -292,7 +294,6 @@ require (
 	github.com/santhosh-tekuri/jsonschema v1.2.4 // indirect
 	github.com/scaleway/scaleway-sdk-go v1.0.0-beta.9 // indirect
 	github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 // indirect
-	github.com/segmentio/fasthash v1.0.3 // indirect
 	github.com/shopspring/decimal v1.2.0 // indirect
 	github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 // indirect
 	github.com/softlayer/softlayer-go v1.0.6 // indirect
@@ -300,7 +301,7 @@ require (
 	github.com/spf13/cast v1.3.1 // indirect
 	github.com/spf13/cobra v1.2.1 // indirect
 	github.com/spf13/pflag v1.0.5 // indirect
-	github.com/stretchr/objx v0.4.0 // indirect
+	github.com/stretchr/objx v0.5.0 // indirect
 	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/common v1.0.490 // indirect
 	github.com/tencentcloud/tencentcloud-sdk-go/tencentcloud/dnspod v1.0.490 // indirect
 	github.com/theupdateframework/notary v0.6.1 // indirect
@@ -330,9 +331,9 @@ require (
 	golang.org/x/crypto v0.0.0-20220427172511-eb4f295cb31f // indirect
 	golang.org/x/lint v0.0.0-20210508222113-6edffad5e616 // indirect
 	golang.org/x/oauth2 v0.0.0-20220909003341-f21342109be1 // indirect
-	golang.org/x/sync v0.0.0-20210220032951-036812b2e83c // indirect
-	golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab // indirect
-	golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 // indirect
+	golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 // indirect
+	golang.org/x/sys v0.1.0 // indirect
+	golang.org/x/term v0.1.0 // indirect
 	golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 // indirect
 	google.golang.org/api v0.44.0 // indirect
 	google.golang.org/appengine v1.6.7 // indirect
diff --git a/go.sum b/go.sum
index 7801b7c1b1..cece3818eb 100644
--- a/go.sum
+++ b/go.sum
@@ -488,8 +488,9 @@ github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pq
 github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
 github.com/coreos/go-systemd/v22 v22.0.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
 github.com/coreos/go-systemd/v22 v22.1.0/go.mod h1:xO0FLkIi5MaZafQlIrOotqXZ90ih+1atmu1JpKERPPk=
-github.com/coreos/go-systemd/v22 v22.3.2 h1:D9/bQk5vlXQFZ6Kwuu6zaiXJ9oTPe68++AzAJc1DzSI=
 github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
+github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534 h1:rtAn27wIbmOGUs7RIbVgPEjb31ehTVniDwPGXyMxm5U=
+github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
 github.com/coreos/pkg v0.0.0-20160727233714-3ac0863d7acf/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA=
 github.com/cpu/goacmedns v0.1.1 h1:DM3H2NiN2oam7QljgGY5ygy4yDXhK5Z4JUnqaugs2C4=
@@ -1553,6 +1554,8 @@ github.com/mwitkow/grpc-proxy v0.0.0-20181017164139-0f1106ef9c76/go.mod h1:x5OoJ
 github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw=
 github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04 h1:o6uBwrhM5C8Ll3MAAxrQxRHEu7FkapwTuI2WmL1rw4g=
 github.com/namedotcom/go v0.0.0-20180403034216-08470befbe04/go.mod h1:5sN+Lt1CaY4wsPvgQH/jsuJi4XO2ssZbdsIizr4CVC8=
+github.com/natefinch/lumberjack v0.0.0-20201021141957-47ffae23317c h1:194MYKszq5DlJ73wpFuOTEsC/ryOOxt2F901D/07tec=
+github.com/natefinch/lumberjack v0.0.0-20201021141957-47ffae23317c/go.mod h1:tanojtwrLPxkEzT+bGGz9kb6bm8+yVwgAE44c3v1Au4=
 github.com/nats-io/jwt v0.3.0/go.mod h1:fRYCDE99xlTsqUzISS1Bi75UBJ6ljOJQOAAu5VglpSg=
 github.com/nats-io/jwt v0.3.2/go.mod h1:/euKqTS1ZD+zzjYrY7pseZrTtWQSjujC7xjPc8wL6eU=
 github.com/nats-io/nats-server/v2 v2.1.2/go.mod h1:Afk+wRZqkMQs/p45uXdrVLuab3gwv3Z8C4HTBu8GD/k=
@@ -1801,9 +1804,12 @@ github.com/rogpeppe/go-internal v1.8.1 h1:geMPLpDpQOgVyCg5z5GoRwLHepNdb71NXb67XF
 github.com/rs/cors v1.7.0 h1:+88SsELBHx5r+hZ8TCkggzSstaWNbDvThkVK8H6f9ik=
 github.com/rs/cors v1.7.0/go.mod h1:gFx+x8UowdsKA9AchylcLynDq+nNFfI8FkUZdN/jGCU=
 github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ=
+github.com/rs/xid v1.4.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
 github.com/rs/zerolog v1.4.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
 github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU=
 github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc=
+github.com/rs/zerolog v1.28.0 h1:MirSo27VyNi7RJYP3078AA1+Cyzd2GB66qy3aUHvsWY=
+github.com/rs/zerolog v1.28.0/go.mod h1:NILgTygv/Uej1ra5XxGf82ZFSLk58MFGAUS2o6usyD0=
 github.com/russross/blackfriday v1.5.2/go.mod h1:JO/DiYxRf+HjHt06OyowR9PTA263kcR/rfWxYHBV53g=
 github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
 github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
@@ -1833,7 +1839,6 @@ github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529 h1:nn5Wsu0esKSJiIVhscUt
 github.com/sean-/seed v0.0.0-20170313163322-e2103e2c3529/go.mod h1:DxrIzT+xaE7yg65j358z/aeFdxmN0P9QXhEzd20vsDc=
 github.com/seccomp/libseccomp-golang v0.9.1/go.mod h1:GbW5+tmTXfcxTToHLXlScSlAvWlF4P2Ca7zGrPiEpWo=
 github.com/segmentio/fasthash v1.0.3 h1:EI9+KE1EwvMLBWwjpRDc+fEM+prwxDYbslddQGtrmhM=
-github.com/segmentio/fasthash v1.0.3/go.mod h1:waKX8l2N8yckOgmSsXJi7x1ZfdKZ4x7KRMzBtS3oedY=
 github.com/segmentio/kafka-go v0.4.29/go.mod h1:m1lXeqJtIFYZayv0shM/tjrAFljvWLTprxBHd+3PnaU=
 github.com/sergi/go-diff v1.0.0/go.mod h1:0CfEIISq7TuYL3j771MWULgwwjU+GofnZX9QAmXWZgo=
 github.com/sergi/go-diff v1.1.0/go.mod h1:STckp+ISIX8hZLjrqAeVduY0gWCT9IjLuqbuNXdaHfM=
@@ -1875,8 +1880,9 @@ github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMB
 github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
 github.com/sirupsen/logrus v1.6.0/go.mod h1:7uNnSEd1DgxDLC74fIahvMZmmYsHGZGEOFrfsX/uA88=
 github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
-github.com/sirupsen/logrus v1.8.1 h1:dJKuHgqk1NNQlqoA6BTlM1Wf9DOH3NBjQyu0h9+AZZE=
 github.com/sirupsen/logrus v1.8.1/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0=
+github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0=
+github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
 github.com/smartystreets/assertions v0.0.0-20180927180507-b2de0cb4f26d/go.mod h1:OnSkiWE9lh6wB0YB77sQom3nweQdgAjqCqsofrRNTgc=
 github.com/smartystreets/assertions v1.0.1 h1:voD4ITNjPL5jjBfgR/r8fPIIBrliWrWHeiJApdr3r4w=
 github.com/smartystreets/go-aws-auth v0.0.0-20180515143844-0c1422d1fdb9 h1:hp2CYQUINdZMHdvTdXtPOY2ainKl4IoMcpAXEf2xj3Q=
@@ -1938,8 +1944,9 @@ github.com/stretchr/objx v0.0.0-20180129172003-8a3f7159479f/go.mod h1:HFkY916IF+
 github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
 github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE=
-github.com/stretchr/objx v0.4.0 h1:M2gUjqZET1qApGOWNSnZ49BAIMX4F/1plDv3+l31EJ4=
 github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
+github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c=
+github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
 github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v0.0.0-20180303142811-b89eecf5ca5d/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
@@ -1950,8 +1957,9 @@ github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5
 github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
 github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
-github.com/stretchr/testify v1.8.0 h1:pSgiaMZlXftHpm5L7V1+rVB+AZJydKsMxsQBIJw4PKk=
 github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
+github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
+github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
 github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154 h1:XGopsea1Dw7ecQ8JscCNQXDGYAKDiWjDeXnpN/+BY9g=
 github.com/stvp/go-udp-testing v0.0.0-20191102171040-06b61409b154/go.mod h1:7jxmlfBCDBXRzr0eAQJ48XC1hBu1np4CS5+cHEYfwpc=
 github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s=
@@ -2057,8 +2065,8 @@ github.com/vmihailenco/tagparser v0.1.1/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgq
 github.com/vmihailenco/tagparser v0.1.2/go.mod h1:OeAg3pn3UbLjkWt+rN9oFYB6u/cQgqMEUPoW2WPyhdI=
 github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV6mEfg5OIWRZA9qds=
 github.com/vmware/govmomi v0.18.0/go.mod h1:URlwyTFZX72RmxtxuaFL2Uj3fD1JTvZdx59bHWk6aFU=
-github.com/vulcand/oxy v1.4.1 h1:8FUsbr5xhSJqNlSrpUBcw93WuZIEI9JUyvThB9YqqF8=
-github.com/vulcand/oxy v1.4.1/go.mod h1:Yq8OBb0XWU/7nPSglwUH5LS2Pcp4yvad8SVayobZbSo=
+github.com/vulcand/oxy/v2 v2.0.0-20221121151423-d5cb734e4467 h1:Dbv3KJLgwtDKLpCZzTf1ISeG5ZYudPaLfTdYi4O2dSU=
+github.com/vulcand/oxy/v2 v2.0.0-20221121151423-d5cb734e4467/go.mod h1:0kOEB8mKzSeGHknF53gTM47UEvQnPoAPnM+58baqn2o=
 github.com/vulcand/predicate v1.2.0 h1:uFsW1gcnnR7R+QTID+FVcs0sSYlIGntoGOTb3rQJt50=
 github.com/vulcand/predicate v1.2.0/go.mod h1:VipoNYXny6c8N381zGUWkjuuNHiRbeAZhE7Qm9c+2GA=
 github.com/vultr/govultr/v2 v2.17.2 h1:gej/rwr91Puc/tgh+j33p/BLR16UrIPnSr+AIwYWZQs=
@@ -2273,8 +2281,9 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.3.1-0.20200828183125-ce943fd02449/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
 golang.org/x/mod v0.4.1/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
-golang.org/x/mod v0.4.2 h1:Gz96sIWK3OalVv/I/qNygP42zyoKp3xptRVCWRFEBvo=
 golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4 h1:6zppjxzCulZykYSLyVDYbneBfbaBIQPYMevg0bEwv2s=
+golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
 golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
 golang.org/x/net v0.0.0-20180811021610-c39426892332/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
@@ -2350,8 +2359,8 @@ golang.org/x/net v0.0.0-20211216030914-fe4d6282115f/go.mod h1:9nx3DQGgdP8bBQD5qx
 golang.org/x/net v0.0.0-20220127200216-cd36cc0744dd/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk=
 golang.org/x/net v0.0.0-20220624214902-1bab6f366d9e/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.0.0-20220927171203-f486391704dc h1:FxpXZdoBqT8RjqTy6i1E8nXHhW21wK7ptQ/EPIGxzPQ=
-golang.org/x/net v0.0.0-20220927171203-f486391704dc/go.mod h1:YDH+HFinaLZZlnHAfSS6ZXJJ9M9t4Dl22yv3iI2vPwk=
+golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
+golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
 golang.org/x/oauth2 v0.0.0-20180724155351-3d292e4d0cdc/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
 golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
@@ -2383,8 +2392,9 @@ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJ
 golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
-golang.org/x/sync v0.0.0-20210220032951-036812b2e83c h1:5KslGYwFpkhGh+Q16bwMP3cOontH8FOep7tGV86Y7SQ=
 golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4 h1:uVc8UZUe6tr40fFVnUP5Oj+veunVezqYl9z7DYw9xzw=
+golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
 golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180823144017-11551d06cbcc/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
 golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
@@ -2516,13 +2526,16 @@ golang.org/x/sys v0.0.0-20220209214540-3681064d5158/go.mod h1:oPkhp1MJrh7nUepCBc
 golang.org/x/sys v0.0.0-20220227234510-4e6760a101f9/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
-golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab h1:2QkjZIsXupsJbJIdSjjUOgWK3aEtzyuh2mPt3l/CkeU=
+golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
+golang.org/x/sys v0.1.0 h1:kunALQeHf1/185U1i0GOB/fy1IPRDDpuoOOqRReG57U=
+golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
 golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
 golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 golang.org/x/term v0.0.0-20210220032956-6a3ed077a48d/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
-golang.org/x/term v0.0.0-20210927222741-03fcf44c2211 h1:JGgROgKl9N8DuW20oFS5gxc+lE67/N3FcwmBPMe7ArY=
 golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
+golang.org/x/term v0.1.0 h1:g6Z6vPFA9dYBAF7DWcH6sCcOntplXsDKcliusYijMlw=
+golang.org/x/term v0.1.0/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
 golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
@@ -2532,8 +2545,9 @@ golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
-golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
 golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
+golang.org/x/text v0.4.0 h1:BrVqGRd7+k1DiOgtnFvAkoQEWQvBc25ouMJM6429SFg=
+golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
 golang.org/x/time v0.0.0-20180412165947-fbb02b2291d2/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20181108054448-85acf8d2951c/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
 golang.org/x/time v0.0.0-20190308202827-9d24e82272b4/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ=
@@ -2627,8 +2641,9 @@ golang.org/x/tools v0.1.0/go.mod h1:xkSsbof2nBLbhDlRMhhhyNLN/zl3eTqcnHD5viDpcZ0=
 golang.org/x/tools v0.1.1/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
 golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
-golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2 h1:BonxutuHCTL0rBDnZlKjpGIQFTjyUVTexFOdWkB6Fg0=
 golang.org/x/tools v0.1.6-0.20210726203631-07bc1bf47fb2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk=
+golang.org/x/tools v0.1.12 h1:VveCTK38A2rkS8ZqFY25HIDFscX5X9OoEhJd3quQmXU=
+golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
 golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
 golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
diff --git a/integration/fake_dns_server.go b/integration/fake_dns_server.go
index f0dbfca1aa..14aa295b04 100644
--- a/integration/fake_dns_server.go
+++ b/integration/fake_dns_server.go
@@ -6,7 +6,7 @@ import (
 	"os"
 
 	"github.com/miekg/dns"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 )
 
 type handler struct {
@@ -17,14 +17,12 @@ type handler struct {
 // Simplified version of the Challenge Test Server from Boulder
 // https://github.com/letsencrypt/boulder/blob/a6597b9f120207eff192c3e4107a7e49972a0250/test/challtestsrv/dnsone.go#L40
 func (s *handler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
-	logger := log.WithoutContext()
-
 	m := new(dns.Msg)
 	m.SetReply(r)
 	m.Compress = false
 
 	for _, q := range r.Question {
-		logger.Infof("Query -- [%s] %s", q.Name, dns.TypeToString[q.Qtype])
+		log.Info().Msgf("Query -- [%s] %s", q.Name, dns.TypeToString[q.Qtype])
 
 		switch q.Qtype {
 		case dns.TypeA:
@@ -94,7 +92,7 @@ func (s *handler) ServeDNS(w dns.ResponseWriter, r *dns.Msg) {
 	m.Ns = append(m.Ns, auth)
 
 	if err := w.WriteMsg(m); err != nil {
-		logger.Fatalf("Failed to write message %v", err)
+		log.Fatal().Err(err).Msg("Failed to write message")
 	}
 }
 
@@ -106,9 +104,9 @@ func startFakeDNSServer(traefikIP string) *dns.Server {
 	}
 
 	go func() {
-		log.WithoutContext().Infof("Start a fake DNS server.")
+		log.Info().Msg("Start a fake DNS server.")
 		if err := srv.ListenAndServe(); err != nil {
-			log.WithoutContext().Fatalf("Failed to set udp listener %v", err)
+			log.Fatal().Err(err).Msg("Failed to set udp listener")
 		}
 	}()
 
diff --git a/integration/fixtures/acme/acme_base.toml b/integration/fixtures/acme/acme_base.toml
index 75682d94b9..01306cd706 100644
--- a/integration/fixtures/acme/acme_base.toml
+++ b/integration/fixtures/acme/acme_base.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/acme/acme_domains.toml b/integration/fixtures/acme/acme_domains.toml
index a88e634bc0..a587ce7dca 100644
--- a/integration/fixtures/acme/acme_domains.toml
+++ b/integration/fixtures/acme/acme_domains.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/acme/acme_multiple_resolvers.toml b/integration/fixtures/acme/acme_multiple_resolvers.toml
index bd16de8c26..b2d41fae6d 100644
--- a/integration/fixtures/acme/acme_multiple_resolvers.toml
+++ b/integration/fixtures/acme/acme_multiple_resolvers.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/acme/acme_store_domains.toml b/integration/fixtures/acme/acme_store_domains.toml
index 14bb799d9b..22fa26de4b 100644
--- a/integration/fixtures/acme/acme_store_domains.toml
+++ b/integration/fixtures/acme/acme_store_domains.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/acme/acme_tcp.toml b/integration/fixtures/acme/acme_tcp.toml
index 898eb859ad..1cb7e7eaa8 100644
--- a/integration/fixtures/acme/acme_tcp.toml
+++ b/integration/fixtures/acme/acme_tcp.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/consul/simple.toml b/integration/fixtures/consul/simple.toml
index d40f13db10..e47f45b34a 100644
--- a/integration/fixtures/consul/simple.toml
+++ b/integration/fixtures/consul/simple.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints.web]
   address = ":8000"
diff --git a/integration/fixtures/consul_catalog/connect.toml b/integration/fixtures/consul_catalog/connect.toml
index f093c499ad..0b8d4b5d22 100644
--- a/integration/fixtures/consul_catalog/connect.toml
+++ b/integration/fixtures/consul_catalog/connect.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/consul_catalog/connect_by_default.toml b/integration/fixtures/consul_catalog/connect_by_default.toml
index 67f823ad46..f0c00b7181 100644
--- a/integration/fixtures/consul_catalog/connect_by_default.toml
+++ b/integration/fixtures/consul_catalog/connect_by_default.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/consul_catalog/connect_not_aware.toml b/integration/fixtures/consul_catalog/connect_not_aware.toml
index 6a6c10d94e..0efa4b8801 100644
--- a/integration/fixtures/consul_catalog/connect_not_aware.toml
+++ b/integration/fixtures/consul_catalog/connect_not_aware.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/consul_catalog/default_not_exposed.toml b/integration/fixtures/consul_catalog/default_not_exposed.toml
index f47ba97dba..6b5ce0b96c 100644
--- a/integration/fixtures/consul_catalog/default_not_exposed.toml
+++ b/integration/fixtures/consul_catalog/default_not_exposed.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/consul_catalog/simple.toml b/integration/fixtures/consul_catalog/simple.toml
index 93265e7d98..435473c19a 100644
--- a/integration/fixtures/consul_catalog/simple.toml
+++ b/integration/fixtures/consul_catalog/simple.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/consul_catalog/simple_watch.toml b/integration/fixtures/consul_catalog/simple_watch.toml
index 9f73dcaf1f..7015386575 100644
--- a/integration/fixtures/consul_catalog/simple_watch.toml
+++ b/integration/fixtures/consul_catalog/simple_watch.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/custom_ping_termination_status_code.toml b/integration/fixtures/custom_ping_termination_status_code.toml
index f234963ff2..34a493f82d 100644
--- a/integration/fixtures/custom_ping_termination_status_code.toml
+++ b/integration/fixtures/custom_ping_termination_status_code.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
 
@@ -13,4 +14,4 @@
       requestAcceptGraceTimeout = "10s"
 
 [ping]
-    terminatingStatusCode = 204
\ No newline at end of file
+    terminatingStatusCode = 204
diff --git a/integration/fixtures/docker/minimal.toml b/integration/fixtures/docker/minimal.toml
index 8e6024d845..d12a5b5199 100644
--- a/integration/fixtures/docker/minimal.toml
+++ b/integration/fixtures/docker/minimal.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/docker/simple.toml b/integration/fixtures/docker/simple.toml
index a2c0de53fb..5248e67efb 100644
--- a/integration/fixtures/docker/simple.toml
+++ b/integration/fixtures/docker/simple.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/error_pages/error.toml b/integration/fixtures/error_pages/error.toml
index 12b38bca82..3e1e9f847d 100644
--- a/integration/fixtures/error_pages/error.toml
+++ b/integration/fixtures/error_pages/error.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/error_pages/simple.toml b/integration/fixtures/error_pages/simple.toml
index a734098b97..a60f7393d6 100644
--- a/integration/fixtures/error_pages/simple.toml
+++ b/integration/fixtures/error_pages/simple.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/etcd/simple.toml b/integration/fixtures/etcd/simple.toml
index f634fff60b..995b087a10 100644
--- a/integration/fixtures/etcd/simple.toml
+++ b/integration/fixtures/etcd/simple.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints.web]
   address = ":8000"
diff --git a/integration/fixtures/file/56-simple-panic.toml b/integration/fixtures/file/56-simple-panic.toml
index 09aadf0a11..b49084f7fe 100644
--- a/integration/fixtures/file/56-simple-panic.toml
+++ b/integration/fixtures/file/56-simple-panic.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/file/directory.toml b/integration/fixtures/file/directory.toml
index b60354f0fa..4896332d18 100644
--- a/integration/fixtures/file/directory.toml
+++ b/integration/fixtures/file/directory.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/file/simple-hosts.toml b/integration/fixtures/file/simple-hosts.toml
index e2f5485a17..22731d7bb9 100644
--- a/integration/fixtures/file/simple-hosts.toml
+++ b/integration/fixtures/file/simple-hosts.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/file/simple.toml b/integration/fixtures/file/simple.toml
index 736eb4f2b9..30ad3ef650 100644
--- a/integration/fixtures/file/simple.toml
+++ b/integration/fixtures/file/simple.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/grpc/config.toml b/integration/fixtures/grpc/config.toml
index 9d448ae5b7..309c6dfcb6 100644
--- a/integration/fixtures/grpc/config.toml
+++ b/integration/fixtures/grpc/config.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [serversTransport]
   rootCAs = [ """{{ .CertContent }}""" ]
diff --git a/integration/fixtures/grpc/config_h2c.toml b/integration/fixtures/grpc/config_h2c.toml
index 683b72f583..76eb1d54dd 100644
--- a/integration/fixtures/grpc/config_h2c.toml
+++ b/integration/fixtures/grpc/config_h2c.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/grpc/config_h2c_termination.toml b/integration/fixtures/grpc/config_h2c_termination.toml
index a51bcc21bb..f902724dc9 100644
--- a/integration/fixtures/grpc/config_h2c_termination.toml
+++ b/integration/fixtures/grpc/config_h2c_termination.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.websecure]
diff --git a/integration/fixtures/grpc/config_insecure.toml b/integration/fixtures/grpc/config_insecure.toml
index 264360ad50..350e86cfc1 100644
--- a/integration/fixtures/grpc/config_insecure.toml
+++ b/integration/fixtures/grpc/config_insecure.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [serversTransport]
   insecureSkipVerify = true
diff --git a/integration/fixtures/grpc/config_retry.toml b/integration/fixtures/grpc/config_retry.toml
index 6f7a3a96a2..c0dc5929c3 100644
--- a/integration/fixtures/grpc/config_retry.toml
+++ b/integration/fixtures/grpc/config_retry.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [serversTransport]
   rootCAs = [ """{{ .CertContent }}""" ]
diff --git a/integration/fixtures/headers/basic.toml b/integration/fixtures/headers/basic.toml
index 29b1eb79ed..a28e6a7e14 100644
--- a/integration/fixtures/headers/basic.toml
+++ b/integration/fixtures/headers/basic.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/headers/cors.toml b/integration/fixtures/headers/cors.toml
index 3b0622e968..8f14c3a670 100644
--- a/integration/fixtures/headers/cors.toml
+++ b/integration/fixtures/headers/cors.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/headers/secure.toml b/integration/fixtures/headers/secure.toml
index 47404d6169..6d6ad1cdc9 100644
--- a/integration/fixtures/headers/secure.toml
+++ b/integration/fixtures/headers/secure.toml
@@ -7,6 +7,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/headers/secure_multiple.toml b/integration/fixtures/headers/secure_multiple.toml
index 9603046e50..c84f98125c 100644
--- a/integration/fixtures/headers/secure_multiple.toml
+++ b/integration/fixtures/headers/secure_multiple.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/healthcheck/multiple-entrypoints.toml b/integration/fixtures/healthcheck/multiple-entrypoints.toml
index 12bab72744..f2a6784ee5 100644
--- a/integration/fixtures/healthcheck/multiple-entrypoints.toml
+++ b/integration/fixtures/healthcheck/multiple-entrypoints.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.http1]
diff --git a/integration/fixtures/healthcheck/multiple-routers-one-same-service.toml b/integration/fixtures/healthcheck/multiple-routers-one-same-service.toml
index a2d4d5147f..86243f825b 100644
--- a/integration/fixtures/healthcheck/multiple-routers-one-same-service.toml
+++ b/integration/fixtures/healthcheck/multiple-routers-one-same-service.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web1]
diff --git a/integration/fixtures/healthcheck/port_overload.toml b/integration/fixtures/healthcheck/port_overload.toml
index eb7accedd7..2684d0ee86 100644
--- a/integration/fixtures/healthcheck/port_overload.toml
+++ b/integration/fixtures/healthcheck/port_overload.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/healthcheck/propagate.toml b/integration/fixtures/healthcheck/propagate.toml
index a5f11c2b4f..1227bb4d8e 100644
--- a/integration/fixtures/healthcheck/propagate.toml
+++ b/integration/fixtures/healthcheck/propagate.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/healthcheck/propagate_no_healthcheck.toml b/integration/fixtures/healthcheck/propagate_no_healthcheck.toml
index 6fb2a64323..4889bec1ca 100644
--- a/integration/fixtures/healthcheck/propagate_no_healthcheck.toml
+++ b/integration/fixtures/healthcheck/propagate_no_healthcheck.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/healthcheck/reload_with_healthcheck.toml b/integration/fixtures/healthcheck/reload_with_healthcheck.toml
index d2e794c91d..8e72e5db4e 100644
--- a/integration/fixtures/healthcheck/reload_with_healthcheck.toml
+++ b/integration/fixtures/healthcheck/reload_with_healthcheck.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/healthcheck/reload_without_healthcheck.toml b/integration/fixtures/healthcheck/reload_without_healthcheck.toml
index 350cb4fd96..fa45f8b663 100644
--- a/integration/fixtures/healthcheck/reload_without_healthcheck.toml
+++ b/integration/fixtures/healthcheck/reload_without_healthcheck.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/healthcheck/simple.toml b/integration/fixtures/healthcheck/simple.toml
index a2338a8b09..219f81c447 100644
--- a/integration/fixtures/healthcheck/simple.toml
+++ b/integration/fixtures/healthcheck/simple.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/http/simple.toml b/integration/fixtures/http/simple.toml
index 36df1414ad..b6136193ea 100644
--- a/integration/fixtures/http/simple.toml
+++ b/integration/fixtures/http/simple.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/https/clientca/https_1ca1config.toml b/integration/fixtures/https/clientca/https_1ca1config.toml
index bfa9617eeb..2b200ff203 100644
--- a/integration/fixtures/https/clientca/https_1ca1config.toml
+++ b/integration/fixtures/https/clientca/https_1ca1config.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.websecure]
diff --git a/integration/fixtures/https/clientca/https_2ca1config.toml b/integration/fixtures/https/clientca/https_2ca1config.toml
index 5d23204160..78e8f67b8b 100644
--- a/integration/fixtures/https/clientca/https_2ca1config.toml
+++ b/integration/fixtures/https/clientca/https_2ca1config.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.websecure]
@@ -49,4 +50,4 @@
 
 [tls.options]
   [tls.options.default.clientAuth]
-    caFiles = ["fixtures/https/clientca/ca1and2.crt"]
\ No newline at end of file
+    caFiles = ["fixtures/https/clientca/ca1and2.crt"]
diff --git a/integration/fixtures/https/clientca/https_2ca2config.toml b/integration/fixtures/https/clientca/https_2ca2config.toml
index 0530b52d22..1653970adb 100644
--- a/integration/fixtures/https/clientca/https_2ca2config.toml
+++ b/integration/fixtures/https/clientca/https_2ca2config.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.websecure]
diff --git a/integration/fixtures/https/dynamic_https_sni.toml b/integration/fixtures/https/dynamic_https_sni.toml
index a06c7129aa..9a3ce02514 100644
--- a/integration/fixtures/https/dynamic_https_sni.toml
+++ b/integration/fixtures/https/dynamic_https_sni.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.websecure]
diff --git a/integration/fixtures/https/dynamic_https_sni_default_cert.toml b/integration/fixtures/https/dynamic_https_sni_default_cert.toml
index f954d98455..424f357814 100644
--- a/integration/fixtures/https/dynamic_https_sni_default_cert.toml
+++ b/integration/fixtures/https/dynamic_https_sni_default_cert.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.websecure]
diff --git a/integration/fixtures/https/https_domain_fronting.toml b/integration/fixtures/https/https_domain_fronting.toml
index 80011ee8ed..d60883b658 100644
--- a/integration/fixtures/https/https_domain_fronting.toml
+++ b/integration/fixtures/https/https_domain_fronting.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints.websecure]
   address = ":4443"
diff --git a/integration/fixtures/https/https_redirect.toml b/integration/fixtures/https/https_redirect.toml
index 488e2bc00c..52f2addb63 100644
--- a/integration/fixtures/https/https_redirect.toml
+++ b/integration/fixtures/https/https_redirect.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/https/https_sni.toml b/integration/fixtures/https/https_sni.toml
index bd034d468e..5ea0dc268a 100644
--- a/integration/fixtures/https/https_sni.toml
+++ b/integration/fixtures/https/https_sni.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.websecure]
diff --git a/integration/fixtures/https/https_sni_case_insensitive_dynamic.toml b/integration/fixtures/https/https_sni_case_insensitive_dynamic.toml
index ef5814caf5..718477cb3b 100644
--- a/integration/fixtures/https/https_sni_case_insensitive_dynamic.toml
+++ b/integration/fixtures/https/https_sni_case_insensitive_dynamic.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.websecure]
diff --git a/integration/fixtures/https/https_sni_default_cert.toml b/integration/fixtures/https/https_sni_default_cert.toml
index f954d98455..424f357814 100644
--- a/integration/fixtures/https/https_sni_default_cert.toml
+++ b/integration/fixtures/https/https_sni_default_cert.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.websecure]
diff --git a/integration/fixtures/https/https_sni_strict.toml b/integration/fixtures/https/https_sni_strict.toml
index fca8d96566..c8e6af4ae4 100644
--- a/integration/fixtures/https/https_sni_strict.toml
+++ b/integration/fixtures/https/https_sni_strict.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.websecure]
diff --git a/integration/fixtures/https/https_tls_options.toml b/integration/fixtures/https/https_tls_options.toml
index e8eccf3a59..9eec70c170 100644
--- a/integration/fixtures/https/https_tls_options.toml
+++ b/integration/fixtures/https/https_tls_options.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.websecure]
diff --git a/integration/fixtures/https/rootcas/https.toml b/integration/fixtures/https/rootcas/https.toml
index 37e450fc96..b2eff40912 100644
--- a/integration/fixtures/https/rootcas/https.toml
+++ b/integration/fixtures/https/rootcas/https.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [serversTransport]
   # Use certificate in net/internal/testcert.go
diff --git a/integration/fixtures/https/rootcas/https_with_file.toml b/integration/fixtures/https/rootcas/https_with_file.toml
index 62a79ac113..3bb9c1bf43 100644
--- a/integration/fixtures/https/rootcas/https_with_file.toml
+++ b/integration/fixtures/https/rootcas/https_with_file.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [serversTransport]
   # Use certificate in net/internal/testcert.go
diff --git a/integration/fixtures/k8s_crd.toml b/integration/fixtures/k8s_crd.toml
index 86e85ee91a..f4e36a4827 100644
--- a/integration/fixtures/k8s_crd.toml
+++ b/integration/fixtures/k8s_crd.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [api]
 
diff --git a/integration/fixtures/k8s_crd_label_selector.toml b/integration/fixtures/k8s_crd_label_selector.toml
index ca7e51cbd7..7e1edcae9d 100644
--- a/integration/fixtures/k8s_crd_label_selector.toml
+++ b/integration/fixtures/k8s_crd_label_selector.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [api]
 
diff --git a/integration/fixtures/k8s_default.toml b/integration/fixtures/k8s_default.toml
index f94ecf45be..baff67ac53 100644
--- a/integration/fixtures/k8s_default.toml
+++ b/integration/fixtures/k8s_default.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [api]
   insecure = true
diff --git a/integration/fixtures/k8s_gateway.toml b/integration/fixtures/k8s_gateway.toml
index c0cc76e04e..0649ab249c 100644
--- a/integration/fixtures/k8s_gateway.toml
+++ b/integration/fixtures/k8s_gateway.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [api]
   insecure = true
diff --git a/integration/fixtures/k8s_ingress_label_selector.toml b/integration/fixtures/k8s_ingress_label_selector.toml
index 86f411e984..8b38d9c212 100644
--- a/integration/fixtures/k8s_ingress_label_selector.toml
+++ b/integration/fixtures/k8s_ingress_label_selector.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [api]
   insecure = true
diff --git a/integration/fixtures/k8s_ingressclass.toml b/integration/fixtures/k8s_ingressclass.toml
index 51848e0261..a5293be3e1 100644
--- a/integration/fixtures/k8s_ingressclass.toml
+++ b/integration/fixtures/k8s_ingressclass.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [api]
   insecure = true
diff --git a/integration/fixtures/marathon/simple.toml b/integration/fixtures/marathon/simple.toml
index f8b4e72667..d43cdbc2ed 100644
--- a/integration/fixtures/marathon/simple.toml
+++ b/integration/fixtures/marathon/simple.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/mirror.toml b/integration/fixtures/mirror.toml
index 55d0cf0409..4c99f83b82 100644
--- a/integration/fixtures/mirror.toml
+++ b/integration/fixtures/mirror.toml
@@ -7,6 +7,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
 
diff --git a/integration/fixtures/multiple_provider.toml b/integration/fixtures/multiple_provider.toml
index e5f972d38e..a2684e2ccd 100644
--- a/integration/fixtures/multiple_provider.toml
+++ b/integration/fixtures/multiple_provider.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/multiprovider.toml b/integration/fixtures/multiprovider.toml
index bc59d7faba..14aff0b8b5 100644
--- a/integration/fixtures/multiprovider.toml
+++ b/integration/fixtures/multiprovider.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [api]
   insecure = true
diff --git a/integration/fixtures/proxy-protocol/with.toml b/integration/fixtures/proxy-protocol/with.toml
index c47e97dc54..0c51207e00 100644
--- a/integration/fixtures/proxy-protocol/with.toml
+++ b/integration/fixtures/proxy-protocol/with.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/proxy-protocol/without.toml b/integration/fixtures/proxy-protocol/without.toml
index 71b982486e..4346c99268 100644
--- a/integration/fixtures/proxy-protocol/without.toml
+++ b/integration/fixtures/proxy-protocol/without.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/ratelimit/simple.toml b/integration/fixtures/ratelimit/simple.toml
index aed8d0293e..f89251cd93 100644
--- a/integration/fixtures/ratelimit/simple.toml
+++ b/integration/fixtures/ratelimit/simple.toml
@@ -7,6 +7,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/redis/simple.toml b/integration/fixtures/redis/simple.toml
index 9fcfe670cc..7864cfc28f 100644
--- a/integration/fixtures/redis/simple.toml
+++ b/integration/fixtures/redis/simple.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints.web]
   address = ":8000"
diff --git a/integration/fixtures/reqacceptgrace.toml b/integration/fixtures/reqacceptgrace.toml
index 945a7d1685..1ffaf7e6d5 100644
--- a/integration/fixtures/reqacceptgrace.toml
+++ b/integration/fixtures/reqacceptgrace.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
 
diff --git a/integration/fixtures/rest/simple.toml b/integration/fixtures/rest/simple.toml
index 8bf7ff14ce..779ccffd0b 100644
--- a/integration/fixtures/rest/simple.toml
+++ b/integration/fixtures/rest/simple.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/rest/simple_secure.toml b/integration/fixtures/rest/simple_secure.toml
index 312c8f628a..a583eba6dd 100644
--- a/integration/fixtures/rest/simple_secure.toml
+++ b/integration/fixtures/rest/simple_secure.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/retry/backoff.toml b/integration/fixtures/retry/backoff.toml
index a869c1c60c..f9f56b73ac 100644
--- a/integration/fixtures/retry/backoff.toml
+++ b/integration/fixtures/retry/backoff.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/router_errors.toml b/integration/fixtures/router_errors.toml
index c46c25f6ec..7ef2be2566 100644
--- a/integration/fixtures/router_errors.toml
+++ b/integration/fixtures/router_errors.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.websecure]
diff --git a/integration/fixtures/service_errors.toml b/integration/fixtures/service_errors.toml
index be90ddf52d..599f0b2ed3 100644
--- a/integration/fixtures/service_errors.toml
+++ b/integration/fixtures/service_errors.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.websecure]
@@ -34,4 +35,4 @@
   [http.services.service2]
     [http.services.service2.loadBalancer]
       [[http.services.service2.loadBalancer.servers]]
-        url = "http://127.0.0.1:9010"
\ No newline at end of file
+        url = "http://127.0.0.1:9010"
diff --git a/integration/fixtures/simple_auth.toml b/integration/fixtures/simple_auth.toml
index 1edc7ea6b0..64f3b18ee5 100644
--- a/integration/fixtures/simple_auth.toml
+++ b/integration/fixtures/simple_auth.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/simple_contenttype.toml b/integration/fixtures/simple_contenttype.toml
index c76967f33b..2743cd6593 100644
--- a/integration/fixtures/simple_contenttype.toml
+++ b/integration/fixtures/simple_contenttype.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
@@ -58,4 +59,4 @@
 autoDetect=true
 
 [http.middlewares.noautodetect.contentType]
-autoDetect=false
\ No newline at end of file
+autoDetect=false
diff --git a/integration/fixtures/simple_stats.toml b/integration/fixtures/simple_stats.toml
index eeb4c25330..4afe628393 100644
--- a/integration/fixtures/simple_stats.toml
+++ b/integration/fixtures/simple_stats.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/simple_web.toml b/integration/fixtures/simple_web.toml
index 1d17ccb57d..ddb75e282c 100644
--- a/integration/fixtures/simple_web.toml
+++ b/integration/fixtures/simple_web.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/tcp/catch-all-no-tls-with-https.toml b/integration/fixtures/tcp/catch-all-no-tls-with-https.toml
index 1b10712463..229a6bb00a 100644
--- a/integration/fixtures/tcp/catch-all-no-tls-with-https.toml
+++ b/integration/fixtures/tcp/catch-all-no-tls-with-https.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.tcp]
diff --git a/integration/fixtures/tcp/catch-all-no-tls.toml b/integration/fixtures/tcp/catch-all-no-tls.toml
index c052832e85..6ed733b5ef 100644
--- a/integration/fixtures/tcp/catch-all-no-tls.toml
+++ b/integration/fixtures/tcp/catch-all-no-tls.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.tcp]
diff --git a/integration/fixtures/tcp/mixed.toml b/integration/fixtures/tcp/mixed.toml
index eea3acabf1..fccf305250 100644
--- a/integration/fixtures/tcp/mixed.toml
+++ b/integration/fixtures/tcp/mixed.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.tcp]
diff --git a/integration/fixtures/tcp/multi-tls-options.toml b/integration/fixtures/tcp/multi-tls-options.toml
index f0b4a931c8..877ebcc091 100644
--- a/integration/fixtures/tcp/multi-tls-options.toml
+++ b/integration/fixtures/tcp/multi-tls-options.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.tcp]
diff --git a/integration/fixtures/tcp/non-tls-fallback.toml b/integration/fixtures/tcp/non-tls-fallback.toml
index 2043694a1f..03d298a111 100644
--- a/integration/fixtures/tcp/non-tls-fallback.toml
+++ b/integration/fixtures/tcp/non-tls-fallback.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.tcp]
diff --git a/integration/fixtures/tcp/non-tls.toml b/integration/fixtures/tcp/non-tls.toml
index 3b0b5efd50..df270f7e1e 100644
--- a/integration/fixtures/tcp/non-tls.toml
+++ b/integration/fixtures/tcp/non-tls.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.tcp]
diff --git a/integration/fixtures/tcp/service_errors.toml b/integration/fixtures/tcp/service_errors.toml
index b6949da716..98e552f6b6 100644
--- a/integration/fixtures/tcp/service_errors.toml
+++ b/integration/fixtures/tcp/service_errors.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.websecure]
diff --git a/integration/fixtures/tcp/wrr.toml b/integration/fixtures/tcp/wrr.toml
index b9d35e65c5..b60164b670 100644
--- a/integration/fixtures/tcp/wrr.toml
+++ b/integration/fixtures/tcp/wrr.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.tcp]
diff --git a/integration/fixtures/throttling/simple.toml b/integration/fixtures/throttling/simple.toml
index 889a35d094..668d4988ba 100644
--- a/integration/fixtures/throttling/simple.toml
+++ b/integration/fixtures/throttling/simple.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/timeout/forwarding_timeouts.toml b/integration/fixtures/timeout/forwarding_timeouts.toml
index 0dc5d4933e..363c020270 100644
--- a/integration/fixtures/timeout/forwarding_timeouts.toml
+++ b/integration/fixtures/timeout/forwarding_timeouts.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [serversTransport.forwardingTimeouts]
   dialTimeout = "300ms"
diff --git a/integration/fixtures/timeout/keepalive.toml b/integration/fixtures/timeout/keepalive.toml
index a2b9637efc..421d7b6399 100644
--- a/integration/fixtures/timeout/keepalive.toml
+++ b/integration/fixtures/timeout/keepalive.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [serversTransport.forwardingTimeouts]
   idleConnTimeout = "{{ .IdleConnTimeout }}"
diff --git a/integration/fixtures/tracing/simple-jaeger-collector.toml b/integration/fixtures/tracing/simple-jaeger-collector.toml
index f5636d7993..e858e2a570 100644
--- a/integration/fixtures/tracing/simple-jaeger-collector.toml
+++ b/integration/fixtures/tracing/simple-jaeger-collector.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [api]
   insecure = true
diff --git a/integration/fixtures/tracing/simple-jaeger.toml b/integration/fixtures/tracing/simple-jaeger.toml
index 87a4fb67e7..bf02ced6af 100644
--- a/integration/fixtures/tracing/simple-jaeger.toml
+++ b/integration/fixtures/tracing/simple-jaeger.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [api]
   insecure = true
diff --git a/integration/fixtures/tracing/simple-zipkin.toml b/integration/fixtures/tracing/simple-zipkin.toml
index b5549bc150..ef935fa28d 100644
--- a/integration/fixtures/tracing/simple-zipkin.toml
+++ b/integration/fixtures/tracing/simple-zipkin.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [api]
   insecure = true
diff --git a/integration/fixtures/udp/service_errors.toml b/integration/fixtures/udp/service_errors.toml
index 3031f9f734..83564b1dc9 100644
--- a/integration/fixtures/udp/service_errors.toml
+++ b/integration/fixtures/udp/service_errors.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.websecure]
diff --git a/integration/fixtures/udp/wrr.toml b/integration/fixtures/udp/wrr.toml
index 463f2debab..f16515f51c 100644
--- a/integration/fixtures/udp/wrr.toml
+++ b/integration/fixtures/udp/wrr.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.udp]
diff --git a/integration/fixtures/websocket/config.toml b/integration/fixtures/websocket/config.toml
index 83a499ce75..5d97bcf43f 100644
--- a/integration/fixtures/websocket/config.toml
+++ b/integration/fixtures/websocket/config.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
   [entryPoints.web]
diff --git a/integration/fixtures/websocket/config_https.toml b/integration/fixtures/websocket/config_https.toml
index 91549eeaa6..cdc92b8624 100644
--- a/integration/fixtures/websocket/config_https.toml
+++ b/integration/fixtures/websocket/config_https.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [serversTransport]
   insecureSkipVerify=true
diff --git a/integration/fixtures/wrr.toml b/integration/fixtures/wrr.toml
index 67c6801fc4..4f3fbc0b6c 100644
--- a/integration/fixtures/wrr.toml
+++ b/integration/fixtures/wrr.toml
@@ -7,6 +7,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
 
diff --git a/integration/fixtures/wrr_sticky.toml b/integration/fixtures/wrr_sticky.toml
index 0d63558c5e..6bd76f2aff 100644
--- a/integration/fixtures/wrr_sticky.toml
+++ b/integration/fixtures/wrr_sticky.toml
@@ -7,6 +7,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints]
 
diff --git a/integration/fixtures/zookeeper/simple.toml b/integration/fixtures/zookeeper/simple.toml
index 3b40dc1695..e6fc06232a 100644
--- a/integration/fixtures/zookeeper/simple.toml
+++ b/integration/fixtures/zookeeper/simple.toml
@@ -4,6 +4,7 @@
 
 [log]
   level = "DEBUG"
+  noColor = true
 
 [entryPoints.web]
   address = ":8000"
diff --git a/integration/try/try.go b/integration/try/try.go
index af54eb8f0b..886459e1e0 100644
--- a/integration/try/try.go
+++ b/integration/try/try.go
@@ -7,7 +7,7 @@ import (
 	"os"
 	"time"
 
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 )
 
 const (
@@ -166,7 +166,7 @@ func doRequest(action timedAction, timeout time.Duration, request *http.Request,
 func applyCIMultiplier(timeout time.Duration) time.Duration {
 	ci := os.Getenv("CI")
 	if len(ci) > 0 {
-		log.Debug("Apply CI multiplier:", CITimeoutMultiplier)
+		log.Debug().Msgf("Apply CI multiplier: %d", CITimeoutMultiplier)
 		return time.Duration(float64(timeout) * CITimeoutMultiplier)
 	}
 	return timeout
diff --git a/internal/gendoc.go b/internal/gendoc.go
index afc6320f99..397f700eee 100644
--- a/internal/gendoc.go
+++ b/internal/gendoc.go
@@ -10,12 +10,12 @@ import (
 	"strings"
 
 	"github.com/BurntSushi/toml"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/paerser/env"
 	"github.com/traefik/paerser/flag"
 	"github.com/traefik/paerser/generator"
 	"github.com/traefik/paerser/parser"
 	"github.com/traefik/traefik/v2/cmd"
-	"github.com/traefik/traefik/v2/pkg/log"
 )
 
 func main() {
@@ -27,7 +27,7 @@ func main() {
 }
 
 func genStaticConfDoc(outputFile, prefix string, encodeFn func(interface{}) ([]parser.Flat, error)) {
-	logger := log.WithoutContext().WithField("file", outputFile)
+	logger := log.With().Str("file", outputFile).Logger()
 
 	element := &cmd.NewTraefikConfiguration().Configuration
 
@@ -35,17 +35,17 @@ func genStaticConfDoc(outputFile, prefix string, encodeFn func(interface{}) ([]p
 
 	flats, err := encodeFn(element)
 	if err != nil {
-		logger.Fatal(err)
+		logger.Fatal().Err(err).Send()
 	}
 
 	err = os.RemoveAll(outputFile)
 	if err != nil {
-		logger.Fatal(err)
+		logger.Fatal().Err(err).Send()
 	}
 
 	file, err := os.OpenFile(outputFile, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0o666)
 	if err != nil {
-		logger.Fatal(err)
+		logger.Fatal().Err(err).Send()
 	}
 
 	defer file.Close()
@@ -86,7 +86,7 @@ THIS FILE MUST NOT BE EDITED BY HAND
 	}
 
 	if w.err != nil {
-		logger.Fatal(err)
+		logger.Fatal().Err(err).Send()
 	}
 }
 
@@ -108,19 +108,19 @@ func genKVDynConfDoc(outputFile string) {
 	conf := map[string]interface{}{}
 	_, err := toml.DecodeFile(dynConfPath, &conf)
 	if err != nil {
-		log.Fatal(err)
+		log.Fatal().Err(err).Send()
 	}
 
 	file, err := os.Create(outputFile)
 	if err != nil {
-		log.Fatal(err)
+		log.Fatal().Err(err).Send()
 	}
 
 	store := storeWriter{data: map[string]string{}}
 	c := client{store: store}
 	err = c.load("traefik", conf)
 	if err != nil {
-		log.Fatal(err)
+		log.Fatal().Err(err).Send()
 	}
 
 	var keys []string
diff --git a/pkg/api/handler.go b/pkg/api/handler.go
index f0623d4cc7..01ec203610 100644
--- a/pkg/api/handler.go
+++ b/pkg/api/handler.go
@@ -7,10 +7,10 @@ import (
 	"strings"
 
 	"github.com/gorilla/mux"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
 	"github.com/traefik/traefik/v2/pkg/config/runtime"
 	"github.com/traefik/traefik/v2/pkg/config/static"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/version"
 )
 
@@ -138,7 +138,7 @@ func (h Handler) getRuntimeConfiguration(rw http.ResponseWriter, request *http.R
 
 	err := json.NewEncoder(rw).Encode(result)
 	if err != nil {
-		log.FromContext(request.Context()).Error(err)
+		log.Ctx(request.Context()).Error().Err(err).Send()
 		http.Error(rw, err.Error(), http.StatusInternalServerError)
 	}
 }
diff --git a/pkg/api/handler_entrypoint.go b/pkg/api/handler_entrypoint.go
index 38801e8c7a..104ab38535 100644
--- a/pkg/api/handler_entrypoint.go
+++ b/pkg/api/handler_entrypoint.go
@@ -8,8 +8,8 @@ import (
 	"strconv"
 
 	"github.com/gorilla/mux"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/static"
-	"github.com/traefik/traefik/v2/pkg/log"
 )
 
 type entryPointRepresentation struct {
@@ -43,7 +43,7 @@ func (h Handler) getEntryPoints(rw http.ResponseWriter, request *http.Request) {
 
 	err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex])
 	if err != nil {
-		log.FromContext(request.Context()).Error(err)
+		log.Ctx(request.Context()).Error().Err(err).Send()
 		writeError(rw, err.Error(), http.StatusInternalServerError)
 	}
 }
@@ -66,7 +66,7 @@ func (h Handler) getEntryPoint(rw http.ResponseWriter, request *http.Request) {
 
 	err := json.NewEncoder(rw).Encode(result)
 	if err != nil {
-		log.FromContext(request.Context()).Error(err)
+		log.Ctx(request.Context()).Error().Err(err).Send()
 		writeError(rw, err.Error(), http.StatusInternalServerError)
 	}
 }
diff --git a/pkg/api/handler_http.go b/pkg/api/handler_http.go
index b2643cdfae..ebf35d2062 100644
--- a/pkg/api/handler_http.go
+++ b/pkg/api/handler_http.go
@@ -9,8 +9,8 @@ import (
 	"strings"
 
 	"github.com/gorilla/mux"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/runtime"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/tls"
 )
 
@@ -93,7 +93,7 @@ func (h Handler) getRouters(rw http.ResponseWriter, request *http.Request) {
 
 	err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex])
 	if err != nil {
-		log.FromContext(request.Context()).Error(err)
+		log.Ctx(request.Context()).Error().Err(err).Send()
 		writeError(rw, err.Error(), http.StatusInternalServerError)
 	}
 }
@@ -113,7 +113,7 @@ func (h Handler) getRouter(rw http.ResponseWriter, request *http.Request) {
 
 	err := json.NewEncoder(rw).Encode(result)
 	if err != nil {
-		log.FromContext(request.Context()).Error(err)
+		log.Ctx(request.Context()).Error().Err(err).Send()
 		writeError(rw, err.Error(), http.StatusInternalServerError)
 	}
 }
@@ -145,7 +145,7 @@ func (h Handler) getServices(rw http.ResponseWriter, request *http.Request) {
 
 	err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex])
 	if err != nil {
-		log.FromContext(request.Context()).Error(err)
+		log.Ctx(request.Context()).Error().Err(err).Send()
 		writeError(rw, err.Error(), http.StatusInternalServerError)
 	}
 }
@@ -165,7 +165,7 @@ func (h Handler) getService(rw http.ResponseWriter, request *http.Request) {
 
 	err := json.NewEncoder(rw).Encode(result)
 	if err != nil {
-		log.FromContext(request.Context()).Error(err)
+		log.Ctx(request.Context()).Error().Err(err).Send()
 		writeError(rw, err.Error(), http.StatusInternalServerError)
 	}
 }
@@ -197,7 +197,7 @@ func (h Handler) getMiddlewares(rw http.ResponseWriter, request *http.Request) {
 
 	err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex])
 	if err != nil {
-		log.FromContext(request.Context()).Error(err)
+		log.Ctx(request.Context()).Error().Err(err).Send()
 		writeError(rw, err.Error(), http.StatusInternalServerError)
 	}
 }
@@ -217,7 +217,7 @@ func (h Handler) getMiddleware(rw http.ResponseWriter, request *http.Request) {
 
 	err := json.NewEncoder(rw).Encode(result)
 	if err != nil {
-		log.FromContext(request.Context()).Error(err)
+		log.Ctx(request.Context()).Error().Err(err).Send()
 		writeError(rw, err.Error(), http.StatusInternalServerError)
 	}
 }
diff --git a/pkg/api/handler_overview.go b/pkg/api/handler_overview.go
index d04abbc870..7f423b09fb 100644
--- a/pkg/api/handler_overview.go
+++ b/pkg/api/handler_overview.go
@@ -5,9 +5,9 @@ import (
 	"net/http"
 	"reflect"
 
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/runtime"
 	"github.com/traefik/traefik/v2/pkg/config/static"
-	"github.com/traefik/traefik/v2/pkg/log"
 )
 
 type schemeOverview struct {
@@ -62,7 +62,7 @@ func (h Handler) getOverview(rw http.ResponseWriter, request *http.Request) {
 
 	err := json.NewEncoder(rw).Encode(result)
 	if err != nil {
-		log.FromContext(request.Context()).Error(err)
+		log.Ctx(request.Context()).Error().Err(err).Send()
 		writeError(rw, err.Error(), http.StatusInternalServerError)
 	}
 }
diff --git a/pkg/api/handler_tcp.go b/pkg/api/handler_tcp.go
index 390e1179c0..8efb130687 100644
--- a/pkg/api/handler_tcp.go
+++ b/pkg/api/handler_tcp.go
@@ -9,8 +9,8 @@ import (
 	"strings"
 
 	"github.com/gorilla/mux"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/runtime"
-	"github.com/traefik/traefik/v2/pkg/log"
 )
 
 type tcpRouterRepresentation struct {
@@ -86,7 +86,7 @@ func (h Handler) getTCPRouters(rw http.ResponseWriter, request *http.Request) {
 
 	err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex])
 	if err != nil {
-		log.FromContext(request.Context()).Error(err)
+		log.Ctx(request.Context()).Error().Err(err).Send()
 		writeError(rw, err.Error(), http.StatusInternalServerError)
 	}
 }
@@ -106,7 +106,7 @@ func (h Handler) getTCPRouter(rw http.ResponseWriter, request *http.Request) {
 
 	err := json.NewEncoder(rw).Encode(result)
 	if err != nil {
-		log.FromContext(request.Context()).Error(err)
+		log.Ctx(request.Context()).Error().Err(err).Send()
 		writeError(rw, err.Error(), http.StatusInternalServerError)
 	}
 }
@@ -138,7 +138,7 @@ func (h Handler) getTCPServices(rw http.ResponseWriter, request *http.Request) {
 
 	err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex])
 	if err != nil {
-		log.FromContext(request.Context()).Error(err)
+		log.Ctx(request.Context()).Error().Err(err).Send()
 		writeError(rw, err.Error(), http.StatusInternalServerError)
 	}
 }
@@ -158,7 +158,7 @@ func (h Handler) getTCPService(rw http.ResponseWriter, request *http.Request) {
 
 	err := json.NewEncoder(rw).Encode(result)
 	if err != nil {
-		log.FromContext(request.Context()).Error(err)
+		log.Ctx(request.Context()).Error().Err(err).Send()
 		writeError(rw, err.Error(), http.StatusInternalServerError)
 	}
 }
@@ -190,7 +190,7 @@ func (h Handler) getTCPMiddlewares(rw http.ResponseWriter, request *http.Request
 
 	err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex])
 	if err != nil {
-		log.FromContext(request.Context()).Error(err)
+		log.Ctx(request.Context()).Error().Err(err).Send()
 		writeError(rw, err.Error(), http.StatusInternalServerError)
 	}
 }
@@ -210,7 +210,7 @@ func (h Handler) getTCPMiddleware(rw http.ResponseWriter, request *http.Request)
 
 	err := json.NewEncoder(rw).Encode(result)
 	if err != nil {
-		log.FromContext(request.Context()).Error(err)
+		log.Ctx(request.Context()).Error().Err(err).Send()
 		writeError(rw, err.Error(), http.StatusInternalServerError)
 	}
 }
diff --git a/pkg/api/handler_udp.go b/pkg/api/handler_udp.go
index 4cfe465d93..13adfafea7 100644
--- a/pkg/api/handler_udp.go
+++ b/pkg/api/handler_udp.go
@@ -9,8 +9,8 @@ import (
 	"strings"
 
 	"github.com/gorilla/mux"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/runtime"
-	"github.com/traefik/traefik/v2/pkg/log"
 )
 
 type udpRouterRepresentation struct {
@@ -70,7 +70,7 @@ func (h Handler) getUDPRouters(rw http.ResponseWriter, request *http.Request) {
 
 	err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex])
 	if err != nil {
-		log.FromContext(request.Context()).Error(err)
+		log.Ctx(request.Context()).Error().Err(err).Send()
 		writeError(rw, err.Error(), http.StatusInternalServerError)
 	}
 }
@@ -90,7 +90,7 @@ func (h Handler) getUDPRouter(rw http.ResponseWriter, request *http.Request) {
 
 	err := json.NewEncoder(rw).Encode(result)
 	if err != nil {
-		log.FromContext(request.Context()).Error(err)
+		log.Ctx(request.Context()).Error().Err(err).Send()
 		writeError(rw, err.Error(), http.StatusInternalServerError)
 	}
 }
@@ -122,7 +122,7 @@ func (h Handler) getUDPServices(rw http.ResponseWriter, request *http.Request) {
 
 	err = json.NewEncoder(rw).Encode(results[pageInfo.startIndex:pageInfo.endIndex])
 	if err != nil {
-		log.FromContext(request.Context()).Error(err)
+		log.Ctx(request.Context()).Error().Err(err).Send()
 		writeError(rw, err.Error(), http.StatusInternalServerError)
 	}
 }
@@ -142,7 +142,7 @@ func (h Handler) getUDPService(rw http.ResponseWriter, request *http.Request) {
 
 	err := json.NewEncoder(rw).Encode(result)
 	if err != nil {
-		log.FromContext(request.Context()).Error(err)
+		log.Ctx(request.Context()).Error().Err(err).Send()
 		writeError(rw, err.Error(), http.StatusInternalServerError)
 	}
 }
diff --git a/pkg/cli/loader_env.go b/pkg/cli/loader_env.go
index f40b342a29..d7412c5d35 100644
--- a/pkg/cli/loader_env.go
+++ b/pkg/cli/loader_env.go
@@ -5,9 +5,9 @@ import (
 	"os"
 	"strings"
 
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/paerser/cli"
 	"github.com/traefik/paerser/env"
-	"github.com/traefik/traefik/v2/pkg/log"
 )
 
 // EnvLoader loads a configuration from all the environment variables prefixed with "TRAEFIK_".
@@ -21,11 +21,11 @@ func (e *EnvLoader) Load(_ []string, cmd *cli.Command) (bool, error) {
 	}
 
 	if err := env.Decode(vars, env.DefaultNamePrefix, cmd.Configuration); err != nil {
-		log.WithoutContext().Debug("environment variables", strings.Join(vars, ", "))
-		return false, fmt.Errorf("failed to decode configuration from environment variables: %w ", err)
+		log.Debug().Msgf("environment variables: %s", strings.Join(vars, ", "))
+		return false, fmt.Errorf("failed to decode configuration from environment variables: %w", err)
 	}
 
-	log.WithoutContext().Println("Configuration loaded from environment variables.")
+	log.Print("Configuration loaded from environment variables")
 
 	return true, nil
 }
diff --git a/pkg/cli/loader_file.go b/pkg/cli/loader_file.go
index f01eb763f8..f5f11fa511 100644
--- a/pkg/cli/loader_file.go
+++ b/pkg/cli/loader_file.go
@@ -4,10 +4,10 @@ import (
 	"os"
 	"strings"
 
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/paerser/cli"
 	"github.com/traefik/paerser/file"
 	"github.com/traefik/paerser/flag"
-	"github.com/traefik/traefik/v2/pkg/log"
 )
 
 // FileLoader loads a configuration from a file.
@@ -52,11 +52,10 @@ func (f *FileLoader) Load(args []string, cmd *cli.Command) (bool, error) {
 		return false, nil
 	}
 
-	logger := log.WithoutContext()
-	logger.Printf("Configuration loaded from file: %s", configFile)
+	log.Printf("Configuration loaded from file: %s", configFile)
 
 	content, _ := os.ReadFile(configFile)
-	logger.Debug(string(content))
+	log.Debug().Str("configFile", configFile).Bytes("content", content).Send()
 
 	return true, nil
 }
diff --git a/pkg/cli/loader_flag.go b/pkg/cli/loader_flag.go
index 097ff03a59..15136ad19d 100644
--- a/pkg/cli/loader_flag.go
+++ b/pkg/cli/loader_flag.go
@@ -3,9 +3,9 @@ package cli
 import (
 	"fmt"
 
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/paerser/cli"
 	"github.com/traefik/paerser/flag"
-	"github.com/traefik/traefik/v2/pkg/log"
 )
 
 // FlagLoader loads configuration from flags.
@@ -21,7 +21,7 @@ func (*FlagLoader) Load(args []string, cmd *cli.Command) (bool, error) {
 		return false, fmt.Errorf("failed to decode configuration from flags: %w", err)
 	}
 
-	log.WithoutContext().Println("Configuration loaded from flags.")
+	log.Print("Configuration loaded from flags")
 
 	return true, nil
 }
diff --git a/pkg/collector/collector.go b/pkg/collector/collector.go
index 50ce0666d1..49f679a93a 100644
--- a/pkg/collector/collector.go
+++ b/pkg/collector/collector.go
@@ -10,8 +10,8 @@ import (
 	"time"
 
 	"github.com/mitchellh/hashstructure"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/static"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/redactor"
 	"github.com/traefik/traefik/v2/pkg/version"
 )
@@ -49,7 +49,7 @@ func createBody(staticConfiguration *static.Configuration) (*bytes.Buffer, error
 		return nil, err
 	}
 
-	log.WithoutContext().Infof("Anonymous stats sent to %s: %s", collectorURL, anonConfig)
+	log.Debug().Msgf("Anonymous stats sent to %s: %s", collectorURL, anonConfig)
 
 	hashConf, err := hashstructure.Hash(staticConfiguration, nil)
 	if err != nil {
diff --git a/pkg/config/runtime/runtime.go b/pkg/config/runtime/runtime.go
index 7135415388..8420722792 100644
--- a/pkg/config/runtime/runtime.go
+++ b/pkg/config/runtime/runtime.go
@@ -4,8 +4,9 @@ import (
 	"sort"
 	"strings"
 
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 )
 
 // Status of the router/service.
@@ -116,8 +117,6 @@ func (c *Configuration) PopulateUsedBy() {
 		return
 	}
 
-	logger := log.WithoutContext()
-
 	for routerName, routerInfo := range c.Routers {
 		// lazily initialize Status in case caller forgot to do it
 		if routerInfo.Status == "" {
@@ -126,7 +125,7 @@ func (c *Configuration) PopulateUsedBy() {
 
 		providerName := getProviderName(routerName)
 		if providerName == "" {
-			logger.WithField(log.RouterName, routerName).Error("router name is not fully qualified")
+			log.Error().Str(logs.RouterName, routerName).Msg("Router name is not fully qualified")
 			continue
 		}
 
@@ -171,7 +170,7 @@ func (c *Configuration) PopulateUsedBy() {
 
 		providerName := getProviderName(routerName)
 		if providerName == "" {
-			logger.WithField(log.RouterName, routerName).Error("tcp router name is not fully qualified")
+			log.Error().Str(logs.RouterName, routerName).Msg("TCP router name is not fully qualified")
 			continue
 		}
 
@@ -208,7 +207,7 @@ func (c *Configuration) PopulateUsedBy() {
 
 		providerName := getProviderName(routerName)
 		if providerName == "" {
-			logger.WithField(log.RouterName, routerName).Error("udp router name is not fully qualified")
+			log.Error().Str(logs.RouterName, routerName).Msg("UDP router name is not fully qualified")
 			continue
 		}
 
diff --git a/pkg/config/runtime/runtime_http.go b/pkg/config/runtime/runtime_http.go
index 1361c701cd..788a23a26b 100644
--- a/pkg/config/runtime/runtime_http.go
+++ b/pkg/config/runtime/runtime_http.go
@@ -6,8 +6,9 @@ import (
 	"sort"
 	"sync"
 
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 )
 
 // GetRoutersByEntryPoints returns all the http routers by entry points name and routers name.
@@ -19,14 +20,14 @@ func (c *Configuration) GetRoutersByEntryPoints(ctx context.Context, entryPoints
 			continue
 		}
 
-		logger := log.FromContext(log.With(ctx, log.Str(log.RouterName, rtName)))
+		logger := log.Ctx(ctx).With().Str(logs.RouterName, rtName).Logger()
 
 		entryPointsCount := 0
 		for _, entryPointName := range rt.EntryPoints {
 			if !contains(entryPoints, entryPointName) {
 				rt.AddError(fmt.Errorf("entryPoint %q doesn't exist", entryPointName), false)
-				logger.WithField(log.EntryPointName, entryPointName).
-					Errorf("entryPoint %q doesn't exist", entryPointName)
+				logger.Error().Str(logs.EntryPointName, entryPointName).
+					Msg("EntryPoint doesn't exist")
 				continue
 			}
 
@@ -42,7 +43,7 @@ func (c *Configuration) GetRoutersByEntryPoints(ctx context.Context, entryPoints
 
 		if entryPointsCount == 0 {
 			rt.AddError(fmt.Errorf("no valid entryPoint for this router"), true)
-			logger.Error("no valid entryPoint for this router")
+			logger.Error().Msg("No valid entryPoint for this router")
 		}
 
 		rt.Using = unique(rt.Using)
diff --git a/pkg/config/runtime/runtime_tcp.go b/pkg/config/runtime/runtime_tcp.go
index 72a8b3a146..665da4525e 100644
--- a/pkg/config/runtime/runtime_tcp.go
+++ b/pkg/config/runtime/runtime_tcp.go
@@ -4,8 +4,9 @@ import (
 	"context"
 	"fmt"
 
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 )
 
 // GetTCPRoutersByEntryPoints returns all the tcp routers by entry points name and routers name.
@@ -13,14 +14,14 @@ func (c *Configuration) GetTCPRoutersByEntryPoints(ctx context.Context, entryPoi
 	entryPointsRouters := make(map[string]map[string]*TCPRouterInfo)
 
 	for rtName, rt := range c.TCPRouters {
-		logger := log.FromContext(log.With(ctx, log.Str(log.RouterName, rtName)))
+		logger := log.Ctx(ctx).With().Str(logs.RouterName, rtName).Logger()
 
 		entryPointsCount := 0
 		for _, entryPointName := range rt.EntryPoints {
 			if !contains(entryPoints, entryPointName) {
 				rt.AddError(fmt.Errorf("entryPoint %q doesn't exist", entryPointName), false)
-				logger.WithField(log.EntryPointName, entryPointName).
-					Errorf("entryPoint %q doesn't exist", entryPointName)
+				logger.Error().Str(logs.EntryPointName, entryPointName).
+					Msg("EntryPoint doesn't exist")
 				continue
 			}
 
@@ -36,7 +37,7 @@ func (c *Configuration) GetTCPRoutersByEntryPoints(ctx context.Context, entryPoi
 
 		if entryPointsCount == 0 {
 			rt.AddError(fmt.Errorf("no valid entryPoint for this router"), true)
-			logger.Error("no valid entryPoint for this router")
+			logger.Error().Msg("No valid entryPoint for this router")
 		}
 	}
 
diff --git a/pkg/config/runtime/runtime_udp.go b/pkg/config/runtime/runtime_udp.go
index f75922c04c..ed9a448147 100644
--- a/pkg/config/runtime/runtime_udp.go
+++ b/pkg/config/runtime/runtime_udp.go
@@ -4,8 +4,9 @@ import (
 	"context"
 	"fmt"
 
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 )
 
 // GetUDPRoutersByEntryPoints returns all the UDP routers by entry points name and routers name.
@@ -13,11 +14,11 @@ func (c *Configuration) GetUDPRoutersByEntryPoints(ctx context.Context, entryPoi
 	entryPointsRouters := make(map[string]map[string]*UDPRouterInfo)
 
 	for rtName, rt := range c.UDPRouters {
-		logger := log.FromContext(log.With(ctx, log.Str(log.RouterName, rtName)))
+		logger := log.Ctx(ctx).With().Str(logs.RouterName, rtName).Logger()
 
 		eps := rt.EntryPoints
 		if len(eps) == 0 {
-			logger.Debugf("No entryPoint defined for this router, using the default one(s) instead: %+v", entryPoints)
+			logger.Debug().Msgf("No entryPoint defined for this router, using the default one(s) instead: %+v", entryPoints)
 			eps = entryPoints
 		}
 
@@ -25,8 +26,8 @@ func (c *Configuration) GetUDPRoutersByEntryPoints(ctx context.Context, entryPoi
 		for _, entryPointName := range eps {
 			if !contains(entryPoints, entryPointName) {
 				rt.AddError(fmt.Errorf("entryPoint %q doesn't exist", entryPointName), false)
-				logger.WithField(log.EntryPointName, entryPointName).
-					Errorf("entryPoint %q doesn't exist", entryPointName)
+				logger.Error().Str(logs.EntryPointName, entryPointName).
+					Msg("EntryPoint doesn't exist")
 				continue
 			}
 
@@ -42,7 +43,7 @@ func (c *Configuration) GetUDPRoutersByEntryPoints(ctx context.Context, entryPoi
 
 		if entryPointsCount == 0 {
 			rt.AddError(fmt.Errorf("no valid entryPoint for this router"), true)
-			logger.Error("no valid entryPoint for this router")
+			logger.Error().Msg("No valid entryPoint for this router")
 		}
 	}
 
diff --git a/pkg/config/static/hub.go b/pkg/config/static/hub.go
index ee78de9756..5cf97ae32c 100644
--- a/pkg/config/static/hub.go
+++ b/pkg/config/static/hub.go
@@ -3,7 +3,8 @@ package static
 import (
 	"errors"
 
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/provider/hub"
 )
 
@@ -18,7 +19,9 @@ func (c *Configuration) initHubProvider() error {
 		ep.SetDefaults()
 		ep.Address = ":9901"
 		c.EntryPoints[hub.TunnelEntrypoint] = &ep
-		log.WithoutContext().Infof("The entryPoint %q is created on port 9901 to allow exposition of services.", hub.TunnelEntrypoint)
+
+		log.Info().Str(logs.EntryPointName, hub.TunnelEntrypoint).
+			Msg("The entryPoint is created on port 9901 to allow exposition of services.")
 	}
 
 	if c.Hub.TLS == nil {
@@ -34,7 +37,7 @@ func (c *Configuration) initHubProvider() error {
 	}
 
 	if c.Hub.TLS.Insecure {
-		log.WithoutContext().Warn("Hub is in `insecure` mode. Do not run in production with this setup.")
+		log.Warn().Msg("Hub is in `insecure` mode. Do not run in production with this setup.")
 	}
 
 	if _, ok := c.EntryPoints[hub.APIEntrypoint]; !ok {
@@ -42,7 +45,9 @@ func (c *Configuration) initHubProvider() error {
 		ep.SetDefaults()
 		ep.Address = ":9900"
 		c.EntryPoints[hub.APIEntrypoint] = &ep
-		log.WithoutContext().Infof("The entryPoint %q is created on port 9900 to allow Traefik to communicate with the Hub Agent for Traefik.", hub.APIEntrypoint)
+
+		log.Info().Str(logs.EntryPointName, hub.APIEntrypoint).
+			Msg("The entryPoint is created on port 9900 to allow Traefik to communicate with the Hub Agent for Traefik.")
 	}
 
 	c.EntryPoints[hub.APIEntrypoint].HTTP.TLS = &TLSConfig{
diff --git a/pkg/config/static/static_config.go b/pkg/config/static/static_config.go
index 4a2558b8b1..dad5f9f5b2 100644
--- a/pkg/config/static/static_config.go
+++ b/pkg/config/static/static_config.go
@@ -2,14 +2,14 @@ package static
 
 import (
 	"fmt"
-	stdlog "log"
 	"strings"
 	"time"
 
 	legolog "github.com/go-acme/lego/v4/log"
-	"github.com/sirupsen/logrus"
+	"github.com/rs/zerolog"
+	"github.com/rs/zerolog/log"
 	ptypes "github.com/traefik/paerser/types"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/ping"
 	acmeprovider "github.com/traefik/traefik/v2/pkg/provider/acme"
 	"github.com/traefik/traefik/v2/pkg/provider/consulcatalog"
@@ -242,9 +242,9 @@ func (c *Configuration) SetEffectiveConfiguration() {
 	if c.Hub != nil {
 		if err := c.initHubProvider(); err != nil {
 			c.Hub = nil
-			log.WithoutContext().Errorf("Unable to activate the Hub provider: %v", err)
+			log.Error().Err(err).Msg("Unable to activate the Hub provider")
 		} else {
-			log.WithoutContext().Debugf("Experimental Hub provider has been activated.")
+			log.Debug().Msg("Experimental Hub provider has been activated")
 		}
 	}
 
@@ -278,14 +278,15 @@ func (c *Configuration) SetEffectiveConfiguration() {
 		for epName, ep := range c.EntryPoints {
 			if ep.HTTP3 != nil {
 				ep.HTTP3 = nil
-				log.WithoutContext().Debugf("Disabling HTTP3 configuration for entryPoint %q: HTTP3 is disabled in the experimental configuration section", epName)
+				log.Debug().Str(logs.EntryPointName, epName).
+					Msgf("Disabling HTTP3 configuration for entryPoint %q: HTTP3 is disabled in the experimental configuration section", epName)
 			}
 		}
 	}
 
 	// Configure Gateway API provider
 	if c.Providers.KubernetesGateway != nil {
-		log.WithoutContext().Debugf("Experimental Kubernetes Gateway provider has been activated")
+		log.Debug().Msg("Experimental Kubernetes Gateway provider has been activated")
 		entryPoints := make(map[string]gateway.Entrypoint)
 		for epName, entryPoint := range c.EntryPoints {
 			entryPoints[epName] = gateway.Entrypoint{Address: entryPoint.GetAddress(), HasHTTPTLSConf: entryPoint.HTTP.TLS != nil}
@@ -319,7 +320,8 @@ func (c *Configuration) initACMEProvider() {
 		}
 	}
 
-	legolog.Logger = stdlog.New(log.WithoutContext().WriterLevel(logrus.DebugLevel), "legolog: ", 0)
+	logger := logs.NoLevel(log.Logger, zerolog.DebugLevel).With().Str("lib", "lego").Logger()
+	legolog.Logger = logs.NewLogrusWrapper(logger)
 }
 
 // ValidateConfiguration validate that configuration is coherent.
@@ -366,15 +368,13 @@ func getSafeACMECAServer(caServerSrc string) string {
 
 	if strings.HasPrefix(caServerSrc, "https://acme-v01.api.letsencrypt.org") {
 		caServer := strings.Replace(caServerSrc, "v01", "v02", 1)
-		log.WithoutContext().
-			Warnf("The CA server %[1]q refers to a v01 endpoint of the ACME API, please change to %[2]q. Fallback to %[2]q.", caServerSrc, caServer)
+		log.Warn().Msgf("The CA server %[1]q refers to a v01 endpoint of the ACME API, please change to %[2]q. Fallback to %[2]q.", caServerSrc, caServer)
 		return caServer
 	}
 
 	if strings.HasPrefix(caServerSrc, "https://acme-staging.api.letsencrypt.org") {
 		caServer := strings.Replace(caServerSrc, "https://acme-staging.api.letsencrypt.org", "https://acme-staging-v02.api.letsencrypt.org", 1)
-		log.WithoutContext().
-			Warnf("The CA server %[1]q refers to a v01 endpoint of the ACME API, please change to %[2]q. Fallback to %[2]q.", caServerSrc, caServer)
+		log.Warn().Msgf("The CA server %[1]q refers to a v01 endpoint of the ACME API, please change to %[2]q. Fallback to %[2]q.", caServerSrc, caServer)
 		return caServer
 	}
 
diff --git a/pkg/healthcheck/healthcheck.go b/pkg/healthcheck/healthcheck.go
index ca9a91069a..ef19e30cbe 100644
--- a/pkg/healthcheck/healthcheck.go
+++ b/pkg/healthcheck/healthcheck.go
@@ -11,9 +11,9 @@ import (
 	"time"
 
 	gokitmetrics "github.com/go-kit/kit/metrics"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
 	"github.com/traefik/traefik/v2/pkg/config/runtime"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"google.golang.org/grpc"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/credentials/insecure"
@@ -55,22 +55,22 @@ type ServiceHealthChecker struct {
 }
 
 func NewServiceHealthChecker(ctx context.Context, metrics metricsHealthCheck, config *dynamic.ServerHealthCheck, service StatusSetter, info *runtime.ServiceInfo, transport http.RoundTripper, targets map[string]*url.URL) *ServiceHealthChecker {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
 	interval := time.Duration(config.Interval)
 	if interval <= 0 {
-		logger.Error("Health check interval smaller than zero")
+		logger.Error().Msg("Health check interval smaller than zero")
 		interval = time.Duration(dynamic.DefaultHealthCheckInterval)
 	}
 
 	timeout := time.Duration(config.Timeout)
 	if timeout <= 0 {
-		logger.Error("Health check timeout smaller than zero")
+		logger.Error().Msg("Health check timeout smaller than zero")
 		timeout = time.Duration(dynamic.DefaultHealthCheckTimeout)
 	}
 
 	if timeout >= interval {
-		logger.Warnf("Health check timeout should be lower than the health check interval. Interval set to timeout + 1 second (%s).", interval)
+		logger.Warn().Msgf("Health check timeout should be lower than the health check interval. Interval set to timeout + 1 second (%s).", interval)
 		interval = timeout + time.Second
 	}
 
@@ -122,10 +122,10 @@ func (shc *ServiceHealthChecker) Launch(ctx context.Context) {
 						return
 					}
 
-					log.FromContext(ctx).
-						WithField("targetURL", target.String()).
-						WithError(err).
-						Warn("Health check failed.")
+					log.Ctx(ctx).Warn().
+						Str("targetURL", target.String()).
+						Err(err).
+						Msg("Health check failed.")
 
 					up = false
 					serverUpMetricValue = float64(0)
diff --git a/pkg/log/deprecated.go b/pkg/log/deprecated.go
deleted file mode 100644
index 3bde3f8411..0000000000
--- a/pkg/log/deprecated.go
+++ /dev/null
@@ -1,133 +0,0 @@
-package log
-
-import (
-	"bufio"
-	"io"
-	"runtime"
-
-	"github.com/sirupsen/logrus"
-)
-
-// Debug logs a message at level Debug on the standard logger.
-// Deprecated: use log.FromContext(ctx).Debug(...) instead.
-func Debug(args ...interface{}) {
-	mainLogger.Debug(args...)
-}
-
-// Debugf logs a message at level Debug on the standard logger.
-// Deprecated: use log.FromContext(ctx).Debugf(...) instead.
-func Debugf(format string, args ...interface{}) {
-	mainLogger.Debugf(format, args...)
-}
-
-// Info logs a message at level Info on the standard logger.
-// Deprecated: use log.FromContext(ctx).Info(...) instead.
-func Info(args ...interface{}) {
-	mainLogger.Info(args...)
-}
-
-// Infof logs a message at level Info on the standard logger.
-// Deprecated: use log.FromContext(ctx).Infof(...) instead.
-func Infof(format string, args ...interface{}) {
-	mainLogger.Infof(format, args...)
-}
-
-// Warn logs a message at level Warn on the standard logger.
-// Deprecated: use log.FromContext(ctx).Warn(...) instead.
-func Warn(args ...interface{}) {
-	mainLogger.Warn(args...)
-}
-
-// Warnf logs a message at level Warn on the standard logger.
-// Deprecated: use log.FromContext(ctx).Warnf(...) instead.
-func Warnf(format string, args ...interface{}) {
-	mainLogger.Warnf(format, args...)
-}
-
-// Error logs a message at level Error on the standard logger.
-// Deprecated: use log.FromContext(ctx).Error(...) instead.
-func Error(args ...interface{}) {
-	mainLogger.Error(args...)
-}
-
-// Errorf logs a message at level Error on the standard logger.
-// Deprecated: use log.FromContext(ctx).Errorf(...) instead.
-func Errorf(format string, args ...interface{}) {
-	mainLogger.Errorf(format, args...)
-}
-
-// Panic logs a message at level Panic on the standard logger.
-// Deprecated: use log.FromContext(ctx).Panic(...) instead.
-func Panic(args ...interface{}) {
-	mainLogger.Panic(args...)
-}
-
-// Fatal logs a message at level Fatal on the standard logger.
-// Deprecated: use log.FromContext(ctx).Fatal(...) instead.
-func Fatal(args ...interface{}) {
-	mainLogger.Fatal(args...)
-}
-
-// Fatalf logs a message at level Fatal on the standard logger.
-// Deprecated: use log.FromContext(ctx).Fatalf(...) instead.
-func Fatalf(format string, args ...interface{}) {
-	mainLogger.Fatalf(format, args...)
-}
-
-// AddHook adds a hook to the standard logger hooks.
-func AddHook(hook logrus.Hook) {
-	logrus.AddHook(hook)
-}
-
-// CustomWriterLevel logs writer for a specific level. (with a custom scanner buffer size.)
-// adapted from github.com/Sirupsen/logrus/writer.go.
-func CustomWriterLevel(level logrus.Level, maxScanTokenSize int) *io.PipeWriter {
-	reader, writer := io.Pipe()
-
-	var printFunc func(args ...interface{})
-
-	switch level {
-	case logrus.DebugLevel:
-		printFunc = Debug
-	case logrus.InfoLevel:
-		printFunc = Info
-	case logrus.WarnLevel:
-		printFunc = Warn
-	case logrus.ErrorLevel:
-		printFunc = Error
-	case logrus.FatalLevel:
-		printFunc = Fatal
-	case logrus.PanicLevel:
-		printFunc = Panic
-	default:
-		printFunc = mainLogger.Print
-	}
-
-	go writerScanner(reader, maxScanTokenSize, printFunc)
-	runtime.SetFinalizer(writer, writerFinalizer)
-
-	return writer
-}
-
-// extract from github.com/Sirupsen/logrus/writer.go
-// Hack the buffer size.
-func writerScanner(reader io.ReadCloser, scanTokenSize int, printFunc func(args ...interface{})) {
-	scanner := bufio.NewScanner(reader)
-
-	if scanTokenSize > bufio.MaxScanTokenSize {
-		buf := make([]byte, bufio.MaxScanTokenSize)
-		scanner.Buffer(buf, scanTokenSize)
-	}
-
-	for scanner.Scan() {
-		printFunc(scanner.Text())
-	}
-	if err := scanner.Err(); err != nil {
-		Errorf("Error while reading from Writer: %s", err)
-	}
-	reader.Close()
-}
-
-func writerFinalizer(writer *io.PipeWriter) {
-	writer.Close()
-}
diff --git a/pkg/log/log.go b/pkg/log/log.go
deleted file mode 100644
index 999a5e1fdf..0000000000
--- a/pkg/log/log.go
+++ /dev/null
@@ -1,144 +0,0 @@
-package log
-
-import (
-	"context"
-	"fmt"
-	"io"
-	"os"
-
-	"github.com/sirupsen/logrus"
-)
-
-type contextKey int
-
-const (
-	loggerKey contextKey = iota
-)
-
-// Logger the Traefik logger.
-type Logger interface {
-	logrus.FieldLogger
-	WriterLevel(logrus.Level) *io.PipeWriter
-}
-
-var (
-	mainLogger  Logger
-	logFilePath string
-	logFile     *os.File
-)
-
-func init() {
-	mainLogger = logrus.StandardLogger()
-	logrus.SetOutput(os.Stdout)
-}
-
-// SetLogger sets the logger.
-func SetLogger(l Logger) {
-	mainLogger = l
-}
-
-// SetOutput sets the standard logger output.
-func SetOutput(out io.Writer) {
-	logrus.SetOutput(out)
-}
-
-// SetFormatter sets the standard logger formatter.
-func SetFormatter(formatter logrus.Formatter) {
-	logrus.SetFormatter(formatter)
-}
-
-// SetLevel sets the standard logger level.
-func SetLevel(level logrus.Level) {
-	logrus.SetLevel(level)
-}
-
-// GetLevel returns the standard logger level.
-func GetLevel() logrus.Level {
-	return logrus.GetLevel()
-}
-
-// Str adds a string field.
-func Str(key, value string) func(logrus.Fields) {
-	return func(fields logrus.Fields) {
-		fields[key] = value
-	}
-}
-
-// With Adds fields.
-func With(ctx context.Context, opts ...func(logrus.Fields)) context.Context {
-	logger := FromContext(ctx)
-
-	fields := make(logrus.Fields)
-	for _, opt := range opts {
-		opt(fields)
-	}
-	logger = logger.WithFields(fields)
-
-	return context.WithValue(ctx, loggerKey, logger)
-}
-
-// FromContext Gets the logger from context.
-func FromContext(ctx context.Context) Logger {
-	if ctx == nil {
-		panic("nil context")
-	}
-
-	logger, ok := ctx.Value(loggerKey).(Logger)
-	if !ok {
-		logger = mainLogger
-	}
-
-	return logger
-}
-
-// WithoutContext Gets the main logger.
-func WithoutContext() Logger {
-	return mainLogger
-}
-
-// OpenFile opens the log file using the specified path.
-func OpenFile(path string) error {
-	logFilePath = path
-
-	var err error
-	logFile, err = os.OpenFile(logFilePath, os.O_RDWR|os.O_CREATE|os.O_APPEND, 0o666)
-	if err != nil {
-		return err
-	}
-
-	SetOutput(logFile)
-	return nil
-}
-
-// CloseFile closes the log and sets the Output to stdout.
-func CloseFile() error {
-	logrus.SetOutput(os.Stdout)
-
-	if logFile != nil {
-		return logFile.Close()
-	}
-	return nil
-}
-
-// RotateFile closes and reopens the log file to allow for rotation by an external source.
-// If the log isn't backed by a file then it does nothing.
-func RotateFile() error {
-	logger := FromContext(context.Background())
-
-	if logFile == nil && logFilePath == "" {
-		logger.Debug("Traefik log is not writing to a file, ignoring rotate request")
-		return nil
-	}
-
-	if logFile != nil {
-		defer func(f *os.File) {
-			_ = f.Close()
-		}(logFile)
-	}
-
-	if err := OpenFile(logFilePath); err != nil {
-		return fmt.Errorf("error opening log file: %w", err)
-	}
-
-	return nil
-}
diff --git a/pkg/logs/aws.go b/pkg/logs/aws.go
new file mode 100644
index 0000000000..a4cc27469f
--- /dev/null
+++ b/pkg/logs/aws.go
@@ -0,0 +1,16 @@
+package logs
+
+import (
+	"github.com/aws/aws-sdk-go/aws"
+	"github.com/rs/zerolog"
+)
+
+func NewAWSWrapper(logger zerolog.Logger) aws.LoggerFunc {
+	if logger.GetLevel() > zerolog.DebugLevel {
+		return func(args ...interface{}) {}
+	}
+
+	return func(args ...interface{}) {
+		logger.Debug().CallerSkipFrame(2).MsgFunc(msgFunc(args...))
+	}
+}
diff --git a/pkg/logs/datadog.go b/pkg/logs/datadog.go
new file mode 100644
index 0000000000..f6fac925fd
--- /dev/null
+++ b/pkg/logs/datadog.go
@@ -0,0 +1,15 @@
+package logs
+
+import "github.com/rs/zerolog"
+
+type DatadogLogger struct {
+	logger zerolog.Logger
+}
+
+func NewDatadogLogger(logger zerolog.Logger) *DatadogLogger {
+	return &DatadogLogger{logger: logger}
+}
+
+func (d DatadogLogger) Log(msg string) {
+	d.logger.Debug().CallerSkipFrame(1).Msg(msg)
+}
diff --git a/pkg/logs/elastic.go b/pkg/logs/elastic.go
new file mode 100644
index 0000000000..557ebf1944
--- /dev/null
+++ b/pkg/logs/elastic.go
@@ -0,0 +1,19 @@
+package logs
+
+import "github.com/rs/zerolog"
+
+type ElasticLogger struct {
+	logger zerolog.Logger
+}
+
+func NewElasticLogger(logger zerolog.Logger) *ElasticLogger {
+	return &ElasticLogger{logger: logger}
+}
+
+func (l ElasticLogger) Debugf(format string, args ...interface{}) {
+	l.logger.Debug().CallerSkipFrame(1).Msgf(format, args...)
+}
+
+func (l ElasticLogger) Errorf(format string, args ...interface{}) {
+	l.logger.Error().CallerSkipFrame(1).Msgf(format, args...)
+}
diff --git a/pkg/log/fields.go b/pkg/logs/fields.go
similarity index 90%
rename from pkg/log/fields.go
rename to pkg/logs/fields.go
index ff113f6c99..27e05198f0 100644
--- a/pkg/log/fields.go
+++ b/pkg/logs/fields.go
@@ -1,4 +1,4 @@
-package log
+package logs
 
 // Log entry names.
 const (
@@ -12,6 +12,7 @@ const (
 	MetricsProviderName  = "metricsProviderName"
 	TracingProviderName  = "tracingProviderName"
 	ServerName           = "serverName"
+	ServerIndex          = "serverIndex"
 	TLSStoreName         = "tlsStoreName"
 	ServersTransportName = "serversTransport"
 )
diff --git a/pkg/logs/gokit.go b/pkg/logs/gokit.go
new file mode 100644
index 0000000000..9940d861f9
--- /dev/null
+++ b/pkg/logs/gokit.go
@@ -0,0 +1,17 @@
+package logs
+
+import (
+	kitlog "github.com/go-kit/kit/log"
+	"github.com/rs/zerolog"
+)
+
+func NewGoKitWrapper(logger zerolog.Logger) kitlog.LoggerFunc {
+	if logger.GetLevel() > zerolog.DebugLevel {
+		return func(args ...interface{}) error { return nil }
+	}
+
+	return func(args ...interface{}) error {
+		logger.Debug().CallerSkipFrame(2).MsgFunc(msgFunc(args...))
+		return nil
+	}
+}
diff --git a/pkg/logs/haystack.go b/pkg/logs/haystack.go
new file mode 100644
index 0000000000..6f3db7361b
--- /dev/null
+++ b/pkg/logs/haystack.go
@@ -0,0 +1,28 @@
+package logs
+
+import (
+	"github.com/rs/zerolog"
+)
+
+type HaystackLogger struct {
+	logger zerolog.Logger
+}
+
+func NewHaystackLogger(logger zerolog.Logger) *HaystackLogger {
+	return &HaystackLogger{logger: logger}
+}
+
+// Error prints the error message.
+func (l HaystackLogger) Error(format string, v ...interface{}) {
+	l.logger.Error().CallerSkipFrame(1).Msgf(format, v...)
+}
+
+// Info prints the info message.
+func (l HaystackLogger) Info(format string, v ...interface{}) {
+	l.logger.Info().CallerSkipFrame(1).Msgf(format, v...)
+}
+
+// Debug prints the info message.
+func (l HaystackLogger) Debug(format string, v ...interface{}) {
+	l.logger.Debug().CallerSkipFrame(1).Msgf(format, v...)
+}
diff --git a/pkg/logs/instana.go b/pkg/logs/instana.go
new file mode 100644
index 0000000000..be4f522d61
--- /dev/null
+++ b/pkg/logs/instana.go
@@ -0,0 +1,29 @@
+package logs
+
+import (
+	"github.com/rs/zerolog"
+)
+
+type InstanaLogger struct {
+	logger zerolog.Logger
+}
+
+func NewInstanaLogger(logger zerolog.Logger) *InstanaLogger {
+	return &InstanaLogger{logger: logger}
+}
+
+func (l InstanaLogger) Debug(args ...interface{}) {
+	l.logger.Debug().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
+}
+
+func (l InstanaLogger) Info(args ...interface{}) {
+	l.logger.Info().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
+}
+
+func (l InstanaLogger) Warn(args ...interface{}) {
+	l.logger.Warn().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
+}
+
+func (l InstanaLogger) Error(args ...interface{}) {
+	l.logger.Error().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
+}
diff --git a/pkg/logs/jaeger.go b/pkg/logs/jaeger.go
new file mode 100644
index 0000000000..2faedc2fb2
--- /dev/null
+++ b/pkg/logs/jaeger.go
@@ -0,0 +1,23 @@
+package logs
+
+import (
+	"github.com/rs/zerolog"
+)
+
+// JaegerLogger is an implementation of the Logger interface that delegates to traefik log.
+type JaegerLogger struct {
+	logger zerolog.Logger
+}
+
+func NewJaegerLogger(logger zerolog.Logger) *JaegerLogger {
+	return &JaegerLogger{logger: logger}
+}
+
+func (l *JaegerLogger) Error(msg string) {
+	l.logger.Error().CallerSkipFrame(1).Msg(msg)
+}
+
+// Infof logs a message at debug priority.
+func (l *JaegerLogger) Infof(msg string, args ...interface{}) {
+	l.logger.Debug().CallerSkipFrame(1).Msgf(msg, args...)
+}
diff --git a/pkg/logs/log.go b/pkg/logs/log.go
new file mode 100644
index 0000000000..f83dc3be6e
--- /dev/null
+++ b/pkg/logs/log.go
@@ -0,0 +1,35 @@
+package logs
+
+import (
+	"fmt"
+
+	"github.com/rs/zerolog"
+)
+
+func NoLevel(logger zerolog.Logger, level zerolog.Level) zerolog.Logger {
+	return logger.Hook(NewNoLevelHook(logger.GetLevel(), level))
+}
+
+type NoLevelHook struct {
+	minLevel zerolog.Level
+	level    zerolog.Level
+}
+
+func NewNoLevelHook(minLevel zerolog.Level, level zerolog.Level) *NoLevelHook {
+	return &NoLevelHook{minLevel: minLevel, level: level}
+}
+
+func (n NoLevelHook) Run(e *zerolog.Event, level zerolog.Level, _ string) {
+	if n.minLevel > n.level {
+		e.Discard()
+		return
+	}
+
+	if level == zerolog.NoLevel {
+		e.Str("level", n.level.String())
+	}
+}
+
+func msgFunc(i ...any) func() string {
+	return func() string { return fmt.Sprint(i...) }
+}
diff --git a/pkg/logs/logrus.go b/pkg/logs/logrus.go
new file mode 100644
index 0000000000..68de8555bf
--- /dev/null
+++ b/pkg/logs/logrus.go
@@ -0,0 +1,49 @@
+package logs
+
+import (
+	"github.com/rs/zerolog"
+)
+
+type LogrusStdWrapper struct {
+	logger zerolog.Logger
+}
+
+func NewLogrusWrapper(logger zerolog.Logger) *LogrusStdWrapper {
+	return &LogrusStdWrapper{logger: logger}
+}
+
+func (l LogrusStdWrapper) Print(args ...interface{}) {
+	l.logger.Debug().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
+}
+
+func (l LogrusStdWrapper) Printf(s string, args ...interface{}) {
+	l.logger.Debug().CallerSkipFrame(1).Msgf(s, args...)
+}
+
+func (l LogrusStdWrapper) Println(args ...interface{}) {
+	l.logger.Debug().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
+}
+
+func (l LogrusStdWrapper) Fatal(args ...interface{}) {
+	l.logger.Fatal().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
+}
+
+func (l LogrusStdWrapper) Fatalf(s string, args ...interface{}) {
+	l.logger.Fatal().CallerSkipFrame(1).Msgf(s, args...)
+}
+
+func (l LogrusStdWrapper) Fatalln(args ...interface{}) {
+	l.logger.Fatal().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
+}
+
+func (l LogrusStdWrapper) Panic(args ...interface{}) {
+	l.logger.Panic().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
+}
+
+func (l LogrusStdWrapper) Panicf(s string, args ...interface{}) {
+	l.logger.Panic().CallerSkipFrame(1).Msgf(s, args...)
+}
+
+func (l LogrusStdWrapper) Panicln(args ...interface{}) {
+	l.logger.Panic().CallerSkipFrame(1).MsgFunc(msgFunc(args...))
+}
diff --git a/pkg/logs/oxy.go b/pkg/logs/oxy.go
new file mode 100644
index 0000000000..6f0f90aed3
--- /dev/null
+++ b/pkg/logs/oxy.go
@@ -0,0 +1,27 @@
+package logs
+
+import "github.com/rs/zerolog"
+
+type OxyWrapper struct {
+	logger zerolog.Logger
+}
+
+func NewOxyWrapper(logger zerolog.Logger) *OxyWrapper {
+	return &OxyWrapper{logger: logger}
+}
+
+func (l OxyWrapper) Debug(s string, i ...interface{}) {
+	l.logger.Debug().CallerSkipFrame(1).Msgf(s, i...)
+}
+
+func (l OxyWrapper) Info(s string, i ...interface{}) {
+	l.logger.Info().CallerSkipFrame(1).Msgf(s, i...)
+}
+
+func (l OxyWrapper) Warn(s string, i ...interface{}) {
+	l.logger.Warn().CallerSkipFrame(1).Msgf(s, i...)
+}
+
+func (l OxyWrapper) Error(s string, i ...interface{}) {
+	l.logger.Error().CallerSkipFrame(1).Msgf(s, i...)
+}
diff --git a/pkg/metrics/datadog.go b/pkg/metrics/datadog.go
index 596bc700d3..fce4c4cc4d 100644
--- a/pkg/metrics/datadog.go
+++ b/pkg/metrics/datadog.go
@@ -4,9 +4,9 @@ import (
 	"context"
 	"time"
 
-	kitlog "github.com/go-kit/kit/log"
 	"github.com/go-kit/kit/metrics/dogstatsd"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/safe"
 	"github.com/traefik/traefik/v2/pkg/types"
 )
@@ -58,10 +58,7 @@ func RegisterDatadog(ctx context.Context, config *types.Datadog) Registry {
 		config.Prefix = defaultMetricsPrefix
 	}
 
-	datadogClient = dogstatsd.New(config.Prefix+".", kitlog.LoggerFunc(func(keyvals ...interface{}) error {
-		log.WithoutContext().WithField(log.MetricsProviderName, "datadog").Info(keyvals...)
-		return nil
-	}))
+	datadogClient = dogstatsd.New(config.Prefix+".", logs.NewGoKitWrapper(log.Logger.With().Str(logs.MetricsProviderName, "datadog").Logger()))
 
 	initDatadogClient(ctx, config)
 
diff --git a/pkg/metrics/influxdb.go b/pkg/metrics/influxdb.go
index f9f5485b7d..d90ec75fc2 100644
--- a/pkg/metrics/influxdb.go
+++ b/pkg/metrics/influxdb.go
@@ -8,10 +8,10 @@ import (
 	"regexp"
 	"time"
 
-	kitlog "github.com/go-kit/kit/log"
 	"github.com/go-kit/kit/metrics/influx"
 	influxdb "github.com/influxdata/influxdb1-client/v2"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/safe"
 	"github.com/traefik/traefik/v2/pkg/types"
 )
@@ -112,30 +112,30 @@ func RegisterInfluxDB(ctx context.Context, config *types.InfluxDB) Registry {
 
 // initInfluxDBClient creates a influxDBClient.
 func initInfluxDBClient(ctx context.Context, config *types.InfluxDB) *influx.Influx {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
 	// TODO deprecated: move this switch into configuration.SetEffectiveConfiguration when web provider will be removed.
 	switch config.Protocol {
 	case protocolUDP:
 		if len(config.Database) > 0 || len(config.RetentionPolicy) > 0 {
-			logger.Warn("Database and RetentionPolicy options have no effect with UDP.")
+			logger.Warn().Msg("Database and RetentionPolicy options have no effect with UDP.")
 			config.Database = ""
 			config.RetentionPolicy = ""
 		}
 	case protocolHTTP:
 		if u, err := url.Parse(config.Address); err == nil {
 			if u.Scheme != "http" && u.Scheme != "https" {
-				logger.Warnf("InfluxDB address %s should specify a scheme (http or https): falling back on HTTP.", config.Address)
+				logger.Warn().Msgf("InfluxDB address %s should specify a scheme (http or https): falling back on HTTP.", config.Address)
 				config.Address = "http://" + config.Address
 			}
 		} else {
-			logger.Errorf("Unable to parse the InfluxDB address %v: falling back on UDP.", err)
+			logger.Error().Err(err).Msg("Unable to parse the InfluxDB address: falling back on UDP.")
 			config.Protocol = protocolUDP
 			config.Database = ""
 			config.RetentionPolicy = ""
 		}
 	default:
-		logger.Warnf("Unsupported protocol %s: falling back on UDP.", config.Protocol)
+		logger.Warn().Msgf("Unsupported protocol %s: falling back on UDP.", config.Protocol)
 		config.Protocol = protocolUDP
 		config.Database = ""
 		config.RetentionPolicy = ""
@@ -147,10 +147,8 @@ func initInfluxDBClient(ctx context.Context, config *types.InfluxDB) *influx.Inf
 			Database:        config.Database,
 			RetentionPolicy: config.RetentionPolicy,
 		},
-		kitlog.LoggerFunc(func(keyvals ...interface{}) error {
-			log.WithoutContext().WithField(log.MetricsProviderName, "influxdb").Info(keyvals...)
-			return nil
-		}))
+		logs.NewGoKitWrapper(*logger),
+	)
 }
 
 // initInfluxDBTicker initializes metrics pusher.
@@ -190,10 +188,10 @@ func (w *influxDBWriter) Write(bp influxdb.BatchPoints) error {
 	defer c.Close()
 
 	if writeErr := c.Write(bp); writeErr != nil {
-		ctx := log.With(context.Background(), log.Str(log.MetricsProviderName, "influxdb"))
-		log.FromContext(ctx).Errorf("Error while writing to InfluxDB: %s", writeErr.Error())
+		logger := log.With().Str(logs.MetricsProviderName, "influxdb").Logger()
+		logger.Error().Err(writeErr).Msg("Error while writing to InfluxDB")
 
-		if handleErr := w.handleWriteError(ctx, c, writeErr); handleErr != nil {
+		if handleErr := w.handleWriteError(logger.WithContext(context.Background()), c, writeErr); handleErr != nil {
 			return handleErr
 		}
 		// Retry write after successful handling of writeErr
@@ -232,9 +230,9 @@ func (w *influxDBWriter) handleWriteError(ctx context.Context, c influxdb.Client
 		qStr = fmt.Sprintf("%s WITH NAME \"%s\"", qStr, w.config.RetentionPolicy)
 	}
 
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
-	logger.Debugf("InfluxDB database not found: attempting to create one with %s", qStr)
+	logger.Debug().Msgf("InfluxDB database not found: attempting to create one with %s", qStr)
 
 	q := influxdb.NewQuery(qStr, "", "")
 	response, queryErr := c.Query(q)
@@ -242,10 +240,10 @@ func (w *influxDBWriter) handleWriteError(ctx context.Context, c influxdb.Client
 		queryErr = response.Error()
 	}
 	if queryErr != nil {
-		logger.Errorf("Error while creating the InfluxDB database %s", queryErr)
+		logger.Error().Err(queryErr).Msg("Error while creating the InfluxDB database")
 		return queryErr
 	}
 
-	logger.Debugf("Successfully created the InfluxDB database %s", w.config.Database)
+	logger.Debug().Msgf("Successfully created the InfluxDB database %s", w.config.Database)
 	return nil
 }
diff --git a/pkg/metrics/influxdb2.go b/pkg/metrics/influxdb2.go
index 27be18f2ac..6cf02e8f76 100644
--- a/pkg/metrics/influxdb2.go
+++ b/pkg/metrics/influxdb2.go
@@ -5,14 +5,14 @@ import (
 	"errors"
 	"time"
 
-	kitlog "github.com/go-kit/kit/log"
 	"github.com/go-kit/kit/metrics/influx"
 	influxdb2 "github.com/influxdata/influxdb-client-go/v2"
 	influxdb2api "github.com/influxdata/influxdb-client-go/v2/api"
 	"github.com/influxdata/influxdb-client-go/v2/api/write"
 	influxdb2log "github.com/influxdata/influxdb-client-go/v2/log"
 	influxdb "github.com/influxdata/influxdb1-client/v2"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/safe"
 	"github.com/traefik/traefik/v2/pkg/types"
 )
@@ -25,10 +25,12 @@ var (
 
 // RegisterInfluxDB2 creates metrics exporter for InfluxDB2.
 func RegisterInfluxDB2(ctx context.Context, config *types.InfluxDB2) Registry {
+	logger := log.Ctx(ctx)
+
 	if influxDB2Client == nil {
 		var err error
 		if influxDB2Client, err = newInfluxDB2Client(config); err != nil {
-			log.FromContext(ctx).Error(err)
+			logger.Error().Err(err).Send()
 			return nil
 		}
 	}
@@ -37,10 +39,7 @@ func RegisterInfluxDB2(ctx context.Context, config *types.InfluxDB2) Registry {
 		influxDB2Store = influx.New(
 			config.AdditionalLabels,
 			influxdb.BatchPointsConfig{},
-			kitlog.LoggerFunc(func(kv ...interface{}) error {
-				log.FromContext(ctx).Error(kv...)
-				return nil
-			}),
+			logs.NewGoKitWrapper(*logger),
 		)
 
 		influxDB2Ticker = time.NewTicker(time.Duration(config.PushInterval))
@@ -127,14 +126,13 @@ type influxDB2Writer struct {
 }
 
 func (w influxDB2Writer) Write(bp influxdb.BatchPoints) error {
-	ctx := log.With(context.Background(), log.Str(log.MetricsProviderName, "influxdb2"))
-	logger := log.FromContext(ctx)
+	logger := log.With().Str(logs.MetricsProviderName, "influxdb2").Logger()
 
 	wps := make([]*write.Point, 0, len(bp.Points()))
 	for _, p := range bp.Points() {
 		fields, err := p.Fields()
 		if err != nil {
-			logger.Errorf("Error while getting %s point fields: %s", p.Name(), err)
+			logger.Error().Err(err).Msgf("Error while getting %s point fields", p.Name())
 			continue
 		}
 
@@ -146,5 +144,7 @@ func (w influxDB2Writer) Write(bp influxdb.BatchPoints) error {
 		))
 	}
 
+	ctx := logger.WithContext(context.Background())
+
 	return w.wc.WritePoint(ctx, wps...)
 }
diff --git a/pkg/metrics/prometheus.go b/pkg/metrics/prometheus.go
index 3248640877..edad9e6f74 100644
--- a/pkg/metrics/prometheus.go
+++ b/pkg/metrics/prometheus.go
@@ -11,8 +11,8 @@ import (
 	stdprometheus "github.com/prometheus/client_golang/prometheus"
 	"github.com/prometheus/client_golang/prometheus/collectors"
 	"github.com/prometheus/client_golang/prometheus/promhttp"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/types"
 )
 
@@ -90,14 +90,14 @@ func RegisterPrometheus(ctx context.Context, config *types.Prometheus) Registry
 	if err := promRegistry.Register(collectors.NewProcessCollector(collectors.ProcessCollectorOpts{})); err != nil {
 		var arErr stdprometheus.AlreadyRegisteredError
 		if !errors.As(err, &arErr) {
-			log.FromContext(ctx).Warn("ProcessCollector is already registered")
+			log.Ctx(ctx).Warn().Msg("ProcessCollector is already registered")
 		}
 	}
 
 	if err := promRegistry.Register(collectors.NewGoCollector()); err != nil {
 		var arErr stdprometheus.AlreadyRegisteredError
 		if !errors.As(err, &arErr) {
-			log.FromContext(ctx).Warn("GoCollector is already registered")
+			log.Ctx(ctx).Warn().Msg("GoCollector is already registered")
 		}
 	}
 
@@ -306,15 +306,15 @@ func registerPromState(ctx context.Context) bool {
 		return true
 	}
 
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
 	var arErr stdprometheus.AlreadyRegisteredError
 	if errors.As(err, &arErr) {
-		logger.Debug("Prometheus collector already registered.")
+		logger.Debug().Msg("Prometheus collector already registered.")
 		return true
 	}
 
-	logger.Errorf("Unable to register Traefik to Prometheus: %v", err)
+	logger.Error().Err(err).Msg("Unable to register Traefik to Prometheus")
 	return false
 }
 
diff --git a/pkg/metrics/statsd.go b/pkg/metrics/statsd.go
index c84a7cf5a7..2a1c80a9ca 100644
--- a/pkg/metrics/statsd.go
+++ b/pkg/metrics/statsd.go
@@ -4,9 +4,9 @@ import (
 	"context"
 	"time"
 
-	kitlog "github.com/go-kit/kit/log"
 	"github.com/go-kit/kit/metrics/statsd"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/safe"
 	"github.com/traefik/traefik/v2/pkg/types"
 )
@@ -55,10 +55,7 @@ func RegisterStatsd(ctx context.Context, config *types.Statsd) Registry {
 		config.Prefix = defaultMetricsPrefix
 	}
 
-	statsdClient = statsd.New(config.Prefix+".", kitlog.LoggerFunc(func(keyvals ...interface{}) error {
-		log.WithoutContext().WithField(log.MetricsProviderName, "statsd").Info(keyvals...)
-		return nil
-	}))
+	statsdClient = statsd.New(config.Prefix+".", logs.NewGoKitWrapper(*log.Ctx(ctx)))
 
 	if statsdTicker == nil {
 		statsdTicker = initStatsdTicker(ctx, config)
diff --git a/pkg/middlewares/accesslog/field_middleware.go b/pkg/middlewares/accesslog/field_middleware.go
index b37e0dda96..a7a63f6834 100644
--- a/pkg/middlewares/accesslog/field_middleware.go
+++ b/pkg/middlewares/accesslog/field_middleware.go
@@ -4,9 +4,10 @@ import (
 	"net/http"
 	"time"
 
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/middlewares/capture"
-	"github.com/vulcand/oxy/utils"
+	"github.com/vulcand/oxy/v2/utils"
 )
 
 // FieldApply function hook to add data in accesslog.
@@ -57,7 +58,7 @@ func AddOriginFields(rw http.ResponseWriter, req *http.Request, next http.Handle
 	ctx := req.Context()
 	capt, err := capture.FromContext(ctx)
 	if err != nil {
-		log.FromContext(log.With(ctx, log.Str(log.MiddlewareType, "AccessLogs"))).Errorf("Could not get Capture: %v", err)
+		log.Ctx(ctx).Error().Err(err).Str(logs.MiddlewareType, "AccessLogs").Msg("Could not get Capture")
 		return
 	}
 
diff --git a/pkg/middlewares/accesslog/logger.go b/pkg/middlewares/accesslog/logger.go
index e5ade7b482..5c5b95dadf 100644
--- a/pkg/middlewares/accesslog/logger.go
+++ b/pkg/middlewares/accesslog/logger.go
@@ -16,9 +16,10 @@ import (
 	"time"
 
 	"github.com/containous/alice"
+	"github.com/rs/zerolog/log"
 	"github.com/sirupsen/logrus"
 	ptypes "github.com/traefik/paerser/types"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/middlewares/capture"
 	traefiktls "github.com/traefik/traefik/v2/pkg/tls"
 	"github.com/traefik/traefik/v2/pkg/types"
@@ -94,7 +95,7 @@ func NewHandler(config *types.AccessLog) (*Handler, error) {
 	case JSONFormat:
 		formatter = new(logrus.JSONFormatter)
 	default:
-		log.WithoutContext().Errorf("unsupported access log format: %q, defaulting to common format instead.", config.Format)
+		log.Error().Msgf("Unsupported access log format: %q, defaulting to common format instead.", config.Format)
 		formatter = new(CommonLogFormatter)
 	}
 
@@ -125,7 +126,7 @@ func NewHandler(config *types.AccessLog) (*Handler, error) {
 
 	if config.Filters != nil {
 		if httpCodeRanges, err := types.NewHTTPCodeRanges(config.Filters.StatusCodes); err != nil {
-			log.WithoutContext().Errorf("Failed to create new HTTP code ranges: %s", err)
+			log.Error().Err(err).Msg("Failed to create new HTTP code ranges")
 		} else {
 			logHandler.httpCodeRanges = httpCodeRanges
 		}
@@ -233,9 +234,7 @@ func (h *Handler) ServeHTTP(rw http.ResponseWriter, req *http.Request, next http
 	ctx := req.Context()
 	capt, err := capture.FromContext(ctx)
 	if err != nil {
-		log.FromContext(log.With(ctx, log.Str(log.MiddlewareType, "AccessLogs"))).
-			WithError(err).
-			Errorf("Could not get Capture")
+		log.Ctx(ctx).Error().Err(err).Str(logs.MiddlewareType, "AccessLogs").Msg("Could not get Capture")
 		return
 	}
 
diff --git a/pkg/middlewares/addprefix/add_prefix.go b/pkg/middlewares/addprefix/add_prefix.go
index 13e4f9284c..83c4b0648e 100644
--- a/pkg/middlewares/addprefix/add_prefix.go
+++ b/pkg/middlewares/addprefix/add_prefix.go
@@ -7,7 +7,6 @@ import (
 
 	"github.com/opentracing/opentracing-go/ext"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/middlewares"
 	"github.com/traefik/traefik/v2/pkg/tracing"
 )
@@ -25,7 +24,7 @@ type addPrefix struct {
 
 // New creates a new handler.
 func New(ctx context.Context, next http.Handler, config dynamic.AddPrefix, name string) (http.Handler, error) {
-	log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware")
+	middlewares.GetLogger(ctx, name, typeName).Debug().Msg("Creating middleware")
 	var result *addPrefix
 
 	if len(config.Prefix) > 0 {
@@ -46,16 +45,16 @@ func (a *addPrefix) GetTracingInformation() (string, ext.SpanKindEnum) {
 }
 
 func (a *addPrefix) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
-	logger := log.FromContext(middlewares.GetLoggerCtx(req.Context(), a.name, typeName))
+	logger := middlewares.GetLogger(req.Context(), a.name, typeName)
 
 	oldURLPath := req.URL.Path
 	req.URL.Path = ensureLeadingSlash(a.prefix + req.URL.Path)
-	logger.Debugf("URL.Path is now %s (was %s).", req.URL.Path, oldURLPath)
+	logger.Debug().Msgf("URL.Path is now %s (was %s).", req.URL.Path, oldURLPath)
 
 	if req.URL.RawPath != "" {
 		oldURLRawPath := req.URL.RawPath
 		req.URL.RawPath = ensureLeadingSlash(a.prefix + req.URL.RawPath)
-		logger.Debugf("URL.RawPath is now %s (was %s).", req.URL.RawPath, oldURLRawPath)
+		logger.Debug().Msgf("URL.RawPath is now %s (was %s).", req.URL.RawPath, oldURLRawPath)
 	}
 	req.RequestURI = req.URL.RequestURI()
 
diff --git a/pkg/middlewares/auth/basic_auth.go b/pkg/middlewares/auth/basic_auth.go
index b14a080614..b67eaf643a 100644
--- a/pkg/middlewares/auth/basic_auth.go
+++ b/pkg/middlewares/auth/basic_auth.go
@@ -10,7 +10,6 @@ import (
 	goauth "github.com/abbot/go-http-auth"
 	"github.com/opentracing/opentracing-go/ext"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/middlewares"
 	"github.com/traefik/traefik/v2/pkg/middlewares/accesslog"
 	"github.com/traefik/traefik/v2/pkg/tracing"
@@ -31,7 +30,8 @@ type basicAuth struct {
 
 // NewBasic creates a basicAuth middleware.
 func NewBasic(ctx context.Context, next http.Handler, authConfig dynamic.BasicAuth, name string) (http.Handler, error) {
-	log.FromContext(middlewares.GetLoggerCtx(ctx, name, basicTypeName)).Debug("Creating middleware")
+	middlewares.GetLogger(ctx, name, basicTypeName).Debug().Msg("Creating middleware")
+
 	users, err := getUsers(authConfig.UsersFile, authConfig.Users, basicUserParser)
 	if err != nil {
 		return nil, err
@@ -60,7 +60,7 @@ func (b *basicAuth) GetTracingInformation() (string, ext.SpanKindEnum) {
 }
 
 func (b *basicAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
-	logger := log.FromContext(middlewares.GetLoggerCtx(req.Context(), b.name, basicTypeName))
+	logger := middlewares.GetLogger(req.Context(), b.name, basicTypeName)
 
 	user, password, ok := req.BasicAuth()
 	if ok {
@@ -76,14 +76,14 @@ func (b *basicAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 	}
 
 	if !ok {
-		logger.Debug("Authentication failed")
+		logger.Debug().Msg("Authentication failed")
 		tracing.SetErrorWithEvent(req, "Authentication failed")
 
 		b.auth.RequireAuth(rw, req)
 		return
 	}
 
-	logger.Debug("Authentication succeeded")
+	logger.Debug().Msg("Authentication succeeded")
 	req.URL.User = url.User(user)
 
 	if b.headerField != "" {
@@ -91,7 +91,7 @@ func (b *basicAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 	}
 
 	if b.removeHeader {
-		logger.Debug("Removing authorization header")
+		logger.Debug().Msg("Removing authorization header")
 		req.Header.Del(authorizationHeader)
 	}
 	b.next.ServeHTTP(rw, req)
diff --git a/pkg/middlewares/auth/digest_auth.go b/pkg/middlewares/auth/digest_auth.go
index 454a972951..214e968ee7 100644
--- a/pkg/middlewares/auth/digest_auth.go
+++ b/pkg/middlewares/auth/digest_auth.go
@@ -10,7 +10,6 @@ import (
 	goauth "github.com/abbot/go-http-auth"
 	"github.com/opentracing/opentracing-go/ext"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/middlewares"
 	"github.com/traefik/traefik/v2/pkg/middlewares/accesslog"
 	"github.com/traefik/traefik/v2/pkg/tracing"
@@ -31,7 +30,8 @@ type digestAuth struct {
 
 // NewDigest creates a digest auth middleware.
 func NewDigest(ctx context.Context, next http.Handler, authConfig dynamic.DigestAuth, name string) (http.Handler, error) {
-	log.FromContext(middlewares.GetLoggerCtx(ctx, name, digestTypeName)).Debug("Creating middleware")
+	middlewares.GetLogger(ctx, name, digestTypeName).Debug().Msg("Creating middleware")
+
 	users, err := getUsers(authConfig.UsersFile, authConfig.Users, digestUserParser)
 	if err != nil {
 		return nil, err
@@ -59,7 +59,7 @@ func (d *digestAuth) GetTracingInformation() (string, ext.SpanKindEnum) {
 }
 
 func (d *digestAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
-	logger := log.FromContext(middlewares.GetLoggerCtx(req.Context(), d.name, digestTypeName))
+	logger := middlewares.GetLogger(req.Context(), d.name, digestTypeName)
 
 	username, authinfo := d.auth.CheckAuth(req)
 	if username == "" {
@@ -77,19 +77,19 @@ func (d *digestAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 		}
 
 		if authinfo != nil && *authinfo == "stale" {
-			logger.Debug("Digest authentication failed, possibly because out of order requests")
+			logger.Debug().Msg("Digest authentication failed, possibly because out of order requests")
 			tracing.SetErrorWithEvent(req, "Digest authentication failed, possibly because out of order requests")
 			d.auth.RequireAuthStale(rw, req)
 			return
 		}
 
-		logger.Debug("Digest authentication failed")
+		logger.Debug().Msg("Digest authentication failed")
 		tracing.SetErrorWithEvent(req, "Digest authentication failed")
 		d.auth.RequireAuth(rw, req)
 		return
 	}
 
-	logger.Debug("Digest authentication succeeded")
+	logger.Debug().Msg("Digest authentication succeeded")
 	req.URL.User = url.User(username)
 
 	logData := accesslog.GetLogData(req)
@@ -102,7 +102,7 @@ func (d *digestAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 	}
 
 	if d.removeHeader {
-		logger.Debug("Removing the Authorization header")
+		logger.Debug().Msg("Removing the Authorization header")
 		req.Header.Del(authorizationHeader)
 	}
 	d.next.ServeHTTP(rw, req)
diff --git a/pkg/middlewares/auth/forward.go b/pkg/middlewares/auth/forward.go
index a0cee86cef..b6adb3cb10 100644
--- a/pkg/middlewares/auth/forward.go
+++ b/pkg/middlewares/auth/forward.go
@@ -13,12 +13,11 @@ import (
 
 	"github.com/opentracing/opentracing-go/ext"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/middlewares"
 	"github.com/traefik/traefik/v2/pkg/middlewares/connectionheader"
 	"github.com/traefik/traefik/v2/pkg/tracing"
-	"github.com/vulcand/oxy/forward"
-	"github.com/vulcand/oxy/utils"
+	"github.com/vulcand/oxy/v2/forward"
+	"github.com/vulcand/oxy/v2/utils"
 )
 
 const (
@@ -52,7 +51,7 @@ type forwardAuth struct {
 
 // NewForward creates a forward auth middleware.
 func NewForward(ctx context.Context, next http.Handler, config dynamic.ForwardAuth, name string) (http.Handler, error) {
-	log.FromContext(middlewares.GetLoggerCtx(ctx, name, forwardedTypeName)).Debug("Creating middleware")
+	middlewares.GetLogger(ctx, name, forwardedTypeName).Debug().Msg("Creating middleware")
 
 	fa := &forwardAuth{
 		address:             config.Address,
@@ -98,13 +97,13 @@ func (fa *forwardAuth) GetTracingInformation() (string, ext.SpanKindEnum) {
 }
 
 func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
-	logger := log.FromContext(middlewares.GetLoggerCtx(req.Context(), fa.name, forwardedTypeName))
+	logger := middlewares.GetLogger(req.Context(), fa.name, forwardedTypeName)
 
 	forwardReq, err := http.NewRequest(http.MethodGet, fa.address, nil)
 	tracing.LogRequest(tracing.GetSpan(req), forwardReq)
 	if err != nil {
 		logMessage := fmt.Sprintf("Error calling %s. Cause %s", fa.address, err)
-		logger.Debug(logMessage)
+		logger.Debug().Msg(logMessage)
 		tracing.SetErrorWithEvent(req, logMessage)
 
 		rw.WriteHeader(http.StatusInternalServerError)
@@ -120,7 +119,7 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 	forwardResponse, forwardErr := fa.client.Do(forwardReq)
 	if forwardErr != nil {
 		logMessage := fmt.Sprintf("Error calling %s. Cause: %s", fa.address, forwardErr)
-		logger.Debug(logMessage)
+		logger.Debug().Msg(logMessage)
 		tracing.SetErrorWithEvent(req, logMessage)
 
 		rw.WriteHeader(http.StatusInternalServerError)
@@ -130,7 +129,7 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 	body, readError := io.ReadAll(forwardResponse.Body)
 	if readError != nil {
 		logMessage := fmt.Sprintf("Error reading body %s. Cause: %s", fa.address, readError)
-		logger.Debug(logMessage)
+		logger.Debug().Msg(logMessage)
 		tracing.SetErrorWithEvent(req, logMessage)
 
 		rw.WriteHeader(http.StatusInternalServerError)
@@ -141,7 +140,7 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 	// Pass the forward response's body and selected headers if it
 	// didn't return a response within the range of [200, 300).
 	if forwardResponse.StatusCode < http.StatusOK || forwardResponse.StatusCode >= http.StatusMultipleChoices {
-		logger.Debugf("Remote error %s. StatusCode: %d", fa.address, forwardResponse.StatusCode)
+		logger.Debug().Msgf("Remote error %s. StatusCode: %d", fa.address, forwardResponse.StatusCode)
 
 		utils.CopyHeaders(rw.Header(), forwardResponse.Header)
 		utils.RemoveHeaders(rw.Header(), hopHeaders...)
@@ -152,7 +151,7 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 		if err != nil {
 			if !errors.Is(err, http.ErrNoLocation) {
 				logMessage := fmt.Sprintf("Error reading response location header %s. Cause: %s", fa.address, err)
-				logger.Debug(logMessage)
+				logger.Debug().Msg(logMessage)
 				tracing.SetErrorWithEvent(req, logMessage)
 
 				rw.WriteHeader(http.StatusInternalServerError)
@@ -167,7 +166,7 @@ func (fa *forwardAuth) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 		rw.WriteHeader(forwardResponse.StatusCode)
 
 		if _, err = rw.Write(body); err != nil {
-			logger.Error(err)
+			logger.Error().Err(err).Send()
 		}
 		return
 	}
diff --git a/pkg/middlewares/buffering/buffering.go b/pkg/middlewares/buffering/buffering.go
index 5f6ea6b472..3000524606 100644
--- a/pkg/middlewares/buffering/buffering.go
+++ b/pkg/middlewares/buffering/buffering.go
@@ -5,11 +5,12 @@ import (
 	"net/http"
 
 	"github.com/opentracing/opentracing-go/ext"
+	"github.com/rs/zerolog"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/middlewares"
 	"github.com/traefik/traefik/v2/pkg/tracing"
-	oxybuffer "github.com/vulcand/oxy/buffer"
+	oxybuffer "github.com/vulcand/oxy/v2/buffer"
 )
 
 const (
@@ -23,9 +24,9 @@ type buffer struct {
 
 // New creates a buffering middleware.
 func New(ctx context.Context, next http.Handler, config dynamic.Buffering, name string) (http.Handler, error) {
-	logger := log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName))
-	logger.Debug("Creating middleware")
-	logger.Debugf("Setting up buffering: request limits: %d (mem), %d (max), response limits: %d (mem), %d (max) with retry: '%s'",
+	logger := middlewares.GetLogger(ctx, name, typeName)
+	logger.Debug().Msg("Creating middleware")
+	logger.Debug().Msgf("Setting up buffering: request limits: %d (mem), %d (max), response limits: %d (mem), %d (max) with retry: '%s'",
 		config.MemRequestBodyBytes, config.MaxRequestBodyBytes, config.MemResponseBodyBytes, config.MaxResponseBodyBytes, config.RetryExpression)
 
 	oxyBuffer, err := oxybuffer.New(
@@ -34,7 +35,9 @@ func New(ctx context.Context, next http.Handler, config dynamic.Buffering, name
 		oxybuffer.MaxRequestBodyBytes(config.MaxRequestBodyBytes),
 		oxybuffer.MemResponseBodyBytes(config.MemResponseBodyBytes),
 		oxybuffer.MaxResponseBodyBytes(config.MaxResponseBodyBytes),
-		oxybuffer.CondSetter(len(config.RetryExpression) > 0, oxybuffer.Retry(config.RetryExpression)),
+		oxybuffer.Logger(logs.NewOxyWrapper(*logger)),
+		oxybuffer.Verbose(logger.GetLevel() == zerolog.TraceLevel),
+		oxybuffer.Cond(len(config.RetryExpression) > 0, oxybuffer.Retry(config.RetryExpression)),
 	)
 	if err != nil {
 		return nil, err
diff --git a/pkg/middlewares/chain/chain.go b/pkg/middlewares/chain/chain.go
index 1f04198931..e5fd9aa93c 100644
--- a/pkg/middlewares/chain/chain.go
+++ b/pkg/middlewares/chain/chain.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/containous/alice"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/middlewares"
 )
 
@@ -20,7 +19,7 @@ type chainBuilder interface {
 
 // New creates a chain middleware.
 func New(ctx context.Context, next http.Handler, config dynamic.Chain, builder chainBuilder, name string) (http.Handler, error) {
-	log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware")
+	middlewares.GetLogger(ctx, name, typeName).Debug().Msg("Creating middleware")
 
 	middlewareChain := builder.BuildChain(ctx, config.Middlewares)
 	return middlewareChain.Then(next)
diff --git a/pkg/middlewares/circuitbreaker/circuit_breaker.go b/pkg/middlewares/circuitbreaker/circuit_breaker.go
index 77b489e7bf..8e3f3339b0 100644
--- a/pkg/middlewares/circuitbreaker/circuit_breaker.go
+++ b/pkg/middlewares/circuitbreaker/circuit_breaker.go
@@ -6,11 +6,13 @@ import (
 	"time"
 
 	"github.com/opentracing/opentracing-go/ext"
+	"github.com/rs/zerolog"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/middlewares"
 	"github.com/traefik/traefik/v2/pkg/tracing"
-	"github.com/vulcand/oxy/cbreaker"
+	"github.com/vulcand/oxy/v2/cbreaker"
 )
 
 const typeName = "CircuitBreaker"
@@ -24,19 +26,21 @@ type circuitBreaker struct {
 func New(ctx context.Context, next http.Handler, confCircuitBreaker dynamic.CircuitBreaker, name string) (http.Handler, error) {
 	expression := confCircuitBreaker.Expression
 
-	logger := log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName))
-	logger.Debug("Creating middleware")
-	logger.Debugf("Setting up with expression: %s", expression)
+	logger := middlewares.GetLogger(ctx, name, typeName)
+	logger.Debug().Msg("Creating middleware")
+	logger.Debug().Msgf("Setting up with expression: %s", expression)
 
-	cbOpts := []cbreaker.CircuitBreakerOption{
+	cbOpts := []cbreaker.Option{
 		cbreaker.Fallback(http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
 			tracing.SetErrorWithEvent(req, "blocked by circuit-breaker (%q)", expression)
 			rw.WriteHeader(http.StatusServiceUnavailable)
 
 			if _, err := rw.Write([]byte(http.StatusText(http.StatusServiceUnavailable))); err != nil {
-				log.FromContext(req.Context()).Error(err)
+				log.Ctx(req.Context()).Error().Err(err).Send()
 			}
 		})),
+		cbreaker.Logger(logs.NewOxyWrapper(*logger)),
+		cbreaker.Verbose(logger.GetLevel() == zerolog.TraceLevel),
 	}
 
 	if confCircuitBreaker.CheckPeriod > 0 {
@@ -55,6 +59,7 @@ func New(ctx context.Context, next http.Handler, confCircuitBreaker dynamic.Circ
 	if err != nil {
 		return nil, err
 	}
+
 	return &circuitBreaker{
 		circuitBreaker: oxyCircuitBreaker,
 		name:           name,
diff --git a/pkg/middlewares/compress/compress.go b/pkg/middlewares/compress/compress.go
index 55caa51b0d..4db054d656 100644
--- a/pkg/middlewares/compress/compress.go
+++ b/pkg/middlewares/compress/compress.go
@@ -10,7 +10,6 @@ import (
 	"github.com/klauspost/compress/gzhttp"
 	"github.com/opentracing/opentracing-go/ext"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/middlewares"
 	"github.com/traefik/traefik/v2/pkg/middlewares/compress/brotli"
 	"github.com/traefik/traefik/v2/pkg/tracing"
@@ -35,7 +34,7 @@ type compress struct {
 
 // New creates a new compress middleware.
 func New(ctx context.Context, next http.Handler, conf dynamic.Compress, name string) (http.Handler, error) {
-	log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware")
+	middlewares.GetLogger(ctx, name, typeName).Debug().Msg("Creating middleware")
 
 	excludes := []string{"application/grpc"}
 	for _, v := range conf.ExcludedContentTypes {
@@ -74,7 +73,7 @@ func New(ctx context.Context, next http.Handler, conf dynamic.Compress, name str
 }
 
 func (c *compress) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
-	logger := log.FromContext(middlewares.GetLoggerCtx(req.Context(), c.name, typeName))
+	logger := middlewares.GetLogger(req.Context(), c.name, typeName)
 
 	if req.Method == http.MethodHead {
 		c.next.ServeHTTP(rw, req)
@@ -83,7 +82,7 @@ func (c *compress) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 
 	mediaType, _, err := mime.ParseMediaType(req.Header.Get("Content-Type"))
 	if err != nil {
-		logger.WithError(err).Debug("Unable to parse MIME type")
+		logger.Debug().Err(err).Msg("Unable to parse MIME type")
 	}
 
 	// Notably for text/event-stream requests the response should not be compressed.
diff --git a/pkg/middlewares/customerrors/custom_errors.go b/pkg/middlewares/customerrors/custom_errors.go
index e98dfd7d6d..55684f82ec 100644
--- a/pkg/middlewares/customerrors/custom_errors.go
+++ b/pkg/middlewares/customerrors/custom_errors.go
@@ -12,11 +12,10 @@ import (
 
 	"github.com/opentracing/opentracing-go/ext"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/middlewares"
 	"github.com/traefik/traefik/v2/pkg/tracing"
 	"github.com/traefik/traefik/v2/pkg/types"
-	"github.com/vulcand/oxy/utils"
+	"github.com/vulcand/oxy/v2/utils"
 )
 
 // Compile time validation that the response recorder implements http interfaces correctly.
@@ -42,7 +41,7 @@ type customErrors struct {
 
 // New creates a new custom error pages middleware.
 func New(ctx context.Context, next http.Handler, config dynamic.ErrorPage, serviceBuilder serviceBuilder, name string) (http.Handler, error) {
-	log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware")
+	middlewares.GetLogger(ctx, name, typeName).Debug().Msg("Creating middleware")
 
 	httpCodeRanges, err := types.NewHTTPCodeRanges(config.Status)
 	if err != nil {
@@ -68,11 +67,10 @@ func (c *customErrors) GetTracingInformation() (string, ext.SpanKindEnum) {
 }
 
 func (c *customErrors) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
-	ctx := middlewares.GetLoggerCtx(req.Context(), c.name, typeName)
-	logger := log.FromContext(ctx)
+	logger := middlewares.GetLogger(req.Context(), c.name, typeName)
 
 	if c.backendHandler == nil {
-		logger.Error("Error pages: no backend handler.")
+		logger.Error().Msg("Error pages: no backend handler.")
 		tracing.SetErrorWithEvent(req, "Error pages: no backend handler.")
 		c.next.ServeHTTP(rw, req)
 		return
@@ -86,7 +84,7 @@ func (c *customErrors) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 
 	// check the recorder code against the configured http status code ranges
 	code := catcher.getCode()
-	logger.Debugf("Caught HTTP Status Code %d, returning error page", code)
+	logger.Debug().Msgf("Caught HTTP Status Code %d, returning error page", code)
 
 	var query string
 	if len(c.backendQuery) > 0 {
@@ -97,7 +95,7 @@ func (c *customErrors) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 
 	pageReq, err := newRequest("http://" + req.Host + query)
 	if err != nil {
-		logger.Error(err)
+		logger.Error().Err(err).Send()
 		http.Error(rw, http.StatusText(code), code)
 		return
 	}
diff --git a/pkg/middlewares/extractor.go b/pkg/middlewares/extractor.go
index 559ab4ff7d..6766132c96 100644
--- a/pkg/middlewares/extractor.go
+++ b/pkg/middlewares/extractor.go
@@ -6,9 +6,9 @@ import (
 	"fmt"
 	"net/http"
 
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
-	"github.com/vulcand/oxy/utils"
+	"github.com/vulcand/oxy/v2/utils"
 )
 
 // GetSourceExtractor returns the SourceExtractor function corresponding to the given sourceMatcher.
@@ -35,26 +35,26 @@ func GetSourceExtractor(ctx context.Context, sourceMatcher *dynamic.SourceCriter
 		}
 	}
 
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 	if sourceMatcher.IPStrategy != nil {
 		strategy, err := sourceMatcher.IPStrategy.Get()
 		if err != nil {
 			return nil, err
 		}
 
-		logger.Debug("Using IPStrategy")
+		logger.Debug().Msg("Using IPStrategy")
 		return utils.ExtractorFunc(func(req *http.Request) (string, int64, error) {
 			return strategy.GetIP(req), 1, nil
 		}), nil
 	}
 
 	if sourceMatcher.RequestHeaderName != "" {
-		logger.Debug("Using RequestHeaderName")
+		logger.Debug().Msg("Using RequestHeaderName")
 		return utils.NewExtractor(fmt.Sprintf("request.header.%s", sourceMatcher.RequestHeaderName))
 	}
 
 	if sourceMatcher.RequestHost {
-		logger.Debug("Using RequestHost")
+		logger.Debug().Msg("Using RequestHost")
 		return utils.NewExtractor("request.host")
 	}
 
diff --git a/pkg/middlewares/grpcweb/grpcweb.go b/pkg/middlewares/grpcweb/grpcweb.go
index b5dffe6f06..e0813fed9c 100644
--- a/pkg/middlewares/grpcweb/grpcweb.go
+++ b/pkg/middlewares/grpcweb/grpcweb.go
@@ -6,7 +6,6 @@ import (
 
 	"github.com/improbable-eng/grpc-web/go/grpcweb"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/middlewares"
 )
 
@@ -14,7 +13,7 @@ const typeName = "grpc-web"
 
 // New builds a new gRPC web request converter.
 func New(ctx context.Context, next http.Handler, config dynamic.GrpcWeb, name string) http.Handler {
-	log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware")
+	middlewares.GetLogger(ctx, name, typeName).Debug().Msg("Creating middleware")
 
 	return grpcweb.WrapHandler(next, grpcweb.WithCorsForRegisteredEndpointsOnly(false), grpcweb.WithOriginFunc(func(origin string) bool {
 		for _, originCfg := range config.AllowOrigins {
diff --git a/pkg/middlewares/headers/header.go b/pkg/middlewares/headers/header.go
index 7c5a99af54..99556fdd05 100644
--- a/pkg/middlewares/headers/header.go
+++ b/pkg/middlewares/headers/header.go
@@ -8,8 +8,9 @@ import (
 	"strconv"
 	"strings"
 
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 )
 
 // Header is a middleware that helps setup a few basic security features.
@@ -28,7 +29,8 @@ func NewHeader(next http.Handler, cfg dynamic.Headers) (*Header, error) {
 	hasCustomHeaders := cfg.HasCustomHeadersDefined()
 	hasCorsHeaders := cfg.HasCorsHeadersDefined()
 
-	ctx := log.With(context.Background(), log.Str(log.MiddlewareType, typeName))
+	ctx := log.With().Str(logs.MiddlewareType, typeName).Logger().WithContext(context.Background())
+
 	handleDeprecation(ctx, &cfg)
 
 	regexes := make([]*regexp.Regexp, len(cfg.AccessControlAllowOriginListRegex))
diff --git a/pkg/middlewares/headers/headers.go b/pkg/middlewares/headers/headers.go
index 503944f24f..dc4518768a 100644
--- a/pkg/middlewares/headers/headers.go
+++ b/pkg/middlewares/headers/headers.go
@@ -7,8 +7,8 @@ import (
 	"net/http"
 
 	"github.com/opentracing/opentracing-go/ext"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/middlewares"
 	"github.com/traefik/traefik/v2/pkg/middlewares/connectionheader"
 	"github.com/traefik/traefik/v2/pkg/tracing"
@@ -19,20 +19,22 @@ const (
 )
 
 func handleDeprecation(ctx context.Context, cfg *dynamic.Headers) {
+	logger := log.Ctx(ctx).Warn()
+
 	if cfg.SSLRedirect {
-		log.FromContext(ctx).Warn("SSLRedirect is deprecated, please use entrypoint redirection instead.")
+		logger.Msg("SSLRedirect is deprecated, please use entrypoint redirection instead.")
 	}
 	if cfg.SSLTemporaryRedirect {
-		log.FromContext(ctx).Warn("SSLTemporaryRedirect is deprecated, please use entrypoint redirection instead.")
+		logger.Msg("SSLTemporaryRedirect is deprecated, please use entrypoint redirection instead.")
 	}
 	if cfg.SSLHost != "" {
-		log.FromContext(ctx).Warn("SSLHost is deprecated, please use RedirectRegex middleware instead.")
+		logger.Msg("SSLHost is deprecated, please use RedirectRegex middleware instead.")
 	}
 	if cfg.SSLForceHost {
-		log.FromContext(ctx).Warn("SSLForceHost is deprecated, please use RedirectScheme middleware instead.")
+		logger.Msg("SSLForceHost is deprecated, please use RedirectScheme middleware instead.")
 	}
 	if cfg.FeaturePolicy != "" {
-		log.FromContext(ctx).Warn("FeaturePolicy is deprecated, please use PermissionsPolicy header instead.")
+		logger.Msg("FeaturePolicy is deprecated, please use PermissionsPolicy header instead.")
 	}
 }
 
@@ -44,9 +46,10 @@ type headers struct {
 // New creates a Headers middleware.
 func New(ctx context.Context, next http.Handler, cfg dynamic.Headers, name string) (http.Handler, error) {
 	// HeaderMiddleware -> SecureMiddleWare -> next
-	mCtx := middlewares.GetLoggerCtx(ctx, name, typeName)
-	logger := log.FromContext(mCtx)
-	logger.Debug("Creating middleware")
+	logger := middlewares.GetLogger(ctx, name, typeName)
+	logger.Debug().Msg("Creating middleware")
+
+	mCtx := logger.WithContext(ctx)
 
 	handleDeprecation(mCtx, &cfg)
 
@@ -62,13 +65,13 @@ func New(ctx context.Context, next http.Handler, cfg dynamic.Headers, name strin
 	nextHandler := next
 
 	if hasSecureHeaders {
-		logger.Debugf("Setting up secureHeaders from %v", cfg)
+		logger.Debug().Msgf("Setting up secureHeaders from %v", cfg)
 		handler = newSecure(next, cfg, name)
 		nextHandler = handler
 	}
 
 	if hasCustomHeaders || hasCorsHeaders {
-		logger.Debugf("Setting up customHeaders/Cors from %v", cfg)
+		logger.Debug().Msgf("Setting up customHeaders/Cors from %v", cfg)
 		h, err := NewHeader(nextHandler, cfg)
 		if err != nil {
 			return nil, err
diff --git a/pkg/middlewares/headers/responsewriter.go b/pkg/middlewares/headers/responsewriter.go
index 893af32c83..e50277ce93 100644
--- a/pkg/middlewares/headers/responsewriter.go
+++ b/pkg/middlewares/headers/responsewriter.go
@@ -6,7 +6,7 @@ import (
 	"net"
 	"net/http"
 
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 )
 
 type responseModifier struct {
@@ -55,7 +55,7 @@ func (r *responseModifier) WriteHeader(code int) {
 		// we are propagating when we are called in Write, but we're logging anyway,
 		// because we could be called from another place which does not take care of
 		// checking w.modifierErr.
-		log.WithoutContext().Errorf("Error when applying response modifier: %v", err)
+		log.Error().Err(err).Msg("Error when applying response modifier")
 		r.rw.WriteHeader(http.StatusInternalServerError)
 		return
 	}
diff --git a/pkg/middlewares/inflightreq/inflight_req.go b/pkg/middlewares/inflightreq/inflight_req.go
index fb235dd850..c34982befc 100644
--- a/pkg/middlewares/inflightreq/inflight_req.go
+++ b/pkg/middlewares/inflightreq/inflight_req.go
@@ -6,11 +6,12 @@ import (
 	"net/http"
 
 	"github.com/opentracing/opentracing-go/ext"
+	"github.com/rs/zerolog"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/middlewares"
 	"github.com/traefik/traefik/v2/pkg/tracing"
-	"github.com/vulcand/oxy/connlimit"
+	"github.com/vulcand/oxy/v2/connlimit"
 )
 
 const (
@@ -25,8 +26,10 @@ type inFlightReq struct {
 // New creates a max request middleware.
 // If no source criterion is provided in the config, it defaults to RequestHost.
 func New(ctx context.Context, next http.Handler, config dynamic.InFlightReq, name string) (http.Handler, error) {
-	ctxLog := log.With(ctx, log.Str(log.MiddlewareName, name), log.Str(log.MiddlewareType, typeName))
-	log.FromContext(ctxLog).Debug("Creating middleware")
+	logger := middlewares.GetLogger(ctx, name, typeName)
+	logger.Debug().Msg("Creating middleware")
+
+	ctxLog := logger.WithContext(ctx)
 
 	if config.SourceCriterion == nil ||
 		config.SourceCriterion.IPStrategy == nil &&
@@ -41,7 +44,9 @@ func New(ctx context.Context, next http.Handler, config dynamic.InFlightReq, nam
 		return nil, fmt.Errorf("error creating requests limiter: %w", err)
 	}
 
-	handler, err := connlimit.New(next, sourceMatcher, config.Amount)
+	handler, err := connlimit.New(next, sourceMatcher, config.Amount,
+		connlimit.Logger(logs.NewOxyWrapper(*logger)),
+		connlimit.Verbose(logger.GetLevel() == zerolog.TraceLevel))
 	if err != nil {
 		return nil, fmt.Errorf("error creating connection limit: %w", err)
 	}
diff --git a/pkg/middlewares/ipallowlist/ip_allowlist.go b/pkg/middlewares/ipallowlist/ip_allowlist.go
index d700a92793..b9ddaf94b9 100644
--- a/pkg/middlewares/ipallowlist/ip_allowlist.go
+++ b/pkg/middlewares/ipallowlist/ip_allowlist.go
@@ -7,9 +7,9 @@ import (
 	"net/http"
 
 	"github.com/opentracing/opentracing-go/ext"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
 	"github.com/traefik/traefik/v2/pkg/ip"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/middlewares"
 	"github.com/traefik/traefik/v2/pkg/tracing"
 )
@@ -28,8 +28,8 @@ type ipAllowLister struct {
 
 // New builds a new IPAllowLister given a list of CIDR-Strings to allow.
 func New(ctx context.Context, next http.Handler, config dynamic.IPAllowList, name string) (http.Handler, error) {
-	logger := log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName))
-	logger.Debug("Creating middleware")
+	logger := middlewares.GetLogger(ctx, name, typeName)
+	logger.Debug().Msg("Creating middleware")
 
 	if len(config.SourceRange) == 0 {
 		return nil, errors.New("sourceRange is empty, IPAllowLister not created")
@@ -45,7 +45,7 @@ func New(ctx context.Context, next http.Handler, config dynamic.IPAllowList, nam
 		return nil, err
 	}
 
-	logger.Debugf("Setting up IPAllowLister with sourceRange: %s", config.SourceRange)
+	logger.Debug().Msgf("Setting up IPAllowLister with sourceRange: %s", config.SourceRange)
 
 	return &ipAllowLister{
 		strategy:    strategy,
@@ -60,19 +60,19 @@ func (al *ipAllowLister) GetTracingInformation() (string, ext.SpanKindEnum) {
 }
 
 func (al *ipAllowLister) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
-	ctx := middlewares.GetLoggerCtx(req.Context(), al.name, typeName)
-	logger := log.FromContext(ctx)
+	logger := middlewares.GetLogger(req.Context(), al.name, typeName)
+	ctx := logger.WithContext(req.Context())
 
 	clientIP := al.strategy.GetIP(req)
 	err := al.allowLister.IsAuthorized(clientIP)
 	if err != nil {
 		msg := fmt.Sprintf("Rejecting IP %s: %v", clientIP, err)
-		logger.Debug(msg)
+		logger.Debug().Msg(msg)
 		tracing.SetErrorWithEvent(req, msg)
 		reject(ctx, rw)
 		return
 	}
-	logger.Debugf("Accepting IP %s", clientIP)
+	logger.Debug().Msgf("Accepting IP %s", clientIP)
 
 	al.next.ServeHTTP(rw, req)
 }
@@ -83,6 +83,6 @@ func reject(ctx context.Context, rw http.ResponseWriter) {
 	rw.WriteHeader(statusCode)
 	_, err := rw.Write([]byte(http.StatusText(statusCode)))
 	if err != nil {
-		log.FromContext(ctx).Error(err)
+		log.Ctx(ctx).Error().Err(err).Send()
 	}
 }
diff --git a/pkg/middlewares/metrics/metrics.go b/pkg/middlewares/metrics/metrics.go
index 4286da1654..b04a49c85d 100644
--- a/pkg/middlewares/metrics/metrics.go
+++ b/pkg/middlewares/metrics/metrics.go
@@ -10,7 +10,7 @@ import (
 
 	"github.com/containous/alice"
 	gokitmetrics "github.com/go-kit/kit/metrics"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/metrics"
 	"github.com/traefik/traefik/v2/pkg/middlewares"
 	"github.com/traefik/traefik/v2/pkg/middlewares/capture"
@@ -44,7 +44,7 @@ type metricsMiddleware struct {
 
 // NewEntryPointMiddleware creates a new metrics middleware for an Entrypoint.
 func NewEntryPointMiddleware(ctx context.Context, next http.Handler, registry metrics.Registry, entryPointName string) http.Handler {
-	log.FromContext(middlewares.GetLoggerCtx(ctx, nameEntrypoint, typeName)).Debug("Creating middleware")
+	middlewares.GetLogger(ctx, nameEntrypoint, typeName).Debug().Msg("Creating middleware")
 
 	return &metricsMiddleware{
 		next:                 next,
@@ -60,7 +60,7 @@ func NewEntryPointMiddleware(ctx context.Context, next http.Handler, registry me
 
 // NewRouterMiddleware creates a new metrics middleware for a Router.
 func NewRouterMiddleware(ctx context.Context, next http.Handler, registry metrics.Registry, routerName string, serviceName string) http.Handler {
-	log.FromContext(middlewares.GetLoggerCtx(ctx, nameRouter, typeName)).Debug("Creating middleware")
+	middlewares.GetLogger(ctx, nameRouter, typeName).Debug().Msg("Creating middleware")
 
 	return &metricsMiddleware{
 		next:                 next,
@@ -76,7 +76,7 @@ func NewRouterMiddleware(ctx context.Context, next http.Handler, registry metric
 
 // NewServiceMiddleware creates a new metrics middleware for a Service.
 func NewServiceMiddleware(ctx context.Context, next http.Handler, registry metrics.Registry, serviceName string) http.Handler {
-	log.FromContext(middlewares.GetLoggerCtx(ctx, nameService, typeName)).Debug("Creating middleware")
+	middlewares.GetLogger(ctx, nameService, typeName).Debug().Msg("Creating middleware")
 
 	return &metricsMiddleware{
 		next:                 next,
@@ -129,10 +129,12 @@ func (m *metricsMiddleware) ServeHTTP(rw http.ResponseWriter, req *http.Request)
 
 	capt, err := capture.FromContext(ctx)
 	if err != nil {
+		with := log.Ctx(ctx).With()
 		for i := 0; i < len(m.baseLabels); i += 2 {
-			ctx = log.With(ctx, log.Str(m.baseLabels[i], m.baseLabels[i+1]))
+			with = with.Str(m.baseLabels[i], m.baseLabels[i+1])
 		}
-		log.FromContext(ctx).WithError(err).Errorf("Could not get Capture")
+		logger := with.Logger()
+		logger.Error().Err(err).Msg("Could not get Capture")
 		return
 	}
 
@@ -223,7 +225,7 @@ func containsHeader(req *http.Request, name, value string) bool {
 //nolint:usestdlibvars
 func getMethod(r *http.Request) string {
 	if !utf8.ValidString(r.Method) {
-		log.WithoutContext().Warnf("Invalid HTTP method encoding: %s", r.Method)
+		log.Warn().Msgf("Invalid HTTP method encoding: %s", r.Method)
 		return "NON_UTF8_HTTP_METHOD"
 	}
 
diff --git a/pkg/middlewares/middleware.go b/pkg/middlewares/middleware.go
index d5689704b3..32aadf29a6 100644
--- a/pkg/middlewares/middleware.go
+++ b/pkg/middlewares/middleware.go
@@ -3,10 +3,17 @@ package middlewares
 import (
 	"context"
 
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog"
+	"github.com/rs/zerolog/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 )
 
-// GetLoggerCtx creates a logger context with the middleware fields.
-func GetLoggerCtx(ctx context.Context, middleware, middlewareType string) context.Context {
-	return log.With(ctx, log.Str(log.MiddlewareName, middleware), log.Str(log.MiddlewareType, middlewareType))
+// GetLogger creates a logger with the middleware fields.
+func GetLogger(ctx context.Context, middleware, middlewareType string) *zerolog.Logger {
+	logger := log.Ctx(ctx).With().
+		Str(logs.MiddlewareName, middleware).
+		Str(logs.MiddlewareType, middlewareType).
+		Logger()
+
+	return &logger
 }
diff --git a/pkg/middlewares/passtlsclientcert/pass_tls_client_cert.go b/pkg/middlewares/passtlsclientcert/pass_tls_client_cert.go
index cddf0ad328..3f0f804239 100644
--- a/pkg/middlewares/passtlsclientcert/pass_tls_client_cert.go
+++ b/pkg/middlewares/passtlsclientcert/pass_tls_client_cert.go
@@ -12,8 +12,8 @@ import (
 	"strings"
 
 	"github.com/opentracing/opentracing-go/ext"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/middlewares"
 	"github.com/traefik/traefik/v2/pkg/tracing"
 )
@@ -130,7 +130,7 @@ type passTLSClientCert struct {
 
 // New constructs a new PassTLSClientCert instance from supplied frontend header struct.
 func New(ctx context.Context, next http.Handler, config dynamic.PassTLSClientCert, name string) (http.Handler, error) {
-	log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware")
+	middlewares.GetLogger(ctx, name, typeName).Debug().Msg("Creating middleware")
 
 	return &passTLSClientCert{
 		next: next,
@@ -145,14 +145,14 @@ func (p *passTLSClientCert) GetTracingInformation() (string, ext.SpanKindEnum) {
 }
 
 func (p *passTLSClientCert) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
-	ctx := middlewares.GetLoggerCtx(req.Context(), p.name, typeName)
-	logger := log.FromContext(ctx)
+	logger := middlewares.GetLogger(req.Context(), p.name, typeName)
+	ctx := logger.WithContext(req.Context())
 
 	if p.pem {
 		if req.TLS != nil && len(req.TLS.PeerCertificates) > 0 {
 			req.Header.Set(xForwardedTLSClientCert, getCertificates(ctx, req.TLS.PeerCertificates))
 		} else {
-			logger.Warn("Tried to extract a certificate on a request without mutual TLS")
+			logger.Warn().Msg("Tried to extract a certificate on a request without mutual TLS")
 		}
 	}
 
@@ -161,7 +161,7 @@ func (p *passTLSClientCert) ServeHTTP(rw http.ResponseWriter, req *http.Request)
 			headerContent := p.getCertInfo(ctx, req.TLS.PeerCertificates)
 			req.Header.Set(xForwardedTLSClientCertInfo, url.QueryEscape(headerContent))
 		} else {
-			logger.Warn("Tried to extract a certificate on a request without mutual TLS")
+			logger.Warn().Msg("Tried to extract a certificate on a request without mutual TLS")
 		}
 	}
 
@@ -318,7 +318,7 @@ func writePart(ctx context.Context, content io.StringWriter, entry, prefix strin
 	if len(entry) > 0 {
 		_, err := content.WriteString(fmt.Sprintf("%s=%s%s", prefix, entry, subFieldSeparator))
 		if err != nil {
-			log.FromContext(ctx).Error(err)
+			log.Ctx(ctx).Error().Err(err).Send()
 		}
 	}
 }
@@ -347,7 +347,7 @@ func getCertificates(ctx context.Context, certs []*x509.Certificate) string {
 func extractCertificate(ctx context.Context, cert *x509.Certificate) string {
 	certPEM := pem.EncodeToMemory(&pem.Block{Type: "CERTIFICATE", Bytes: cert.Raw})
 	if certPEM == nil {
-		log.FromContext(ctx).Error("Cannot extract the certificate content")
+		log.Ctx(ctx).Error().Msg("Cannot extract the certificate content")
 		return ""
 	}
 
diff --git a/pkg/middlewares/ratelimiter/rate_limiter.go b/pkg/middlewares/ratelimiter/rate_limiter.go
index 403c97199a..303b20e8ec 100644
--- a/pkg/middlewares/ratelimiter/rate_limiter.go
+++ b/pkg/middlewares/ratelimiter/rate_limiter.go
@@ -10,11 +10,11 @@ import (
 
 	"github.com/mailgun/ttlmap"
 	"github.com/opentracing/opentracing-go/ext"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/middlewares"
 	"github.com/traefik/traefik/v2/pkg/tracing"
-	"github.com/vulcand/oxy/utils"
+	"github.com/vulcand/oxy/v2/utils"
 	"golang.org/x/time/rate"
 )
 
@@ -45,8 +45,10 @@ type rateLimiter struct {
 
 // New returns a rate limiter middleware.
 func New(ctx context.Context, next http.Handler, config dynamic.RateLimit, name string) (http.Handler, error) {
-	ctxLog := log.With(ctx, log.Str(log.MiddlewareName, name), log.Str(log.MiddlewareType, typeName))
-	log.FromContext(ctxLog).Debug("Creating middleware")
+	logger := middlewares.GetLogger(ctx, name, typeName)
+	logger.Debug().Msg("Creating middleware")
+
+	ctxLog := logger.WithContext(ctx)
 
 	if config.SourceCriterion == nil ||
 		config.SourceCriterion.IPStrategy == nil &&
@@ -122,19 +124,19 @@ func (rl *rateLimiter) GetTracingInformation() (string, ext.SpanKindEnum) {
 	return rl.name, tracing.SpanKindNoneEnum
 }
 
-func (rl *rateLimiter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-	ctx := middlewares.GetLoggerCtx(r.Context(), rl.name, typeName)
-	logger := log.FromContext(ctx)
+func (rl *rateLimiter) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
+	logger := middlewares.GetLogger(req.Context(), rl.name, typeName)
+	ctx := logger.WithContext(req.Context())
 
-	source, amount, err := rl.sourceMatcher.Extract(r)
+	source, amount, err := rl.sourceMatcher.Extract(req)
 	if err != nil {
-		logger.Errorf("could not extract source of request: %v", err)
-		http.Error(w, "could not extract source of request", http.StatusInternalServerError)
+		logger.Error().Err(err).Msg("Could not extract source of request")
+		http.Error(rw, "could not extract source of request", http.StatusInternalServerError)
 		return
 	}
 
 	if amount != 1 {
-		logger.Infof("ignoring token bucket amount > 1: %d", amount)
+		logger.Info().Msgf("ignoring token bucket amount > 1: %d", amount)
 	}
 
 	var bucket *rate.Limiter
@@ -148,8 +150,8 @@ func (rl *rateLimiter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	// because we want to update the expiryTime everytime we get the source,
 	// as the expiryTime is supposed to reflect the activity (or lack thereof) on that source.
 	if err := rl.buckets.Set(source, bucket, rl.ttl); err != nil {
-		logger.Errorf("could not insert/update bucket: %v", err)
-		http.Error(w, "could not insert/update bucket", http.StatusInternalServerError)
+		logger.Error().Err(err).Msg("Could not insert/update bucket")
+		http.Error(rw, "could not insert/update bucket", http.StatusInternalServerError)
 		return
 	}
 
@@ -159,19 +161,19 @@ func (rl *rateLimiter) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	// However, for now we take advantage of this behavior to provide the no-limit ratelimiter when config.Average is 0.
 	res := bucket.Reserve()
 	if !res.OK() {
-		http.Error(w, "No bursty traffic allowed", http.StatusTooManyRequests)
+		http.Error(rw, "No bursty traffic allowed", http.StatusTooManyRequests)
 		return
 	}
 
 	delay := res.Delay()
 	if delay > rl.maxDelay {
 		res.Cancel()
-		rl.serveDelayError(ctx, w, delay)
+		rl.serveDelayError(ctx, rw, delay)
 		return
 	}
 
 	time.Sleep(delay)
-	rl.next.ServeHTTP(w, r)
+	rl.next.ServeHTTP(rw, req)
 }
 
 func (rl *rateLimiter) serveDelayError(ctx context.Context, w http.ResponseWriter, delay time.Duration) {
@@ -180,6 +182,6 @@ func (rl *rateLimiter) serveDelayError(ctx context.Context, w http.ResponseWrite
 	w.WriteHeader(http.StatusTooManyRequests)
 
 	if _, err := w.Write([]byte(http.StatusText(http.StatusTooManyRequests))); err != nil {
-		log.FromContext(ctx).Errorf("could not serve 429: %v", err)
+		log.Ctx(ctx).Error().Err(err).Msg("Could not serve 429")
 	}
 }
diff --git a/pkg/middlewares/recovery/recovery.go b/pkg/middlewares/recovery/recovery.go
index 753a5801b1..90e0b36595 100644
--- a/pkg/middlewares/recovery/recovery.go
+++ b/pkg/middlewares/recovery/recovery.go
@@ -5,7 +5,6 @@ import (
 	"net/http"
 	"runtime"
 
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/middlewares"
 )
 
@@ -20,7 +19,7 @@ type recovery struct {
 
 // New creates recovery middleware.
 func New(ctx context.Context, next http.Handler) (http.Handler, error) {
-	log.FromContext(middlewares.GetLoggerCtx(ctx, middlewareName, typeName)).Debug("Creating middleware")
+	middlewares.GetLogger(ctx, middlewareName, typeName).Debug().Msg("Creating middleware")
 
 	return &recovery{
 		next: next,
@@ -32,19 +31,19 @@ func (re *recovery) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 	re.next.ServeHTTP(rw, req)
 }
 
-func recoverFunc(rw http.ResponseWriter, r *http.Request) {
+func recoverFunc(rw http.ResponseWriter, req *http.Request) {
 	if err := recover(); err != nil {
-		logger := log.FromContext(middlewares.GetLoggerCtx(r.Context(), middlewareName, typeName))
+		logger := middlewares.GetLogger(req.Context(), middlewareName, typeName)
 		if !shouldLogPanic(err) {
-			logger.Debugf("Request has been aborted [%s - %s]: %v", r.RemoteAddr, r.URL, err)
+			logger.Debug().Msgf("Request has been aborted [%s - %s]: %v", req.RemoteAddr, req.URL, err)
 			return
 		}
 
-		logger.Errorf("Recovered from panic in HTTP handler [%s - %s]: %+v", r.RemoteAddr, r.URL, err)
+		logger.Error().Msgf("Recovered from panic in HTTP handler [%s - %s]: %+v", req.RemoteAddr, req.URL, err)
 		const size = 64 << 10
 		buf := make([]byte, size)
 		buf = buf[:runtime.Stack(buf, false)]
-		logger.Errorf("Stack: %s", buf)
+		logger.Error().Msgf("Stack: %s", buf)
 
 		http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
 	}
diff --git a/pkg/middlewares/redirect/redirect.go b/pkg/middlewares/redirect/redirect.go
index e09611e377..a48053f8a9 100644
--- a/pkg/middlewares/redirect/redirect.go
+++ b/pkg/middlewares/redirect/redirect.go
@@ -7,7 +7,7 @@ import (
 
 	"github.com/opentracing/opentracing-go/ext"
 	"github.com/traefik/traefik/v2/pkg/tracing"
-	"github.com/vulcand/oxy/utils"
+	"github.com/vulcand/oxy/v2/utils"
 )
 
 const (
diff --git a/pkg/middlewares/redirect/redirect_regex.go b/pkg/middlewares/redirect/redirect_regex.go
index bc4ae3843b..62e5dc9866 100644
--- a/pkg/middlewares/redirect/redirect_regex.go
+++ b/pkg/middlewares/redirect/redirect_regex.go
@@ -6,7 +6,6 @@ import (
 	"strings"
 
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/middlewares"
 )
 
@@ -14,9 +13,9 @@ const typeRegexName = "RedirectRegex"
 
 // NewRedirectRegex creates a redirect middleware.
 func NewRedirectRegex(ctx context.Context, next http.Handler, conf dynamic.RedirectRegex, name string) (http.Handler, error) {
-	logger := log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeRegexName))
-	logger.Debug("Creating middleware")
-	logger.Debugf("Setting up redirection from %s to %s", conf.Regex, conf.Replacement)
+	logger := middlewares.GetLogger(ctx, name, typeRegexName)
+	logger.Debug().Msg("Creating middleware")
+	logger.Debug().Msgf("Setting up redirection from %s to %s", conf.Regex, conf.Replacement)
 
 	return newRedirect(next, conf.Regex, conf.Replacement, conf.Permanent, rawURL, name)
 }
diff --git a/pkg/middlewares/redirect/redirect_scheme.go b/pkg/middlewares/redirect/redirect_scheme.go
index 063d6c4bc8..b828ff287b 100644
--- a/pkg/middlewares/redirect/redirect_scheme.go
+++ b/pkg/middlewares/redirect/redirect_scheme.go
@@ -8,7 +8,6 @@ import (
 	"strings"
 
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/middlewares"
 )
 
@@ -20,9 +19,9 @@ const (
 
 // NewRedirectScheme creates a new RedirectScheme middleware.
 func NewRedirectScheme(ctx context.Context, next http.Handler, conf dynamic.RedirectScheme, name string) (http.Handler, error) {
-	logger := log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeSchemeName))
-	logger.Debug("Creating middleware")
-	logger.Debugf("Setting up redirection to %s %s", conf.Scheme, conf.Port)
+	logger := middlewares.GetLogger(ctx, name, typeSchemeName)
+	logger.Debug().Msg("Creating middleware")
+	logger.Debug().Msgf("Setting up redirection to %s %s", conf.Scheme, conf.Port)
 
 	if len(conf.Scheme) == 0 {
 		return nil, errors.New("you must provide a target scheme")
diff --git a/pkg/middlewares/replacepath/replace_path.go b/pkg/middlewares/replacepath/replace_path.go
index d730a931ce..e193610564 100644
--- a/pkg/middlewares/replacepath/replace_path.go
+++ b/pkg/middlewares/replacepath/replace_path.go
@@ -7,7 +7,6 @@ import (
 
 	"github.com/opentracing/opentracing-go/ext"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/middlewares"
 	"github.com/traefik/traefik/v2/pkg/tracing"
 )
@@ -27,7 +26,7 @@ type replacePath struct {
 
 // New creates a new replace path middleware.
 func New(ctx context.Context, next http.Handler, config dynamic.ReplacePath, name string) (http.Handler, error) {
-	log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware")
+	middlewares.GetLogger(ctx, name, typeName).Debug().Msg("Creating middleware")
 
 	return &replacePath{
 		next: next,
@@ -52,7 +51,7 @@ func (r *replacePath) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 	var err error
 	req.URL.Path, err = url.PathUnescape(req.URL.RawPath)
 	if err != nil {
-		log.FromContext(middlewares.GetLoggerCtx(context.Background(), r.name, typeName)).Error(err)
+		middlewares.GetLogger(context.Background(), r.name, typeName).Error().Err(err).Send()
 		http.Error(rw, err.Error(), http.StatusInternalServerError)
 		return
 	}
diff --git a/pkg/middlewares/replacepathregex/replace_path_regex.go b/pkg/middlewares/replacepathregex/replace_path_regex.go
index fea6fa2dec..2f2621b8c6 100644
--- a/pkg/middlewares/replacepathregex/replace_path_regex.go
+++ b/pkg/middlewares/replacepathregex/replace_path_regex.go
@@ -10,7 +10,6 @@ import (
 
 	"github.com/opentracing/opentracing-go/ext"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/middlewares"
 	"github.com/traefik/traefik/v2/pkg/middlewares/replacepath"
 	"github.com/traefik/traefik/v2/pkg/tracing"
@@ -28,7 +27,7 @@ type replacePathRegex struct {
 
 // New creates a new replace path regex middleware.
 func New(ctx context.Context, next http.Handler, config dynamic.ReplacePathRegex, name string) (http.Handler, error) {
-	log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware")
+	middlewares.GetLogger(ctx, name, typeName).Debug().Msg("Creating middleware")
 
 	exp, err := regexp.Compile(strings.TrimSpace(config.Regex))
 	if err != nil {
@@ -63,7 +62,7 @@ func (rp *replacePathRegex) ServeHTTP(rw http.ResponseWriter, req *http.Request)
 		var err error
 		req.URL.Path, err = url.PathUnescape(req.URL.RawPath)
 		if err != nil {
-			log.FromContext(middlewares.GetLoggerCtx(context.Background(), rp.name, typeName)).Error(err)
+			middlewares.GetLogger(context.Background(), rp.name, typeName).Error().Err(err).Send()
 			http.Error(rw, err.Error(), http.StatusInternalServerError)
 			return
 		}
diff --git a/pkg/middlewares/requestdecorator/hostresolver.go b/pkg/middlewares/requestdecorator/hostresolver.go
index 40eee21f70..d600360a7c 100644
--- a/pkg/middlewares/requestdecorator/hostresolver.go
+++ b/pkg/middlewares/requestdecorator/hostresolver.go
@@ -10,7 +10,7 @@ import (
 
 	"github.com/miekg/dns"
 	"github.com/patrickmn/go-cache"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 )
 
 type cnameResolv struct {
@@ -46,12 +46,12 @@ func (hr *Resolver) CNAMEFlatten(ctx context.Context, host string) string {
 		return value.(string)
 	}
 
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 	cacheDuration := 0 * time.Second
 	for depth := 0; depth < hr.ResolvDepth; depth++ {
 		resolv, err := cnameResolve(ctx, request, hr.ResolvConfig)
 		if err != nil {
-			logger.Error(err)
+			logger.Error().Err(err).Send()
 			break
 		}
 		if resolv == nil {
@@ -66,7 +66,7 @@ func (hr *Resolver) CNAMEFlatten(ctx context.Context, host string) string {
 	}
 
 	if err := hr.cache.Add(host, result, cacheDuration); err != nil {
-		logger.Error(err)
+		logger.Error().Err(err).Send()
 	}
 
 	return result
@@ -88,7 +88,7 @@ func cnameResolve(ctx context.Context, host, resolvPath string) (*cnameResolv, e
 	for _, server := range config.Servers {
 		tempRecord, err := getRecord(client, m, server, config.Port)
 		if err != nil {
-			log.FromContext(ctx).Errorf("Failed to resolve host %s: %v", host, err)
+			log.Ctx(ctx).Error().Err(err).Msgf("Failed to resolve host %s", host)
 			continue
 		}
 		result = append(result, tempRecord)
diff --git a/pkg/middlewares/retry/retry.go b/pkg/middlewares/retry/retry.go
index a05ced9706..b9f3b6af78 100644
--- a/pkg/middlewares/retry/retry.go
+++ b/pkg/middlewares/retry/retry.go
@@ -14,7 +14,6 @@ import (
 	"github.com/cenkalti/backoff/v4"
 	"github.com/opentracing/opentracing-go/ext"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/middlewares"
 	"github.com/traefik/traefik/v2/pkg/tracing"
 )
@@ -46,7 +45,7 @@ type retry struct {
 
 // New returns a new retry middleware.
 func New(ctx context.Context, next http.Handler, config dynamic.Retry, listener Listener, name string) (http.Handler, error) {
-	log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware")
+	middlewares.GetLogger(ctx, name, typeName).Debug().Msg("Creating middleware")
 
 	if config.Attempts <= 0 {
 		return nil, fmt.Errorf("incorrect (or empty) value for attempt (%d)", config.Attempts)
@@ -106,19 +105,19 @@ func (r *retry) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 		return fmt.Errorf("attempt %d failed", attempts-1)
 	}
 
+	logger := middlewares.GetLogger(req.Context(), r.name, typeName)
+
 	backOff := backoff.WithContext(r.newBackOff(), req.Context())
 
 	notify := func(err error, d time.Duration) {
-		log.FromContext(middlewares.GetLoggerCtx(req.Context(), r.name, typeName)).
-			Debugf("New attempt %d for request: %v", attempts, req.URL)
+		logger.Debug().Msgf("New attempt %d for request: %v", attempts, req.URL)
 
 		r.listener.Retried(req, attempts)
 	}
 
 	err := backoff.RetryNotify(operation, backOff, notify)
 	if err != nil {
-		log.FromContext(middlewares.GetLoggerCtx(req.Context(), r.name, typeName)).
-			Debugf("Final retry attempt failed: %v", err.Error())
+		logger.Debug().Err(err).Msg("Final retry attempt failed")
 	}
 }
 
diff --git a/pkg/middlewares/snicheck/snicheck.go b/pkg/middlewares/snicheck/snicheck.go
index e18b605cbf..27f8148d12 100644
--- a/pkg/middlewares/snicheck/snicheck.go
+++ b/pkg/middlewares/snicheck/snicheck.go
@@ -5,7 +5,7 @@ import (
 	"net/http"
 	"strings"
 
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/middlewares/requestdecorator"
 	traefiktls "github.com/traefik/traefik/v2/pkg/tls"
 )
@@ -36,11 +36,11 @@ func (s SNICheck) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 		tlsOptionSNI := findTLSOptionName(s.tlsOptionsForHost, serverName, false)
 
 		if tlsOptionHeader != tlsOptionSNI {
-			log.WithoutContext().
-				WithField("host", host).
-				WithField("req.Host", req.Host).
-				WithField("req.TLS.ServerName", req.TLS.ServerName).
-				Debugf("TLS options difference: SNI:%s, Header:%s", tlsOptionSNI, tlsOptionHeader)
+			log.Debug().
+				Str("host", host).
+				Str("req.Host", req.Host).
+				Str("req.TLS.ServerName", req.TLS.ServerName).
+				Msgf("TLS options difference: SNI:%s, Header:%s", tlsOptionSNI, tlsOptionHeader)
 			http.Error(rw, http.StatusText(http.StatusMisdirectedRequest), http.StatusMisdirectedRequest)
 			return
 		}
diff --git a/pkg/middlewares/stripprefix/strip_prefix.go b/pkg/middlewares/stripprefix/strip_prefix.go
index 406b13ab24..38caa7e3a5 100644
--- a/pkg/middlewares/stripprefix/strip_prefix.go
+++ b/pkg/middlewares/stripprefix/strip_prefix.go
@@ -7,7 +7,6 @@ import (
 
 	"github.com/opentracing/opentracing-go/ext"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/middlewares"
 	"github.com/traefik/traefik/v2/pkg/tracing"
 )
@@ -28,7 +27,7 @@ type stripPrefix struct {
 
 // New creates a new strip prefix middleware.
 func New(ctx context.Context, next http.Handler, config dynamic.StripPrefix, name string) (http.Handler, error) {
-	log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware")
+	middlewares.GetLogger(ctx, name, typeName).Debug().Msg("Creating middleware")
 	return &stripPrefix{
 		prefixes:   config.Prefixes,
 		forceSlash: config.ForceSlash,
diff --git a/pkg/middlewares/stripprefixregex/strip_prefix_regex.go b/pkg/middlewares/stripprefixregex/strip_prefix_regex.go
index ee0722ad32..debeba16dd 100644
--- a/pkg/middlewares/stripprefixregex/strip_prefix_regex.go
+++ b/pkg/middlewares/stripprefixregex/strip_prefix_regex.go
@@ -8,7 +8,6 @@ import (
 
 	"github.com/opentracing/opentracing-go/ext"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/middlewares"
 	"github.com/traefik/traefik/v2/pkg/middlewares/stripprefix"
 	"github.com/traefik/traefik/v2/pkg/tracing"
@@ -27,7 +26,7 @@ type stripPrefixRegex struct {
 
 // New builds a new StripPrefixRegex middleware.
 func New(ctx context.Context, next http.Handler, config dynamic.StripPrefixRegex, name string) (http.Handler, error) {
-	log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName)).Debug("Creating middleware")
+	middlewares.GetLogger(ctx, name, typeName).Debug().Msg("Creating middleware")
 
 	stripPrefix := stripPrefixRegex{
 		next: next,
diff --git a/pkg/middlewares/tcp/inflightconn/inflight_conn.go b/pkg/middlewares/tcp/inflightconn/inflight_conn.go
index ddbb4bdaba..6213d4e426 100644
--- a/pkg/middlewares/tcp/inflightconn/inflight_conn.go
+++ b/pkg/middlewares/tcp/inflightconn/inflight_conn.go
@@ -7,7 +7,6 @@ import (
 	"sync"
 
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/middlewares"
 	"github.com/traefik/traefik/v2/pkg/tcp"
 )
@@ -26,8 +25,8 @@ type inFlightConn struct {
 // New creates a max connections middleware.
 // The connections are identified and grouped by remote IP.
 func New(ctx context.Context, next tcp.Handler, config dynamic.TCPInFlightConn, name string) (tcp.Handler, error) {
-	logger := log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName))
-	logger.Debug("Creating middleware")
+	logger := middlewares.GetLogger(ctx, name, typeName)
+	logger.Debug().Msg("Creating middleware")
 
 	return &inFlightConn{
 		name:           name,
@@ -39,18 +38,17 @@ func New(ctx context.Context, next tcp.Handler, config dynamic.TCPInFlightConn,
 
 // ServeTCP serves the given TCP connection.
 func (i *inFlightConn) ServeTCP(conn tcp.WriteCloser) {
-	ctx := middlewares.GetLoggerCtx(context.Background(), i.name, typeName)
-	logger := log.FromContext(ctx)
+	logger := middlewares.GetLogger(context.Background(), i.name, typeName)
 
 	ip, _, err := net.SplitHostPort(conn.RemoteAddr().String())
 	if err != nil {
-		logger.Errorf("Cannot parse IP from remote addr: %v", err)
+		logger.Error().Err(err).Msg("Cannot parse IP from remote addr")
 		conn.Close()
 		return
 	}
 
 	if err = i.increment(ip); err != nil {
-		logger.Errorf("Connection rejected: %v", err)
+		logger.Error().Err(err).Msg("Connection rejected")
 		conn.Close()
 		return
 	}
diff --git a/pkg/middlewares/tcp/ipallowlist/ip_allowlist.go b/pkg/middlewares/tcp/ipallowlist/ip_allowlist.go
index dd5d14d432..e2a513cf05 100644
--- a/pkg/middlewares/tcp/ipallowlist/ip_allowlist.go
+++ b/pkg/middlewares/tcp/ipallowlist/ip_allowlist.go
@@ -7,7 +7,6 @@ import (
 
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
 	"github.com/traefik/traefik/v2/pkg/ip"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/middlewares"
 	"github.com/traefik/traefik/v2/pkg/tcp"
 )
@@ -25,8 +24,8 @@ type ipAllowLister struct {
 
 // New builds a new TCP IPAllowLister given a list of CIDR-Strings to allow.
 func New(ctx context.Context, next tcp.Handler, config dynamic.TCPIPAllowList, name string) (tcp.Handler, error) {
-	logger := log.FromContext(middlewares.GetLoggerCtx(ctx, name, typeName))
-	logger.Debug("Creating middleware")
+	logger := middlewares.GetLogger(ctx, name, typeName)
+	logger.Debug().Msg("Creating middleware")
 
 	if len(config.SourceRange) == 0 {
 		return nil, errors.New("sourceRange is empty, IPAllowLister not created")
@@ -37,7 +36,7 @@ func New(ctx context.Context, next tcp.Handler, config dynamic.TCPIPAllowList, n
 		return nil, fmt.Errorf("cannot parse CIDRs %s: %w", config.SourceRange, err)
 	}
 
-	logger.Debugf("Setting up IPAllowLister with sourceRange: %s", config.SourceRange)
+	logger.Debug().Msgf("Setting up IPAllowLister with sourceRange: %s", config.SourceRange)
 
 	return &ipAllowLister{
 		allowLister: checker,
@@ -47,19 +46,18 @@ func New(ctx context.Context, next tcp.Handler, config dynamic.TCPIPAllowList, n
 }
 
 func (al *ipAllowLister) ServeTCP(conn tcp.WriteCloser) {
-	ctx := middlewares.GetLoggerCtx(context.Background(), al.name, typeName)
-	logger := log.FromContext(ctx)
+	logger := middlewares.GetLogger(context.Background(), al.name, typeName)
 
 	addr := conn.RemoteAddr().String()
 
 	err := al.allowLister.IsAuthorized(addr)
 	if err != nil {
-		logger.Errorf("Connection from %s rejected: %v", addr, err)
+		logger.Error().Err(err).Msgf("Connection from %s rejected", addr)
 		conn.Close()
 		return
 	}
 
-	logger.Debugf("Connection from %s accepted", addr)
+	logger.Debug().Msgf("Connection from %s accepted", addr)
 
 	al.next.ServeTCP(conn)
 }
diff --git a/pkg/middlewares/tracing/entrypoint.go b/pkg/middlewares/tracing/entrypoint.go
index 71a1da6d3f..d665823a22 100644
--- a/pkg/middlewares/tracing/entrypoint.go
+++ b/pkg/middlewares/tracing/entrypoint.go
@@ -7,7 +7,6 @@ import (
 	"github.com/containous/alice"
 	"github.com/opentracing/opentracing-go"
 	"github.com/opentracing/opentracing-go/ext"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/middlewares"
 	"github.com/traefik/traefik/v2/pkg/tracing"
 )
@@ -18,7 +17,7 @@ const (
 
 // NewEntryPoint creates a new middleware that the incoming request.
 func NewEntryPoint(ctx context.Context, t *tracing.Tracing, entryPointName string, next http.Handler) http.Handler {
-	log.FromContext(middlewares.GetLoggerCtx(ctx, "tracing", entryPointTypeName)).Debug("Creating middleware")
+	middlewares.GetLogger(ctx, "tracing", entryPointTypeName).Debug().Msg("Creating middleware")
 
 	return &entryPointMiddleware{
 		entryPoint: entryPointName,
@@ -36,8 +35,8 @@ type entryPointMiddleware struct {
 func (e *entryPointMiddleware) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 	spanCtx, err := e.Extract(opentracing.HTTPHeaders, opentracing.HTTPHeadersCarrier(req.Header))
 	if err != nil {
-		log.FromContext(middlewares.GetLoggerCtx(req.Context(), "tracing", entryPointTypeName)).
-			Debugf("Failed to extract the context: %v", err)
+		middlewares.GetLogger(req.Context(), "tracing", entryPointTypeName).
+			Debug().Err(err).Msg("Failed to extract the context")
 	}
 
 	span, req, finish := e.StartSpanf(req, ext.SpanKindRPCServerEnum, "EntryPoint", []string{e.entryPoint, req.Host}, " ", ext.RPCServerOption(spanCtx))
diff --git a/pkg/middlewares/tracing/forwarder.go b/pkg/middlewares/tracing/forwarder.go
index b57f62330c..2ebf5fe779 100644
--- a/pkg/middlewares/tracing/forwarder.go
+++ b/pkg/middlewares/tracing/forwarder.go
@@ -5,7 +5,7 @@ import (
 	"net/http"
 
 	"github.com/opentracing/opentracing-go/ext"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/middlewares"
 	"github.com/traefik/traefik/v2/pkg/tracing"
 )
@@ -22,8 +22,8 @@ type forwarderMiddleware struct {
 
 // NewForwarder creates a new forwarder middleware that traces the outgoing request.
 func NewForwarder(ctx context.Context, router, service string, next http.Handler) http.Handler {
-	log.FromContext(middlewares.GetLoggerCtx(ctx, "tracing", forwarderTypeName)).
-		Debugf("Added outgoing tracing middleware %s", service)
+	middlewares.GetLogger(ctx, "tracing", forwarderTypeName).
+		Debug().Str(logs.ServiceName, service).Msg("Added outgoing tracing middleware")
 
 	return &forwarderMiddleware{
 		router:  router,
diff --git a/pkg/middlewares/tracing/wrapper.go b/pkg/middlewares/tracing/wrapper.go
index 9386bd14ef..e13209d548 100644
--- a/pkg/middlewares/tracing/wrapper.go
+++ b/pkg/middlewares/tracing/wrapper.go
@@ -6,7 +6,8 @@ import (
 
 	"github.com/containous/alice"
 	"github.com/opentracing/opentracing-go/ext"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/tracing"
 )
 
@@ -28,7 +29,7 @@ func Wrap(ctx context.Context, constructor alice.Constructor) alice.Constructor
 
 		if traceableHandler, ok := handler.(Traceable); ok {
 			name, spanKind := traceableHandler.GetTracingInformation()
-			log.FromContext(ctx).WithField(log.MiddlewareName, name).Debug("Adding tracing to middleware")
+			log.Ctx(ctx).Debug().Str(logs.MiddlewareName, name).Msg("Adding tracing to middleware")
 			return NewWrapper(handler, name, spanKind), nil
 		}
 		return handler, nil
diff --git a/pkg/muxer/http/mux.go b/pkg/muxer/http/mux.go
index fefab67a92..16f6e69fe7 100644
--- a/pkg/muxer/http/mux.go
+++ b/pkg/muxer/http/mux.go
@@ -7,8 +7,8 @@ import (
 	"unicode/utf8"
 
 	"github.com/gorilla/mux"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/ip"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/middlewares/requestdecorator"
 	"github.com/traefik/traefik/v2/pkg/rules"
 	"github.com/vulcand/predicate"
@@ -143,7 +143,7 @@ func host(route *mux.Route, hosts ...string) error {
 		if len(reqHost) == 0 {
 			// If the request is an HTTP/1.0 request, then a Host may not be defined.
 			if req.ProtoAtLeast(1, 1) {
-				log.FromContext(req.Context()).Warnf("Could not retrieve CanonizedHost, rejecting %s", req.Host)
+				log.Ctx(req.Context()).Warn().Msgf("Could not retrieve CanonizedHost, rejecting %s", req.Host)
 			}
 
 			return false
@@ -155,7 +155,7 @@ func host(route *mux.Route, hosts ...string) error {
 				if strings.EqualFold(reqHost, host) || strings.EqualFold(flatH, host) {
 					return true
 				}
-				log.FromContext(req.Context()).Debugf("CNAMEFlattening: request %s which resolved to %s, is not matched to route %s", reqHost, flatH, host)
+				log.Ctx(req.Context()).Debug().Msgf("CNAMEFlattening: request %s which resolved to %s, is not matched to route %s", reqHost, flatH, host)
 			}
 			return false
 		}
@@ -197,7 +197,7 @@ func clientIP(route *mux.Route, clientIPs ...string) error {
 	route.MatcherFunc(func(req *http.Request, _ *mux.RouteMatch) bool {
 		ok, err := checker.Contains(strategy.GetIP(req))
 		if err != nil {
-			log.FromContext(req.Context()).Warnf("\"ClientIP\" matcher: could not match remote address : %w", err)
+			log.Ctx(req.Context()).Warn().Err(err).Msg("\"ClientIP\" matcher: could not match remote address")
 			return false
 		}
 
diff --git a/pkg/muxer/tcp/mux.go b/pkg/muxer/tcp/mux.go
index e21dfc75de..6d74211192 100644
--- a/pkg/muxer/tcp/mux.go
+++ b/pkg/muxer/tcp/mux.go
@@ -11,8 +11,8 @@ import (
 	"strings"
 
 	"github.com/go-acme/lego/v4/challenge/tlsalpn01"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/ip"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/rules"
 	"github.com/traefik/traefik/v2/pkg/tcp"
 	"github.com/traefik/traefik/v2/pkg/types"
@@ -246,7 +246,7 @@ type matchersTree struct {
 func (m *matchersTree) match(meta ConnData) bool {
 	if m == nil {
 		// This should never happen as it should have been detected during parsing.
-		log.WithoutContext().Warnf("Rule matcher is nil")
+		log.Warn().Msg("Rule matcher is nil")
 		return false
 	}
 
@@ -261,7 +261,7 @@ func (m *matchersTree) match(meta ConnData) bool {
 		return m.left.match(meta) && m.right.match(meta)
 	default:
 		// This should never happen as it should have been detected during parsing.
-		log.WithoutContext().Warnf("Invalid rule operator %s", m.operator)
+		log.Warn().Msgf("Invalid rule operator %s", m.operator)
 		return false
 	}
 }
@@ -279,7 +279,7 @@ func clientIP(tree *matchersTree, clientIPs ...string) error {
 
 		ok, err := checker.Contains(meta.remoteIP)
 		if err != nil {
-			log.WithoutContext().Warnf("\"ClientIP\" matcher: could not match remote address: %v", err)
+			log.Warn().Err(err).Msg("\"ClientIP\" matcher: could not match remote address")
 			return false
 		}
 		return ok
diff --git a/pkg/plugins/builder.go b/pkg/plugins/builder.go
index cf99b0e6b9..aa3d4c9829 100644
--- a/pkg/plugins/builder.go
+++ b/pkg/plugins/builder.go
@@ -6,8 +6,9 @@ import (
 	"net/http"
 	"os"
 
-	"github.com/sirupsen/logrus"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog"
+	"github.com/rs/zerolog/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/yaegi/interp"
 	"github.com/traefik/yaegi/stdlib"
 )
@@ -35,12 +36,13 @@ func NewBuilder(client *Client, plugins map[string]Descriptor, localPlugins map[
 			return nil, fmt.Errorf("%s: failed to read manifest: %w", desc.ModuleName, err)
 		}
 
-		logger := log.WithoutContext().WithFields(logrus.Fields{"plugin": "plugin-" + pName, "module": desc.ModuleName})
+		logger := log.With().Str("plugin", "plugin-"+pName).Str("module", desc.ModuleName).Logger()
+
 		i := interp.New(interp.Options{
 			GoPath: client.GoPath(),
 			Env:    os.Environ(),
-			Stdout: logger.WriterLevel(logrus.DebugLevel),
-			Stderr: logger.WriterLevel(logrus.ErrorLevel),
+			Stdout: logs.NoLevel(logger, zerolog.DebugLevel),
+			Stderr: logs.NoLevel(logger, zerolog.ErrorLevel),
 		})
 
 		err = i.Use(stdlib.Symbols)
@@ -83,12 +85,13 @@ func NewBuilder(client *Client, plugins map[string]Descriptor, localPlugins map[
 			return nil, fmt.Errorf("%s: failed to read manifest: %w", desc.ModuleName, err)
 		}
 
-		logger := log.WithoutContext().WithFields(logrus.Fields{"plugin": "plugin-" + pName, "module": desc.ModuleName})
+		logger := log.With().Str("plugin", "plugin-"+pName).Str("module", desc.ModuleName).Logger()
+
 		i := interp.New(interp.Options{
 			GoPath: localGoPath,
 			Env:    os.Environ(),
-			Stdout: logger.WriterLevel(logrus.DebugLevel),
-			Stderr: logger.WriterLevel(logrus.ErrorLevel),
+			Stdout: logs.NoLevel(logger, zerolog.DebugLevel),
+			Stderr: logs.NoLevel(logger, zerolog.ErrorLevel),
 		})
 
 		err = i.Use(stdlib.Symbols)
diff --git a/pkg/plugins/plugins.go b/pkg/plugins/plugins.go
index fdfb9fbcea..1ebaf9cfa0 100644
--- a/pkg/plugins/plugins.go
+++ b/pkg/plugins/plugins.go
@@ -7,7 +7,7 @@ import (
 	"strings"
 
 	"github.com/hashicorp/go-multierror"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 )
 
 const localGoPath = "./plugins-local/"
@@ -27,7 +27,7 @@ func SetupRemotePlugins(client *Client, plugins map[string]Descriptor) error {
 	ctx := context.Background()
 
 	for pAlias, desc := range plugins {
-		log.FromContext(ctx).Debugf("loading of plugin: %s: %s@%s", pAlias, desc.ModuleName, desc.Version)
+		log.Ctx(ctx).Debug().Msgf("Loading of plugin: %s: %s@%s", pAlias, desc.ModuleName, desc.Version)
 
 		hash, err := client.Download(ctx, desc.ModuleName, desc.Version)
 		if err != nil {
diff --git a/pkg/plugins/providers.go b/pkg/plugins/providers.go
index abec8d1b82..ea024c8364 100644
--- a/pkg/plugins/providers.go
+++ b/pkg/plugins/providers.go
@@ -9,8 +9,9 @@ import (
 	"strings"
 
 	"github.com/mitchellh/mapstructure"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/provider"
 	"github.com/traefik/traefik/v2/pkg/safe"
 	"github.com/traefik/yaegi/interp"
@@ -158,21 +159,21 @@ func (p *Provider) Init() error {
 func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error {
 	defer func() {
 		if err := recover(); err != nil {
-			log.WithoutContext().WithField(log.ProviderName, p.name).Errorf("panic inside the plugin %v", err)
+			log.Error().Str(logs.ProviderName, p.name).Msgf("Panic inside the plugin %v", err)
 		}
 	}()
 
 	cfgChan := make(chan json.Marshaler)
 
 	pool.GoCtx(func(ctx context.Context) {
-		logger := log.FromContext(log.With(ctx, log.Str(log.ProviderName, p.name)))
+		logger := log.Ctx(ctx).With().Str(logs.ProviderName, p.name).Logger()
 
 		for {
 			select {
 			case <-ctx.Done():
 				err := p.pp.Stop()
 				if err != nil {
-					logger.Errorf("failed to stop the provider: %v", err)
+					logger.Error().Err(err).Msg("Failed to stop the provider")
 				}
 
 				return
@@ -180,14 +181,14 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 			case cfgPg := <-cfgChan:
 				marshalJSON, err := cfgPg.MarshalJSON()
 				if err != nil {
-					logger.Errorf("failed to marshal configuration: %v", err)
+					logger.Error().Err(err).Msg("Failed to marshal configuration")
 					continue
 				}
 
 				cfg := &dynamic.Configuration{}
 				err = json.Unmarshal(marshalJSON, cfg)
 				if err != nil {
-					logger.Errorf("failed to unmarshal configuration: %v", err)
+					logger.Error().Err(err).Msg("Failed to unmarshal configuration")
 					continue
 				}
 
diff --git a/pkg/provider/acme/account.go b/pkg/provider/acme/account.go
index 4f12e14a28..6bac6c186d 100644
--- a/pkg/provider/acme/account.go
+++ b/pkg/provider/acme/account.go
@@ -9,7 +9,8 @@ import (
 
 	"github.com/go-acme/lego/v4/certcrypto"
 	"github.com/go-acme/lego/v4/registration"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 )
 
 // Account is used to store lets encrypt registration info.
@@ -56,8 +57,8 @@ func (a *Account) GetRegistration() *registration.Resource {
 func (a *Account) GetPrivateKey() crypto.PrivateKey {
 	privateKey, err := x509.ParsePKCS1PrivateKey(a.PrivateKey)
 	if err != nil {
-		log.WithoutContext().WithField(log.ProviderName, "acme").
-			Errorf("Cannot unmarshal private key %+v: %v", a.PrivateKey, err)
+		log.Error().Str(logs.ProviderName, "acme").
+			Err(err).Msgf("Cannot unmarshal private key %+v", a.PrivateKey)
 		return nil
 	}
 
@@ -66,7 +67,7 @@ func (a *Account) GetPrivateKey() crypto.PrivateKey {
 
 // GetKeyType used to determine which algo to used.
 func GetKeyType(ctx context.Context, value string) certcrypto.KeyType {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
 	switch value {
 	case "EC256":
@@ -80,10 +81,10 @@ func GetKeyType(ctx context.Context, value string) certcrypto.KeyType {
 	case "RSA8192":
 		return certcrypto.RSA8192
 	case "":
-		logger.Infof("The key type is empty. Use default key type %v.", certcrypto.RSA4096)
+		logger.Info().Msgf("The key type is empty. Use default key type %v.", certcrypto.RSA4096)
 		return certcrypto.RSA4096
 	default:
-		logger.Infof("Unable to determine the key type value %q: falling back on %v.", value, certcrypto.RSA4096)
+		logger.Info().Msgf("Unable to determine the key type value %q: falling back on %v.", value, certcrypto.RSA4096)
 		return certcrypto.RSA4096
 	}
 }
diff --git a/pkg/provider/acme/challenge_http.go b/pkg/provider/acme/challenge_http.go
index a6eaf32d92..80d6a6c67c 100644
--- a/pkg/provider/acme/challenge_http.go
+++ b/pkg/provider/acme/challenge_http.go
@@ -13,7 +13,8 @@ import (
 
 	"github.com/cenkalti/backoff/v4"
 	"github.com/go-acme/lego/v4/challenge/http01"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/safe"
 )
 
@@ -70,12 +71,11 @@ func (c *ChallengeHTTP) Timeout() (timeout, interval time.Duration) {
 }
 
 func (c *ChallengeHTTP) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
-	ctx := log.With(req.Context(), log.Str(log.ProviderName, "acme"))
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(req.Context()).With().Str(logs.ProviderName, "acme").Logger()
 
 	token, err := getPathParam(req.URL)
 	if err != nil {
-		logger.Errorf("Unable to get token: %v.", err)
+		logger.Error().Err(err).Msg("Unable to get token")
 		rw.WriteHeader(http.StatusNotFound)
 		return
 	}
@@ -83,16 +83,16 @@ func (c *ChallengeHTTP) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 	if token != "" {
 		domain, _, err := net.SplitHostPort(req.Host)
 		if err != nil {
-			logger.Debugf("Unable to split host and port: %v. Fallback to request host.", err)
+			logger.Debug().Err(err).Msg("Unable to split host and port. Fallback to request host.")
 			domain = req.Host
 		}
 
-		tokenValue := c.getTokenValue(ctx, token, domain)
+		tokenValue := c.getTokenValue(logger.WithContext(req.Context()), token, domain)
 		if len(tokenValue) > 0 {
 			rw.WriteHeader(http.StatusOK)
 			_, err = rw.Write(tokenValue)
 			if err != nil {
-				logger.Errorf("Unable to write token: %v", err)
+				logger.Error().Err(err).Msg("Unable to write token")
 			}
 			return
 		}
@@ -102,8 +102,8 @@ func (c *ChallengeHTTP) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 }
 
 func (c *ChallengeHTTP) getTokenValue(ctx context.Context, token, domain string) []byte {
-	logger := log.FromContext(ctx)
-	logger.Debugf("Retrieving the ACME challenge for %s (token %q)...", domain, token)
+	logger := log.Ctx(ctx)
+	logger.Debug().Msgf("Retrieving the ACME challenge for %s (token %q)...", domain, token)
 
 	var result []byte
 
@@ -125,14 +125,14 @@ func (c *ChallengeHTTP) getTokenValue(ctx context.Context, token, domain string)
 	}
 
 	notify := func(err error, time time.Duration) {
-		logger.Errorf("Error getting challenge for token retrying in %s", time)
+		logger.Error().Msgf("Error getting challenge for token retrying in %s", time)
 	}
 
 	ebo := backoff.NewExponentialBackOff()
 	ebo.MaxElapsedTime = 60 * time.Second
 	err := backoff.RetryNotify(safe.OperationWithRecover(operation), ebo, notify)
 	if err != nil {
-		logger.Errorf("Cannot retrieve the ACME challenge for %s (token %q): %v", domain, token, err)
+		logger.Error().Err(err).Msgf("Cannot retrieve the ACME challenge for %s (token %q)", domain, token)
 		return []byte{}
 	}
 
diff --git a/pkg/provider/acme/challenge_tls.go b/pkg/provider/acme/challenge_tls.go
index 6249bc52ed..a53f83f2b5 100644
--- a/pkg/provider/acme/challenge_tls.go
+++ b/pkg/provider/acme/challenge_tls.go
@@ -6,8 +6,9 @@ import (
 	"time"
 
 	"github.com/go-acme/lego/v4/challenge/tlsalpn01"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/safe"
 	traefiktls "github.com/traefik/traefik/v2/pkg/tls"
 	"github.com/traefik/traefik/v2/pkg/types"
@@ -36,8 +37,8 @@ func NewChallengeTLSALPN() *ChallengeTLSALPN {
 
 // Present presents a challenge to obtain new ACME certificate.
 func (c *ChallengeTLSALPN) Present(domain, _, keyAuth string) error {
-	logger := log.WithoutContext().WithField(log.ProviderName, providerNameALPN)
-	logger.Debugf("TLS Challenge Present temp certificate for %s", domain)
+	logger := log.With().Str(logs.ProviderName, providerNameALPN).Logger()
+	logger.Debug().Msgf("TLS Challenge Present temp certificate for %s", domain)
 
 	certPEMBlock, keyPEMBlock, err := tlsalpn01.ChallengeBlocks(domain, keyAuth)
 	if err != nil {
@@ -71,7 +72,7 @@ func (c *ChallengeTLSALPN) Present(domain, _, keyAuth string) error {
 
 		err = c.CleanUp(domain, "", keyAuth)
 		if err != nil {
-			logger.Errorf("Failed to clean up TLS challenge: %v", err)
+			logger.Error().Err(err).Msg("Failed to clean up TLS challenge")
 		}
 
 		return fmt.Errorf("timeout %s", t)
@@ -83,8 +84,8 @@ func (c *ChallengeTLSALPN) Present(domain, _, keyAuth string) error {
 
 // CleanUp cleans the challenges when certificate is obtained.
 func (c *ChallengeTLSALPN) CleanUp(domain, _, keyAuth string) error {
-	log.WithoutContext().WithField(log.ProviderName, providerNameALPN).
-		Debugf("TLS Challenge CleanUp temp certificate for %s", domain)
+	log.Debug().Str(logs.ProviderName, providerNameALPN).
+		Msgf("TLS Challenge CleanUp temp certificate for %s", domain)
 
 	c.muCerts.Lock()
 	delete(c.certs, keyAuth)
diff --git a/pkg/provider/acme/local_store.go b/pkg/provider/acme/local_store.go
index 33084b3204..57a26207b3 100644
--- a/pkg/provider/acme/local_store.go
+++ b/pkg/provider/acme/local_store.go
@@ -6,7 +6,8 @@ import (
 	"os"
 	"sync"
 
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/safe"
 )
 
@@ -52,7 +53,7 @@ func (s *LocalStore) get(resolverName string) (*StoredData, error) {
 		}
 
 		if hasData {
-			logger := log.WithoutContext().WithField(log.ProviderName, "acme")
+			logger := log.With().Str(logs.ProviderName, "acme").Logger()
 
 			f, err := os.Open(s.filename)
 			if err != nil {
@@ -76,7 +77,7 @@ func (s *LocalStore) get(resolverName string) (*StoredData, error) {
 			for _, storedData := range s.storedData {
 				for _, certificate := range storedData.Certificates {
 					if len(certificate.Certificate.Certificate) == 0 || len(certificate.Key) == 0 {
-						logger.Debugf("Deleting empty certificate %v for %v", certificate, certificate.Domain.ToStrArray())
+						logger.Debug().Msgf("Deleting empty certificate %v for %v", certificate, certificate.Domain.ToStrArray())
 						continue
 					}
 					certificates = append(certificates, certificate)
@@ -101,16 +102,16 @@ func (s *LocalStore) get(resolverName string) (*StoredData, error) {
 // listenSaveAction listens to a chan to store ACME data in json format into `LocalStore.filename`.
 func (s *LocalStore) listenSaveAction() {
 	safe.Go(func() {
-		logger := log.WithoutContext().WithField(log.ProviderName, "acme")
+		logger := log.With().Str(logs.ProviderName, "acme").Logger()
 		for object := range s.saveDataChan {
 			data, err := json.MarshalIndent(object, "", "  ")
 			if err != nil {
-				logger.Error(err)
+				logger.Error().Err(err).Send()
 			}
 
 			err = os.WriteFile(s.filename, data, 0o600)
 			if err != nil {
-				logger.Error(err)
+				logger.Error().Err(err).Send()
 			}
 		}
 	})
diff --git a/pkg/provider/acme/provider.go b/pkg/provider/acme/provider.go
index be211ab043..e57c84d5ba 100644
--- a/pkg/provider/acme/provider.go
+++ b/pkg/provider/acme/provider.go
@@ -19,9 +19,10 @@ import (
 	"github.com/go-acme/lego/v4/lego"
 	"github.com/go-acme/lego/v4/providers/dns"
 	"github.com/go-acme/lego/v4/registration"
+	"github.com/rs/zerolog/log"
 	ptypes "github.com/traefik/paerser/types"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	httpmuxer "github.com/traefik/traefik/v2/pkg/muxer/http"
 	tcpmuxer "github.com/traefik/traefik/v2/pkg/muxer/tcp"
 	"github.com/traefik/traefik/v2/pkg/safe"
@@ -131,8 +132,7 @@ func (p *Provider) ListenConfiguration(config dynamic.Configuration) {
 
 // Init for compatibility reason the BaseProvider implements an empty Init.
 func (p *Provider) Init() error {
-	ctx := log.With(context.Background(), log.Str(log.ProviderName, p.ResolverName+".acme"))
-	logger := log.FromContext(ctx)
+	logger := log.With().Str(logs.ProviderName, p.ResolverName+".acme").Logger()
 
 	if len(p.Configuration.Storage) == 0 {
 		return errors.New("unable to initialize ACME provider with no storage location for the certificates")
@@ -149,8 +149,8 @@ func (p *Provider) Init() error {
 	}
 
 	// Reset Account if caServer changed, thus registration URI can be updated
-	if p.account != nil && p.account.Registration != nil && !isAccountMatchingCaServer(ctx, p.account.Registration.URI, p.CAServer) {
-		logger.Info("Account URI does not match the current CAServer. The account will be reset.")
+	if p.account != nil && p.account.Registration != nil && !isAccountMatchingCaServer(logger.WithContext(context.Background()), p.account.Registration.URI, p.CAServer) {
+		logger.Info().Msg("Account URI does not match the current CAServer. The account will be reset.")
 		p.account = nil
 	}
 
@@ -169,17 +169,17 @@ func (p *Provider) Init() error {
 }
 
 func isAccountMatchingCaServer(ctx context.Context, accountURI, serverURI string) bool {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
 	aru, err := url.Parse(accountURI)
 	if err != nil {
-		logger.Infof("Unable to parse account.Registration URL: %v", err)
+		logger.Info().Err(err).Str("registrationURL", accountURI).Msg("Unable to parse account.Registration URL")
 		return false
 	}
 
 	cau, err := url.Parse(serverURI)
 	if err != nil {
-		logger.Infof("Unable to parse CAServer URL: %v", err)
+		logger.Info().Err(err).Str("caServerURL", serverURI).Msg("Unable to parse CAServer URL")
 		return false
 	}
 
@@ -194,9 +194,9 @@ func (p *Provider) ThrottleDuration() time.Duration {
 // Provide allows the file provider to provide configurations to traefik
 // using the given Configuration channel.
 func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error {
-	ctx := log.With(context.Background(),
-		log.Str(log.ProviderName, p.ResolverName+".acme"),
-		log.Str("ACME CA", p.Configuration.CAServer))
+	logger := log.With().Str(logs.ProviderName, p.ResolverName+".acme").Str("acmeCA", p.Configuration.CAServer).
+		Logger()
+	ctx := logger.WithContext(context.Background())
 
 	p.pool = pool
 
@@ -211,7 +211,7 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 	p.configurationChan <- msg
 
 	renewPeriod, renewInterval := getCertificateRenewDurations(p.CertificatesDuration)
-	log.FromContext(ctx).Debugf("Attempt to renew certificates %q before expiry and check every %q",
+	logger.Debug().Msgf("Attempt to renew certificates %q before expiry and check every %q",
 		renewPeriod, renewInterval)
 
 	p.renewCertificates(ctx, renewPeriod)
@@ -236,8 +236,9 @@ func (p *Provider) getClient() (*lego.Client, error) {
 	p.clientMutex.Lock()
 	defer p.clientMutex.Unlock()
 
-	ctx := log.With(context.Background(), log.Str(log.ProviderName, p.ResolverName+".acme"))
-	logger := log.FromContext(ctx)
+	logger := log.With().Str(logs.ProviderName, p.ResolverName+".acme").Logger()
+
+	ctx := logger.WithContext(context.Background())
 
 	if p.client != nil {
 		return p.client, nil
@@ -248,13 +249,13 @@ func (p *Provider) getClient() (*lego.Client, error) {
 		return nil, err
 	}
 
-	logger.Debug("Building ACME client...")
+	logger.Debug().Msg("Building ACME client...")
 
 	caServer := lego.LEDirectoryProduction
 	if len(p.CAServer) > 0 {
 		caServer = p.CAServer
 	}
-	logger.Debug(caServer)
+	logger.Debug().Msg(caServer)
 
 	config := lego.NewConfig(account)
 	config.CADirURL = caServer
@@ -290,7 +291,7 @@ func (p *Provider) getClient() (*lego.Client, error) {
 	}
 
 	if p.DNSChallenge != nil && len(p.DNSChallenge.Provider) > 0 {
-		logger.Debugf("Using DNS Challenge provider: %s", p.DNSChallenge.Provider)
+		logger.Debug().Msgf("Using DNS Challenge provider: %s", p.DNSChallenge.Provider)
 
 		var provider challenge.Provider
 		provider, err = dns.NewDNSChallengeProviderByName(p.DNSChallenge.Provider)
@@ -302,7 +303,7 @@ func (p *Provider) getClient() (*lego.Client, error) {
 			dns01.CondOption(len(p.DNSChallenge.Resolvers) > 0, dns01.AddRecursiveNameservers(p.DNSChallenge.Resolvers)),
 			dns01.WrapPreCheck(func(domain, fqdn, value string, check dns01.PreCheckFunc) (bool, error) {
 				if p.DNSChallenge.DelayBeforeCheck > 0 {
-					logger.Debugf("Delaying %d rather than validating DNS propagation now.", p.DNSChallenge.DelayBeforeCheck)
+					logger.Debug().Msgf("Delaying %d rather than validating DNS propagation now.", p.DNSChallenge.DelayBeforeCheck)
 					time.Sleep(time.Duration(p.DNSChallenge.DelayBeforeCheck))
 				}
 
@@ -319,7 +320,7 @@ func (p *Provider) getClient() (*lego.Client, error) {
 	}
 
 	if p.HTTPChallenge != nil && len(p.HTTPChallenge.EntryPoint) > 0 {
-		logger.Debug("Using HTTP Challenge provider.")
+		logger.Debug().Msg("Using HTTP Challenge provider.")
 
 		err = client.Challenge.SetHTTP01Provider(p.HTTPChallengeProvider)
 		if err != nil {
@@ -328,7 +329,7 @@ func (p *Provider) getClient() (*lego.Client, error) {
 	}
 
 	if p.TLSChallenge != nil {
-		logger.Debug("Using TLS Challenge provider.")
+		logger.Debug().Msg("Using TLS Challenge provider.")
 
 		err = client.Challenge.SetTLSALPN01Provider(p.TLSChallengeProvider)
 		if err != nil {
@@ -358,30 +359,30 @@ func (p *Provider) initAccount(ctx context.Context) (*Account, error) {
 }
 
 func (p *Provider) register(ctx context.Context, client *lego.Client) (*registration.Resource, error) {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
 	if p.EAB != nil {
-		logger.Info("Register with external account binding...")
+		logger.Info().Msg("Register with external account binding...")
 
 		eabOptions := registration.RegisterEABOptions{TermsOfServiceAgreed: true, Kid: p.EAB.Kid, HmacEncoded: p.EAB.HmacEncoded}
 
 		return client.Registration.RegisterWithExternalAccountBinding(eabOptions)
 	}
 
-	logger.Info("Register...")
+	logger.Info().Msg("Register...")
 
 	return client.Registration.Register(registration.RegisterOptions{TermsOfServiceAgreed: true})
 }
 
 func (p *Provider) resolveDomains(ctx context.Context, domains []string, tlsStore string) {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
 	if len(domains) == 0 {
-		logger.Debug("No domain parsed in provider ACME")
+		logger.Debug().Msg("No domain parsed in provider ACME")
 		return
 	}
 
-	logger.Debugf("Trying to challenge certificate for domain %v found in HostSNI rule", domains)
+	logger.Debug().Msgf("Trying to challenge certificate for domain %v found in HostSNI rule", domains)
 
 	var domain types.Domain
 	if len(domains) > 0 {
@@ -393,20 +394,22 @@ func (p *Provider) resolveDomains(ctx context.Context, domains []string, tlsStor
 		safe.Go(func() {
 			dom, cert, err := p.resolveCertificate(ctx, domain, tlsStore)
 			if err != nil {
-				logger.Errorf("Unable to obtain ACME certificate for domains %q: %v", strings.Join(domains, ","), err)
+				logger.Error().Err(err).Strs("domains", domains).Msg("Unable to obtain ACME certificate for domains")
 				return
 			}
 
 			err = p.addCertificateForDomain(dom, cert, tlsStore)
 			if err != nil {
-				logger.WithError(err).Error("Error adding certificate for domain")
+				logger.Error().Err(err).Strs("domains", dom.ToStrArray()).Msg("Error adding certificate for domains")
 			}
 		})
 	}
 }
 
 func (p *Provider) watchNewDomains(ctx context.Context) {
-	ctx = log.With(ctx, log.Str(log.ProviderName, p.ResolverName+".acme"))
+	rootLogger := log.Ctx(ctx).With().Str(logs.ProviderName, p.ResolverName+".acme").Logger()
+	ctx = rootLogger.WithContext(ctx)
+
 	p.pool.GoCtx(func(ctxPool context.Context) {
 		for {
 			select {
@@ -417,8 +420,8 @@ func (p *Provider) watchNewDomains(ctx context.Context) {
 							continue
 						}
 
-						ctxRouter := log.With(ctx, log.Str(log.RouterName, routerName), log.Str(log.Rule, route.Rule))
-						logger := log.FromContext(ctxRouter)
+						logger := rootLogger.With().Str(logs.RouterName, routerName).Str(logs.Rule, route.Rule).Logger()
+						ctxRouter := logger.WithContext(ctx)
 
 						if len(route.TLS.Domains) > 0 {
 							domains := deleteUnnecessaryDomains(ctxRouter, route.TLS.Domains)
@@ -427,20 +430,20 @@ func (p *Provider) watchNewDomains(ctx context.Context) {
 								safe.Go(func() {
 									dom, cert, err := p.resolveCertificate(ctx, domain, traefiktls.DefaultTLSStoreName)
 									if err != nil {
-										logger.WithError(err).Errorf("Unable to obtain ACME certificate for domains %q", strings.Join(domain.ToStrArray(), ","))
+										logger.Error().Err(err).Strs("domains", domain.ToStrArray()).Msg("Unable to obtain ACME certificate for domains")
 										return
 									}
 
 									err = p.addCertificateForDomain(dom, cert, traefiktls.DefaultTLSStoreName)
 									if err != nil {
-										logger.WithError(err).Error("Error adding certificate for domain")
+										logger.Error().Err(err).Strs("domains", dom.ToStrArray()).Msg("Error adding certificate for domains")
 									}
 								})
 							}
 						} else {
 							domains, err := tcpmuxer.ParseHostSNI(route.Rule)
 							if err != nil {
-								logger.WithError(err).Errorf("Error parsing domains in provider ACME")
+								logger.Error().Err(err).Msg("Error parsing domains in provider ACME")
 								continue
 							}
 							p.resolveDomains(ctxRouter, domains, traefiktls.DefaultTLSStoreName)
@@ -454,8 +457,8 @@ func (p *Provider) watchNewDomains(ctx context.Context) {
 							continue
 						}
 
-						ctxRouter := log.With(ctx, log.Str(log.RouterName, routerName), log.Str(log.Rule, route.Rule))
-						logger := log.FromContext(ctxRouter)
+						logger := rootLogger.With().Str(logs.RouterName, routerName).Str(logs.Rule, route.Rule).Logger()
+						ctxRouter := logger.WithContext(ctx)
 
 						if len(route.TLS.Domains) > 0 {
 							domains := deleteUnnecessaryDomains(ctxRouter, route.TLS.Domains)
@@ -464,20 +467,20 @@ func (p *Provider) watchNewDomains(ctx context.Context) {
 								safe.Go(func() {
 									dom, cert, err := p.resolveCertificate(ctx, domain, traefiktls.DefaultTLSStoreName)
 									if err != nil {
-										logger.WithError(err).Errorf("Unable to obtain ACME certificate for domains %q", strings.Join(domain.ToStrArray(), ","))
+										logger.Error().Err(err).Strs("domains", domain.ToStrArray()).Msg("Unable to obtain ACME certificate for domains")
 										return
 									}
 
 									err = p.addCertificateForDomain(dom, cert, traefiktls.DefaultTLSStoreName)
 									if err != nil {
-										logger.WithError(err).Error("Error adding certificate for domain")
+										logger.Error().Err(err).Strs("domains", dom.ToStrArray()).Msg("Error adding certificate for domain")
 									}
 								})
 							}
 						} else {
 							domains, err := httpmuxer.ParseDomains(route.Rule)
 							if err != nil {
-								logger.WithError(err).Errorf("Error parsing domains in provider ACME")
+								logger.Error().Err(err).Msg("Error parsing domains in provider ACME")
 								continue
 							}
 							p.resolveDomains(ctxRouter, domains, traefiktls.DefaultTLSStoreName)
@@ -490,11 +493,10 @@ func (p *Provider) watchNewDomains(ctx context.Context) {
 				}
 
 				for tlsStoreName, tlsStore := range config.TLS.Stores {
-					ctxTLSStore := log.With(ctx, log.Str(log.TLSStoreName, tlsStoreName))
-					logger := log.FromContext(ctxTLSStore)
+					logger := rootLogger.With().Str(logs.TLSStoreName, tlsStoreName).Logger()
 
 					if tlsStore.DefaultCertificate != nil && tlsStore.DefaultGeneratedCert != nil {
-						logger.Warn("defaultCertificate and defaultGeneratedCert cannot be defined at the same time.")
+						logger.Warn().Msg("defaultCertificate and defaultGeneratedCert cannot be defined at the same time.")
 					}
 
 					// Gives precedence to the user defined default certificate.
@@ -503,7 +505,7 @@ func (p *Provider) watchNewDomains(ctx context.Context) {
 					}
 
 					if tlsStore.DefaultGeneratedCert.Domain == nil || tlsStore.DefaultGeneratedCert.Resolver == "" {
-						logger.Warn("default generated certificate domain or resolver is missing.")
+						logger.Warn().Msg("default generated certificate domain or resolver is missing.")
 						continue
 					}
 
@@ -513,18 +515,18 @@ func (p *Provider) watchNewDomains(ctx context.Context) {
 
 					validDomains, err := p.sanitizeDomains(ctx, *tlsStore.DefaultGeneratedCert.Domain)
 					if err != nil {
-						logger.WithError(err).Errorf("domains validation: %s", strings.Join(tlsStore.DefaultGeneratedCert.Domain.ToStrArray(), ","))
+						logger.Error().Err(err).Strs("domains", tlsStore.DefaultGeneratedCert.Domain.ToStrArray()).Msg("domains validation")
 					}
 
 					if p.certExists(validDomains) {
-						logger.Debug("Default ACME certificate generation is not required.")
+						logger.Debug().Msg("Default ACME certificate generation is not required.")
 						continue
 					}
 
 					safe.Go(func() {
 						cert, err := p.resolveDefaultCertificate(ctx, validDomains)
 						if err != nil {
-							logger.WithError(err).Errorf("Unable to obtain ACME certificate for domain %q", strings.Join(validDomains, ","))
+							logger.Error().Err(err).Strs("domains", validDomains).Msgf("Unable to obtain ACME certificate for domain")
 							return
 						}
 
@@ -537,7 +539,7 @@ func (p *Provider) watchNewDomains(ctx context.Context) {
 
 						err = p.addCertificateForDomain(domain, cert, traefiktls.DefaultTLSStoreName)
 						if err != nil {
-							logger.WithError(err).Error("Error adding certificate for domain")
+							logger.Error().Err(err).Msg("Error adding certificate for domain")
 						}
 					})
 				}
@@ -549,7 +551,7 @@ func (p *Provider) watchNewDomains(ctx context.Context) {
 }
 
 func (p *Provider) resolveDefaultCertificate(ctx context.Context, domains []string) (*certificate.Resource, error) {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
 	p.resolvingDomainsMutex.Lock()
 
@@ -571,7 +573,7 @@ func (p *Provider) resolveDefaultCertificate(ctx context.Context, domains []stri
 
 	defer p.removeResolvingDomains(append(domains, domainKey))
 
-	logger.Debugf("Loading ACME certificates %+v...", domains)
+	logger.Debug().Msgf("Loading ACME certificates %+v...", domains)
 
 	client, err := p.getClient()
 	if err != nil {
@@ -596,7 +598,7 @@ func (p *Provider) resolveDefaultCertificate(ctx context.Context, domains []stri
 		return nil, fmt.Errorf("certificate for domains %v is empty: %v", domains, cert)
 	}
 
-	logger.Debugf("Default certificate obtained for domains %+v", domains)
+	logger.Debug().Msgf("Default certificate obtained for domains %+v", domains)
 
 	return cert, nil
 }
@@ -615,8 +617,8 @@ func (p *Provider) resolveCertificate(ctx context.Context, domain types.Domain,
 
 	defer p.removeResolvingDomains(uncheckedDomains)
 
-	logger := log.FromContext(ctx)
-	logger.Debugf("Loading ACME certificates %+v...", uncheckedDomains)
+	logger := log.Ctx(ctx)
+	logger.Debug().Msgf("Loading ACME certificates %+v...", uncheckedDomains)
 
 	client, err := p.getClient()
 	if err != nil {
@@ -641,7 +643,7 @@ func (p *Provider) resolveCertificate(ctx context.Context, domain types.Domain,
 		return types.Domain{}, nil, fmt.Errorf("certificate for domains %v is empty: %v", uncheckedDomains, cert)
 	}
 
-	logger.Debugf("Certificates obtained for domains %+v", uncheckedDomains)
+	logger.Debug().Msgf("Certificates obtained for domains %+v", uncheckedDomains)
 
 	domain = types.Domain{Main: uncheckedDomains[0]}
 	if len(uncheckedDomains) > 1 {
@@ -712,7 +714,7 @@ func getCertificateRenewDurations(certificatesDuration int) (time.Duration, time
 func deleteUnnecessaryDomains(ctx context.Context, domains []types.Domain) []types.Domain {
 	var newDomains []types.Domain
 
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
 	for idxDomainToCheck, domainToCheck := range domains {
 		keepDomain := true
@@ -724,7 +726,7 @@ func deleteUnnecessaryDomains(ctx context.Context, domains []types.Domain) []typ
 
 			if reflect.DeepEqual(domain, domainToCheck) {
 				if idxDomainToCheck > idxDomain {
-					logger.Warnf("The domain %v is duplicated in the configuration but will be process by ACME provider only once.", domainToCheck)
+					logger.Warn().Msgf("The domain %v is duplicated in the configuration but will be process by ACME provider only once.", domainToCheck)
 					keepDomain = false
 				}
 				break
@@ -736,11 +738,11 @@ func deleteUnnecessaryDomains(ctx context.Context, domains []types.Domain) []typ
 			for _, domainProcessed := range domainToCheck.ToStrArray() {
 				if idxDomain < idxDomainToCheck && isDomainAlreadyChecked(domainProcessed, domain.ToStrArray()) {
 					// The domain is duplicated in a CN
-					logger.Warnf("Domain %q is duplicated in the configuration or validated by the domain %v. It will be processed once.", domainProcessed, domain)
+					logger.Warn().Msgf("Domain %q is duplicated in the configuration or validated by the domain %v. It will be processed once.", domainProcessed, domain)
 					continue
 				} else if domain.Main != domainProcessed && strings.HasPrefix(domain.Main, "*") && isDomainAlreadyChecked(domainProcessed, []string{domain.Main}) {
 					// Check if a wildcard can validate the domain
-					logger.Warnf("Domain %q will not be processed by ACME provider because it is validated by the wildcard %q", domainProcessed, domain.Main)
+					logger.Warn().Msgf("Domain %q will not be processed by ACME provider because it is validated by the wildcard %q", domainProcessed, domain.Main)
 					continue
 				}
 				newDomainsToCheck = append(newDomainsToCheck, domainProcessed)
@@ -791,9 +793,9 @@ func (p *Provider) buildMessage() dynamic.Message {
 }
 
 func (p *Provider) renewCertificates(ctx context.Context, renewPeriod time.Duration) {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
-	logger.Info("Testing certificate renew...")
+	logger.Info().Msg("Testing certificate renew...")
 
 	p.certificatesMu.RLock()
 
@@ -811,11 +813,11 @@ func (p *Provider) renewCertificates(ctx context.Context, renewPeriod time.Durat
 	for _, cert := range certificates {
 		client, err := p.getClient()
 		if err != nil {
-			logger.WithError(err).Infof("Error renewing certificate from LE : %+v", cert.Domain)
+			logger.Info().Err(err).Msgf("Error renewing certificate from LE : %+v", cert.Domain)
 			continue
 		}
 
-		logger.Infof("Renewing certificate from LE : %+v", cert.Domain)
+		logger.Info().Msgf("Renewing certificate from LE : %+v", cert.Domain)
 
 		renewedCert, err := client.Certificate.Renew(certificate.Resource{
 			Domain:      cert.Domain.Main,
@@ -823,18 +825,18 @@ func (p *Provider) renewCertificates(ctx context.Context, renewPeriod time.Durat
 			Certificate: cert.Certificate.Certificate,
 		}, true, ocspMustStaple, p.PreferredChain)
 		if err != nil {
-			logger.WithError(err).Errorf("Error renewing certificate from LE: %v", cert.Domain)
+			logger.Error().Err(err).Msgf("Error renewing certificate from LE: %v", cert.Domain)
 			continue
 		}
 
 		if len(renewedCert.Certificate) == 0 || len(renewedCert.PrivateKey) == 0 {
-			logger.Errorf("domains %v renew certificate with no value: %v", cert.Domain.ToStrArray(), cert)
+			logger.Error().Msgf("domains %v renew certificate with no value: %v", cert.Domain.ToStrArray(), cert)
 			continue
 		}
 
 		err = p.addCertificateForDomain(cert.Domain, renewedCert, cert.Store)
 		if err != nil {
-			logger.WithError(err).Error("Error adding certificate for domain")
+			logger.Error().Err(err).Msg("Error adding certificate for domain")
 		}
 	}
 }
@@ -842,7 +844,7 @@ func (p *Provider) renewCertificates(ctx context.Context, renewPeriod time.Durat
 // Get provided certificate which check a domains list (Main and SANs)
 // from static and dynamic provided certificates.
 func (p *Provider) getUncheckedDomains(ctx context.Context, domainsToCheck []string, tlsStore string) []string {
-	log.FromContext(ctx).Debugf("Looking for provided certificate(s) to validate %q...", domainsToCheck)
+	log.Ctx(ctx).Debug().Msgf("Looking for provided certificate(s) to validate %q...", domainsToCheck)
 
 	var allDomains []string
 	store := p.tlsManager.GetStore(tlsStore)
@@ -884,21 +886,24 @@ func searchUncheckedDomains(ctx context.Context, domainsToCheck, existentDomains
 		}
 	}
 
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 	if len(uncheckedDomains) == 0 {
-		logger.Debugf("No ACME certificate generation required for domains %q.", domainsToCheck)
+		logger.Debug().Strs("domains", domainsToCheck).Msg("No ACME certificate generation required for domains")
 	} else {
-		logger.Debugf("Domains %q need ACME certificates generation for domains %q.", domainsToCheck, strings.Join(uncheckedDomains, ","))
+		logger.Debug().Strs("domains", domainsToCheck).Msgf("Domains need ACME certificates generation for domains %q.", strings.Join(uncheckedDomains, ","))
 	}
 	return uncheckedDomains
 }
 
 func getX509Certificate(ctx context.Context, cert *Certificate) (*x509.Certificate, error) {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
 	tlsCert, err := tls.X509KeyPair(cert.Certificate, cert.Key)
 	if err != nil {
-		logger.WithError(err).Errorf("Failed to load TLS key pair from ACME certificate for domain %q (SAN : %q), certificate will be renewed", cert.Domain.Main, strings.Join(cert.Domain.SANs, ","))
+		logger.Error().Err(err).
+			Str("domain", cert.Domain.Main).
+			Strs("SANs", cert.Domain.SANs).
+			Msg("Failed to load TLS key pair from ACME certificate for domain, certificate will be renewed")
 		return nil, err
 	}
 
@@ -906,7 +911,10 @@ func getX509Certificate(ctx context.Context, cert *Certificate) (*x509.Certifica
 	if crt == nil {
 		crt, err = x509.ParseCertificate(tlsCert.Certificate[0])
 		if err != nil {
-			logger.WithError(err).Errorf("Failed to parse TLS key pair from ACME certificate for domain %q (SAN : %q), certificate will be renewed", cert.Domain.Main, strings.Join(cert.Domain.SANs, ","))
+			logger.Error().Err(err).
+				Str("domain", cert.Domain.Main).
+				Strs("SANs", cert.Domain.SANs).
+				Msg("Failed to parse TLS key pair from ACME certificate for domain, certificate will be renewed")
 		}
 	}
 
@@ -935,7 +943,7 @@ func (p *Provider) sanitizeDomains(ctx context.Context, domain types.Domain) ([]
 		canonicalDomain := types.CanonicalDomain(dom)
 		cleanDomain := dns01.UnFqdn(canonicalDomain)
 		if canonicalDomain != cleanDomain {
-			log.FromContext(ctx).Warnf("FQDN detected, please remove the trailing dot: %s", canonicalDomain)
+			log.Ctx(ctx).Warn().Msgf("FQDN detected, please remove the trailing dot: %s", canonicalDomain)
 		}
 
 		cleanDomains = append(cleanDomains, cleanDomain)
diff --git a/pkg/provider/aggregator/aggregator.go b/pkg/provider/aggregator/aggregator.go
index 3ddb1c3f25..8001711fca 100644
--- a/pkg/provider/aggregator/aggregator.go
+++ b/pkg/provider/aggregator/aggregator.go
@@ -4,9 +4,9 @@ import (
 	"context"
 	"time"
 
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
 	"github.com/traefik/traefik/v2/pkg/config/static"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/provider"
 	"github.com/traefik/traefik/v2/pkg/provider/file"
 	"github.com/traefik/traefik/v2/pkg/provider/traefik"
@@ -148,7 +148,7 @@ func NewProviderAggregator(conf static.Providers) ProviderAggregator {
 func (p *ProviderAggregator) quietAddProvider(provider provider.Provider) {
 	err := p.AddProvider(provider)
 	if err != nil {
-		log.WithoutContext().Errorf("Error while initializing provider %T: %v", provider, err)
+		log.Error().Err(err).Msgf("Error while initializing provider %T", provider)
 	}
 }
 
@@ -201,14 +201,14 @@ func (p ProviderAggregator) Provide(configurationChan chan<- dynamic.Message, po
 func (p ProviderAggregator) launchProvider(configurationChan chan<- dynamic.Message, pool *safe.Pool, prd provider.Provider) {
 	jsonConf, err := redactor.RemoveCredentials(prd)
 	if err != nil {
-		log.WithoutContext().Debugf("Cannot marshal the provider configuration %T: %v", prd, err)
+		log.Debug().Err(err).Msgf("Cannot marshal the provider configuration %T", prd)
 	}
 
-	log.WithoutContext().Infof("Starting provider %T", prd)
-	log.WithoutContext().Debugf("%T provider configuration: %s", prd, jsonConf)
+	log.Info().Msgf("Starting provider %T", prd)
+	log.Debug().RawJSON("config", []byte(jsonConf)).Msgf("%T provider configuration", prd)
 
 	if err := maybeThrottledProvide(prd, p.providersThrottleDuration)(configurationChan, pool); err != nil {
-		log.WithoutContext().Errorf("Cannot start the provider %T: %v", prd, err)
+		log.Error().Err(err).Msgf("Cannot start the provider %T", prd)
 		return
 	}
 }
diff --git a/pkg/provider/configuration.go b/pkg/provider/configuration.go
index 1000e696f6..c0119c885d 100644
--- a/pkg/provider/configuration.go
+++ b/pkg/provider/configuration.go
@@ -10,13 +10,14 @@ import (
 	"unicode"
 
 	"github.com/Masterminds/sprig/v3"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 )
 
 // Merge Merges multiple configurations.
 func Merge(ctx context.Context, configurations map[string]*dynamic.Configuration) *dynamic.Configuration {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
 	configuration := &dynamic.Configuration{
 		HTTP: &dynamic.HTTPConfiguration{
@@ -136,56 +137,56 @@ func Merge(ctx context.Context, configurations map[string]*dynamic.Configuration
 	}
 
 	for serviceName := range servicesToDelete {
-		logger.WithField(log.ServiceName, serviceName).
-			Errorf("Service defined multiple times with different configurations in %v", services[serviceName])
+		logger.Error().Str(logs.ServiceName, serviceName).
+			Msgf("Service defined multiple times with different configurations in %v", services[serviceName])
 		delete(configuration.HTTP.Services, serviceName)
 	}
 
 	for routerName := range routersToDelete {
-		logger.WithField(log.RouterName, routerName).
-			Errorf("Router defined multiple times with different configurations in %v", routers[routerName])
+		logger.Error().Str(logs.RouterName, routerName).
+			Msgf("Router defined multiple times with different configurations in %v", routers[routerName])
 		delete(configuration.HTTP.Routers, routerName)
 	}
 
 	for transportName := range transportsToDelete {
-		logger.WithField(log.ServersTransportName, transportName).
-			Errorf("ServersTransport defined multiple times with different configurations in %v", transports[transportName])
+		logger.Error().Str(logs.ServersTransportName, transportName).
+			Msgf("ServersTransport defined multiple times with different configurations in %v", transports[transportName])
 		delete(configuration.HTTP.ServersTransports, transportName)
 	}
 
 	for serviceName := range servicesTCPToDelete {
-		logger.WithField(log.ServiceName, serviceName).
-			Errorf("Service TCP defined multiple times with different configurations in %v", servicesTCP[serviceName])
+		logger.Error().Str(logs.ServiceName, serviceName).
+			Msgf("Service TCP defined multiple times with different configurations in %v", servicesTCP[serviceName])
 		delete(configuration.TCP.Services, serviceName)
 	}
 
 	for routerName := range routersTCPToDelete {
-		logger.WithField(log.RouterName, routerName).
-			Errorf("Router TCP defined multiple times with different configurations in %v", routersTCP[routerName])
+		logger.Error().Str(logs.RouterName, routerName).
+			Msgf("Router TCP defined multiple times with different configurations in %v", routersTCP[routerName])
 		delete(configuration.TCP.Routers, routerName)
 	}
 
 	for serviceName := range servicesUDPToDelete {
-		logger.WithField(log.ServiceName, serviceName).
-			Errorf("UDP service defined multiple times with different configurations in %v", servicesUDP[serviceName])
+		logger.Error().Str(logs.ServiceName, serviceName).
+			Msgf("UDP service defined multiple times with different configurations in %v", servicesUDP[serviceName])
 		delete(configuration.UDP.Services, serviceName)
 	}
 
 	for routerName := range routersUDPToDelete {
-		logger.WithField(log.RouterName, routerName).
-			Errorf("UDP router defined multiple times with different configurations in %v", routersUDP[routerName])
+		logger.Error().Str(logs.RouterName, routerName).
+			Msgf("UDP router defined multiple times with different configurations in %v", routersUDP[routerName])
 		delete(configuration.UDP.Routers, routerName)
 	}
 
 	for middlewareName := range middlewaresToDelete {
-		logger.WithField(log.MiddlewareName, middlewareName).
-			Errorf("Middleware defined multiple times with different configurations in %v", middlewares[middlewareName])
+		logger.Error().Str(logs.MiddlewareName, middlewareName).
+			Msgf("Middleware defined multiple times with different configurations in %v", middlewares[middlewareName])
 		delete(configuration.HTTP.Middlewares, middlewareName)
 	}
 
 	for middlewareName := range middlewaresTCPToDelete {
-		logger.WithField(log.MiddlewareName, middlewareName).
-			Errorf("TCP Middleware defined multiple times with different configurations in %v", middlewaresTCP[middlewareName])
+		logger.Error().Str(logs.MiddlewareName, middlewareName).
+			Msgf("TCP Middleware defined multiple times with different configurations in %v", middlewaresTCP[middlewareName])
 		delete(configuration.TCP.Middlewares, middlewareName)
 	}
 
@@ -342,18 +343,19 @@ func MakeDefaultRuleTemplate(defaultRule string, funcMap template.FuncMap) (*tem
 // BuildTCPRouterConfiguration Builds a router configuration.
 func BuildTCPRouterConfiguration(ctx context.Context, configuration *dynamic.TCPConfiguration) {
 	for routerName, router := range configuration.Routers {
-		loggerRouter := log.FromContext(ctx).WithField(log.RouterName, routerName)
+		loggerRouter := log.Ctx(ctx).With().Str(logs.RouterName, routerName).Logger()
+
 		if len(router.Rule) == 0 {
 			delete(configuration.Routers, routerName)
-			loggerRouter.Errorf("Empty rule")
+			loggerRouter.Error().Msg("Empty rule")
 			continue
 		}
 
 		if len(router.Service) == 0 {
 			if len(configuration.Services) > 1 {
 				delete(configuration.Routers, routerName)
-				loggerRouter.
-					Error("Could not define the service name for the router: too many services")
+				loggerRouter.Error().
+					Msg("Could not define the service name for the router: too many services")
 				continue
 			}
 
@@ -367,7 +369,8 @@ func BuildTCPRouterConfiguration(ctx context.Context, configuration *dynamic.TCP
 // BuildUDPRouterConfiguration Builds a router configuration.
 func BuildUDPRouterConfiguration(ctx context.Context, configuration *dynamic.UDPConfiguration) {
 	for routerName, router := range configuration.Routers {
-		loggerRouter := log.FromContext(ctx).WithField(log.RouterName, routerName)
+		loggerRouter := log.Ctx(ctx).With().Str(logs.RouterName, routerName).Logger()
+
 		if len(router.Service) > 0 {
 			continue
 		}
@@ -375,7 +378,7 @@ func BuildUDPRouterConfiguration(ctx context.Context, configuration *dynamic.UDP
 		if len(configuration.Services) > 1 {
 			delete(configuration.Routers, routerName)
 			loggerRouter.
-				Error("Could not define the service name for the router: too many services")
+				Error().Msg("Could not define the service name for the router: too many services")
 			continue
 		}
 
@@ -390,7 +393,7 @@ func BuildUDPRouterConfiguration(ctx context.Context, configuration *dynamic.UDP
 func BuildRouterConfiguration(ctx context.Context, configuration *dynamic.HTTPConfiguration, defaultRouterName string, defaultRuleTpl *template.Template, model interface{}) {
 	if len(configuration.Routers) == 0 {
 		if len(configuration.Services) > 1 {
-			log.FromContext(ctx).Info("Could not create a router for the container: too many services")
+			log.Ctx(ctx).Info().Msg("Could not create a router for the container: too many services")
 		} else {
 			configuration.Routers = make(map[string]*dynamic.Router)
 			configuration.Routers[defaultRouterName] = &dynamic.Router{}
@@ -398,18 +401,19 @@ func BuildRouterConfiguration(ctx context.Context, configuration *dynamic.HTTPCo
 	}
 
 	for routerName, router := range configuration.Routers {
-		loggerRouter := log.FromContext(ctx).WithField(log.RouterName, routerName)
+		loggerRouter := log.Ctx(ctx).With().Str(logs.RouterName, routerName).Logger()
+
 		if len(router.Rule) == 0 {
 			writer := &bytes.Buffer{}
 			if err := defaultRuleTpl.Execute(writer, model); err != nil {
-				loggerRouter.Errorf("Error while parsing default rule: %v", err)
+				loggerRouter.Error().Err(err).Msg("Error while parsing default rule")
 				delete(configuration.Routers, routerName)
 				continue
 			}
 
 			router.Rule = writer.String()
 			if len(router.Rule) == 0 {
-				loggerRouter.Error("Undefined rule")
+				loggerRouter.Error().Msg("Undefined rule")
 				delete(configuration.Routers, routerName)
 				continue
 			}
@@ -418,8 +422,8 @@ func BuildRouterConfiguration(ctx context.Context, configuration *dynamic.HTTPCo
 		if len(router.Service) == 0 {
 			if len(configuration.Services) > 1 {
 				delete(configuration.Routers, routerName)
-				loggerRouter.
-					Error("Could not define the service name for the router: too many services")
+				loggerRouter.Error().
+					Msg("Could not define the service name for the router: too many services")
 				continue
 			}
 
diff --git a/pkg/provider/consulcatalog/config.go b/pkg/provider/consulcatalog/config.go
index aaa2aa724b..af8e7830c1 100644
--- a/pkg/provider/consulcatalog/config.go
+++ b/pkg/provider/consulcatalog/config.go
@@ -10,9 +10,10 @@ import (
 	"strings"
 
 	"github.com/hashicorp/consul/api"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
 	"github.com/traefik/traefik/v2/pkg/config/label"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/provider"
 	"github.com/traefik/traefik/v2/pkg/provider/constraints"
 )
@@ -22,17 +23,17 @@ func (p *Provider) buildConfiguration(ctx context.Context, items []itemData, cer
 
 	for _, item := range items {
 		svcName := provider.Normalize(item.Node + "-" + item.Name + "-" + item.ID)
-		ctxSvc := log.With(ctx, log.Str(log.ServiceName, svcName))
+
+		logger := log.Ctx(ctx).With().Str(logs.ServiceName, svcName).Logger()
+		ctxSvc := logger.WithContext(ctx)
 
 		if !p.keepContainer(ctxSvc, item) {
 			continue
 		}
 
-		logger := log.FromContext(ctxSvc)
-
 		confFromLabel, err := label.DecodeConfiguration(item.Labels)
 		if err != nil {
-			logger.Error(err)
+			logger.Error().Err(err).Send()
 			continue
 		}
 
@@ -41,7 +42,7 @@ func (p *Provider) buildConfiguration(ctx context.Context, items []itemData, cer
 			tcpOrUDP = true
 
 			if err := p.buildTCPServiceConfiguration(item, confFromLabel.TCP); err != nil {
-				logger.Error(err)
+				logger.Error().Err(err).Send()
 				continue
 			}
 
@@ -52,7 +53,7 @@ func (p *Provider) buildConfiguration(ctx context.Context, items []itemData, cer
 			tcpOrUDP = true
 
 			if err := p.buildUDPServiceConfiguration(item, confFromLabel.UDP); err != nil {
-				logger.Error(err)
+				logger.Error().Err(err).Send()
 				continue
 			}
 			provider.BuildUDPRouterConfiguration(ctxSvc, confFromLabel.UDP)
@@ -77,7 +78,7 @@ func (p *Provider) buildConfiguration(ctx context.Context, items []itemData, cer
 		}
 
 		if err = p.buildServiceConfiguration(item, confFromLabel.HTTP); err != nil {
-			logger.Error(err)
+			logger.Error().Err(err).Send()
 			continue
 		}
 
@@ -98,30 +99,30 @@ func (p *Provider) buildConfiguration(ctx context.Context, items []itemData, cer
 }
 
 func (p *Provider) keepContainer(ctx context.Context, item itemData) bool {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
 	if !item.ExtraConf.Enable {
-		logger.Debug("Filtering disabled item")
+		logger.Debug().Msg("Filtering disabled item")
 		return false
 	}
 
 	if !p.ConnectAware && item.ExtraConf.ConsulCatalog.Connect {
-		logger.Debugf("Filtering out Connect aware item, Connect support is not enabled")
+		logger.Debug().Msg("Filtering out Connect aware item, Connect support is not enabled")
 		return false
 	}
 
 	matches, err := constraints.MatchTags(item.Tags, p.Constraints)
 	if err != nil {
-		logger.Errorf("Error matching constraint expressions: %v", err)
+		logger.Error().Err(err).Msg("Error matching constraint expressions")
 		return false
 	}
 	if !matches {
-		logger.Debugf("Container pruned by constraint expressions: %q", p.Constraints)
+		logger.Debug().Msgf("Container pruned by constraint expressions: %q", p.Constraints)
 		return false
 	}
 
 	if item.Status != api.HealthPassing && item.Status != api.HealthWarning {
-		logger.Debug("Filtering unhealthy or starting item")
+		logger.Debug().Msg("Filtering unhealthy or starting item")
 		return false
 	}
 
diff --git a/pkg/provider/consulcatalog/consul_catalog.go b/pkg/provider/consulcatalog/consul_catalog.go
index effd67cacf..dc0b82267f 100644
--- a/pkg/provider/consulcatalog/consul_catalog.go
+++ b/pkg/provider/consulcatalog/consul_catalog.go
@@ -11,11 +11,12 @@ import (
 	"github.com/hashicorp/consul/api"
 	"github.com/hashicorp/consul/api/watch"
 	"github.com/hashicorp/go-hclog"
-	"github.com/sirupsen/logrus"
+	"github.com/rs/zerolog"
+	"github.com/rs/zerolog/log"
 	ptypes "github.com/traefik/paerser/types"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
 	"github.com/traefik/traefik/v2/pkg/job"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/provider"
 	"github.com/traefik/traefik/v2/pkg/provider/constraints"
 	"github.com/traefik/traefik/v2/pkg/safe"
@@ -58,7 +59,7 @@ func (p *ProviderBuilder) BuildProviders() []*Provider {
 	// We can warn about that, because we've already made sure before that
 	// Namespace and Namespaces are mutually exclusive.
 	if p.Namespace != "" {
-		log.WithoutContext().Warnf("Namespace option is deprecated, please use the Namespaces option instead.")
+		log.Warn().Msg("Namespace option is deprecated, please use the Namespaces option instead.")
 	}
 
 	if len(p.Namespaces) == 0 {
@@ -166,8 +167,8 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 	}
 
 	pool.GoCtx(func(routineCtx context.Context) {
-		ctxLog := log.With(routineCtx, log.Str(log.ProviderName, p.name))
-		logger := log.FromContext(ctxLog)
+		logger := log.Ctx(routineCtx).With().Str(logs.ProviderName, p.name).Logger()
+		ctxLog := logger.WithContext(routineCtx)
 
 		operation := func() error {
 			ctx, cancel := context.WithCancel(ctxLog)
@@ -194,7 +195,7 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 			// that gets resolved before the certificates are available
 			// will cause an error condition.
 			if p.ConnectAware && !certInfo.isReady() {
-				logger.Infof("Waiting for Connect certificate before building first configuration")
+				logger.Info().Msg("Waiting for Connect certificate before building first configuration")
 				select {
 				case <-ctx.Done():
 					return nil
@@ -242,12 +243,12 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 		}
 
 		notify := func(err error, time time.Duration) {
-			logger.Errorf("Provider connection error %+v, retrying in %s", err, time)
+			logger.Error().Err(err).Msgf("Provider connection error, retrying in %s", time)
 		}
 
 		err := backoff.RetryNotify(safe.OperationWithRecover(operation), backoff.WithContext(job.NewBackOff(backoff.NewExponentialBackOff()), ctxLog), notify)
 		if err != nil {
-			logger.Errorf("Cannot connect to consul catalog server %+v", err)
+			logger.Error().Err(err).Msg("Cannot connect to consul catalog server")
 		}
 	})
 
@@ -281,32 +282,32 @@ func (p *Provider) getConsulServicesData(ctx context.Context) ([]itemData, error
 
 	var data []itemData
 	for name, tags := range serviceNames {
-		logger := log.FromContext(log.With(ctx, log.Str("serviceName", name)))
+		logger := log.Ctx(ctx).With().Str("serviceName", name).Logger()
 
 		extraConf, err := p.getExtraConf(tagsToNeutralLabels(tags, p.Prefix))
 		if err != nil {
-			logger.Errorf("Skip service: %v", err)
+			logger.Error().Err(err).Msg("Skip service")
 			continue
 		}
 
 		if !extraConf.Enable {
-			logger.Debug("Filtering disabled item")
+			logger.Debug().Msg("Filtering disabled item")
 			continue
 		}
 
 		matches, err := constraints.MatchTags(tags, p.Constraints)
 		if err != nil {
-			logger.Errorf("Error matching constraint expressions: %v", err)
+			logger.Error().Err(err).Msg("Error matching constraint expressions")
 			continue
 		}
 
 		if !matches {
-			logger.Debugf("Container pruned by constraint expressions: %q", p.Constraints)
+			logger.Debug().Msgf("Container pruned by constraint expressions: %q", p.Constraints)
 			continue
 		}
 
 		if !p.ConnectAware && extraConf.ConsulCatalog.Connect {
-			logger.Debugf("Filtering out Connect aware item, Connect support is not enabled")
+			logger.Debug().Msg("Filtering out Connect aware item, Connect support is not enabled")
 			continue
 		}
 
@@ -346,7 +347,7 @@ func (p *Provider) getConsulServicesData(ctx context.Context) ([]itemData, error
 
 			extraConf, err := p.getExtraConf(item.Labels)
 			if err != nil {
-				log.FromContext(ctx).Errorf("Skip item %s: %v", item.Name, err)
+				log.Ctx(ctx).Error().Err(err).Msgf("Skip item %s", item.Name)
 				continue
 			}
 			item.ExtraConf = extraConf
@@ -424,8 +425,9 @@ func (p *Provider) watchServices(ctx context.Context) error {
 
 	logger := hclog.New(&hclog.LoggerOptions{
 		Name:       "consulcatalog",
-		Level:      hclog.LevelFromString(logrus.GetLevel().String()),
+		Level:      hclog.LevelFromString(log.Logger.GetLevel().String()),
 		JSONFormat: true,
+		Output:     logs.NoLevel(log.Logger, zerolog.DebugLevel),
 	})
 
 	errChan := make(chan error, 2)
@@ -455,13 +457,13 @@ func (p *Provider) watchServices(ctx context.Context) error {
 func rootsWatchHandler(ctx context.Context, dest chan<- []string) func(watch.BlockingParamVal, interface{}) {
 	return func(_ watch.BlockingParamVal, raw interface{}) {
 		if raw == nil {
-			log.FromContext(ctx).Errorf("Root certificate watcher called with nil")
+			log.Ctx(ctx).Error().Msg("Root certificate watcher called with nil")
 			return
 		}
 
 		v, ok := raw.(*api.CARootList)
 		if !ok || v == nil {
-			log.FromContext(ctx).Errorf("Invalid result for root certificate watcher")
+			log.Ctx(ctx).Error().Msg("Invalid result for root certificate watcher")
 			return
 		}
 
@@ -485,13 +487,13 @@ type keyPair struct {
 func leafWatcherHandler(ctx context.Context, dest chan<- keyPair) func(watch.BlockingParamVal, interface{}) {
 	return func(_ watch.BlockingParamVal, raw interface{}) {
 		if raw == nil {
-			log.FromContext(ctx).Errorf("Leaf certificate watcher called with nil")
+			log.Ctx(ctx).Error().Msg("Leaf certificate watcher called with nil")
 			return
 		}
 
 		v, ok := raw.(*api.LeafCert)
 		if !ok || v == nil {
-			log.FromContext(ctx).Errorf("Invalid result for leaf certificate watcher")
+			log.Ctx(ctx).Error().Msg("Invalid result for leaf certificate watcher")
 			return
 		}
 
@@ -531,8 +533,9 @@ func (p *Provider) watchConnectTLS(ctx context.Context) error {
 
 	hclogger := hclog.New(&hclog.LoggerOptions{
 		Name:       "consulcatalog",
-		Level:      hclog.LevelFromString(logrus.GetLevel().String()),
+		Level:      hclog.LevelFromString(log.Logger.GetLevel().String()),
 		JSONFormat: true,
+		Output:     logs.NoLevel(log.Logger, zerolog.DebugLevel),
 	})
 
 	errChan := make(chan error, 2)
@@ -573,7 +576,7 @@ func (p *Provider) watchConnectTLS(ctx context.Context) error {
 			leaf: leafCerts,
 		}
 		if newCertInfo.isReady() && !newCertInfo.equals(certInfo) {
-			log.FromContext(ctx).Debugf("Updating connect certs for service %s", p.ServiceName)
+			log.Ctx(ctx).Debug().Msgf("Updating connect certs for service %s", p.ServiceName)
 
 			certInfo = newCertInfo
 
diff --git a/pkg/provider/docker/config.go b/pkg/provider/docker/config.go
index f006d7b57a..e23575caf5 100644
--- a/pkg/provider/docker/config.go
+++ b/pkg/provider/docker/config.go
@@ -9,9 +9,10 @@ import (
 
 	dockertypes "github.com/docker/docker/api/types"
 	"github.com/docker/go-connections/nat"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
 	"github.com/traefik/traefik/v2/pkg/config/label"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/provider"
 	"github.com/traefik/traefik/v2/pkg/provider/constraints"
 )
@@ -21,17 +22,17 @@ func (p *Provider) buildConfiguration(ctx context.Context, containersInspected [
 
 	for _, container := range containersInspected {
 		containerName := getServiceName(container) + "-" + container.ID
-		ctxContainer := log.With(ctx, log.Str("container", containerName))
+
+		logger := log.Ctx(ctx).With().Str("container", containerName).Logger()
+		ctxContainer := logger.WithContext(ctx)
 
 		if !p.keepContainer(ctxContainer, container) {
 			continue
 		}
 
-		logger := log.FromContext(ctxContainer)
-
 		confFromLabel, err := label.DecodeConfiguration(container.Labels)
 		if err != nil {
-			logger.Error(err)
+			logger.Error().Err(err).Send()
 			continue
 		}
 
@@ -41,7 +42,7 @@ func (p *Provider) buildConfiguration(ctx context.Context, containersInspected [
 
 			err := p.buildTCPServiceConfiguration(ctxContainer, container, confFromLabel.TCP)
 			if err != nil {
-				logger.Error(err)
+				logger.Error().Err(err).Send()
 				continue
 			}
 			provider.BuildTCPRouterConfiguration(ctxContainer, confFromLabel.TCP)
@@ -52,7 +53,7 @@ func (p *Provider) buildConfiguration(ctx context.Context, containersInspected [
 
 			err := p.buildUDPServiceConfiguration(ctxContainer, container, confFromLabel.UDP)
 			if err != nil {
-				logger.Error(err)
+				logger.Error().Err(err).Send()
 				continue
 			}
 			provider.BuildUDPRouterConfiguration(ctxContainer, confFromLabel.UDP)
@@ -67,7 +68,7 @@ func (p *Provider) buildConfiguration(ctx context.Context, containersInspected [
 
 		err = p.buildServiceConfiguration(ctxContainer, container, confFromLabel.HTTP)
 		if err != nil {
-			logger.Error(err)
+			logger.Error().Err(err).Send()
 			continue
 		}
 
@@ -106,7 +107,7 @@ func (p *Provider) buildTCPServiceConfiguration(ctx context.Context, container d
 	}
 
 	for name, service := range configuration.Services {
-		ctx := log.With(ctx, log.Str(log.ServiceName, name))
+		ctx := log.Ctx(ctx).With().Str(logs.ServiceName, name).Logger().WithContext(ctx)
 		if err := p.addServerTCP(ctx, container, service.LoadBalancer); err != nil {
 			return fmt.Errorf("service %q error: %w", name, err)
 		}
@@ -130,7 +131,7 @@ func (p *Provider) buildUDPServiceConfiguration(ctx context.Context, container d
 	}
 
 	for name, service := range configuration.Services {
-		ctx := log.With(ctx, log.Str(log.ServiceName, name))
+		ctx := log.Ctx(ctx).With().Str(logs.ServiceName, name).Logger().WithContext(ctx)
 		if err := p.addServerUDP(ctx, container, service.LoadBalancer); err != nil {
 			return fmt.Errorf("service %q error: %w", name, err)
 		}
@@ -156,7 +157,7 @@ func (p *Provider) buildServiceConfiguration(ctx context.Context, container dock
 	}
 
 	for name, service := range configuration.Services {
-		ctx := log.With(ctx, log.Str(log.ServiceName, name))
+		ctx := log.Ctx(ctx).With().Str(logs.ServiceName, name).Logger().WithContext(ctx)
 		if err := p.addServer(ctx, container, service.LoadBalancer); err != nil {
 			return fmt.Errorf("service %q error: %w", name, err)
 		}
@@ -166,25 +167,25 @@ func (p *Provider) buildServiceConfiguration(ctx context.Context, container dock
 }
 
 func (p *Provider) keepContainer(ctx context.Context, container dockerData) bool {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
 	if !container.ExtraConf.Enable {
-		logger.Debug("Filtering disabled container")
+		logger.Debug().Msg("Filtering disabled container")
 		return false
 	}
 
 	matches, err := constraints.MatchLabels(container.Labels, p.Constraints)
 	if err != nil {
-		logger.Errorf("Error matching constraints expression: %v", err)
+		logger.Error().Err(err).Msg("Error matching constraints expression")
 		return false
 	}
 	if !matches {
-		logger.Debugf("Container pruned by constraint expression: %q", p.Constraints)
+		logger.Debug().Msgf("Container pruned by constraint expression: %q", p.Constraints)
 		return false
 	}
 
 	if !p.AllowEmptyServices && container.Health != "" && container.Health != dockertypes.Healthy {
-		logger.Debug("Filtering unhealthy or starting container")
+		logger.Debug().Msg("Filtering unhealthy or starting container")
 		return false
 	}
 
@@ -274,7 +275,7 @@ func (p *Provider) addServer(ctx context.Context, container dockerData, loadBala
 }
 
 func (p *Provider) getIPPort(ctx context.Context, container dockerData, serverPort string) (string, string, error) {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
 	var ip, port string
 	usedBound := false
@@ -283,9 +284,9 @@ func (p *Provider) getIPPort(ctx context.Context, container dockerData, serverPo
 		portBinding, err := p.getPortBinding(container, serverPort)
 		switch {
 		case err != nil:
-			logger.Infof("Unable to find a binding for container %q, falling back on its internal IP/Port.", container.Name)
+			logger.Info().Msgf("Unable to find a binding for container %q, falling back on its internal IP/Port.", container.Name)
 		case portBinding.HostIP == "0.0.0.0" || len(portBinding.HostIP) == 0:
-			logger.Infof("Cannot determine the IP address (got %q) for %q's binding, falling back on its internal IP/Port.", portBinding.HostIP, container.Name)
+			logger.Info().Msgf("Cannot determine the IP address (got %q) for %q's binding, falling back on its internal IP/Port.", portBinding.HostIP, container.Name)
 		default:
 			ip = portBinding.HostIP
 			port = portBinding.HostPort
@@ -306,7 +307,7 @@ func (p *Provider) getIPPort(ctx context.Context, container dockerData, serverPo
 }
 
 func (p Provider) getIPAddress(ctx context.Context, container dockerData) string {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
 	if container.ExtraConf.Docker.Network != "" {
 		settings := container.NetworkSettings
@@ -316,7 +317,7 @@ func (p Provider) getIPAddress(ctx context.Context, container dockerData) string
 				return network.Addr
 			}
 
-			logger.Warnf("Could not find network named '%s' for container '%s'! Maybe you're missing the project's prefix in the label? Defaulting to first available network.", container.ExtraConf.Docker.Network, container.Name)
+			logger.Warn().Msgf("Could not find network named '%s' for container '%s'! Maybe you're missing the project's prefix in the label? Defaulting to first available network.", container.ExtraConf.Docker.Network, container.Name)
 		}
 	}
 
@@ -336,14 +337,14 @@ func (p Provider) getIPAddress(ctx context.Context, container dockerData) string
 	if container.NetworkSettings.NetworkMode.IsContainer() {
 		dockerClient, err := p.createClient()
 		if err != nil {
-			logger.Warnf("Unable to get IP address: %s", err)
+			logger.Warn().Err(err).Msg("Unable to get IP address")
 			return ""
 		}
 
 		connectedContainer := container.NetworkSettings.NetworkMode.ConnectedContainer()
 		containerInspected, err := dockerClient.ContainerInspect(context.Background(), connectedContainer)
 		if err != nil {
-			logger.Warnf("Unable to get IP address for container %s : Failed to inspect container ID %s, error: %s", container.Name, connectedContainer, err)
+			logger.Warn().Err(err).Msgf("Unable to get IP address for container %s: failed to inspect container ID %s", container.Name, connectedContainer)
 			return ""
 		}
 
@@ -352,7 +353,7 @@ func (p Provider) getIPAddress(ctx context.Context, container dockerData) string
 		containerParsed := parseContainer(containerInspected)
 		extraConf, err := p.getConfiguration(containerParsed)
 		if err != nil {
-			logger.Warnf("Unable to get IP address for container %s : failed to get extra configuration for container %s: %s", container.Name, containerInspected.Name, err)
+			logger.Warn().Err(err).Msgf("Unable to get IP address for container %s : failed to get extra configuration for container %s", container.Name, containerInspected.Name)
 			return ""
 		}
 
@@ -368,7 +369,7 @@ func (p Provider) getIPAddress(ctx context.Context, container dockerData) string
 		return network.Addr
 	}
 
-	logger.Warn("Unable to find the IP address.")
+	logger.Warn().Msg("Unable to find the IP address.")
 	return ""
 }
 
diff --git a/pkg/provider/docker/docker.go b/pkg/provider/docker/docker.go
index 0516620ce8..eefd737ad7 100644
--- a/pkg/provider/docker/docker.go
+++ b/pkg/provider/docker/docker.go
@@ -23,10 +23,11 @@ import (
 	"github.com/docker/docker/client"
 	"github.com/docker/go-connections/nat"
 	"github.com/docker/go-connections/sockets"
+	"github.com/rs/zerolog/log"
 	ptypes "github.com/traefik/paerser/types"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
 	"github.com/traefik/traefik/v2/pkg/job"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/provider"
 	"github.com/traefik/traefik/v2/pkg/safe"
 	"github.com/traefik/traefik/v2/pkg/types"
@@ -162,7 +163,7 @@ func (p *Provider) getClientOpts() ([]client.Opt, error) {
 	}
 
 	if p.TLS != nil {
-		ctx := log.With(context.Background(), log.Str(log.ProviderName, "docker"))
+		ctx := log.With().Str(logs.ProviderName, "docker").Logger().WithContext(context.Background())
 
 		conf, err := p.TLS.CreateTLSConfig(ctx)
 		if err != nil {
@@ -191,40 +192,41 @@ func (p *Provider) getClientOpts() ([]client.Opt, error) {
 // Provide allows the docker provider to provide configurations to traefik using the given configuration channel.
 func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error {
 	pool.GoCtx(func(routineCtx context.Context) {
-		ctxLog := log.With(routineCtx, log.Str(log.ProviderName, "docker"))
-		logger := log.FromContext(ctxLog)
+		logger := log.Ctx(routineCtx).With().Str(logs.ProviderName, "docker").Logger()
+		ctxLog := logger.WithContext(routineCtx)
 
 		operation := func() error {
 			var err error
 			ctx, cancel := context.WithCancel(ctxLog)
 			defer cancel()
-
-			ctx = log.With(ctx, log.Str(log.ProviderName, "docker"))
+			ctx = log.Ctx(ctx).With().Str(logs.ProviderName, "docker").Logger().WithContext(ctx)
 
 			dockerClient, err := p.createClient()
 			if err != nil {
-				logger.Errorf("Failed to create a client for docker, error: %s", err)
+				logger.Error().Err(err).Msg("Failed to create a client for docker, error")
 				return err
 			}
 			defer dockerClient.Close()
 
 			serverVersion, err := dockerClient.ServerVersion(ctx)
 			if err != nil {
-				logger.Errorf("Failed to retrieve information of the docker client and server host: %s", err)
+				logger.Error().Err(err).Msg("Failed to retrieve information of the docker client and server host")
 				return err
 			}
-			logger.Debugf("Provider connection established with docker %s (API %s)", serverVersion.Version, serverVersion.APIVersion)
+
+			logger.Debug().Msgf("Provider connection established with docker %s (API %s)", serverVersion.Version, serverVersion.APIVersion)
+
 			var dockerDataList []dockerData
 			if p.SwarmMode {
 				dockerDataList, err = p.listServices(ctx, dockerClient)
 				if err != nil {
-					logger.Errorf("Failed to list services for docker swarm mode, error %s", err)
+					logger.Error().Err(err).Msg("Failed to list services for docker swarm mode")
 					return err
 				}
 			} else {
 				dockerDataList, err = p.listContainers(ctx, dockerClient)
 				if err != nil {
-					logger.Errorf("Failed to list containers for docker, error %s", err)
+					logger.Error().Err(err).Msg("Failed to list containers for docker")
 					return err
 				}
 			}
@@ -242,8 +244,8 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 					ticker := time.NewTicker(time.Duration(p.SwarmModeRefreshSeconds))
 
 					pool.GoCtx(func(ctx context.Context) {
-						ctx = log.With(ctx, log.Str(log.ProviderName, "docker"))
-						logger := log.FromContext(ctx)
+						logger := log.Ctx(ctx).With().Str(logs.ProviderName, "docker").Logger()
+						ctx = logger.WithContext(ctx)
 
 						defer close(errChan)
 						for {
@@ -251,7 +253,7 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 							case <-ticker.C:
 								services, err := p.listServices(ctx, dockerClient)
 								if err != nil {
-									logger.Errorf("Failed to list services for docker swarm mode, error %s", err)
+									logger.Error().Err(err).Msg("Failed to list services for docker swarm mode")
 									errChan <- err
 									return
 								}
@@ -282,10 +284,10 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 					}
 
 					startStopHandle := func(m eventtypes.Message) {
-						logger.Debugf("Provider event received %+v", m)
+						logger.Debug().Msgf("Provider event received %+v", m)
 						containers, err := p.listContainers(ctx, dockerClient)
 						if err != nil {
-							logger.Errorf("Failed to list containers for docker, error %s", err)
+							logger.Error().Err(err).Msg("Failed to list containers for docker")
 							// Call cancel to get out of the monitor
 							return
 						}
@@ -314,7 +316,7 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 							}
 						case err := <-errc:
 							if errors.Is(err, io.EOF) {
-								logger.Debug("Provider event stream closed")
+								logger.Debug().Msg("Provider event stream closed")
 							}
 							return err
 						case <-ctx.Done():
@@ -327,11 +329,11 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 		}
 
 		notify := func(err error, time time.Duration) {
-			logger.Errorf("Provider connection error %+v, retrying in %s", err, time)
+			logger.Error().Err(err).Msgf("Provider connection error, retrying in %s", time)
 		}
 		err := backoff.RetryNotify(safe.OperationWithRecover(operation), backoff.WithContext(job.NewBackOff(backoff.NewExponentialBackOff()), ctxLog), notify)
 		if err != nil {
-			logger.Errorf("Cannot connect to docker server %+v", err)
+			logger.Error().Err(err).Msg("Cannot connect to docker server")
 		}
 	})
 
@@ -354,7 +356,7 @@ func (p *Provider) listContainers(ctx context.Context, dockerClient client.Conta
 
 		extraConf, err := p.getConfiguration(dData)
 		if err != nil {
-			log.FromContext(ctx).Errorf("Skip container %s: %v", getServiceName(dData), err)
+			log.Ctx(ctx).Error().Err(err).Msgf("Skip container %s", getServiceName(dData))
 			continue
 		}
 		dData.ExtraConf = extraConf
@@ -367,7 +369,7 @@ func (p *Provider) listContainers(ctx context.Context, dockerClient client.Conta
 func inspectContainers(ctx context.Context, dockerClient client.ContainerAPIClient, containerID string) dockerData {
 	containerInspected, err := dockerClient.ContainerInspect(ctx, containerID)
 	if err != nil {
-		log.FromContext(ctx).Warnf("Failed to inspect container %s, error: %s", containerID, err)
+		log.Ctx(ctx).Warn().Err(err).Msgf("Failed to inspect container %s", containerID)
 		return dockerData{}
 	}
 
@@ -428,7 +430,7 @@ func parseContainer(container dockertypes.ContainerJSON) dockerData {
 }
 
 func (p *Provider) listServices(ctx context.Context, dockerClient client.APIClient) ([]dockerData, error) {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
 	serviceList, err := dockerClient.ServiceList(ctx, dockertypes.ServiceListOptions{})
 	if err != nil {
@@ -450,7 +452,7 @@ func (p *Provider) listServices(ctx context.Context, dockerClient client.APIClie
 
 	networkList, err := dockerClient.NetworkList(ctx, dockertypes.NetworkListOptions{Filters: networkListArgs})
 	if err != nil {
-		logger.Debugf("Failed to network inspect on client for docker, error: %s", err)
+		logger.Debug().Err(err).Msg("Failed to network inspect on client for docker")
 		return nil, err
 	}
 
@@ -466,7 +468,7 @@ func (p *Provider) listServices(ctx context.Context, dockerClient client.APIClie
 	for _, service := range serviceList {
 		dData, err := p.parseService(ctx, service, networkMap)
 		if err != nil {
-			logger.Errorf("Skip container %s: %v", getServiceName(dData), err)
+			logger.Error().Err(err).Msgf("Skip container %s", getServiceName(dData))
 			continue
 		}
 
@@ -478,7 +480,7 @@ func (p *Provider) listServices(ctx context.Context, dockerClient client.APIClie
 			isGlobalSvc := service.Spec.Mode.Global != nil
 			dockerDataListTasks, err = listTasks(ctx, dockerClient, service.ID, dData, networkMap, isGlobalSvc)
 			if err != nil {
-				logger.Warn(err)
+				logger.Warn().Err(err).Send()
 			} else {
 				dockerDataList = append(dockerDataList, dockerDataListTasks...)
 			}
@@ -488,7 +490,7 @@ func (p *Provider) listServices(ctx context.Context, dockerClient client.APIClie
 }
 
 func (p *Provider) parseService(ctx context.Context, service swarmtypes.Service, networkMap map[string]*dockertypes.NetworkResource) (dockerData, error) {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
 	dData := dockerData{
 		ID:              service.ID,
@@ -507,7 +509,7 @@ func (p *Provider) parseService(ctx context.Context, service swarmtypes.Service,
 	if service.Spec.EndpointSpec != nil {
 		if service.Spec.EndpointSpec.Mode == swarmtypes.ResolutionModeDNSRR {
 			if dData.ExtraConf.Docker.LBSwarm {
-				logger.Warnf("Ignored %s endpoint-mode not supported, service name: %s. Fallback to Traefik load balancing", swarmtypes.ResolutionModeDNSRR, service.Spec.Annotations.Name)
+				logger.Warn().Msgf("Ignored %s endpoint-mode not supported, service name: %s. Fallback to Traefik load balancing", swarmtypes.ResolutionModeDNSRR, service.Spec.Annotations.Name)
 			}
 		} else if service.Spec.EndpointSpec.Mode == swarmtypes.ResolutionModeVIP {
 			dData.NetworkSettings.Networks = make(map[string]*networkData)
@@ -523,10 +525,10 @@ func (p *Provider) parseService(ctx context.Context, service swarmtypes.Service,
 						}
 						dData.NetworkSettings.Networks[network.Name] = network
 					} else {
-						logger.Debugf("No virtual IPs found in network %s", virtualIP.NetworkID)
+						logger.Debug().Msgf("No virtual IPs found in network %s", virtualIP.NetworkID)
 					}
 				} else {
-					logger.Debugf("Network not found, id: %s", virtualIP.NetworkID)
+					logger.Debug().Msgf("Network not found, id: %s", virtualIP.NetworkID)
 				}
 			}
 		}
@@ -591,7 +593,7 @@ func parseTasks(ctx context.Context, task swarmtypes.Task, serviceDockerData doc
 						dData.NetworkSettings.Networks[network.Name] = network
 					}
 				} else {
-					log.FromContext(ctx).Debugf("No IP addresses found for network %s", virtualIP.Network.ID)
+					log.Ctx(ctx).Debug().Msgf("No IP addresses found for network %s", virtualIP.Network.ID)
 				}
 			}
 		}
diff --git a/pkg/provider/ecs/config.go b/pkg/provider/ecs/config.go
index 8c93625142..8dbdf15502 100644
--- a/pkg/provider/ecs/config.go
+++ b/pkg/provider/ecs/config.go
@@ -10,9 +10,9 @@ import (
 
 	"github.com/aws/aws-sdk-go/service/ec2"
 	"github.com/docker/go-connections/nat"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
 	"github.com/traefik/traefik/v2/pkg/config/label"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/provider"
 	"github.com/traefik/traefik/v2/pkg/provider/constraints"
 )
@@ -22,17 +22,16 @@ func (p *Provider) buildConfiguration(ctx context.Context, instances []ecsInstan
 
 	for _, instance := range instances {
 		instanceName := getServiceName(instance) + "-" + instance.ID
-		ctxContainer := log.With(ctx, log.Str("ecs-instance", instanceName))
+		logger := log.Ctx(ctx).With().Str("ecs-instance", instanceName).Logger()
+		ctxContainer := logger.WithContext(ctx)
 
 		if !p.filterInstance(ctxContainer, instance) {
 			continue
 		}
 
-		logger := log.FromContext(ctxContainer)
-
 		confFromLabel, err := label.DecodeConfiguration(instance.Labels)
 		if err != nil {
-			logger.Error(err)
+			logger.Error().Err(err).Send()
 			continue
 		}
 
@@ -42,7 +41,7 @@ func (p *Provider) buildConfiguration(ctx context.Context, instances []ecsInstan
 
 			err := p.buildTCPServiceConfiguration(instance, confFromLabel.TCP)
 			if err != nil {
-				logger.Error(err)
+				logger.Error().Err(err).Send()
 				continue
 			}
 			provider.BuildTCPRouterConfiguration(ctxContainer, confFromLabel.TCP)
@@ -53,7 +52,7 @@ func (p *Provider) buildConfiguration(ctx context.Context, instances []ecsInstan
 
 			err := p.buildUDPServiceConfiguration(instance, confFromLabel.UDP)
 			if err != nil {
-				logger.Error(err)
+				logger.Error().Err(err).Send()
 				continue
 			}
 			provider.BuildUDPRouterConfiguration(ctxContainer, confFromLabel.UDP)
@@ -68,7 +67,7 @@ func (p *Provider) buildConfiguration(ctx context.Context, instances []ecsInstan
 
 		err = p.buildServiceConfiguration(ctxContainer, instance, confFromLabel.HTTP)
 		if err != nil {
-			logger.Error(err)
+			logger.Error().Err(err).Send()
 			continue
 		}
 
@@ -156,40 +155,40 @@ func (p *Provider) buildServiceConfiguration(_ context.Context, instance ecsInst
 }
 
 func (p *Provider) filterInstance(ctx context.Context, instance ecsInstance) bool {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
 	if instance.machine == nil {
-		logger.Debug("Filtering ecs instance with nil machine")
+		logger.Debug().Msg("Filtering ecs instance with nil machine")
 		return false
 	}
 
 	if strings.ToLower(instance.machine.state) != ec2.InstanceStateNameRunning {
-		logger.Debugf("Filtering ecs instance with an incorrect state %s (%s) (state = %s)", instance.Name, instance.ID, instance.machine.state)
+		logger.Debug().Msgf("Filtering ecs instance with an incorrect state %s (%s) (state = %s)", instance.Name, instance.ID, instance.machine.state)
 		return false
 	}
 
 	if instance.machine.healthStatus == "UNHEALTHY" {
-		logger.Debugf("Filtering unhealthy ecs instance %s (%s)", instance.Name, instance.ID)
+		logger.Debug().Msgf("Filtering unhealthy ecs instance %s (%s)", instance.Name, instance.ID)
 		return false
 	}
 
 	if len(instance.machine.privateIP) == 0 {
-		logger.Debugf("Filtering ecs instance without an ip address %s (%s)", instance.Name, instance.ID)
+		logger.Debug().Msgf("Filtering ecs instance without an ip address %s (%s)", instance.Name, instance.ID)
 		return false
 	}
 
 	if !instance.ExtraConf.Enable {
-		logger.Debugf("Filtering disabled ecs instance %s (%s)", instance.Name, instance.ID)
+		logger.Debug().Msgf("Filtering disabled ecs instance %s (%s)", instance.Name, instance.ID)
 		return false
 	}
 
 	matches, err := constraints.MatchLabels(instance.Labels, p.Constraints)
 	if err != nil {
-		logger.Errorf("Error matching constraint expression: %v", err)
+		logger.Error().Err(err).Msg("Error matching constraint expression")
 		return false
 	}
 	if !matches {
-		logger.Debugf("Container pruned by constraint expression: %q", p.Constraints)
+		logger.Debug().Msgf("Container pruned by constraint expression: %q", p.Constraints)
 		return false
 	}
 
diff --git a/pkg/provider/ecs/ecs.go b/pkg/provider/ecs/ecs.go
index fa7d2185ab..ac78b5d6f7 100644
--- a/pkg/provider/ecs/ecs.go
+++ b/pkg/provider/ecs/ecs.go
@@ -17,9 +17,11 @@ import (
 	"github.com/aws/aws-sdk-go/service/ssm"
 	"github.com/cenkalti/backoff/v4"
 	"github.com/patrickmn/go-cache"
+	"github.com/rs/zerolog"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
 	"github.com/traefik/traefik/v2/pkg/job"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/provider"
 	"github.com/traefik/traefik/v2/pkg/safe"
 )
@@ -99,7 +101,7 @@ func (p *Provider) Init() error {
 	return nil
 }
 
-func (p *Provider) createClient(logger log.Logger) (*awsClient, error) {
+func (p *Provider) createClient(logger zerolog.Logger) (*awsClient, error) {
 	sess, err := session.NewSessionWithOptions(session.Options{
 		SharedConfigState: session.SharedConfigEnable,
 	})
@@ -109,7 +111,7 @@ func (p *Provider) createClient(logger log.Logger) (*awsClient, error) {
 
 	ec2meta := ec2metadata.New(sess)
 	if p.Region == "" && ec2meta.Available() {
-		logger.Infoln("No region provided, querying instance metadata endpoint...")
+		logger.Info().Msg("No region provided, querying instance metadata endpoint...")
 		identity, err := ec2meta.GetInstanceIdentityDocument()
 		if err != nil {
 			return nil, err
@@ -137,9 +139,7 @@ func (p *Provider) createClient(logger log.Logger) (*awsClient, error) {
 		cfg.Region = &p.Region
 	}
 
-	cfg.WithLogger(aws.LoggerFunc(func(args ...interface{}) {
-		logger.Debug(args...)
-	}))
+	cfg.WithLogger(logs.NewAWSWrapper(logger))
 
 	return &awsClient{
 		ecs.New(sess, cfg),
@@ -151,8 +151,8 @@ func (p *Provider) createClient(logger log.Logger) (*awsClient, error) {
 // Provide configuration to traefik from ECS.
 func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error {
 	pool.GoCtx(func(routineCtx context.Context) {
-		ctxLog := log.With(routineCtx, log.Str(log.ProviderName, "ecs"))
-		logger := log.FromContext(ctxLog)
+		logger := log.Ctx(routineCtx).With().Str(logs.ProviderName, "ecs").Logger()
+		ctxLog := logger.WithContext(routineCtx)
 
 		operation := func() error {
 			awsClient, err := p.createClient(logger)
@@ -183,11 +183,11 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 		}
 
 		notify := func(err error, time time.Duration) {
-			logger.Errorf("Provider connection error %+v, retrying in %s", err, time)
+			logger.Error().Err(err).Msgf("Provider connection error, retrying in %s", time)
 		}
 		err := backoff.RetryNotify(safe.OperationWithRecover(operation), backoff.WithContext(job.NewBackOff(backoff.NewExponentialBackOff()), routineCtx), notify)
 		if err != nil {
-			logger.Errorf("Cannot connect to Provider api %+v", err)
+			logger.Error().Err(err).Msg("Cannot connect to Provider api")
 		}
 	})
 
@@ -211,7 +211,7 @@ func (p *Provider) loadConfiguration(ctx context.Context, client *awsClient, con
 // Find all running Provider tasks in a cluster, also collect the task definitions (for docker labels)
 // and the EC2 instance data.
 func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsInstance, error) {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
 	var clustersArn []*string
 	var clusters []string
@@ -242,7 +242,7 @@ func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsI
 
 	var instances []ecsInstance
 
-	logger.Debugf("ECS Clusters: %s", clusters)
+	logger.Debug().Msgf("ECS Clusters: %s", clusters)
 	for _, c := range clusters {
 		input := &ecs.ListTasksInput{
 			Cluster:       &c,
@@ -257,11 +257,11 @@ func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsI
 					Cluster: &c,
 				})
 				if err != nil {
-					logger.Errorf("Unable to describe tasks for %v", page.TaskArns)
+					logger.Error().Msgf("Unable to describe tasks for %v", page.TaskArns)
 				} else {
 					for _, t := range resp.Tasks {
 						if p.HealthyTasksOnly && aws.StringValue(t.HealthStatus) != ecs.HealthStatusHealthy {
-							logger.Debugf("Skipping unhealthy task %s", aws.StringValue(t.TaskArn))
+							logger.Debug().Msgf("Skipping unhealthy task %s", aws.StringValue(t.TaskArn))
 							continue
 						}
 
@@ -314,7 +314,7 @@ func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsI
 				}
 
 				if containerDefinition == nil {
-					logger.Debugf("Unable to find container definition for %s", aws.StringValue(container.Name))
+					logger.Debug().Msgf("Unable to find container definition for %s", aws.StringValue(container.Name))
 					continue
 				}
 
@@ -344,7 +344,7 @@ func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsI
 				} else {
 					miContainerInstance := miInstances[aws.StringValue(task.ContainerInstanceArn)]
 					if containerInstance == nil && miContainerInstance == nil {
-						logger.Errorf("Unable to find container instance information for %s", aws.StringValue(container.Name))
+						logger.Error().Msgf("Unable to find container instance information for %s", aws.StringValue(container.Name))
 						continue
 					}
 
@@ -383,7 +383,7 @@ func (p *Provider) listInstances(ctx context.Context, client *awsClient) ([]ecsI
 
 				extraConf, err := p.getConfiguration(instance)
 				if err != nil {
-					log.FromContext(ctx).Errorf("Skip container %s: %w", getServiceName(instance), err)
+					logger.Error().Err(err).Msgf("Skip container %s", getServiceName(instance))
 					continue
 				}
 				instance.ExtraConf = extraConf
@@ -525,13 +525,13 @@ func (p *Provider) lookupEc2Instances(ctx context.Context, client *awsClient, cl
 }
 
 func (p *Provider) lookupTaskDefinitions(ctx context.Context, client *awsClient, taskDefArns map[string]*ecs.Task) (map[string]*ecs.TaskDefinition, error) {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 	taskDef := make(map[string]*ecs.TaskDefinition)
 
 	for arn, task := range taskDefArns {
 		if definition, ok := existingTaskDefCache.Get(arn); ok {
 			taskDef[arn] = definition.(*ecs.TaskDefinition)
-			logger.Debugf("Found cached task definition for %s. Skipping the call", arn)
+			logger.Debug().Msgf("Found cached task definition for %s. Skipping the call", arn)
 		} else {
 			resp, err := client.ecs.DescribeTaskDefinitionWithContext(ctx, &ecs.DescribeTaskDefinitionInput{
 				TaskDefinition: task.TaskDefinitionArn,
diff --git a/pkg/provider/file/file.go b/pkg/provider/file/file.go
index 08ee0896df..839986cf8e 100644
--- a/pkg/provider/file/file.go
+++ b/pkg/provider/file/file.go
@@ -11,9 +11,10 @@ import (
 	"text/template"
 
 	"github.com/Masterminds/sprig/v3"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/paerser/file"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/provider"
 	"github.com/traefik/traefik/v2/pkg/safe"
 	"github.com/traefik/traefik/v2/pkg/tls"
@@ -75,7 +76,7 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 // BuildConfiguration loads configuration either from file or a directory
 // specified by 'Filename'/'Directory' and returns a 'Configuration' object.
 func (p *Provider) BuildConfiguration() (*dynamic.Configuration, error) {
-	ctx := log.With(context.Background(), log.Str(log.ProviderName, providerName))
+	ctx := log.With().Str(logs.ProviderName, providerName).Logger().WithContext(context.Background())
 
 	if len(p.Directory) > 0 {
 		return p.loadFileConfigFromDirectory(ctx, p.Directory, nil)
@@ -117,7 +118,7 @@ func (p *Provider) addWatcher(pool *safe.Pool, directory string, configurationCh
 					callback(configurationChan, evt)
 				}
 			case err := <-watcher.Errors:
-				log.WithoutContext().WithField(log.ProviderName, providerName).Errorf("Watcher event error: %s", err)
+				log.Error().Str(logs.ProviderName, providerName).Err(err).Msg("Watcher event error")
 			}
 		}
 	})
@@ -130,16 +131,16 @@ func (p *Provider) watcherCallback(configurationChan chan<- dynamic.Message, eve
 		watchItem = p.Directory
 	}
 
-	logger := log.WithoutContext().WithField(log.ProviderName, providerName)
+	logger := log.With().Str(logs.ProviderName, providerName).Logger()
 
 	if _, err := os.Stat(watchItem); err != nil {
-		logger.Errorf("Unable to watch %s : %v", watchItem, err)
+		logger.Error().Err(err).Msgf("Unable to watch %s", watchItem)
 		return
 	}
 
 	configuration, err := p.BuildConfiguration()
 	if err != nil {
-		logger.Errorf("Error occurred during watcher callback: %s", err)
+		logger.Error().Err(err).Msg("Error occurred during watcher callback")
 		return
 	}
 
@@ -176,7 +177,7 @@ func (p *Provider) loadFileConfig(ctx context.Context, filename string, parseTem
 				for _, caFile := range options.ClientAuth.CAFiles {
 					content, err := caFile.Read()
 					if err != nil {
-						log.FromContext(ctx).Error(err)
+						log.Ctx(ctx).Error().Err(err).Send()
 						continue
 					}
 
@@ -197,14 +198,14 @@ func (p *Provider) loadFileConfig(ctx context.Context, filename string, parseTem
 
 				content, err := store.DefaultCertificate.CertFile.Read()
 				if err != nil {
-					log.FromContext(ctx).Error(err)
+					log.Ctx(ctx).Error().Err(err).Send()
 					continue
 				}
 				store.DefaultCertificate.CertFile = tls.FileOrContent(content)
 
 				content, err = store.DefaultCertificate.KeyFile.Read()
 				if err != nil {
-					log.FromContext(ctx).Error(err)
+					log.Ctx(ctx).Error().Err(err).Send()
 					continue
 				}
 				store.DefaultCertificate.KeyFile = tls.FileOrContent(content)
@@ -221,14 +222,14 @@ func (p *Provider) loadFileConfig(ctx context.Context, filename string, parseTem
 			for _, cert := range st.Certificates {
 				content, err := cert.CertFile.Read()
 				if err != nil {
-					log.FromContext(ctx).Error(err)
+					log.Ctx(ctx).Error().Err(err).Send()
 					continue
 				}
 				cert.CertFile = tls.FileOrContent(content)
 
 				content, err = cert.KeyFile.Read()
 				if err != nil {
-					log.FromContext(ctx).Error(err)
+					log.Ctx(ctx).Error().Err(err).Send()
 					continue
 				}
 				cert.KeyFile = tls.FileOrContent(content)
@@ -242,7 +243,7 @@ func (p *Provider) loadFileConfig(ctx context.Context, filename string, parseTem
 			for _, rootCA := range st.RootCAs {
 				content, err := rootCA.Read()
 				if err != nil {
-					log.FromContext(ctx).Error(err)
+					log.Ctx(ctx).Error().Err(err).Send()
 					continue
 				}
 
@@ -261,14 +262,14 @@ func flattenCertificates(ctx context.Context, tlsConfig *dynamic.TLSConfiguratio
 	for _, cert := range tlsConfig.Certificates {
 		content, err := cert.Certificate.CertFile.Read()
 		if err != nil {
-			log.FromContext(ctx).Error(err)
+			log.Ctx(ctx).Error().Err(err).Send()
 			continue
 		}
 		cert.Certificate.CertFile = tls.FileOrContent(string(content))
 
 		content, err = cert.Certificate.KeyFile.Read()
 		if err != nil {
-			log.FromContext(ctx).Error(err)
+			log.Ctx(ctx).Error().Err(err).Send()
 			continue
 		}
 		cert.Certificate.KeyFile = tls.FileOrContent(string(content))
@@ -312,10 +313,10 @@ func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory st
 	configTLSMaps := make(map[*tls.CertAndStores]struct{})
 
 	for _, item := range fileList {
-		logger := log.FromContext(log.With(ctx, log.Str("filename", item.Name())))
+		logger := log.Ctx(ctx).With().Str("filename", item.Name()).Logger()
 
 		if item.IsDir() {
-			configuration, err = p.loadFileConfigFromDirectory(ctx, filepath.Join(directory, item.Name()), configuration)
+			configuration, err = p.loadFileConfigFromDirectory(logger.WithContext(ctx), filepath.Join(directory, item.Name()), configuration)
 			if err != nil {
 				return configuration, fmt.Errorf("unable to load content configuration from subdirectory %s: %w", item, err)
 			}
@@ -330,14 +331,14 @@ func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory st
 		}
 
 		var c *dynamic.Configuration
-		c, err = p.loadFileConfig(ctx, filepath.Join(directory, item.Name()), true)
+		c, err = p.loadFileConfig(logger.WithContext(ctx), filepath.Join(directory, item.Name()), true)
 		if err != nil {
 			return configuration, fmt.Errorf("%s: %w", filepath.Join(directory, item.Name()), err)
 		}
 
 		for name, conf := range c.HTTP.Routers {
 			if _, exists := configuration.HTTP.Routers[name]; exists {
-				logger.WithField(log.RouterName, name).Warn("HTTP router already configured, skipping")
+				logger.Warn().Str(logs.RouterName, name).Msg("HTTP router already configured, skipping")
 			} else {
 				configuration.HTTP.Routers[name] = conf
 			}
@@ -345,7 +346,7 @@ func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory st
 
 		for name, conf := range c.HTTP.Middlewares {
 			if _, exists := configuration.HTTP.Middlewares[name]; exists {
-				logger.WithField(log.MiddlewareName, name).Warn("HTTP middleware already configured, skipping")
+				logger.Warn().Str(logs.MiddlewareName, name).Msg("HTTP middleware already configured, skipping")
 			} else {
 				configuration.HTTP.Middlewares[name] = conf
 			}
@@ -353,7 +354,7 @@ func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory st
 
 		for name, conf := range c.HTTP.Services {
 			if _, exists := configuration.HTTP.Services[name]; exists {
-				logger.WithField(log.ServiceName, name).Warn("HTTP service already configured, skipping")
+				logger.Warn().Str(logs.ServiceName, name).Msg("HTTP service already configured, skipping")
 			} else {
 				configuration.HTTP.Services[name] = conf
 			}
@@ -361,7 +362,7 @@ func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory st
 
 		for name, conf := range c.HTTP.ServersTransports {
 			if _, exists := configuration.HTTP.ServersTransports[name]; exists {
-				logger.WithField(log.ServersTransportName, name).Warn("HTTP servers transport already configured, skipping")
+				logger.Warn().Str(logs.ServersTransportName, name).Msg("HTTP servers transport already configured, skipping")
 			} else {
 				configuration.HTTP.ServersTransports[name] = conf
 			}
@@ -369,7 +370,7 @@ func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory st
 
 		for name, conf := range c.TCP.Routers {
 			if _, exists := configuration.TCP.Routers[name]; exists {
-				logger.WithField(log.RouterName, name).Warn("TCP router already configured, skipping")
+				logger.Warn().Str(logs.RouterName, name).Msg("TCP router already configured, skipping")
 			} else {
 				configuration.TCP.Routers[name] = conf
 			}
@@ -377,7 +378,7 @@ func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory st
 
 		for name, conf := range c.TCP.Middlewares {
 			if _, exists := configuration.TCP.Middlewares[name]; exists {
-				logger.WithField(log.MiddlewareName, name).Warn("TCP middleware already configured, skipping")
+				logger.Warn().Str(logs.MiddlewareName, name).Msg("TCP middleware already configured, skipping")
 			} else {
 				configuration.TCP.Middlewares[name] = conf
 			}
@@ -385,7 +386,7 @@ func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory st
 
 		for name, conf := range c.TCP.Services {
 			if _, exists := configuration.TCP.Services[name]; exists {
-				logger.WithField(log.ServiceName, name).Warn("TCP service already configured, skipping")
+				logger.Warn().Str(logs.ServiceName, name).Msg("TCP service already configured, skipping")
 			} else {
 				configuration.TCP.Services[name] = conf
 			}
@@ -393,7 +394,7 @@ func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory st
 
 		for name, conf := range c.UDP.Routers {
 			if _, exists := configuration.UDP.Routers[name]; exists {
-				logger.WithField(log.RouterName, name).Warn("UDP router already configured, skipping")
+				logger.Warn().Str(logs.RouterName, name).Msg("UDP router already configured, skipping")
 			} else {
 				configuration.UDP.Routers[name] = conf
 			}
@@ -401,7 +402,7 @@ func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory st
 
 		for name, conf := range c.UDP.Services {
 			if _, exists := configuration.UDP.Services[name]; exists {
-				logger.WithField(log.ServiceName, name).Warn("UDP service already configured, skipping")
+				logger.Warn().Str(logs.ServiceName, name).Msg("UDP service already configured, skipping")
 			} else {
 				configuration.UDP.Services[name] = conf
 			}
@@ -409,7 +410,7 @@ func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory st
 
 		for _, conf := range c.TLS.Certificates {
 			if _, exists := configTLSMaps[conf]; exists {
-				logger.Warnf("TLS configuration %v already configured, skipping", conf)
+				logger.Warn().Msgf("TLS configuration %v already configured, skipping", conf)
 			} else {
 				configTLSMaps[conf] = struct{}{}
 			}
@@ -417,7 +418,7 @@ func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory st
 
 		for name, conf := range c.TLS.Options {
 			if _, exists := configuration.TLS.Options[name]; exists {
-				logger.Warnf("TLS options %v already configured, skipping", name)
+				logger.Warn().Msgf("TLS options %v already configured, skipping", name)
 			} else {
 				if configuration.TLS.Options == nil {
 					configuration.TLS.Options = map[string]tls.Options{}
@@ -428,7 +429,7 @@ func (p *Provider) loadFileConfigFromDirectory(ctx context.Context, directory st
 
 		for name, conf := range c.TLS.Stores {
 			if _, exists := configuration.TLS.Stores[name]; exists {
-				logger.Warnf("TLS store %v already configured, skipping", name)
+				logger.Warn().Msgf("TLS store %v already configured, skipping", name)
 			} else {
 				if configuration.TLS.Stores == nil {
 					configuration.TLS.Stores = map[string]tls.Store{}
@@ -478,9 +479,9 @@ func (p *Provider) CreateConfiguration(ctx context.Context, filename string, fun
 
 	renderedTemplate := buffer.String()
 	if p.DebugLogGeneratedTemplate {
-		logger := log.FromContext(ctx)
-		logger.Debugf("Template content: %s", tmplContent)
-		logger.Debugf("Rendering results: %s", renderedTemplate)
+		logger := log.Ctx(ctx)
+		logger.Debug().Msgf("Template content: %s", tmplContent)
+		logger.Debug().Msgf("Rendering results: %s", renderedTemplate)
 	}
 
 	return p.decodeConfiguration(filename, renderedTemplate)
diff --git a/pkg/provider/http/http.go b/pkg/provider/http/http.go
index 4d21cfb193..776c9de1b2 100644
--- a/pkg/provider/http/http.go
+++ b/pkg/provider/http/http.go
@@ -9,11 +9,12 @@ import (
 	"time"
 
 	"github.com/cenkalti/backoff/v4"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/paerser/file"
 	ptypes "github.com/traefik/paerser/types"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
 	"github.com/traefik/traefik/v2/pkg/job"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/provider"
 	"github.com/traefik/traefik/v2/pkg/safe"
 	"github.com/traefik/traefik/v2/pkg/tls"
@@ -71,8 +72,8 @@ func (p *Provider) Init() error {
 // Provide allows the provider to provide configurations to traefik using the given configuration channel.
 func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error {
 	pool.GoCtx(func(routineCtx context.Context) {
-		ctxLog := log.With(routineCtx, log.Str(log.ProviderName, "http"))
-		logger := log.FromContext(ctxLog)
+		logger := log.Ctx(routineCtx).With().Str(logs.ProviderName, "http").Logger()
+		ctxLog := logger.WithContext(routineCtx)
 
 		operation := func() error {
 			if err := p.updateConfiguration(configurationChan); err != nil {
@@ -96,11 +97,11 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 		}
 
 		notify := func(err error, time time.Duration) {
-			logger.Errorf("Provider connection error %+v, retrying in %s", err, time)
+			logger.Error().Err(err).Msgf("Provider connection error, retrying in %s", time)
 		}
 		err := backoff.RetryNotify(safe.OperationWithRecover(operation), backoff.WithContext(job.NewBackOff(backoff.NewExponentialBackOff()), ctxLog), notify)
 		if err != nil {
-			logger.Errorf("Cannot connect to server endpoint %+v", err)
+			logger.Error().Err(err).Msg("Cannot connect to server endpoint")
 		}
 	})
 
diff --git a/pkg/provider/hub/handler.go b/pkg/provider/hub/handler.go
index 83d15dfb95..23cc47b1e0 100644
--- a/pkg/provider/hub/handler.go
+++ b/pkg/provider/hub/handler.go
@@ -9,8 +9,8 @@ import (
 	"net/url"
 	"sync/atomic"
 
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
 )
 
 type handler struct {
@@ -59,7 +59,7 @@ func (h *handler) handleConfig(rw http.ResponseWriter, req *http.Request) {
 	payload := &configRequest{Configuration: emptyDynamicConfiguration()}
 	if err := json.NewDecoder(req.Body).Decode(payload); err != nil {
 		err = fmt.Errorf("decoding config request: %w", err)
-		log.WithoutContext().Errorf("Handling config: %v", err)
+		log.Error().Err(err).Msg("Handling config")
 		http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
 		return
 	}
@@ -88,14 +88,14 @@ func (h *handler) handleDiscoverIP(rw http.ResponseWriter, req *http.Request) {
 
 	if err := h.doDiscoveryReq(req.Context(), xff, port, nonce); err != nil {
 		err = fmt.Errorf("doing discovery request: %w", err)
-		log.WithoutContext().Errorf("Handling IP discovery: %v", err)
+		log.Error().Err(err).Msg("Handling IP discovery")
 		http.Error(rw, http.StatusText(http.StatusBadGateway), http.StatusBadGateway)
 		return
 	}
 
 	if err := json.NewEncoder(rw).Encode(xff); err != nil {
 		err = fmt.Errorf("encoding discover ip response: %w", err)
-		log.WithoutContext().Errorf("Handling IP discovery: %v", err)
+		log.Error().Err(err).Msg("Handling IP discovery")
 		http.Error(rw, http.StatusText(http.StatusInternalServerError), http.StatusInternalServerError)
 		return
 	}
@@ -136,7 +136,7 @@ func (h *handler) handleState(rw http.ResponseWriter, req *http.Request) {
 	}
 	if err := json.NewEncoder(rw).Encode(resp); err != nil {
 		err = fmt.Errorf("encoding last config received response: %w", err)
-		log.WithoutContext().Errorf("Handling state: %v", err)
+		log.Error().Err(err).Msg("Handling state")
 		http.Error(rw, http.StatusText(http.StatusBadRequest), http.StatusBadRequest)
 		return
 	}
diff --git a/pkg/provider/hub/hub.go b/pkg/provider/hub/hub.go
index 106a097e2a..07fc50d303 100644
--- a/pkg/provider/hub/hub.go
+++ b/pkg/provider/hub/hub.go
@@ -8,8 +8,9 @@ import (
 	"net"
 	"net/http"
 
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/provider"
 	"github.com/traefik/traefik/v2/pkg/safe"
 	ttls "github.com/traefik/traefik/v2/pkg/tls"
@@ -67,7 +68,7 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, _ *safe.Poo
 	// if/when Provide lifecycle differs with Traefik lifecycle.
 	go func() {
 		if err = p.server.Serve(listener); err != nil {
-			log.WithoutContext().WithField(log.ProviderName, "hub").Errorf("Unexpected error while running server: %v", err)
+			log.Error().Str(logs.ProviderName, "hub").Err(err).Msg("Unexpected error while running server")
 			return
 		}
 	}()
diff --git a/pkg/provider/kubernetes/crd/client.go b/pkg/provider/kubernetes/crd/client.go
index 4dfd4d8a3d..0b40a91aad 100644
--- a/pkg/provider/kubernetes/crd/client.go
+++ b/pkg/provider/kubernetes/crd/client.go
@@ -8,7 +8,7 @@ import (
 	"runtime"
 	"time"
 
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/generated/clientset/versioned"
 	"github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/generated/informers/externalversions"
 	"github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/traefik/v1alpha1"
@@ -220,7 +220,7 @@ func (c *clientWrapper) GetIngressRoutes() []*v1alpha1.IngressRoute {
 	for ns, factory := range c.factoriesCrd {
 		ings, err := factory.Traefik().V1alpha1().IngressRoutes().Lister().List(labels.Everything())
 		if err != nil {
-			log.Errorf("Failed to list ingress routes in namespace %s: %v", ns, err)
+			log.Error().Err(err).Msgf("Failed to list ingress routes in namespace %s", ns)
 		}
 		result = append(result, ings...)
 	}
@@ -234,7 +234,7 @@ func (c *clientWrapper) GetIngressRouteTCPs() []*v1alpha1.IngressRouteTCP {
 	for ns, factory := range c.factoriesCrd {
 		ings, err := factory.Traefik().V1alpha1().IngressRouteTCPs().Lister().List(labels.Everything())
 		if err != nil {
-			log.Errorf("Failed to list tcp ingress routes in namespace %s: %v", ns, err)
+			log.Error().Err(err).Msgf("Failed to list tcp ingress routes in namespace %s", ns)
 		}
 		result = append(result, ings...)
 	}
@@ -248,7 +248,7 @@ func (c *clientWrapper) GetIngressRouteUDPs() []*v1alpha1.IngressRouteUDP {
 	for ns, factory := range c.factoriesCrd {
 		ings, err := factory.Traefik().V1alpha1().IngressRouteUDPs().Lister().List(labels.Everything())
 		if err != nil {
-			log.Errorf("Failed to list udp ingress routes in namespace %s: %v", ns, err)
+			log.Error().Err(err).Msgf("Failed to list udp ingress routes in namespace %s", ns)
 		}
 		result = append(result, ings...)
 	}
@@ -262,7 +262,7 @@ func (c *clientWrapper) GetMiddlewares() []*v1alpha1.Middleware {
 	for ns, factory := range c.factoriesCrd {
 		middlewares, err := factory.Traefik().V1alpha1().Middlewares().Lister().List(labels.Everything())
 		if err != nil {
-			log.Errorf("Failed to list middlewares in namespace %s: %v", ns, err)
+			log.Error().Err(err).Msgf("Failed to list middlewares in namespace %s", ns)
 		}
 		result = append(result, middlewares...)
 	}
@@ -276,7 +276,7 @@ func (c *clientWrapper) GetMiddlewareTCPs() []*v1alpha1.MiddlewareTCP {
 	for ns, factory := range c.factoriesCrd {
 		middlewares, err := factory.Traefik().V1alpha1().MiddlewareTCPs().Lister().List(labels.Everything())
 		if err != nil {
-			log.Errorf("Failed to list TCP middlewares in namespace %s: %v", ns, err)
+			log.Error().Err(err).Msgf("Failed to list TCP middlewares in namespace %s", ns)
 		}
 		result = append(result, middlewares...)
 	}
@@ -302,7 +302,7 @@ func (c *clientWrapper) GetTraefikServices() []*v1alpha1.TraefikService {
 	for ns, factory := range c.factoriesCrd {
 		ings, err := factory.Traefik().V1alpha1().TraefikServices().Lister().List(labels.Everything())
 		if err != nil {
-			log.Errorf("Failed to list Traefik services in namespace %s: %v", ns, err)
+			log.Error().Err(err).Msgf("Failed to list Traefik services in namespace %s", ns)
 		}
 		result = append(result, ings...)
 	}
@@ -310,14 +310,14 @@ func (c *clientWrapper) GetTraefikServices() []*v1alpha1.TraefikService {
 	return result
 }
 
-// GetServersTransport returns all ServersTransport.
+// GetServersTransports returns all ServersTransport.
 func (c *clientWrapper) GetServersTransports() []*v1alpha1.ServersTransport {
 	var result []*v1alpha1.ServersTransport
 
 	for ns, factory := range c.factoriesCrd {
 		serversTransports, err := factory.Traefik().V1alpha1().ServersTransports().Lister().List(labels.Everything())
 		if err != nil {
-			log.Errorf("Failed to list servers transport in namespace %s: %v", ns, err)
+			log.Error().Err(err).Msgf("Failed to list servers transport in namespace %s", ns)
 		}
 		result = append(result, serversTransports...)
 	}
@@ -332,7 +332,7 @@ func (c *clientWrapper) GetTLSOptions() []*v1alpha1.TLSOption {
 	for ns, factory := range c.factoriesCrd {
 		options, err := factory.Traefik().V1alpha1().TLSOptions().Lister().List(labels.Everything())
 		if err != nil {
-			log.Errorf("Failed to list tls options in namespace %s: %v", ns, err)
+			log.Error().Err(err).Msgf("Failed to list tls options in namespace %s", ns)
 		}
 		result = append(result, options...)
 	}
@@ -347,7 +347,7 @@ func (c *clientWrapper) GetTLSStores() []*v1alpha1.TLSStore {
 	for ns, factory := range c.factoriesCrd {
 		stores, err := factory.Traefik().V1alpha1().TLSStores().Lister().List(labels.Everything())
 		if err != nil {
-			log.Errorf("Failed to list tls stores in namespace %s: %v", ns, err)
+			log.Error().Err(err).Msgf("Failed to list tls stores in namespace %s", ns)
 		}
 		result = append(result, stores...)
 	}
diff --git a/pkg/provider/kubernetes/crd/kubernetes.go b/pkg/provider/kubernetes/crd/kubernetes.go
index 31366ba7ce..00eb9a4e08 100644
--- a/pkg/provider/kubernetes/crd/kubernetes.go
+++ b/pkg/provider/kubernetes/crd/kubernetes.go
@@ -17,10 +17,11 @@ import (
 
 	"github.com/cenkalti/backoff/v4"
 	"github.com/mitchellh/hashstructure"
+	"github.com/rs/zerolog/log"
 	ptypes "github.com/traefik/paerser/types"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
 	"github.com/traefik/traefik/v2/pkg/job"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/provider"
 	"github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/traefik/v1alpha1"
 	"github.com/traefik/traefik/v2/pkg/safe"
@@ -62,7 +63,7 @@ func (p *Provider) newK8sClient(ctx context.Context) (*clientWrapper, error) {
 	if err != nil {
 		return nil, fmt.Errorf("invalid label selector: %q", p.LabelSelector)
 	}
-	log.FromContext(ctx).Infof("label selector is: %q", p.LabelSelector)
+	log.Ctx(ctx).Info().Msgf("label selector is: %q", p.LabelSelector)
 
 	withEndpoint := ""
 	if p.Endpoint != "" {
@@ -72,13 +73,13 @@ func (p *Provider) newK8sClient(ctx context.Context) (*clientWrapper, error) {
 	var client *clientWrapper
 	switch {
 	case os.Getenv("KUBERNETES_SERVICE_HOST") != "" && os.Getenv("KUBERNETES_SERVICE_PORT") != "":
-		log.FromContext(ctx).Infof("Creating in-cluster Provider client%s", withEndpoint)
+		log.Ctx(ctx).Info().Msgf("Creating in-cluster Provider client%s", withEndpoint)
 		client, err = newInClusterClient(p.Endpoint)
 	case os.Getenv("KUBECONFIG") != "":
-		log.FromContext(ctx).Infof("Creating cluster-external Provider client from KUBECONFIG %s", os.Getenv("KUBECONFIG"))
+		log.Ctx(ctx).Info().Msgf("Creating cluster-external Provider client from KUBECONFIG %s", os.Getenv("KUBECONFIG"))
 		client, err = newExternalClusterClientFromFile(os.Getenv("KUBECONFIG"))
 	default:
-		log.FromContext(ctx).Infof("Creating cluster-external Provider client%s", withEndpoint)
+		log.Ctx(ctx).Info().Msgf("Creating cluster-external Provider client%s", withEndpoint)
 		client, err = newExternalClusterClient(p.Endpoint, p.Token, p.CertAuthFilePath)
 	}
 
@@ -98,8 +99,8 @@ func (p *Provider) Init() error {
 // Provide allows the k8s provider to provide configurations to traefik
 // using the given configuration channel.
 func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error {
-	ctxLog := log.With(context.Background(), log.Str(log.ProviderName, providerName))
-	logger := log.FromContext(ctxLog)
+	logger := log.With().Str(logs.ProviderName, providerName).Logger()
+	ctxLog := logger.WithContext(context.Background())
 
 	k8sClient, err := p.newK8sClient(ctxLog)
 	if err != nil {
@@ -107,18 +108,18 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 	}
 
 	if p.AllowCrossNamespace {
-		logger.Warn("Cross-namespace reference between IngressRoutes and resources is enabled, please ensure that this is expected (see AllowCrossNamespace option)")
+		logger.Warn().Msg("Cross-namespace reference between IngressRoutes and resources is enabled, please ensure that this is expected (see AllowCrossNamespace option)")
 	}
 
 	if p.AllowExternalNameServices {
-		logger.Warn("ExternalName service loading is enabled, please ensure that this is expected (see AllowExternalNameServices option)")
+		logger.Warn().Msg("ExternalName service loading is enabled, please ensure that this is expected (see AllowExternalNameServices option)")
 	}
 
 	pool.GoCtx(func(ctxPool context.Context) {
 		operation := func() error {
 			eventsChan, err := k8sClient.WatchAll(p.Namespaces, ctxPool.Done())
 			if err != nil {
-				logger.Errorf("Error watching kubernetes events: %v", err)
+				logger.Error().Err(err).Msg("Error watching kubernetes events")
 				timer := time.NewTimer(1 * time.Second)
 				select {
 				case <-timer.C:
@@ -147,9 +148,9 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 					confHash, err := hashstructure.Hash(conf, nil)
 					switch {
 					case err != nil:
-						logger.Error("Unable to hash the configuration")
+						logger.Error().Err(err).Msg("Unable to hash the configuration")
 					case p.lastConfiguration.Get() == confHash:
-						logger.Debugf("Skipping Kubernetes event kind %T", event)
+						logger.Debug().Msgf("Skipping Kubernetes event kind %T", event)
 					default:
 						p.lastConfiguration.Set(confHash)
 						configurationChan <- dynamic.Message{
@@ -167,11 +168,11 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 		}
 
 		notify := func(err error, time time.Duration) {
-			logger.Errorf("Provider connection error: %v; retrying in %s", err, time)
+			logger.Error().Err(err).Msgf("Provider connection error, retrying in %s", time)
 		}
 		err := backoff.RetryNotify(safe.OperationWithRecover(operation), backoff.WithContext(job.NewBackOff(backoff.NewExponentialBackOff()), ctxPool), notify)
 		if err != nil {
-			logger.Errorf("Cannot connect to Provider: %v", err)
+			logger.Error().Err(err).Msg("Cannot connect to Provider")
 		}
 	})
 
@@ -200,29 +201,30 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client)
 
 	for _, middleware := range client.GetMiddlewares() {
 		id := provider.Normalize(makeID(middleware.Namespace, middleware.Name))
-		ctxMid := log.With(ctx, log.Str(log.MiddlewareName, id))
+		logger := log.Ctx(ctx).With().Str(logs.MiddlewareName, id).Logger()
+		ctxMid := logger.WithContext(ctx)
 
 		basicAuth, err := createBasicAuthMiddleware(client, middleware.Namespace, middleware.Spec.BasicAuth)
 		if err != nil {
-			log.FromContext(ctxMid).Errorf("Error while reading basic auth middleware: %v", err)
+			logger.Error().Err(err).Msg("Error while reading basic auth middleware")
 			continue
 		}
 
 		digestAuth, err := createDigestAuthMiddleware(client, middleware.Namespace, middleware.Spec.DigestAuth)
 		if err != nil {
-			log.FromContext(ctxMid).Errorf("Error while reading digest auth middleware: %v", err)
+			logger.Error().Err(err).Msg("Error while reading digest auth middleware")
 			continue
 		}
 
 		forwardAuth, err := createForwardAuthMiddleware(client, middleware.Namespace, middleware.Spec.ForwardAuth)
 		if err != nil {
-			log.FromContext(ctxMid).Errorf("Error while reading forward auth middleware: %v", err)
+			logger.Error().Err(err).Msg("Error while reading forward auth middleware")
 			continue
 		}
 
 		errorPage, errorPageService, err := p.createErrorPageMiddleware(client, middleware.Namespace, middleware.Spec.Errors)
 		if err != nil {
-			log.FromContext(ctxMid).Errorf("Error while reading error page middleware: %v", err)
+			logger.Error().Err(err).Msg("Error while reading error page middleware")
 			continue
 		}
 
@@ -234,25 +236,25 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client)
 
 		plugin, err := createPluginMiddleware(client, middleware.Namespace, middleware.Spec.Plugin)
 		if err != nil {
-			log.FromContext(ctxMid).Errorf("Error while reading plugins middleware: %v", err)
+			logger.Error().Err(err).Msg("Error while reading plugins middleware")
 			continue
 		}
 
 		rateLimit, err := createRateLimitMiddleware(middleware.Spec.RateLimit)
 		if err != nil {
-			log.FromContext(ctxMid).Errorf("Error while reading rateLimit middleware: %v", err)
+			logger.Error().Err(err).Msg("Error while reading rateLimit middleware")
 			continue
 		}
 
 		retry, err := createRetryMiddleware(middleware.Spec.Retry)
 		if err != nil {
-			log.FromContext(ctxMid).Errorf("Error while reading retry middleware: %v", err)
+			logger.Error().Err(err).Msg("Error while reading retry middleware")
 			continue
 		}
 
 		circuitBreaker, err := createCircuitBreakerMiddleware(middleware.Spec.CircuitBreaker)
 		if err != nil {
-			log.FromContext(ctxMid).Errorf("Error while reading circuit breaker middleware: %v", err)
+			logger.Error().Err(err).Msg("Error while reading circuit breaker middleware")
 			continue
 		}
 
@@ -298,20 +300,20 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client)
 	for _, service := range client.GetTraefikServices() {
 		err := cb.buildTraefikService(ctx, service, conf.HTTP.Services)
 		if err != nil {
-			log.FromContext(ctx).WithField(log.ServiceName, service.Name).
-				Errorf("Error while building TraefikService: %v", err)
+			log.Ctx(ctx).Error().Str(logs.ServiceName, service.Name).Err(err).
+				Msg("Error while building TraefikService")
 			continue
 		}
 	}
 
 	for _, serversTransport := range client.GetServersTransports() {
-		logger := log.FromContext(ctx).WithField(log.ServersTransportName, serversTransport.Name)
+		logger := log.Ctx(ctx).With().Str(logs.ServersTransportName, serversTransport.Name).Logger()
 
 		var rootCAs []tls.FileOrContent
 		for _, secret := range serversTransport.Spec.RootCAsSecrets {
 			caSecret, err := loadCASecret(serversTransport.Namespace, secret, client)
 			if err != nil {
-				logger.Errorf("Error while loading rootCAs %s: %v", secret, err)
+				logger.Error().Err(err).Msgf("Error while loading rootCAs %s", secret)
 				continue
 			}
 
@@ -322,7 +324,7 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client)
 		for _, secret := range serversTransport.Spec.CertificatesSecrets {
 			tlsSecret, tlsKey, err := loadAuthTLSSecret(serversTransport.Namespace, secret, client)
 			if err != nil {
-				logger.Errorf("Error while loading certificates %s: %v", secret, err)
+				logger.Error().Err(err).Msgf("Error while loading certificates %s", secret)
 				continue
 			}
 
@@ -339,35 +341,35 @@ func (p *Provider) loadConfigurationFromCRD(ctx context.Context, client Client)
 			if serversTransport.Spec.ForwardingTimeouts.DialTimeout != nil {
 				err := forwardingTimeout.DialTimeout.Set(serversTransport.Spec.ForwardingTimeouts.DialTimeout.String())
 				if err != nil {
-					logger.Errorf("Error while reading DialTimeout: %v", err)
+					logger.Error().Err(err).Msg("Error while reading DialTimeout")
 				}
 			}
 
 			if serversTransport.Spec.ForwardingTimeouts.ResponseHeaderTimeout != nil {
 				err := forwardingTimeout.ResponseHeaderTimeout.Set(serversTransport.Spec.ForwardingTimeouts.ResponseHeaderTimeout.String())
 				if err != nil {
-					logger.Errorf("Error while reading ResponseHeaderTimeout: %v", err)
+					logger.Error().Err(err).Msg("Error while reading ResponseHeaderTimeout")
 				}
 			}
 
 			if serversTransport.Spec.ForwardingTimeouts.IdleConnTimeout != nil {
 				err := forwardingTimeout.IdleConnTimeout.Set(serversTransport.Spec.ForwardingTimeouts.IdleConnTimeout.String())
 				if err != nil {
-					logger.Errorf("Error while reading IdleConnTimeout: %v", err)
+					logger.Error().Err(err).Msg("Error while reading IdleConnTimeout")
 				}
 			}
 
 			if serversTransport.Spec.ForwardingTimeouts.ReadIdleTimeout != nil {
 				err := forwardingTimeout.ReadIdleTimeout.Set(serversTransport.Spec.ForwardingTimeouts.ReadIdleTimeout.String())
 				if err != nil {
-					logger.Errorf("Error while reading ReadIdleTimeout: %v", err)
+					logger.Error().Err(err).Msg("Error while reading ReadIdleTimeout")
 				}
 			}
 
 			if serversTransport.Spec.ForwardingTimeouts.PingTimeout != nil {
 				err := forwardingTimeout.PingTimeout.Set(serversTransport.Spec.ForwardingTimeouts.PingTimeout.String())
 				if err != nil {
-					logger.Errorf("Error while reading PingTimeout: %v", err)
+					logger.Error().Err(err).Msg("Error while reading PingTimeout")
 				}
 			}
 		}
@@ -414,8 +416,8 @@ func getServicePort(svc *corev1.Service, port intstr.IntOrString) (*corev1.Servi
 	}
 
 	if hasValidPort {
-		log.WithoutContext().
-			Warnf("The port %s from IngressRoute doesn't match with ports defined in the ExternalName service %s/%s.", port, svc.Namespace, svc.Name)
+		log.Warn().Msgf("The port %s from IngressRoute doesn't match with ports defined in the ExternalName service %s/%s.",
+			&port, svc.Namespace, svc.Name)
 	}
 
 	return &corev1.ServicePort{Port: port.IntVal}, nil
@@ -811,8 +813,7 @@ func createChainMiddleware(ctx context.Context, namespace string, chain *v1alpha
 	for _, mi := range chain.Middlewares {
 		if strings.Contains(mi.Name, providerNamespaceSeparator) {
 			if len(mi.Namespace) > 0 {
-				log.FromContext(ctx).
-					Warnf("namespace %q is ignored in cross-provider context", mi.Namespace)
+				log.Ctx(ctx).Warn().Msgf("namespace %q is ignored in cross-provider context", mi.Namespace)
 			}
 			mds = append(mds, mi.Name)
 			continue
@@ -838,24 +839,24 @@ func buildTLSOptions(ctx context.Context, client Client) map[string]tls.Options
 	var nsDefault []string
 
 	for _, tlsOption := range tlsOptionsCRD {
-		logger := log.FromContext(log.With(ctx, log.Str("tlsOption", tlsOption.Name), log.Str("namespace", tlsOption.Namespace)))
+		logger := log.Ctx(ctx).With().Str("tlsOption", tlsOption.Name).Str("namespace", tlsOption.Namespace).Logger()
 		var clientCAs []tls.FileOrContent
 
 		for _, secretName := range tlsOption.Spec.ClientAuth.SecretNames {
 			secret, exists, err := client.GetSecret(tlsOption.Namespace, secretName)
 			if err != nil {
-				logger.Errorf("Failed to fetch secret %s/%s: %v", tlsOption.Namespace, secretName, err)
+				logger.Error().Err(err).Msgf("Failed to fetch secret %s/%s", tlsOption.Namespace, secretName)
 				continue
 			}
 
 			if !exists {
-				logger.Warnf("Secret %s/%s does not exist", tlsOption.Namespace, secretName)
+				logger.Warn().Msgf("Secret %s/%s does not exist", tlsOption.Namespace, secretName)
 				continue
 			}
 
 			cert, err := getCABlocks(secret, tlsOption.Namespace, secretName)
 			if err != nil {
-				logger.Errorf("Failed to extract CA from secret %s/%s: %v", tlsOption.Namespace, secretName, err)
+				logger.Error().Err(err).Msgf("Failed to extract CA from secret %s/%s", tlsOption.Namespace, secretName)
 				continue
 			}
 
@@ -890,7 +891,7 @@ func buildTLSOptions(ctx context.Context, client Client) map[string]tls.Options
 
 	if len(nsDefault) > 1 {
 		delete(tlsOptions, tls.DefaultTLSConfigName)
-		log.FromContext(ctx).Errorf("Default TLS Options defined in multiple namespaces: %v", nsDefault)
+		log.Ctx(ctx).Error().Msgf("Default TLS Options defined in multiple namespaces: %v", nsDefault)
 	}
 
 	return tlsOptions
@@ -907,7 +908,7 @@ func buildTLSStores(ctx context.Context, client Client) (map[string]tls.Store, m
 	tlsConfigs := make(map[string]*tls.CertAndStores)
 
 	for _, t := range tlsStoreCRD {
-		logger := log.FromContext(log.With(ctx, log.Str("TLSStore", t.Name), log.Str("namespace", t.Namespace)))
+		logger := log.Ctx(ctx).With().Str("TLSStore", t.Name).Str("namespace", t.Namespace).Logger()
 
 		id := makeID(t.Namespace, t.Name)
 
@@ -924,17 +925,17 @@ func buildTLSStores(ctx context.Context, client Client) (map[string]tls.Store, m
 
 			secret, exists, err := client.GetSecret(t.Namespace, secretName)
 			if err != nil {
-				logger.Errorf("Failed to fetch secret %s/%s: %v", t.Namespace, secretName, err)
+				logger.Error().Err(err).Msgf("Failed to fetch secret %s/%s", t.Namespace, secretName)
 				continue
 			}
 			if !exists {
-				logger.Errorf("Secret %s/%s does not exist", t.Namespace, secretName)
+				logger.Error().Msgf("Secret %s/%s does not exist", t.Namespace, secretName)
 				continue
 			}
 
 			cert, key, err := getCertificateBlocks(secret, t.Namespace, secretName)
 			if err != nil {
-				logger.Errorf("Could not get certificate blocks: %v", err)
+				logger.Error().Err(err).Msg("Could not get certificate blocks")
 				continue
 			}
 
@@ -952,7 +953,7 @@ func buildTLSStores(ctx context.Context, client Client) (map[string]tls.Store, m
 		}
 
 		if err := buildCertificates(client, id, t.Namespace, t.Spec.Certificates, tlsConfigs); err != nil {
-			logger.Errorf("Failed to load certificates: %v", err)
+			logger.Error().Err(err).Msg("Failed to load certificates")
 			continue
 		}
 
@@ -961,7 +962,7 @@ func buildTLSStores(ctx context.Context, client Client) (map[string]tls.Store, m
 
 	if len(nsDefault) > 1 {
 		delete(tlsStores, tls.DefaultTLSStoreName)
-		log.FromContext(ctx).Errorf("Default TLS Stores defined in multiple namespaces: %v", nsDefault)
+		log.Ctx(ctx).Error().Msgf("Default TLS Stores defined in multiple namespaces: %v", nsDefault)
 	}
 
 	return tlsStores, tlsConfigs
@@ -1117,7 +1118,7 @@ func throttleEvents(ctx context.Context, throttleDuration time.Duration, pool *s
 				default:
 					// We already have an event in eventsChanBuffered, so we'll do a refresh as soon as our throttle allows us to.
 					// It's fine to drop the event and keep whatever's in the buffer -- we don't do different things for different events
-					log.FromContext(ctx).Debugf("Dropping event kind %T due to throttling", nextEvent)
+					log.Ctx(ctx).Debug().Msgf("Dropping event kind %T due to throttling", nextEvent)
 				}
 			}
 		}
diff --git a/pkg/provider/kubernetes/crd/kubernetes_http.go b/pkg/provider/kubernetes/crd/kubernetes_http.go
index 2fd8f17e6e..fa8bed11d7 100644
--- a/pkg/provider/kubernetes/crd/kubernetes_http.go
+++ b/pkg/provider/kubernetes/crd/kubernetes_http.go
@@ -8,8 +8,9 @@ import (
 	"strconv"
 	"strings"
 
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/provider"
 	"github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/traefik/v1alpha1"
 	"github.com/traefik/traefik/v2/pkg/tls"
@@ -32,8 +33,7 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli
 	}
 
 	for _, ingressRoute := range client.GetIngressRoutes() {
-		ctxRt := log.With(ctx, log.Str("ingress", ingressRoute.Name), log.Str("namespace", ingressRoute.Namespace))
-		logger := log.FromContext(ctxRt)
+		logger := log.Ctx(ctx).With().Str("ingress", ingressRoute.Name).Str("namespace", ingressRoute.Namespace).Logger()
 
 		// TODO keep the name ingressClass?
 		if !shouldProcessIngress(p.IngressClass, ingressRoute.Annotations[annotationKubernetesIngressClass]) {
@@ -42,7 +42,7 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli
 
 		err := getTLSHTTP(ctx, ingressRoute, client, tlsConfigs)
 		if err != nil {
-			logger.Errorf("Error configuring TLS: %v", err)
+			logger.Error().Err(err).Msg("Error configuring TLS")
 		}
 
 		ingressName := ingressRoute.Name
@@ -59,24 +59,24 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli
 
 		for _, route := range ingressRoute.Spec.Routes {
 			if route.Kind != "Rule" {
-				logger.Errorf("Unsupported match kind: %s. Only \"Rule\" is supported for now.", route.Kind)
+				logger.Error().Msgf("Unsupported match kind: %s. Only \"Rule\" is supported for now.", route.Kind)
 				continue
 			}
 
 			if len(route.Match) == 0 {
-				logger.Errorf("Empty match rule")
+				logger.Error().Msg("Empty match rule")
 				continue
 			}
 
 			serviceKey, err := makeServiceKey(route.Match, ingressName)
 			if err != nil {
-				logger.Error(err)
+				logger.Error().Err(err).Send()
 				continue
 			}
 
 			mds, err := p.makeMiddlewareKeys(ctx, ingressRoute.Namespace, route.Middlewares)
 			if err != nil {
-				logger.Errorf("Failed to create middleware keys: %v", err)
+				logger.Error().Err(err).Msg("Failed to create middleware keys")
 				continue
 			}
 
@@ -92,13 +92,13 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli
 
 				errBuild := cb.buildServicesLB(ctx, ingressRoute.Namespace, spec, serviceName, conf.Services)
 				if errBuild != nil {
-					logger.Error(errBuild)
+					logger.Error().Err(errBuild).Send()
 					continue
 				}
 			} else if len(route.Services) == 1 {
 				fullName, serversLB, err := cb.nameAndService(ctx, ingressRoute.Namespace, route.Services[0].LoadBalancerSpec)
 				if err != nil {
-					logger.Error(err)
+					logger.Error().Err(err).Send()
 					continue
 				}
 
@@ -134,12 +134,12 @@ func (p *Provider) loadIngressRouteConfiguration(ctx context.Context, client Cli
 						tlsOptionsName = makeID(ns, tlsOptionsName)
 					} else if len(ns) > 0 {
 						logger.
-							WithField("TLSOption", ingressRoute.Spec.TLS.Options.Name).
-							Warnf("Namespace %q is ignored in cross-provider context", ns)
+							Warn().Str("TLSOption", ingressRoute.Spec.TLS.Options.Name).
+							Msgf("Namespace %q is ignored in cross-provider context", ns)
 					}
 
 					if !isNamespaceAllowed(p.AllowCrossNamespace, ingressRoute.Namespace, ns) {
-						logger.Errorf("TLSOption %s/%s is not in the IngressRoute namespace %s",
+						logger.Error().Msgf("TLSOption %s/%s is not in the IngressRoute namespace %s",
 							ns, ingressRoute.Spec.TLS.Options.Name, ingressRoute.Namespace)
 						continue
 					}
@@ -170,9 +170,9 @@ func (p *Provider) makeMiddlewareKeys(ctx context.Context, ingRouteNamespace str
 
 		if strings.Contains(name, providerNamespaceSeparator) {
 			if len(mi.Namespace) > 0 {
-				log.FromContext(ctx).
-					WithField(log.MiddlewareName, mi.Name).
-					Warnf("namespace %q is ignored in cross-provider context", mi.Namespace)
+				log.Ctx(ctx).
+					Warn().Str(logs.MiddlewareName, mi.Name).
+					Msgf("namespace %q is ignored in cross-provider context", mi.Namespace)
 			}
 
 			mds = append(mds, name)
@@ -439,7 +439,7 @@ func (c configBuilder) loadServers(parentNamespace string, svc v1alpha1.LoadBala
 // it generates and returns the configuration part for such a service,
 // so that the caller can add it to the global config map.
 func (c configBuilder) nameAndService(ctx context.Context, parentNamespace string, service v1alpha1.LoadBalancerSpec) (string, *dynamic.Service, error) {
-	svcCtx := log.With(ctx, log.Str(log.ServiceName, service.Name))
+	svcCtx := log.Ctx(ctx).With().Str(logs.ServiceName, service.Name).Logger().WithContext(ctx)
 
 	namespace := namespaceOrFallback(service, parentNamespace)
 
@@ -488,7 +488,7 @@ func fullServiceName(ctx context.Context, namespace string, service v1alpha1.Loa
 	}
 
 	if service.Namespace != "" {
-		log.FromContext(ctx).Warnf("namespace %q is ignored in cross-provider context", service.Namespace)
+		log.Ctx(ctx).Warn().Msgf("namespace %q is ignored in cross-provider context", service.Namespace)
 	}
 
 	return provider.Normalize(name) + providerNamespaceSeparator + pName
@@ -507,7 +507,7 @@ func getTLSHTTP(ctx context.Context, ingressRoute *v1alpha1.IngressRoute, k8sCli
 		return nil
 	}
 	if ingressRoute.Spec.TLS.SecretName == "" {
-		log.FromContext(ctx).Debugf("No secret name provided")
+		log.Ctx(ctx).Debug().Msg("No secret name provided")
 		return nil
 	}
 
diff --git a/pkg/provider/kubernetes/crd/kubernetes_tcp.go b/pkg/provider/kubernetes/crd/kubernetes_tcp.go
index cb3be3f3a3..bdb11ee338 100644
--- a/pkg/provider/kubernetes/crd/kubernetes_tcp.go
+++ b/pkg/provider/kubernetes/crd/kubernetes_tcp.go
@@ -8,8 +8,9 @@ import (
 	"strconv"
 	"strings"
 
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/provider"
 	"github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/traefik/v1alpha1"
 	"github.com/traefik/traefik/v2/pkg/tls"
@@ -24,7 +25,7 @@ func (p *Provider) loadIngressRouteTCPConfiguration(ctx context.Context, client
 	}
 
 	for _, ingressRouteTCP := range client.GetIngressRouteTCPs() {
-		logger := log.FromContext(log.With(ctx, log.Str("ingress", ingressRouteTCP.Name), log.Str("namespace", ingressRouteTCP.Namespace)))
+		logger := log.Ctx(ctx).With().Str("ingress", ingressRouteTCP.Name).Str("namespace", ingressRouteTCP.Namespace).Logger()
 
 		if !shouldProcessIngress(p.IngressClass, ingressRouteTCP.Annotations[annotationKubernetesIngressClass]) {
 			continue
@@ -33,7 +34,7 @@ func (p *Provider) loadIngressRouteTCPConfiguration(ctx context.Context, client
 		if ingressRouteTCP.Spec.TLS != nil && !ingressRouteTCP.Spec.TLS.Passthrough {
 			err := getTLSTCP(ctx, ingressRouteTCP, client, tlsConfigs)
 			if err != nil {
-				logger.Errorf("Error configuring TLS: %v", err)
+				logger.Error().Err(err).Msg("Error configuring TLS")
 			}
 		}
 
@@ -44,19 +45,19 @@ func (p *Provider) loadIngressRouteTCPConfiguration(ctx context.Context, client
 
 		for _, route := range ingressRouteTCP.Spec.Routes {
 			if len(route.Match) == 0 {
-				logger.Errorf("Empty match rule")
+				logger.Error().Msg("Empty match rule")
 				continue
 			}
 
 			key, err := makeServiceKey(route.Match, ingressName)
 			if err != nil {
-				logger.Error(err)
+				logger.Error().Err(err).Send()
 				continue
 			}
 
 			mds, err := p.makeMiddlewareTCPKeys(ctx, ingressRouteTCP.Namespace, route.Middlewares)
 			if err != nil {
-				logger.Errorf("Failed to create middleware keys: %v", err)
+				logger.Error().Err(err).Msg("Failed to create middleware keys")
 				continue
 			}
 
@@ -65,10 +66,11 @@ func (p *Provider) loadIngressRouteTCPConfiguration(ctx context.Context, client
 			for _, service := range route.Services {
 				balancerServerTCP, err := p.createLoadBalancerServerTCP(client, ingressRouteTCP.Namespace, service)
 				if err != nil {
-					logger.
-						WithField("serviceName", service.Name).
-						WithField("servicePort", service.Port).
-						Errorf("Cannot create service: %v", err)
+					logger.Error().
+						Str("serviceName", service.Name).
+						Stringer("servicePort", &service.Port).
+						Err(err).
+						Msg("Cannot create service")
 					continue
 				}
 
@@ -119,13 +121,13 @@ func (p *Provider) loadIngressRouteTCPConfiguration(ctx context.Context, client
 						}
 						tlsOptionsName = makeID(ns, tlsOptionsName)
 					} else if len(ns) > 0 {
-						logger.
-							WithField("TLSOption", ingressRouteTCP.Spec.TLS.Options.Name).
-							Warnf("Namespace %q is ignored in cross-provider context", ns)
+						logger.Warn().
+							Str("TLSOption", ingressRouteTCP.Spec.TLS.Options.Name).
+							Msgf("Namespace %q is ignored in cross-provider context", ns)
 					}
 
 					if !isNamespaceAllowed(p.AllowCrossNamespace, ingressRouteTCP.Namespace, ns) {
-						logger.Errorf("TLSOption %s/%s is not in the IngressRouteTCP namespace %s",
+						logger.Error().Msgf("TLSOption %s/%s is not in the IngressRouteTCP namespace %s",
 							ns, ingressRouteTCP.Spec.TLS.Options.Name, ingressRouteTCP.Namespace)
 						continue
 					}
@@ -147,9 +149,9 @@ func (p *Provider) makeMiddlewareTCPKeys(ctx context.Context, ingRouteTCPNamespa
 	for _, mi := range middlewares {
 		if strings.Contains(mi.Name, providerNamespaceSeparator) {
 			if len(mi.Namespace) > 0 {
-				log.FromContext(ctx).
-					WithField(log.MiddlewareName, mi.Name).
-					Warnf("namespace %q is ignored in cross-provider context", mi.Namespace)
+				log.Ctx(ctx).Warn().
+					Str(logs.MiddlewareName, mi.Name).
+					Msgf("Namespace %q is ignored in cross-provider context", mi.Namespace)
 			}
 			mds = append(mds, mi.Name)
 			continue
@@ -275,7 +277,7 @@ func getTLSTCP(ctx context.Context, ingressRoute *v1alpha1.IngressRouteTCP, k8sC
 		return nil
 	}
 	if ingressRoute.Spec.TLS.SecretName == "" {
-		log.FromContext(ctx).Debugf("No secret name provided")
+		log.Ctx(ctx).Debug().Msg("No secret name provided")
 		return nil
 	}
 
diff --git a/pkg/provider/kubernetes/crd/kubernetes_udp.go b/pkg/provider/kubernetes/crd/kubernetes_udp.go
index 7e6b47ae53..2589ebb0f0 100644
--- a/pkg/provider/kubernetes/crd/kubernetes_udp.go
+++ b/pkg/provider/kubernetes/crd/kubernetes_udp.go
@@ -7,8 +7,8 @@ import (
 	"net"
 	"strconv"
 
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/traefik/v1alpha1"
 	corev1 "k8s.io/api/core/v1"
 )
@@ -20,7 +20,7 @@ func (p *Provider) loadIngressRouteUDPConfiguration(ctx context.Context, client
 	}
 
 	for _, ingressRouteUDP := range client.GetIngressRouteUDPs() {
-		logger := log.FromContext(log.With(ctx, log.Str("ingress", ingressRouteUDP.Name), log.Str("namespace", ingressRouteUDP.Namespace)))
+		logger := log.Ctx(ctx).With().Str("ingress", ingressRouteUDP.Name).Str("namespace", ingressRouteUDP.Namespace).Logger()
 
 		if !shouldProcessIngress(p.IngressClass, ingressRouteUDP.Annotations[annotationKubernetesIngressClass]) {
 			continue
@@ -38,10 +38,11 @@ func (p *Provider) loadIngressRouteUDPConfiguration(ctx context.Context, client
 			for _, service := range route.Services {
 				balancerServerUDP, err := p.createLoadBalancerServerUDP(client, ingressRouteUDP.Namespace, service)
 				if err != nil {
-					logger.
-						WithField("serviceName", service.Name).
-						WithField("servicePort", service.Port).
-						Errorf("Cannot create service: %v", err)
+					logger.Error().
+						Str("serviceName", service.Name).
+						Stringer("servicePort", &service.Port).
+						Err(err).
+						Msg("Cannot create service")
 					continue
 				}
 
diff --git a/pkg/provider/kubernetes/gateway/client.go b/pkg/provider/kubernetes/gateway/client.go
index e8317e45f4..5ad1772bb4 100644
--- a/pkg/provider/kubernetes/gateway/client.go
+++ b/pkg/provider/kubernetes/gateway/client.go
@@ -7,7 +7,7 @@ import (
 	"os"
 	"time"
 
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 	corev1 "k8s.io/api/core/v1"
 	kubeerror "k8s.io/apimachinery/pkg/api/errors"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -252,7 +252,7 @@ func (c *clientWrapper) GetNamespaces(selector labels.Selector) ([]string, error
 	var namespaces []string
 	for _, namespace := range ns {
 		if !c.isWatchedNamespace(namespace.Name) {
-			log.WithoutContext().Warnf("Namespace %q is not within  watched namespaces", selector, namespace)
+			log.Warn().Msgf("Namespace %q is not within %q watched namespaces", selector, namespace)
 			continue
 		}
 		namespaces = append(namespaces, namespace.Name)
@@ -264,7 +264,7 @@ func (c *clientWrapper) GetHTTPRoutes(namespaces []string) ([]*v1alpha2.HTTPRout
 	var httpRoutes []*v1alpha2.HTTPRoute
 	for _, namespace := range namespaces {
 		if !c.isWatchedNamespace(namespace) {
-			log.WithoutContext().Warnf("Failed to get HTTPRoutes: %q is not within watched namespaces", namespace)
+			log.Warn().Msgf("Failed to get HTTPRoutes: %q is not within watched namespaces", namespace)
 			continue
 		}
 
@@ -274,7 +274,7 @@ func (c *clientWrapper) GetHTTPRoutes(namespaces []string) ([]*v1alpha2.HTTPRout
 		}
 
 		if len(routes) == 0 {
-			log.WithoutContext().Debugf("No HTTPRoutes found in namespace %q", namespace)
+			log.Debug().Msgf("No HTTPRoutes found in namespace %q", namespace)
 			continue
 		}
 
@@ -288,7 +288,7 @@ func (c *clientWrapper) GetTCPRoutes(namespaces []string) ([]*v1alpha2.TCPRoute,
 	var tcpRoutes []*v1alpha2.TCPRoute
 	for _, namespace := range namespaces {
 		if !c.isWatchedNamespace(namespace) {
-			log.WithoutContext().Warnf("Failed to get TCPRoutes: %q is not within watched namespaces", namespace)
+			log.Warn().Msgf("Failed to get TCPRoutes: %q is not within watched namespaces", namespace)
 			continue
 		}
 
@@ -298,7 +298,7 @@ func (c *clientWrapper) GetTCPRoutes(namespaces []string) ([]*v1alpha2.TCPRoute,
 		}
 
 		if len(routes) == 0 {
-			log.WithoutContext().Debugf("No TCPRoutes found in namespace %q", namespace)
+			log.Debug().Msgf("No TCPRoutes found in namespace %q", namespace)
 			continue
 		}
 
@@ -311,7 +311,7 @@ func (c *clientWrapper) GetTLSRoutes(namespaces []string) ([]*v1alpha2.TLSRoute,
 	var tlsRoutes []*v1alpha2.TLSRoute
 	for _, namespace := range namespaces {
 		if !c.isWatchedNamespace(namespace) {
-			log.WithoutContext().Warnf("Failed to get TLSRoutes: %q is not within watched namespaces", namespace)
+			log.Warn().Msgf("Failed to get TLSRoutes: %q is not within watched namespaces", namespace)
 			continue
 		}
 
@@ -321,7 +321,7 @@ func (c *clientWrapper) GetTLSRoutes(namespaces []string) ([]*v1alpha2.TLSRoute,
 		}
 
 		if len(routes) == 0 {
-			log.WithoutContext().Debugf("No TLSRoutes found in namespace %q", namespace)
+			log.Debug().Msgf("No TLSRoutes found in namespace %q", namespace)
 			continue
 		}
 
@@ -336,7 +336,7 @@ func (c *clientWrapper) GetGateways() []*v1alpha2.Gateway {
 	for ns, factory := range c.factoriesGateway {
 		gateways, err := factory.Gateway().V1alpha2().Gateways().Lister().List(labels.Everything())
 		if err != nil {
-			log.WithoutContext().Errorf("Failed to list Gateways in namespace %s: %v", ns, err)
+			log.Error().Err(err).Msgf("Failed to list Gateways in namespace %s", ns)
 			continue
 		}
 		result = append(result, gateways...)
diff --git a/pkg/provider/kubernetes/gateway/kubernetes.go b/pkg/provider/kubernetes/gateway/kubernetes.go
index e9da545709..7bc3dc754d 100644
--- a/pkg/provider/kubernetes/gateway/kubernetes.go
+++ b/pkg/provider/kubernetes/gateway/kubernetes.go
@@ -15,10 +15,11 @@ import (
 	"github.com/cenkalti/backoff/v4"
 	"github.com/hashicorp/go-multierror"
 	"github.com/mitchellh/hashstructure"
+	"github.com/rs/zerolog/log"
 	ptypes "github.com/traefik/paerser/types"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
 	"github.com/traefik/traefik/v2/pkg/job"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/provider"
 	traefikv1alpha1 "github.com/traefik/traefik/v2/pkg/provider/kubernetes/crd/traefik/v1alpha1"
 	"github.com/traefik/traefik/v2/pkg/safe"
@@ -67,24 +68,19 @@ func (p *Provider) newK8sClient(ctx context.Context) (*clientWrapper, error) {
 		return nil, fmt.Errorf("invalid label selector: %q", p.LabelSelector)
 	}
 
-	logger := log.FromContext(ctx)
-	logger.Infof("label selector is: %q", p.LabelSelector)
-
-	withEndpoint := ""
-	if p.Endpoint != "" {
-		withEndpoint = fmt.Sprintf(" with endpoint %s", p.Endpoint)
-	}
+	logger := log.Ctx(ctx)
+	logger.Info().Msgf("Label selector is: %q", p.LabelSelector)
 
 	var client *clientWrapper
 	switch {
 	case os.Getenv("KUBERNETES_SERVICE_HOST") != "" && os.Getenv("KUBERNETES_SERVICE_PORT") != "":
-		logger.Infof("Creating in-cluster Provider client%s", withEndpoint)
+		logger.Info().Str("endpoint", p.Endpoint).Msg("Creating in-cluster Provider client")
 		client, err = newInClusterClient(p.Endpoint)
 	case os.Getenv("KUBECONFIG") != "":
-		logger.Infof("Creating cluster-external Provider client from KUBECONFIG %s", os.Getenv("KUBECONFIG"))
+		logger.Info().Msgf("Creating cluster-external Provider client from KUBECONFIG %s", os.Getenv("KUBECONFIG"))
 		client, err = newExternalClusterClientFromFile(os.Getenv("KUBECONFIG"))
 	default:
-		logger.Infof("Creating cluster-external Provider client%s", withEndpoint)
+		logger.Info().Str("endpoint", p.Endpoint).Msg("Creating cluster-external Provider client")
 		client, err = newExternalClusterClient(p.Endpoint, p.Token, p.CertAuthFilePath)
 	}
 
@@ -104,8 +100,8 @@ func (p *Provider) Init() error {
 
 // Provide allows the k8s provider to provide configurations to traefik using the given configuration channel.
 func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error {
-	ctxLog := log.With(context.Background(), log.Str(log.ProviderName, providerName))
-	logger := log.FromContext(ctxLog)
+	logger := log.With().Str(logs.ProviderName, providerName).Logger()
+	ctxLog := logger.WithContext(context.Background())
 
 	k8sClient, err := p.newK8sClient(ctxLog)
 	if err != nil {
@@ -116,7 +112,7 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 		operation := func() error {
 			eventsChan, err := k8sClient.WatchAll(p.Namespaces, ctxPool.Done())
 			if err != nil {
-				logger.Errorf("Error watching kubernetes events: %v", err)
+				logger.Error().Err(err).Msg("Error watching kubernetes events")
 				timer := time.NewTimer(1 * time.Second)
 				select {
 				case <-timer.C:
@@ -145,9 +141,9 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 					confHash, err := hashstructure.Hash(conf, nil)
 					switch {
 					case err != nil:
-						logger.Error("Unable to hash the configuration")
+						logger.Error().Msg("Unable to hash the configuration")
 					case p.lastConfiguration.Get() == confHash:
-						logger.Debugf("Skipping Kubernetes event kind %T", event)
+						logger.Debug().Msgf("Skipping Kubernetes event kind %T", event)
 					default:
 						p.lastConfiguration.Set(confHash)
 						configurationChan <- dynamic.Message{
@@ -165,11 +161,11 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 		}
 
 		notify := func(err error, time time.Duration) {
-			logger.Errorf("Provider connection error: %v; retrying in %s", err, time)
+			logger.Error().Err(err).Msgf("Provider connection error, retrying in %s", time)
 		}
 		err := backoff.RetryNotify(safe.OperationWithRecover(operation), backoff.WithContext(job.NewBackOff(backoff.NewExponentialBackOff()), ctxPool), notify)
 		if err != nil {
-			logger.Errorf("Cannot connect to Provider: %v", err)
+			logger.Error().Err(err).Msg("Cannot connect to Provider")
 		}
 	})
 
@@ -178,13 +174,13 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 
 // TODO Handle errors and update resources statuses (gatewayClass, gateway).
 func (p *Provider) loadConfigurationFromGateway(ctx context.Context, client Client) *dynamic.Configuration {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
 	gatewayClassNames := map[string]struct{}{}
 
 	gatewayClasses, err := client.GetGatewayClasses()
 	if err != nil {
-		logger.Errorf("Cannot find GatewayClasses: %v", err)
+		logger.Error().Err(err).Msg("Cannot find GatewayClasses")
 		return &dynamic.Configuration{
 			UDP: &dynamic.UDPConfiguration{
 				Routers:  map[string]*dynamic.UDPRouter{},
@@ -215,7 +211,7 @@ func (p *Provider) loadConfigurationFromGateway(ctx context.Context, client Clie
 				LastTransitionTime: metav1.Now(),
 			})
 			if err != nil {
-				logger.Errorf("Failed to update %s condition: %v", v1alpha2.GatewayClassConditionStatusAccepted, err)
+				logger.Error().Err(err).Msgf("Failed to update %s condition", v1alpha2.GatewayClassConditionStatusAccepted)
 			}
 		}
 	}
@@ -224,8 +220,8 @@ func (p *Provider) loadConfigurationFromGateway(ctx context.Context, client Clie
 
 	// TODO check if we can only use the default filtering mechanism
 	for _, gateway := range client.GetGateways() {
-		ctxLog := log.With(ctx, log.Str("gateway", gateway.Name), log.Str("namespace", gateway.Namespace))
-		logger := log.FromContext(ctxLog)
+		logger := log.Ctx(ctx).With().Str("gateway", gateway.Name).Str("namespace", gateway.Namespace).Logger()
+		ctxLog := logger.WithContext(ctx)
 
 		if _, ok := gatewayClassNames[string(gateway.Spec.GatewayClassName)]; !ok {
 			continue
@@ -233,7 +229,7 @@ func (p *Provider) loadConfigurationFromGateway(ctx context.Context, client Clie
 
 		cfg, err := p.createGatewayConf(ctxLog, client, gateway)
 		if err != nil {
-			logger.Error(err)
+			logger.Error().Err(err).Send()
 			continue
 		}
 
@@ -315,7 +311,7 @@ func (p *Provider) createGatewayConf(ctx context.Context, client Client, gateway
 }
 
 func (p *Provider) fillGatewayConf(ctx context.Context, client Client, gateway *v1alpha2.Gateway, conf *dynamic.Configuration, tlsConfigs map[string]*tls.CertAndStores) []v1alpha2.ListenerStatus {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 	listenerStatuses := make([]v1alpha2.ListenerStatus, len(gateway.Spec.Listeners))
 	allocatedListeners := make(map[string]struct{})
 
@@ -407,7 +403,7 @@ func (p *Provider) fillGatewayConf(ctx context.Context, client Client, gateway *
 
 			if isTLSPassthrough && len(listener.TLS.CertificateRefs) > 0 {
 				// https://gateway-api.sigs.k8s.io/v1alpha2/references/spec/#gateway.networking.k8s.io/v1alpha2.GatewayTLSConfig
-				logger.Warnf("In case of Passthrough TLS mode, no TLS settings take effect as the TLS session from the client is NOT terminated at the Gateway")
+				logger.Warn().Msg("In case of Passthrough TLS mode, no TLS settings take effect as the TLS session from the client is NOT terminated at the Gateway")
 			}
 
 			// Allowed configurations:
@@ -684,7 +680,7 @@ func gatewayHTTPRouteToHTTPConf(ctx context.Context, ep string, listener v1alpha
 	}
 
 	if len(routes) == 0 {
-		log.FromContext(ctx).Debugf("No HTTPRoutes found")
+		log.Ctx(ctx).Debug().Msg("No HTTPRoutes found")
 		return nil
 	}
 
@@ -825,7 +821,7 @@ func gatewayTCPRouteToTCPConf(ctx context.Context, ep string, listener v1alpha2.
 	}
 
 	if len(routes) == 0 {
-		log.FromContext(ctx).Debugf("No TCPRoutes found")
+		log.Ctx(ctx).Debug().Msg("No TCPRoutes found")
 		return nil
 	}
 
@@ -955,7 +951,7 @@ func gatewayTLSRouteToTCPConf(ctx context.Context, ep string, listener v1alpha2.
 	}
 
 	if len(routes) == 0 {
-		log.FromContext(ctx).Debugf("No TLSRoutes found")
+		log.Ctx(ctx).Debug().Msg("No TLSRoutes found")
 		return nil
 	}
 
@@ -1466,7 +1462,7 @@ func loadServices(client Client, namespace string, backendRefs []v1alpha2.HTTPBa
 			// "DroppedRoutes" reason. The gateway status for this route
 			// should be updated with a condition that describes the error
 			// more specifically.
-			log.WithoutContext().Errorf("A multiple ports Kubernetes Service cannot be used if unspecified backendRef.Port")
+			log.Error().Msg("A multiple ports Kubernetes Service cannot be used if unspecified backendRef.Port")
 			continue
 		}
 
@@ -1587,7 +1583,7 @@ func loadTCPServices(client Client, namespace string, backendRefs []v1alpha2.Bac
 			// "DroppedRoutes" reason. The gateway status for this route
 			// should be updated with a condition that describes the error
 			// more specifically.
-			log.WithoutContext().Errorf("A multiple ports Kubernetes Service cannot be used if unspecified backendRef.Port")
+			log.Error().Msg("A multiple ports Kubernetes Service cannot be used if unspecified backendRef.Port")
 			continue
 		}
 
@@ -1684,7 +1680,7 @@ func throttleEvents(ctx context.Context, throttleDuration time.Duration, pool *s
 				default:
 					// We already have an event in eventsChanBuffered, so we'll do a refresh as soon as our throttle allows us to.
 					// It's fine to drop the event and keep whatever's in the buffer -- we don't do different things for different events
-					log.FromContext(ctx).Debugf("Dropping event kind %T due to throttling", nextEvent)
+					log.Ctx(ctx).Debug().Msgf("Dropping event kind %T due to throttling", nextEvent)
 				}
 			}
 		}
diff --git a/pkg/provider/kubernetes/ingress/client.go b/pkg/provider/kubernetes/ingress/client.go
index b9759b1f05..eccd396571 100644
--- a/pkg/provider/kubernetes/ingress/client.go
+++ b/pkg/provider/kubernetes/ingress/client.go
@@ -10,7 +10,7 @@ import (
 	"time"
 
 	"github.com/hashicorp/go-version"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/provider/kubernetes/k8s"
 	traefikversion "github.com/traefik/traefik/v2/pkg/version"
 	corev1 "k8s.io/api/core/v1"
@@ -246,7 +246,7 @@ func (c *clientWrapper) GetIngresses() []*networkingv1.Ingress {
 			// networking
 			listNew, err := factory.Networking().V1().Ingresses().Lister().List(labels.Everything())
 			if err != nil {
-				log.WithoutContext().Errorf("Failed to list ingresses in namespace %s: %v", ns, err)
+				log.Error().Err(err).Msgf("Failed to list ingresses in namespace %s", ns)
 				continue
 			}
 
@@ -257,14 +257,14 @@ func (c *clientWrapper) GetIngresses() []*networkingv1.Ingress {
 		// networking beta
 		list, err := factory.Networking().V1beta1().Ingresses().Lister().List(labels.Everything())
 		if err != nil {
-			log.WithoutContext().Errorf("Failed to list ingresses in namespace %s: %v", ns, err)
+			log.Error().Err(err).Msgf("Failed to list ingresses in namespace %s", ns)
 			continue
 		}
 
 		for _, ing := range list {
 			n, err := toNetworkingV1(ing)
 			if err != nil {
-				log.WithoutContext().Errorf("Failed to convert ingress %s from networking/v1beta1 to networking/v1: %v", ns, err)
+				log.Error().Err(err).Msgf("Failed to convert ingress %s from networking/v1beta1 to networking/v1", ns)
 				continue
 			}
 
@@ -366,10 +366,10 @@ func (c *clientWrapper) UpdateIngressStatus(src *networkingv1.Ingress, ingStatus
 		return fmt.Errorf("failed to get ingress %s/%s: %w", src.Namespace, src.Name, err)
 	}
 
-	logger := log.WithoutContext().WithField("namespace", ing.Namespace).WithField("ingress", ing.Name)
+	logger := log.With().Str("namespace", ing.Namespace).Str("ingress", ing.Name).Logger()
 
 	if isLoadBalancerIngressEquals(ing.Status.LoadBalancer.Ingress, ingStatus) {
-		logger.Debug("Skipping ingress status update")
+		logger.Debug().Msg("Skipping ingress status update")
 		return nil
 	}
 
@@ -384,7 +384,7 @@ func (c *clientWrapper) UpdateIngressStatus(src *networkingv1.Ingress, ingStatus
 		return fmt.Errorf("failed to update ingress status %s/%s: %w", src.Namespace, src.Name, err)
 	}
 
-	logger.Info("Updated ingress status")
+	logger.Info().Msg("Updated ingress status")
 	return nil
 }
 
@@ -394,10 +394,10 @@ func (c *clientWrapper) updateIngressStatusOld(src *networkingv1.Ingress, ingSta
 		return fmt.Errorf("failed to get ingress %s/%s: %w", src.Namespace, src.Name, err)
 	}
 
-	logger := log.WithoutContext().WithField("namespace", ing.Namespace).WithField("ingress", ing.Name)
+	logger := log.With().Str("namespace", ing.Namespace).Str("ingress", ing.Name).Logger()
 
 	if isLoadBalancerIngressEquals(ing.Status.LoadBalancer.Ingress, ingStatus) {
-		logger.Debug("Skipping ingress status update")
+		logger.Debug().Msg("Skipping ingress status update")
 		return nil
 	}
 
@@ -411,7 +411,7 @@ func (c *clientWrapper) updateIngressStatusOld(src *networkingv1.Ingress, ingSta
 	if err != nil {
 		return fmt.Errorf("failed to update ingress status %s/%s: %w", src.Namespace, src.Name, err)
 	}
-	logger.Info("Updated ingress status")
+	logger.Info().Msg("Updated ingress status")
 	return nil
 }
 
@@ -484,7 +484,7 @@ func (c *clientWrapper) GetIngressClasses() ([]*networkingv1.IngressClass, error
 			if ic.Spec.Controller == traefikDefaultIngressClassController {
 				icN, err := toNetworkingV1IngressClass(ic)
 				if err != nil {
-					log.WithoutContext().Errorf("Failed to convert ingress class %s from networking/v1beta1 to networking/v1: %v", ic.Name, err)
+					log.Error().Err(err).Msgf("Failed to convert ingress class %s from networking/v1beta1 to networking/v1", ic.Name)
 					continue
 				}
 				ics = append(ics, icN)
diff --git a/pkg/provider/kubernetes/ingress/kubernetes.go b/pkg/provider/kubernetes/ingress/kubernetes.go
index 4677b8b5d3..502c7cbe9b 100644
--- a/pkg/provider/kubernetes/ingress/kubernetes.go
+++ b/pkg/provider/kubernetes/ingress/kubernetes.go
@@ -15,10 +15,11 @@ import (
 
 	"github.com/cenkalti/backoff/v4"
 	"github.com/mitchellh/hashstructure"
+	"github.com/rs/zerolog/log"
 	ptypes "github.com/traefik/paerser/types"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
 	"github.com/traefik/traefik/v2/pkg/job"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/provider"
 	"github.com/traefik/traefik/v2/pkg/safe"
 	"github.com/traefik/traefik/v2/pkg/tls"
@@ -62,9 +63,9 @@ func (p *Provider) newK8sClient(ctx context.Context) (*clientWrapper, error) {
 		return nil, fmt.Errorf("invalid ingress label selector: %q", p.LabelSelector)
 	}
 
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
-	logger.Infof("ingress label selector is: %q", p.LabelSelector)
+	logger.Info().Msgf("ingress label selector is: %q", p.LabelSelector)
 
 	withEndpoint := ""
 	if p.Endpoint != "" {
@@ -74,13 +75,13 @@ func (p *Provider) newK8sClient(ctx context.Context) (*clientWrapper, error) {
 	var cl *clientWrapper
 	switch {
 	case os.Getenv("KUBERNETES_SERVICE_HOST") != "" && os.Getenv("KUBERNETES_SERVICE_PORT") != "":
-		logger.Infof("Creating in-cluster Provider client%s", withEndpoint)
+		logger.Info().Msgf("Creating in-cluster Provider client%s", withEndpoint)
 		cl, err = newInClusterClient(p.Endpoint)
 	case os.Getenv("KUBECONFIG") != "":
-		logger.Infof("Creating cluster-external Provider client from KUBECONFIG %s", os.Getenv("KUBECONFIG"))
+		logger.Info().Msgf("Creating cluster-external Provider client from KUBECONFIG %s", os.Getenv("KUBECONFIG"))
 		cl, err = newExternalClusterClientFromFile(os.Getenv("KUBECONFIG"))
 	default:
-		logger.Infof("Creating cluster-external Provider client%s", withEndpoint)
+		logger.Info().Msgf("Creating cluster-external Provider client%s", withEndpoint)
 		cl, err = newExternalClusterClient(p.Endpoint, p.Token, p.CertAuthFilePath)
 	}
 
@@ -100,8 +101,8 @@ func (p *Provider) Init() error {
 // Provide allows the k8s provider to provide configurations to traefik
 // using the given configuration channel.
 func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error {
-	ctxLog := log.With(context.Background(), log.Str(log.ProviderName, "kubernetes"))
-	logger := log.FromContext(ctxLog)
+	logger := log.With().Str(logs.ProviderName, "kubernetes").Logger()
+	ctxLog := logger.WithContext(context.Background())
 
 	k8sClient, err := p.newK8sClient(ctxLog)
 	if err != nil {
@@ -109,14 +110,14 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 	}
 
 	if p.AllowExternalNameServices {
-		logger.Warn("ExternalName service loading is enabled, please ensure that this is expected (see AllowExternalNameServices option)")
+		logger.Warn().Msg("ExternalName service loading is enabled, please ensure that this is expected (see AllowExternalNameServices option)")
 	}
 
 	pool.GoCtx(func(ctxPool context.Context) {
 		operation := func() error {
 			eventsChan, err := k8sClient.WatchAll(p.Namespaces, ctxPool.Done())
 			if err != nil {
-				logger.Errorf("Error watching kubernetes events: %v", err)
+				logger.Error().Err(err).Msg("Error watching kubernetes events")
 				timer := time.NewTimer(1 * time.Second)
 				select {
 				case <-timer.C:
@@ -147,9 +148,9 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 					confHash, err := hashstructure.Hash(conf, nil)
 					switch {
 					case err != nil:
-						logger.Error("Unable to hash the configuration")
+						logger.Error().Msg("Unable to hash the configuration")
 					case p.lastConfiguration.Get() == confHash:
-						logger.Debugf("Skipping Kubernetes event kind %T", event)
+						logger.Debug().Msgf("Skipping Kubernetes event kind %T", event)
 					default:
 						p.lastConfiguration.Set(confHash)
 						configurationChan <- dynamic.Message{
@@ -167,12 +168,12 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 		}
 
 		notify := func(err error, time time.Duration) {
-			logger.Errorf("Provider connection error: %s; retrying in %s", err, time)
+			logger.Error().Err(err).Msgf("Provider connection error, retrying in %s", time)
 		}
 
 		err := backoff.RetryNotify(safe.OperationWithRecover(operation), backoff.WithContext(job.NewBackOff(backoff.NewExponentialBackOff()), ctxPool), notify)
 		if err != nil {
-			logger.Errorf("Cannot connect to Provider: %s", err)
+			logger.Error().Err(err).Msg("Cannot connect to Provider")
 		}
 	})
 
@@ -196,7 +197,7 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl
 	if supportsIngressClass(serverVersion) {
 		ics, err := client.GetIngressClasses()
 		if err != nil {
-			log.FromContext(ctx).Warnf("Failed to list ingress classes: %v", err)
+			log.Ctx(ctx).Warn().Err(err).Msg("Failed to list ingress classes")
 		}
 
 		if p.IngressClass != "" {
@@ -210,7 +211,8 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl
 
 	certConfigs := make(map[string]*tls.CertAndStores)
 	for _, ingress := range ingresses {
-		ctx = log.With(ctx, log.Str("ingress", ingress.Name), log.Str("namespace", ingress.Namespace))
+		logger := log.Ctx(ctx).With().Str("ingress", ingress.Name).Str("namespace", ingress.Namespace).Logger()
+		ctx = logger.WithContext(ctx)
 
 		if !p.shouldProcessIngress(ingress, ingressClasses) {
 			continue
@@ -218,35 +220,36 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl
 
 		rtConfig, err := parseRouterConfig(ingress.Annotations)
 		if err != nil {
-			log.FromContext(ctx).Errorf("Failed to parse annotations: %v", err)
+			logger.Error().Err(err).Msg("Failed to parse annotations")
 			continue
 		}
 
 		err = getCertificates(ctx, ingress, client, certConfigs)
 		if err != nil {
-			log.FromContext(ctx).Errorf("Error configuring TLS: %v", err)
+			logger.Error().Err(err).Msg("Error configuring TLS")
 		}
 
 		if len(ingress.Spec.Rules) == 0 && ingress.Spec.DefaultBackend != nil {
 			if _, ok := conf.HTTP.Services["default-backend"]; ok {
-				log.FromContext(ctx).Error("The default backend already exists.")
+				logger.Error().Msg("The default backend already exists.")
 				continue
 			}
 
 			service, err := p.loadService(client, ingress.Namespace, *ingress.Spec.DefaultBackend)
 			if err != nil {
-				log.FromContext(ctx).
-					WithField("serviceName", ingress.Spec.DefaultBackend.Service.Name).
-					WithField("servicePort", ingress.Spec.DefaultBackend.Service.Port.String()).
-					Errorf("Cannot create service: %v", err)
+				logger.Error().
+					Str("serviceName", ingress.Spec.DefaultBackend.Service.Name).
+					Str("servicePort", ingress.Spec.DefaultBackend.Service.Port.String()).
+					Err(err).
+					Msg("Cannot create service")
 				continue
 			}
 
 			if len(service.LoadBalancer.Servers) == 0 && !p.AllowEmptyServices {
-				log.FromContext(ctx).
-					WithField("serviceName", ingress.Spec.DefaultBackend.Service.Name).
-					WithField("servicePort", ingress.Spec.DefaultBackend.Service.Port.String()).
-					Errorf("Skipping service: no endpoints found")
+				logger.Error().
+					Str("serviceName", ingress.Spec.DefaultBackend.Service.Name).
+					Str("servicePort", ingress.Spec.DefaultBackend.Service.Port.String()).
+					Msg("Skipping service: no endpoints found")
 				continue
 			}
 
@@ -270,7 +273,7 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl
 
 		for _, rule := range ingress.Spec.Rules {
 			if err := p.updateIngressStatus(ingress, client); err != nil {
-				log.FromContext(ctx).Errorf("Error while updating ingress status: %v", err)
+				logger.Error().Err(err).Msg("Error while updating ingress status")
 			}
 
 			if rule.HTTP == nil {
@@ -280,18 +283,19 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl
 			for _, pa := range rule.HTTP.Paths {
 				service, err := p.loadService(client, ingress.Namespace, pa.Backend)
 				if err != nil {
-					log.FromContext(ctx).
-						WithField("serviceName", pa.Backend.Service.Name).
-						WithField("servicePort", pa.Backend.Service.Port.String()).
-						Errorf("Cannot create service: %v", err)
+					logger.Error().
+						Str("serviceName", pa.Backend.Service.Name).
+						Str("servicePort", pa.Backend.Service.Port.String()).
+						Err(err).
+						Msg("Cannot create service")
 					continue
 				}
 
 				if len(service.LoadBalancer.Servers) == 0 && !p.AllowEmptyServices {
-					log.FromContext(ctx).
-						WithField("serviceName", pa.Backend.Service.Name).
-						WithField("servicePort", pa.Backend.Service.Port.String()).
-						Errorf("Skipping service: no endpoints found")
+					logger.Error().
+						Str("serviceName", pa.Backend.Service.Name).
+						Str("servicePort", pa.Backend.Service.Port.String()).
+						Msg("Skipping service: no endpoints found")
 					continue
 				}
 
@@ -315,12 +319,12 @@ func (p *Provider) loadConfigurationFromIngresses(ctx context.Context, client Cl
 				continue
 			}
 
-			log.FromContext(ctx).Debugf("Multiple routers are defined with the same key %q, generating hashes to avoid conflicts", routerKey)
+			logger.Debug().Msgf("Multiple routers are defined with the same key %q, generating hashes to avoid conflicts", routerKey)
 
 			for _, router := range conflictingRouters {
 				key, err := makeRouterKeyWithHash(routerKey, router.Rule)
 				if err != nil {
-					log.FromContext(ctx).Error(err)
+					logger.Error().Err(err).Send()
 					continue
 				}
 
@@ -367,7 +371,7 @@ func (p *Provider) updateIngressStatus(ing *networkingv1.Ingress, k8sClient Clie
 
 	if exists && service.Status.LoadBalancer.Ingress == nil {
 		// service exists, but has no Load Balancer status
-		log.Debugf("Skipping updating Ingress %s/%s due to service %s having no status set", ing.Namespace, ing.Name, p.IngressEndpoint.PublishedService)
+		log.Debug().Msgf("Skipping updating Ingress %s/%s due to service %s having no status set", ing.Namespace, ing.Name, p.IngressEndpoint.PublishedService)
 		return nil
 	}
 
@@ -405,7 +409,7 @@ func buildHostRule(host string) string {
 func getCertificates(ctx context.Context, ingress *networkingv1.Ingress, k8sClient Client, tlsConfigs map[string]*tls.CertAndStores) error {
 	for _, t := range ingress.Spec.TLS {
 		if t.SecretName == "" {
-			log.FromContext(ctx).Debugf("Skipping TLS sub-section: No secret name provided")
+			log.Ctx(ctx).Debug().Msg("Skipping TLS sub-section: No secret name provided")
 			continue
 		}
 
@@ -673,7 +677,7 @@ func throttleEvents(ctx context.Context, throttleDuration time.Duration, pool *s
 					// do a refresh as soon as our throttle allows us to. It's fine
 					// to drop the event and keep whatever's in the buffer -- we
 					// don't do different things for different events.
-					log.FromContext(ctx).Debugf("Dropping event kind %T due to throttling", nextEvent)
+					log.Ctx(ctx).Debug().Msgf("Dropping event kind %T due to throttling", nextEvent)
 				}
 			}
 		}
diff --git a/pkg/provider/kubernetes/k8s/parser.go b/pkg/provider/kubernetes/k8s/parser.go
index 6a3e94f7b3..6b1f43b76c 100644
--- a/pkg/provider/kubernetes/k8s/parser.go
+++ b/pkg/provider/kubernetes/k8s/parser.go
@@ -5,7 +5,7 @@ import (
 	"regexp"
 	"strings"
 
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 	"k8s.io/apimachinery/pkg/runtime"
 	"k8s.io/client-go/kubernetes/scheme"
 )
@@ -28,7 +28,7 @@ func MustParseYaml(content []byte) []runtime.Object {
 		}
 
 		if !acceptedK8sTypes.MatchString(groupVersionKind.Kind) {
-			log.WithoutContext().Debugf("The custom-roles configMap contained K8s object types which are not supported! Skipping object with type: %s", groupVersionKind.Kind)
+			log.Debug().Msgf("The custom-roles configMap contained K8s object types which are not supported! Skipping object with type: %s", groupVersionKind.Kind)
 		} else {
 			retVal = append(retVal, obj)
 		}
diff --git a/pkg/provider/kv/consul/consul.go b/pkg/provider/kv/consul/consul.go
index 86aac23bf4..6b4b5a5fbb 100644
--- a/pkg/provider/kv/consul/consul.go
+++ b/pkg/provider/kv/consul/consul.go
@@ -7,7 +7,7 @@ import (
 	"time"
 
 	"github.com/kvtools/consul"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/provider"
 	"github.com/traefik/traefik/v2/pkg/provider/kv"
 	"github.com/traefik/traefik/v2/pkg/types"
@@ -41,7 +41,7 @@ func (p *ProviderBuilder) BuildProviders() []*Provider {
 	// We can warn about that, because we've already made sure before that
 	// Namespace and Namespaces are mutually exclusive.
 	if p.Namespace != "" {
-		log.WithoutContext().Warnf("Namespace option is deprecated, please use the Namespaces option instead.")
+		log.Warn().Msg("Namespace option is deprecated, please use the Namespaces option instead.")
 	}
 
 	if len(p.Namespaces) == 0 {
diff --git a/pkg/provider/kv/kv.go b/pkg/provider/kv/kv.go
index 3bbf201f9a..abc25a84d1 100644
--- a/pkg/provider/kv/kv.go
+++ b/pkg/provider/kv/kv.go
@@ -10,10 +10,11 @@ import (
 	"github.com/cenkalti/backoff/v4"
 	"github.com/kvtools/valkeyrie"
 	"github.com/kvtools/valkeyrie/store"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
 	"github.com/traefik/traefik/v2/pkg/config/kv"
 	"github.com/traefik/traefik/v2/pkg/job"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/safe"
 )
 
@@ -34,7 +35,7 @@ func (p *Provider) SetDefaults() {
 
 // Init the provider.
 func (p *Provider) Init(storeType, name string, config valkeyrie.Config) error {
-	ctx := log.With(context.Background(), log.Str(log.ProviderName, name))
+	ctx := log.With().Str(logs.ProviderName, name).Logger().WithContext(context.Background())
 
 	p.name = name
 
@@ -50,8 +51,8 @@ func (p *Provider) Init(storeType, name string, config valkeyrie.Config) error {
 
 // Provide allows the docker provider to provide configurations to traefik using the given configuration channel.
 func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error {
-	ctx := log.With(context.Background(), log.Str(log.ProviderName, p.name))
-	logger := log.FromContext(ctx)
+	logger := log.With().Str(logs.ProviderName, p.name).Logger()
+	ctx := logger.WithContext(context.Background())
 
 	operation := func() error {
 		if _, err := p.kvClient.Exists(ctx, path.Join(p.RootKey, "qmslkjdfmqlskdjfmqlksjazçueznbvbwzlkajzebvkwjdcqmlsfj"), nil); err != nil {
@@ -61,8 +62,9 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 	}
 
 	notify := func(err error, time time.Duration) {
-		logger.Errorf("KV connection error: %+v, retrying in %s", err, time)
+		logger.Error().Err(err).Msgf("KV connection error, retrying in %s", time)
 	}
+
 	err := backoff.RetryNotify(safe.OperationWithRecover(operation), backoff.WithContext(job.NewBackOff(backoff.NewExponentialBackOff()), ctx), notify)
 	if err != nil {
 		return fmt.Errorf("cannot connect to KV server: %w", err)
@@ -70,7 +72,7 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 
 	configuration, err := p.buildConfiguration(ctx)
 	if err != nil {
-		logger.Errorf("Cannot build the configuration: %v", err)
+		logger.Error().Err(err).Msg("Cannot build the configuration")
 	} else {
 		configurationChan <- dynamic.Message{
 			ProviderName:  p.name,
@@ -79,11 +81,11 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 	}
 
 	pool.GoCtx(func(ctxPool context.Context) {
-		ctxLog := log.With(ctxPool, log.Str(log.ProviderName, p.name))
+		ctxLog := logger.With().Str(logs.ProviderName, p.name).Logger().WithContext(ctxPool)
 
 		err := p.watchKv(ctxLog, configurationChan)
 		if err != nil {
-			logger.Errorf("Cannot watch KV store: %v", err)
+			logger.Error().Err(err).Msg("Cannot watch KV store")
 		}
 	})
 
@@ -122,7 +124,7 @@ func (p *Provider) watchKv(ctx context.Context, configurationChan chan<- dynamic
 	}
 
 	notify := func(err error, time time.Duration) {
-		log.FromContext(ctx).Errorf("KV connection error: %+v, retrying in %s", err, time)
+		log.Ctx(ctx).Error().Err(err).Msgf("KV connection error, retrying in %s", time)
 	}
 
 	err := backoff.RetryNotify(safe.OperationWithRecover(operation),
diff --git a/pkg/provider/kv/storewrapper.go b/pkg/provider/kv/storewrapper.go
index 2a7b9aad71..382c998042 100644
--- a/pkg/provider/kv/storewrapper.go
+++ b/pkg/provider/kv/storewrapper.go
@@ -4,7 +4,7 @@ import (
 	"context"
 
 	"github.com/kvtools/valkeyrie/store"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 )
 
 type storeWrapper struct {
@@ -12,7 +12,7 @@ type storeWrapper struct {
 }
 
 func (s *storeWrapper) Put(ctx context.Context, key string, value []byte, options *store.WriteOptions) error {
-	log.WithoutContext().Debugf("Put: %s", key, string(value))
+	log.Debug().Msgf("Put: %s %s", key, string(value))
 
 	if s.Store == nil {
 		return nil
@@ -21,7 +21,7 @@ func (s *storeWrapper) Put(ctx context.Context, key string, value []byte, option
 }
 
 func (s *storeWrapper) Get(ctx context.Context, key string, options *store.ReadOptions) (*store.KVPair, error) {
-	log.WithoutContext().Debugf("Get: %s", key)
+	log.Debug().Msgf("Get: %s", key)
 
 	if s.Store == nil {
 		return nil, nil
@@ -30,7 +30,7 @@ func (s *storeWrapper) Get(ctx context.Context, key string, options *store.ReadO
 }
 
 func (s *storeWrapper) Delete(ctx context.Context, key string) error {
-	log.WithoutContext().Debugf("Delete: %s", key)
+	log.Debug().Msgf("Delete: %s", key)
 
 	if s.Store == nil {
 		return nil
@@ -39,7 +39,7 @@ func (s *storeWrapper) Delete(ctx context.Context, key string) error {
 }
 
 func (s *storeWrapper) Exists(ctx context.Context, key string, options *store.ReadOptions) (bool, error) {
-	log.WithoutContext().Debugf("Exists: %s", key)
+	log.Debug().Msgf("Exists: %s", key)
 
 	if s.Store == nil {
 		return true, nil
@@ -48,7 +48,7 @@ func (s *storeWrapper) Exists(ctx context.Context, key string, options *store.Re
 }
 
 func (s *storeWrapper) Watch(ctx context.Context, key string, options *store.ReadOptions) (<-chan *store.KVPair, error) {
-	log.WithoutContext().Debugf("Watch: %s", key)
+	log.Debug().Msgf("Watch: %s", key)
 
 	if s.Store == nil {
 		return nil, nil
@@ -57,7 +57,7 @@ func (s *storeWrapper) Watch(ctx context.Context, key string, options *store.Rea
 }
 
 func (s *storeWrapper) WatchTree(ctx context.Context, directory string, options *store.ReadOptions) (<-chan []*store.KVPair, error) {
-	log.WithoutContext().Debugf("WatchTree: %s", directory)
+	log.Debug().Msgf("WatchTree: %s", directory)
 
 	if s.Store == nil {
 		return nil, nil
@@ -66,7 +66,7 @@ func (s *storeWrapper) WatchTree(ctx context.Context, directory string, options
 }
 
 func (s *storeWrapper) NewLock(ctx context.Context, key string, options *store.LockOptions) (store.Locker, error) {
-	log.WithoutContext().Debugf("NewLock: %s", key)
+	log.Debug().Msgf("NewLock: %s", key)
 
 	if s.Store == nil {
 		return nil, nil
@@ -75,7 +75,7 @@ func (s *storeWrapper) NewLock(ctx context.Context, key string, options *store.L
 }
 
 func (s *storeWrapper) List(ctx context.Context, directory string, options *store.ReadOptions) ([]*store.KVPair, error) {
-	log.WithoutContext().Debugf("List: %s", directory)
+	log.Debug().Msgf("List: %s", directory)
 
 	if s.Store == nil {
 		return nil, nil
@@ -84,7 +84,7 @@ func (s *storeWrapper) List(ctx context.Context, directory string, options *stor
 }
 
 func (s *storeWrapper) DeleteTree(ctx context.Context, directory string) error {
-	log.WithoutContext().Debugf("DeleteTree: %s", directory)
+	log.Debug().Msgf("DeleteTree: %s", directory)
 
 	if s.Store == nil {
 		return nil
@@ -93,7 +93,7 @@ func (s *storeWrapper) DeleteTree(ctx context.Context, directory string) error {
 }
 
 func (s *storeWrapper) AtomicPut(ctx context.Context, key string, value []byte, previous *store.KVPair, options *store.WriteOptions) (bool, *store.KVPair, error) {
-	log.WithoutContext().Debugf("AtomicPut: %s", key, string(value), previous)
+	log.Debug().Msgf("AtomicPut: %s %s %v", key, string(value), previous)
 
 	if s.Store == nil {
 		return true, nil, nil
@@ -102,7 +102,7 @@ func (s *storeWrapper) AtomicPut(ctx context.Context, key string, value []byte,
 }
 
 func (s *storeWrapper) AtomicDelete(ctx context.Context, key string, previous *store.KVPair) (bool, error) {
-	log.WithoutContext().Debugf("AtomicDelete: %s", key, previous)
+	log.Debug().Msgf("AtomicDelete: %s %v", key, previous)
 
 	if s.Store == nil {
 		return true, nil
@@ -111,7 +111,7 @@ func (s *storeWrapper) AtomicDelete(ctx context.Context, key string, previous *s
 }
 
 func (s *storeWrapper) Close() error {
-	log.WithoutContext().Debugf("Close")
+	log.Debug().Msg("Close")
 
 	if s.Store == nil {
 		return nil
diff --git a/pkg/provider/marathon/config.go b/pkg/provider/marathon/config.go
index 4f9216e9b3..9395053519 100644
--- a/pkg/provider/marathon/config.go
+++ b/pkg/provider/marathon/config.go
@@ -10,9 +10,9 @@ import (
 	"strings"
 
 	"github.com/gambol99/go-marathon"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
 	"github.com/traefik/traefik/v2/pkg/config/label"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/provider"
 	"github.com/traefik/traefik/v2/pkg/provider/constraints"
 )
@@ -21,12 +21,12 @@ func (p *Provider) buildConfiguration(ctx context.Context, applications *maratho
 	configurations := make(map[string]*dynamic.Configuration)
 
 	for _, app := range applications.Apps {
-		ctxApp := log.With(ctx, log.Str("applicationID", app.ID))
-		logger := log.FromContext(ctxApp)
+		logger := log.Ctx(ctx).With().Str("applicationID", app.ID).Logger()
+		ctxApp := logger.WithContext(ctx)
 
 		extraConf, err := p.getConfiguration(app)
 		if err != nil {
-			logger.Errorf("Skip application: %v", err)
+			logger.Error().Err(err).Msg("Skip application")
 			continue
 		}
 
@@ -45,7 +45,7 @@ func (p *Provider) buildConfiguration(ctx context.Context, applications *maratho
 
 		confFromLabel, err := label.DecodeConfiguration(labels)
 		if err != nil {
-			logger.Error(err)
+			logger.Error().Err(err).Send()
 			continue
 		}
 
@@ -55,7 +55,7 @@ func (p *Provider) buildConfiguration(ctx context.Context, applications *maratho
 
 			err := p.buildTCPServiceConfiguration(ctxApp, app, extraConf, confFromLabel.TCP)
 			if err != nil {
-				logger.Error(err)
+				logger.Error().Err(err).Send()
 				continue
 			}
 			provider.BuildTCPRouterConfiguration(ctxApp, confFromLabel.TCP)
@@ -66,7 +66,7 @@ func (p *Provider) buildConfiguration(ctx context.Context, applications *maratho
 
 			err := p.buildUDPServiceConfiguration(ctxApp, app, extraConf, confFromLabel.UDP)
 			if err != nil {
-				logger.Error(err)
+				logger.Error().Err(err).Send()
 			} else {
 				provider.BuildUDPRouterConfiguration(ctxApp, confFromLabel.UDP)
 			}
@@ -81,7 +81,7 @@ func (p *Provider) buildConfiguration(ctx context.Context, applications *maratho
 
 		err = p.buildServiceConfiguration(ctxApp, app, extraConf, confFromLabel.HTTP)
 		if err != nil {
-			logger.Error(err)
+			logger.Error().Err(err).Send()
 			continue
 		}
 
@@ -109,7 +109,8 @@ func getServiceName(app marathon.Application) string {
 
 func (p *Provider) buildServiceConfiguration(ctx context.Context, app marathon.Application, extraConf configuration, conf *dynamic.HTTPConfiguration) error {
 	appName := getServiceName(app)
-	appCtx := log.With(ctx, log.Str("ApplicationID", appName))
+
+	logger := log.Ctx(ctx).With().Str("applicationName", appName).Logger()
 
 	if len(conf.Services) == 0 {
 		conf.Services = make(map[string]*dynamic.Service)
@@ -136,7 +137,7 @@ func (p *Provider) buildServiceConfiguration(ctx context.Context, app marathon.A
 			}
 			server, err := p.getServer(app, *task, extraConf, defaultServer)
 			if err != nil {
-				log.FromContext(appCtx).Errorf("Skip task: %v", err)
+				logger.Error().Err(err).Msg("Skip task")
 				continue
 			}
 			servers = append(servers, server)
@@ -152,7 +153,8 @@ func (p *Provider) buildServiceConfiguration(ctx context.Context, app marathon.A
 
 func (p *Provider) buildTCPServiceConfiguration(ctx context.Context, app marathon.Application, extraConf configuration, conf *dynamic.TCPConfiguration) error {
 	appName := getServiceName(app)
-	appCtx := log.With(ctx, log.Str("ApplicationID", appName))
+
+	logger := log.Ctx(ctx).With().Str("applicationName", appName).Logger()
 
 	if len(conf.Services) == 0 {
 		conf.Services = make(map[string]*dynamic.TCPService)
@@ -176,7 +178,7 @@ func (p *Provider) buildTCPServiceConfiguration(ctx context.Context, app maratho
 			if p.taskFilter(ctx, *task, app) {
 				server, err := p.getTCPServer(app, *task, extraConf, defaultServer)
 				if err != nil {
-					log.FromContext(appCtx).Errorf("Skip task: %v", err)
+					logger.Error().Err(err).Msg("Skip task")
 					continue
 				}
 				servers = append(servers, server)
@@ -193,7 +195,7 @@ func (p *Provider) buildTCPServiceConfiguration(ctx context.Context, app maratho
 
 func (p *Provider) buildUDPServiceConfiguration(ctx context.Context, app marathon.Application, extraConf configuration, conf *dynamic.UDPConfiguration) error {
 	appName := getServiceName(app)
-	appCtx := log.With(ctx, log.Str("ApplicationID", appName))
+	logger := log.Ctx(ctx).With().Str("applicationName", appName).Logger()
 
 	if len(conf.Services) == 0 {
 		conf.Services = make(map[string]*dynamic.UDPService)
@@ -217,7 +219,7 @@ func (p *Provider) buildUDPServiceConfiguration(ctx context.Context, app maratho
 			if p.taskFilter(ctx, *task, app) {
 				server, err := p.getUDPServer(app, *task, extraConf, defaultServer)
 				if err != nil {
-					log.FromContext(appCtx).Errorf("Skip task: %v", err)
+					logger.Error().Err(err).Msg("Skip task")
 					continue
 				}
 				servers = append(servers, server)
@@ -233,22 +235,22 @@ func (p *Provider) buildUDPServiceConfiguration(ctx context.Context, app maratho
 }
 
 func (p *Provider) keepApplication(ctx context.Context, extraConf configuration, labels map[string]string) bool {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
 	// Filter disabled application.
 	if !extraConf.Enable {
-		logger.Debug("Filtering disabled Marathon application")
+		logger.Debug().Msg("Filtering disabled Marathon application")
 		return false
 	}
 
 	// Filter by constraints.
 	matches, err := constraints.MatchLabels(labels, p.Constraints)
 	if err != nil {
-		logger.Errorf("Error matching constraints expression: %v", err)
+		logger.Error().Err(err).Msg("Error matching constraints expression")
 		return false
 	}
 	if !matches {
-		logger.Debugf("Marathon application filtered by constraint expression: %q", p.Constraints)
+		logger.Debug().Msgf("Marathon application filtered by constraint expression: %q", p.Constraints)
 		return false
 	}
 
@@ -261,7 +263,7 @@ func (p *Provider) taskFilter(ctx context.Context, task marathon.Task, applicati
 	}
 
 	if ready := p.readyChecker.Do(task, application); !ready {
-		log.FromContext(ctx).Infof("Filtering unready task %s from application %s", task.ID, application.ID)
+		log.Ctx(ctx).Info().Msgf("Filtering unready task %s from application %s", task.ID, application.ID)
 		return false
 	}
 
diff --git a/pkg/provider/marathon/marathon.go b/pkg/provider/marathon/marathon.go
index aab4697856..423573e66f 100644
--- a/pkg/provider/marathon/marathon.go
+++ b/pkg/provider/marathon/marathon.go
@@ -11,11 +11,12 @@ import (
 
 	"github.com/cenkalti/backoff/v4"
 	"github.com/gambol99/go-marathon"
-	"github.com/sirupsen/logrus"
+	"github.com/rs/zerolog"
+	"github.com/rs/zerolog/log"
 	ptypes "github.com/traefik/paerser/types"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
 	"github.com/traefik/traefik/v2/pkg/job"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/provider"
 	"github.com/traefik/traefik/v2/pkg/safe"
 	"github.com/traefik/traefik/v2/pkg/types"
@@ -23,9 +24,8 @@ import (
 
 const (
 	// DefaultTemplateRule The default template for the default rule.
-	DefaultTemplateRule   = "Host(`{{ normalize .Name }}`)"
-	traceMaxScanTokenSize = 1024 * 1024
-	marathonEventIDs      = marathon.EventIDApplications |
+	DefaultTemplateRule = "Host(`{{ normalize .Name }}`)"
+	marathonEventIDs    = marathon.EventIDApplications |
 		marathon.EventIDAddHealthCheck |
 		marathon.EventIDDeploymentSuccess |
 		marathon.EventIDDeploymentFailed |
@@ -108,23 +108,25 @@ func (p *Provider) Init() error {
 // Provide allows the marathon provider to provide configurations to traefik
 // using the given configuration channel.
 func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error {
-	ctx := log.With(context.Background(), log.Str(log.ProviderName, "marathon"))
-	logger := log.FromContext(ctx)
+	logger := log.With().Str(logs.ProviderName, "marathon").Logger()
+	ctx := logger.WithContext(context.Background())
 
 	operation := func() error {
 		confg := marathon.NewDefaultConfig()
 		confg.URL = p.Endpoint
 		confg.EventsTransport = marathon.EventsTransportSSE
+
 		if p.Trace {
-			confg.LogOutput = log.CustomWriterLevel(logrus.DebugLevel, traceMaxScanTokenSize)
+			confg.LogOutput = logs.NoLevel(logger, zerolog.DebugLevel)
 		}
+
 		if p.Basic != nil {
 			confg.HTTPBasicAuthUser = p.Basic.HTTPBasicAuthUser
 			confg.HTTPBasicPassword = p.Basic.HTTPBasicPassword
 		}
 		var rc *readinessChecker
 		if p.RespectReadinessChecks {
-			logger.Debug("Enabling Marathon readiness checker")
+			logger.Debug().Msg("Enabling Marathon readiness checker")
 			rc = defaultReadinessChecker(p.Trace)
 		}
 		p.readyChecker = rc
@@ -149,7 +151,7 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 		}
 		client, err := marathon.NewClient(confg)
 		if err != nil {
-			logger.Errorf("Failed to create a client for marathon, error: %s", err)
+			logger.Error().Err(err).Msg("Failed to create a client for marathon")
 			return err
 		}
 		p.marathonClient = client
@@ -157,7 +159,7 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 		if p.Watch {
 			update, err := client.AddEventsListener(marathonEventIDs)
 			if err != nil {
-				logger.Errorf("Failed to register for events, %s", err)
+				logger.Error().Err(err).Msg("Failed to register for events")
 				return err
 			}
 			pool.GoCtx(func(ctxPool context.Context) {
@@ -167,7 +169,7 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 					case <-ctxPool.Done():
 						return
 					case event := <-update:
-						logger.Debugf("Received provider event %s", event)
+						logger.Debug().Msgf("Received provider event %s", event)
 
 						conf := p.getConfigurations(ctx)
 						if conf != nil {
@@ -190,11 +192,11 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 	}
 
 	notify := func(err error, time time.Duration) {
-		logger.Errorf("Provider connection error %+v, retrying in %s", err, time)
+		logger.Error().Err(err).Msgf("Provider connection error, retrying in %s", time)
 	}
 	err := backoff.RetryNotify(safe.OperationWithRecover(operation), backoff.WithContext(job.NewBackOff(backoff.NewExponentialBackOff()), ctx), notify)
 	if err != nil {
-		logger.Errorf("Cannot connect to Provider server: %+v", err)
+		logger.Error().Err(err).Msg("Cannot connect to Provider server")
 	}
 	return nil
 }
@@ -202,7 +204,7 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 func (p *Provider) getConfigurations(ctx context.Context) *dynamic.Configuration {
 	applications, err := p.getApplications()
 	if err != nil {
-		log.FromContext(ctx).Errorf("Failed to retrieve Marathon applications: %v", err)
+		log.Ctx(ctx).Error().Err(err).Msg("Failed to retrieve Marathon applications")
 		return nil
 	}
 
diff --git a/pkg/provider/marathon/readiness.go b/pkg/provider/marathon/readiness.go
index df65599688..76785df13d 100644
--- a/pkg/provider/marathon/readiness.go
+++ b/pkg/provider/marathon/readiness.go
@@ -4,7 +4,7 @@ import (
 	"time"
 
 	"github.com/gambol99/go-marathon"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 )
 
 const (
@@ -98,7 +98,7 @@ func (rc *readinessChecker) Do(task marathon.Task, app marathon.Application) boo
 		// An unparseable start time should never occur; if it does, we assume the
 		// problem should be surfaced as quickly as possible, which is easiest if
 		// we shun the task from rotation.
-		log.Warnf("Failed to parse start-time %s of task %s from application %s: %s (assuming unready)", task.StartedAt, task.ID, app.ID, err)
+		log.Warn().Err(err).Msgf("Failed to parse start-time %s of task %s from application %s (assuming unready)", task.StartedAt, task.ID, app.ID)
 		return false
 	}
 
@@ -117,6 +117,6 @@ func (rc *readinessChecker) Do(task marathon.Task, app marathon.Application) boo
 
 func (rc *readinessChecker) tracef(format string, args ...interface{}) {
 	if rc.traceLogging {
-		log.Debugf(readinessLogHeader+format, args...)
+		log.Debug().Msgf(readinessLogHeader+format, args...)
 	}
 }
diff --git a/pkg/provider/nomad/config.go b/pkg/provider/nomad/config.go
index d53de0009d..a31b8ee21c 100644
--- a/pkg/provider/nomad/config.go
+++ b/pkg/provider/nomad/config.go
@@ -10,9 +10,10 @@ import (
 	"strconv"
 	"strings"
 
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
 	"github.com/traefik/traefik/v2/pkg/config/label"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/provider"
 	"github.com/traefik/traefik/v2/pkg/provider/constraints"
 )
@@ -22,18 +23,18 @@ func (p *Provider) buildConfig(ctx context.Context, items []item) *dynamic.Confi
 
 	for _, i := range items {
 		svcName := provider.Normalize(i.Node + "-" + i.Name + "-" + i.ID)
-		ctxSvc := log.With(ctx, log.Str(log.ServiceName, svcName))
+		logger := log.Ctx(ctx).With().Str(logs.ServiceName, svcName).Logger()
+		ctxSvc := logger.WithContext(ctx)
 
 		if !p.keepItem(ctxSvc, i) {
 			continue
 		}
 
-		logger := log.FromContext(ctx)
 		labels := tagsToLabels(i.Tags, p.Prefix)
 
 		config, err := label.DecodeConfiguration(labels)
 		if err != nil {
-			logger.Errorf("Failed to decode configuration: %v", err)
+			logger.Error().Err(err).Msg("Failed to decode configuration")
 			continue
 		}
 
@@ -42,7 +43,7 @@ func (p *Provider) buildConfig(ctx context.Context, items []item) *dynamic.Confi
 		if len(config.TCP.Routers) > 0 || len(config.TCP.Services) > 0 {
 			tcpOrUDP = true
 			if err := p.buildTCPConfig(i, config.TCP); err != nil {
-				logger.Errorf("Failed to build TCP service configuration: %v", err)
+				logger.Error().Err(err).Msg("Failed to build TCP service configuration")
 				continue
 			}
 			provider.BuildTCPRouterConfiguration(ctxSvc, config.TCP)
@@ -51,7 +52,7 @@ func (p *Provider) buildConfig(ctx context.Context, items []item) *dynamic.Confi
 		if len(config.UDP.Routers) > 0 || len(config.UDP.Services) > 0 {
 			tcpOrUDP = true
 			if err := p.buildUDPConfig(i, config.UDP); err != nil {
-				logger.Errorf("Failed to build UDP service configuration: %v", err)
+				logger.Error().Err(err).Msg("Failed to build UDP service configuration")
 				continue
 			}
 			provider.BuildUDPRouterConfiguration(ctxSvc, config.UDP)
@@ -67,7 +68,7 @@ func (p *Provider) buildConfig(ctx context.Context, items []item) *dynamic.Confi
 
 		// configure http service
 		if err := p.buildServiceConfig(i, config.HTTP); err != nil {
-			logger.Errorf("Failed to build HTTP service configuration: %v", err)
+			logger.Error().Err(err).Msg("Failed to build HTTP service configuration")
 			continue
 		}
 
@@ -148,20 +149,20 @@ func (p *Provider) buildServiceConfig(i item, configuration *dynamic.HTTPConfigu
 
 // TODO: check whether it is mandatory to filter again.
 func (p *Provider) keepItem(ctx context.Context, i item) bool {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
 	if !i.ExtraConf.Enable {
-		logger.Debug("Filtering disabled item")
+		logger.Debug().Msg("Filtering disabled item")
 		return false
 	}
 
 	matches, err := constraints.MatchTags(i.Tags, p.Constraints)
 	if err != nil {
-		logger.Errorf("Error matching constraint expressions: %v", err)
+		logger.Error().Err(err).Msg("Error matching constraint expressions")
 		return false
 	}
 	if !matches {
-		logger.Debugf("Filtering out item due to constraints: %q", p.Constraints)
+		logger.Debug().Msgf("Filtering out item due to constraints: %q", p.Constraints)
 		return false
 	}
 
diff --git a/pkg/provider/nomad/nomad.go b/pkg/provider/nomad/nomad.go
index e321c05a5f..12cba4939d 100644
--- a/pkg/provider/nomad/nomad.go
+++ b/pkg/provider/nomad/nomad.go
@@ -10,10 +10,11 @@ import (
 
 	"github.com/cenkalti/backoff/v4"
 	"github.com/hashicorp/nomad/api"
+	"github.com/rs/zerolog/log"
 	ptypes "github.com/traefik/paerser/types"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
 	"github.com/traefik/traefik/v2/pkg/job"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/provider"
 	"github.com/traefik/traefik/v2/pkg/provider/constraints"
 	"github.com/traefik/traefik/v2/pkg/safe"
@@ -59,7 +60,7 @@ type ProviderBuilder struct {
 // BuildProviders builds Nomad provider instances for the given namespaces configuration.
 func (p *ProviderBuilder) BuildProviders() []*Provider {
 	if p.Namespace != "" {
-		log.WithoutContext().Warnf("Namespace option is deprecated, please use the Namespaces option instead.")
+		log.Warn().Msg("Namespace option is deprecated, please use the Namespaces option instead.")
 	}
 
 	if len(p.Namespaces) == 0 {
@@ -154,8 +155,8 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 	}
 
 	pool.GoCtx(func(routineCtx context.Context) {
-		ctxLog := log.With(routineCtx, log.Str(log.ProviderName, p.name))
-		logger := log.FromContext(ctxLog)
+		logger := log.Ctx(routineCtx).With().Str(logs.ProviderName, p.name).Logger()
+		ctxLog := logger.WithContext(routineCtx)
 
 		operation := func() error {
 			ctx, cancel := context.WithCancel(ctxLog)
@@ -186,7 +187,7 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 		}
 
 		failure := func(err error, d time.Duration) {
-			logger.Errorf("Provider connection error %+v, retrying in %s", err, d)
+			logger.Error().Err(err).Msgf("Provider connection error, retrying in %s", d)
 		}
 
 		if retryErr := backoff.RetryNotify(
@@ -194,7 +195,7 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 			backoff.WithContext(job.NewBackOff(backoff.NewExponentialBackOff()), ctxLog),
 			failure,
 		); retryErr != nil {
-			logger.Errorf("Cannot connect to Nomad server %+v", retryErr)
+			logger.Error().Err(retryErr).Msg("Cannot connect to Nomad server")
 		}
 	})
 
@@ -273,22 +274,22 @@ func (p *Provider) getNomadServiceData(ctx context.Context) ([]item, error) {
 
 	for _, stub := range stubs {
 		for _, service := range stub.Services {
-			logger := log.FromContext(log.With(ctx, log.Str("serviceName", service.ServiceName)))
+			logger := log.Ctx(ctx).With().Str("serviceName", service.ServiceName).Logger()
 
 			extraConf := p.getExtraConf(service.Tags)
 			if !extraConf.Enable {
-				logger.Debug("Filter Nomad service that is not enabled")
+				logger.Debug().Msg("Filter Nomad service that is not enabled")
 				continue
 			}
 
 			matches, err := constraints.MatchTags(service.Tags, p.Constraints)
 			if err != nil {
-				logger.Errorf("Error matching constraint expressions: %v", err)
+				logger.Error().Err(err).Msg("Error matching constraint expressions")
 				continue
 			}
 
 			if !matches {
-				logger.Debugf("Filter Nomad service not matching constraints: %q", p.Constraints)
+				logger.Debug().Msgf("Filter Nomad service not matching constraints: %q", p.Constraints)
 				continue
 			}
 
diff --git a/pkg/provider/rancher/config.go b/pkg/provider/rancher/config.go
index 1f16e5e6f7..1b0d5339d8 100644
--- a/pkg/provider/rancher/config.go
+++ b/pkg/provider/rancher/config.go
@@ -7,9 +7,9 @@ import (
 	"net"
 	"strings"
 
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
 	"github.com/traefik/traefik/v2/pkg/config/label"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/provider"
 	"github.com/traefik/traefik/v2/pkg/provider/constraints"
 )
@@ -18,17 +18,16 @@ func (p *Provider) buildConfiguration(ctx context.Context, services []rancherDat
 	configurations := make(map[string]*dynamic.Configuration)
 
 	for _, service := range services {
-		ctxService := log.With(ctx, log.Str("service", service.Name))
+		logger := log.Ctx(ctx).With().Str("service", service.Name).Logger()
+		ctxService := logger.WithContext(ctx)
 
 		if !p.keepService(ctx, service) {
 			continue
 		}
 
-		logger := log.FromContext(ctxService)
-
 		confFromLabel, err := label.DecodeConfiguration(service.Labels)
 		if err != nil {
-			logger.Error(err)
+			logger.Error().Err(err).Send()
 			continue
 		}
 
@@ -38,7 +37,7 @@ func (p *Provider) buildConfiguration(ctx context.Context, services []rancherDat
 
 			err := p.buildTCPServiceConfiguration(ctxService, service, confFromLabel.TCP)
 			if err != nil {
-				logger.Error(err)
+				logger.Error().Err(err).Send()
 				continue
 			}
 			provider.BuildTCPRouterConfiguration(ctxService, confFromLabel.TCP)
@@ -49,7 +48,7 @@ func (p *Provider) buildConfiguration(ctx context.Context, services []rancherDat
 
 			err := p.buildUDPServiceConfiguration(ctxService, service, confFromLabel.UDP)
 			if err != nil {
-				logger.Error(err)
+				logger.Error().Err(err).Send()
 				continue
 			}
 			provider.BuildUDPRouterConfiguration(ctxService, confFromLabel.UDP)
@@ -64,7 +63,7 @@ func (p *Provider) buildConfiguration(ctx context.Context, services []rancherDat
 
 		err = p.buildServiceConfiguration(ctx, service, confFromLabel.HTTP)
 		if err != nil {
-			logger.Error(err)
+			logger.Error().Err(err).Send()
 			continue
 		}
 
@@ -151,30 +150,30 @@ func (p *Provider) buildServiceConfiguration(ctx context.Context, service ranche
 }
 
 func (p *Provider) keepService(ctx context.Context, service rancherData) bool {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
 	if !service.ExtraConf.Enable {
-		logger.Debug("Filtering disabled service.")
+		logger.Debug().Msg("Filtering disabled service")
 		return false
 	}
 
 	matches, err := constraints.MatchLabels(service.Labels, p.Constraints)
 	if err != nil {
-		logger.Errorf("Error matching constraint expression: %v", err)
+		logger.Error().Err(err).Msg("Error matching constraint expression")
 		return false
 	}
 	if !matches {
-		logger.Debugf("Service pruned by constraint expression: %q", p.Constraints)
+		logger.Debug().Msgf("Service pruned by constraint expression: %q", p.Constraints)
 		return false
 	}
 
 	if p.EnableServiceHealthFilter {
 		if service.Health != "" && service.Health != healthy && service.Health != updatingHealthy {
-			logger.Debugf("Filtering service %s with healthState of %s \n", service.Name, service.Health)
+			logger.Debug().Msgf("Filtering service %s with healthState of %s", service.Name, service.Health)
 			return false
 		}
 		if service.State != "" && service.State != active && service.State != updatingActive && service.State != upgraded && service.State != upgrading {
-			logger.Debugf("Filtering service %s with state of %s \n", service.Name, service.State)
+			logger.Debug().Msgf("Filtering service %s with state of %s", service.Name, service.State)
 			return false
 		}
 	}
@@ -183,7 +182,7 @@ func (p *Provider) keepService(ctx context.Context, service rancherData) bool {
 }
 
 func (p *Provider) addServerTCP(ctx context.Context, service rancherData, loadBalancer *dynamic.TCPServersLoadBalancer) error {
-	log.FromContext(ctx).Debugf("Trying to add servers for service  %s \n", service.Name)
+	log.Ctx(ctx).Debug().Msgf("Trying to add servers for service %s", service.Name)
 
 	if loadBalancer == nil {
 		return errors.New("load-balancer is not defined")
@@ -217,7 +216,7 @@ func (p *Provider) addServerTCP(ctx context.Context, service rancherData, loadBa
 }
 
 func (p *Provider) addServerUDP(ctx context.Context, service rancherData, loadBalancer *dynamic.UDPServersLoadBalancer) error {
-	log.FromContext(ctx).Debugf("Trying to add servers for service  %s \n", service.Name)
+	log.Ctx(ctx).Debug().Msgf("Trying to add servers for service %s", service.Name)
 
 	if loadBalancer == nil {
 		return errors.New("load-balancer is not defined")
@@ -251,7 +250,7 @@ func (p *Provider) addServerUDP(ctx context.Context, service rancherData, loadBa
 }
 
 func (p *Provider) addServers(ctx context.Context, service rancherData, loadBalancer *dynamic.ServersLoadBalancer) error {
-	log.FromContext(ctx).Debugf("Trying to add servers for service  %s \n", service.Name)
+	log.Ctx(ctx).Debug().Msgf("Trying to add servers for service %s", service.Name)
 
 	if loadBalancer == nil {
 		return errors.New("load-balancer is not defined")
diff --git a/pkg/provider/rancher/rancher.go b/pkg/provider/rancher/rancher.go
index 8de7ad8621..8f74dcc21f 100644
--- a/pkg/provider/rancher/rancher.go
+++ b/pkg/provider/rancher/rancher.go
@@ -8,9 +8,10 @@ import (
 
 	"github.com/cenkalti/backoff/v4"
 	rancher "github.com/rancher/go-rancher-metadata/metadata"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
 	"github.com/traefik/traefik/v2/pkg/job"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/provider"
 	"github.com/traefik/traefik/v2/pkg/safe"
 )
@@ -86,7 +87,7 @@ func (p *Provider) createClient(ctx context.Context) (rancher.Client, error) {
 	metadataServiceURL := fmt.Sprintf("http://rancher-metadata.rancher.internal/%s", p.Prefix)
 	client, err := rancher.NewClientAndWait(metadataServiceURL)
 	if err != nil {
-		log.FromContext(ctx).Errorf("Failed to create Rancher metadata service client: %v", err)
+		log.Ctx(ctx).Error().Err(err).Msg("Failed to create Rancher metadata service client")
 		return nil, err
 	}
 
@@ -96,20 +97,20 @@ func (p *Provider) createClient(ctx context.Context) (rancher.Client, error) {
 // Provide allows the rancher provider to provide configurations to traefik using the given configuration channel.
 func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.Pool) error {
 	pool.GoCtx(func(routineCtx context.Context) {
-		ctxLog := log.With(routineCtx, log.Str(log.ProviderName, "rancher"))
-		logger := log.FromContext(ctxLog)
+		logger := log.Ctx(routineCtx).With().Str(logs.ProviderName, "rancher").Logger()
+		ctxLog := logger.WithContext(routineCtx)
 
 		operation := func() error {
 			client, err := p.createClient(ctxLog)
 			if err != nil {
-				logger.Errorf("Failed to create the metadata client metadata service: %v", err)
+				logger.Error().Err(err).Msg("Failed to create the metadata client metadata service")
 				return err
 			}
 
 			updateConfiguration := func(_ string) {
 				stacks, err := client.GetStacks()
 				if err != nil {
-					logger.Errorf("Failed to query Rancher metadata service: %v", err)
+					logger.Error().Err(err).Msg("Failed to query Rancher metadata service")
 					return
 				}
 
@@ -139,11 +140,11 @@ func (p *Provider) Provide(configurationChan chan<- dynamic.Message, pool *safe.
 		}
 
 		notify := func(err error, time time.Duration) {
-			logger.Errorf("Provider connection error %+v, retrying in %s", err, time)
+			logger.Error().Err(err).Msgf("Provider connection error, retrying in %s", time)
 		}
 		err := backoff.RetryNotify(safe.OperationWithRecover(operation), backoff.WithContext(job.NewBackOff(backoff.NewExponentialBackOff()), ctxLog), notify)
 		if err != nil {
-			logger.Errorf("Cannot connect to Provider server: %+v", err)
+			logger.Error().Err(err).Msg("Cannot connect to Provider server")
 		}
 	})
 
@@ -160,7 +161,7 @@ func (p *Provider) intervalPoll(ctx context.Context, client rancher.Client, upda
 		case <-ticker.C:
 			newVersion, err := client.GetVersion()
 			if err != nil {
-				log.FromContext(ctx).Errorf("Failed to create Rancher metadata service client: %v", err)
+				log.Ctx(ctx).Error().Err(err).Msg("Failed to create Rancher metadata service client")
 			} else if version != newVersion {
 				version = newVersion
 				updateConfiguration(version)
@@ -174,15 +175,15 @@ func (p *Provider) intervalPoll(ctx context.Context, client rancher.Client, upda
 func (p *Provider) parseMetadataSourcedRancherData(ctx context.Context, stacks []rancher.Stack) (rancherDataList []rancherData) {
 	for _, stack := range stacks {
 		for _, service := range stack.Services {
-			ctxSvc := log.With(ctx, log.Str("stack", stack.Name), log.Str("service", service.Name))
-			logger := log.FromContext(ctxSvc)
+			logger := log.Ctx(ctx).With().Str("stack", stack.Name).Str("service", service.Name).Logger()
+			ctxSvc := logger.WithContext(ctx)
 
 			servicePort := ""
 			if len(service.Ports) > 0 {
 				servicePort = service.Ports[0]
 			}
 			for _, port := range service.Ports {
-				logger.Debugf("Set Port %s", port)
+				logger.Debug().Msgf("Set Port %s", port)
 			}
 
 			var containerIPAddresses []string
@@ -202,7 +203,7 @@ func (p *Provider) parseMetadataSourcedRancherData(ctx context.Context, stacks [
 
 			extraConf, err := p.getConfiguration(service)
 			if err != nil {
-				logger.Errorf("Skip container %s: %v", service.Name, err)
+				logger.Error().Err(err).Msgf("Skip container %s", service.Name)
 				continue
 			}
 
@@ -215,15 +216,15 @@ func (p *Provider) parseMetadataSourcedRancherData(ctx context.Context, stacks [
 }
 
 func containerFilter(ctx context.Context, name, healthState, state string) bool {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
 	if healthState != "" && healthState != healthy && healthState != updatingHealthy {
-		logger.Debugf("Filtering container %s with healthState of %s", name, healthState)
+		logger.Debug().Msgf("Filtering container %s with healthState of %s", name, healthState)
 		return false
 	}
 
 	if state != "" && state != running && state != updatingRunning && state != upgraded {
-		logger.Debugf("Filtering container %s with state of %s", name, state)
+		logger.Debug().Msgf("Filtering container %s with state of %s", name, state)
 		return false
 	}
 
diff --git a/pkg/provider/rest/rest.go b/pkg/provider/rest/rest.go
index 14be6ab651..f375bd1dbc 100644
--- a/pkg/provider/rest/rest.go
+++ b/pkg/provider/rest/rest.go
@@ -6,8 +6,8 @@ import (
 	"net/http"
 
 	"github.com/gorilla/mux"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/provider"
 	"github.com/traefik/traefik/v2/pkg/safe"
 	"github.com/unrolled/render"
@@ -48,14 +48,14 @@ func (p *Provider) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 	configuration := new(dynamic.Configuration)
 
 	if err := json.NewDecoder(req.Body).Decode(configuration); err != nil {
-		log.WithoutContext().Errorf("Error parsing configuration %+v", err)
+		log.Error().Err(err).Msg("Error parsing configuration")
 		http.Error(rw, fmt.Sprintf("%+v", err), http.StatusBadRequest)
 		return
 	}
 
 	p.configurationChan <- dynamic.Message{ProviderName: "rest", Configuration: configuration}
 	if err := templatesRenderer.JSON(rw, http.StatusOK, configuration); err != nil {
-		log.WithoutContext().Error(err)
+		log.Error().Err(err).Send()
 	}
 }
 
diff --git a/pkg/provider/tailscale/provider.go b/pkg/provider/tailscale/provider.go
index 6c7c99c9c6..a807dfdcfa 100644
--- a/pkg/provider/tailscale/provider.go
+++ b/pkg/provider/tailscale/provider.go
@@ -9,9 +9,10 @@ import (
 	"sync"
 	"time"
 
+	"github.com/rs/zerolog/log"
 	"github.com/tailscale/tscert"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/muxer/http"
 	"github.com/traefik/traefik/v2/pkg/muxer/tcp"
 	"github.com/traefik/traefik/v2/pkg/safe"
@@ -56,21 +57,20 @@ func (p *Provider) HandleConfigUpdate(cfg dynamic.Configuration) {
 func (p *Provider) Provide(dynMessages chan<- dynamic.Message, pool *safe.Pool) error {
 	p.dynMessages = dynMessages
 
-	fields := log.Str(log.ProviderName, p.ResolverName+".tailscale")
+	logger := log.With().Str(logs.ProviderName, p.ResolverName+".tailscale").Logger()
 
 	pool.GoCtx(func(ctx context.Context) {
-		p.watchDomains(log.With(ctx, fields))
+		p.watchDomains(logger.WithContext(ctx))
 	})
 
 	pool.GoCtx(func(ctx context.Context) {
-		p.renewCertificates(log.With(ctx, fields))
+		p.renewCertificates(logger.WithContext(ctx))
 	})
 
 	return nil
 }
 
-// watchDomains watches for Tailscale domain certificates that should be
-// fetched from the Tailscale daemon.
+// watchDomains watches for Tailscale domain certificates that should be fetched from the Tailscale daemon.
 func (p *Provider) watchDomains(ctx context.Context) {
 	for {
 		select {
@@ -110,9 +110,9 @@ func (p *Provider) renewCertificates(ctx context.Context) {
 			for domain, cert := range p.certByDomain {
 				tlsCert, err := cert.GetCertificateFromBytes()
 				if err != nil {
-					log.FromContext(ctx).
-						WithError(err).
-						Errorf("Unable to get certificate for domain %s", domain)
+					log.Ctx(ctx).
+						Err(err).
+						Msgf("Unable to get certificate for domain %s", domain)
 					continue
 				}
 
@@ -139,7 +139,7 @@ func (p *Provider) renewCertificates(ctx context.Context) {
 // findDomains goes through the given dynamic.Configuration and returns all
 // Tailscale-specific domains found.
 func (p *Provider) findDomains(ctx context.Context, cfg dynamic.Configuration) []string {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
 	var domains []string
 
@@ -162,7 +162,7 @@ func (p *Provider) findDomains(ctx context.Context, cfg dynamic.Configuration) [
 
 			parsedDomains, err := http.ParseDomains(router.Rule)
 			if err != nil {
-				logger.Errorf("Unable to parse HTTP router domains: %v", err)
+				logger.Error().Err(err).Msg("Unable to parse HTTP router domains")
 				continue
 			}
 
@@ -189,7 +189,7 @@ func (p *Provider) findDomains(ctx context.Context, cfg dynamic.Configuration) [
 
 			parsedDomains, err := tcp.ParseHostSNI(router.Rule)
 			if err != nil {
-				logger.Errorf("Unable to parse TCP router domains: %v", err)
+				logger.Error().Err(err).Msg("Unable to parse TCP router domains")
 				continue
 			}
 
@@ -241,16 +241,16 @@ func (p *Provider) purgeUnusedCerts(domains []string) bool {
 // fetchCerts fetches the certificates for the provided domains from the
 // Tailscale daemon.
 func (p *Provider) fetchCerts(ctx context.Context, domains []string) {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
 	for _, domain := range domains {
 		cert, key, err := tscert.CertPair(ctx, domain)
 		if err != nil {
-			logger.WithError(err).Errorf("Unable to fetch certificate for domain %q", domain)
+			logger.Error().Err(err).Msgf("Unable to fetch certificate for domain %q", domain)
 			continue
 		}
 
-		logger.Debugf("Fetched certificate for domain %q", domain)
+		logger.Debug().Msgf("Fetched certificate for domain %q", domain)
 
 		p.certByDomainMu.Lock()
 		p.certByDomain[domain] = traefiktls.Certificate{
@@ -300,7 +300,7 @@ func (p *Provider) sendDynamicConfig() {
 // sanitizeDomains removes duplicated and invalid Tailscale subdomains, from
 // the provided list.
 func sanitizeDomains(ctx context.Context, domains []string) []string {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
 	seen := map[string]struct{}{}
 
@@ -311,7 +311,7 @@ func sanitizeDomains(ctx context.Context, domains []string) []string {
 		}
 
 		if !isTailscaleDomain(domain) {
-			logger.Errorf("Domain %s is not a valid Tailscale domain", domain)
+			logger.Error().Msgf("Domain %s is not a valid Tailscale domain", domain)
 			continue
 		}
 
diff --git a/pkg/provider/traefik/internal.go b/pkg/provider/traefik/internal.go
index 2ced41e857..175ce07b92 100644
--- a/pkg/provider/traefik/internal.go
+++ b/pkg/provider/traefik/internal.go
@@ -8,9 +8,10 @@ import (
 	"regexp"
 	"time"
 
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
 	"github.com/traefik/traefik/v2/pkg/config/static"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/provider"
 	"github.com/traefik/traefik/v2/pkg/safe"
 	"github.com/traefik/traefik/v2/pkg/tls"
@@ -37,7 +38,7 @@ func (i Provider) ThrottleDuration() time.Duration {
 
 // Provide allows the provider to provide configurations to traefik using the given configuration channel.
 func (i *Provider) Provide(configurationChan chan<- dynamic.Message, _ *safe.Pool) error {
-	ctx := log.With(context.Background(), log.Str(log.ProviderName, "internal"))
+	ctx := log.With().Str(logs.ProviderName, "internal").Logger().WithContext(context.Background())
 
 	configurationChan <- dynamic.Message{
 		ProviderName:  "internal",
@@ -118,17 +119,17 @@ func (i *Provider) redirection(ctx context.Context, cfg *dynamic.Configuration)
 			continue
 		}
 
-		logger := log.FromContext(log.With(ctx, log.Str(log.EntryPointName, name)))
+		logger := log.Ctx(ctx).With().Str(logs.EntryPointName, name).Logger()
 
 		def := ep.HTTP.Redirections
 		if def.EntryPoint == nil || def.EntryPoint.To == "" {
-			logger.Error("Unable to create redirection: the entry point or the port is missing")
+			logger.Error().Msg("Unable to create redirection: the entry point or the port is missing")
 			continue
 		}
 
 		port, err := i.getRedirectPort(name, def)
 		if err != nil {
-			logger.Error(err)
+			logger.Error().Err(err).Send()
 			continue
 		}
 
diff --git a/pkg/safe/routine.go b/pkg/safe/routine.go
index 71af2530db..ab6913c87a 100644
--- a/pkg/safe/routine.go
+++ b/pkg/safe/routine.go
@@ -7,7 +7,7 @@ import (
 	"sync"
 
 	"github.com/cenkalti/backoff/v4"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 )
 
 type routineCtx func(ctx context.Context)
@@ -61,9 +61,8 @@ func GoWithRecover(goroutine func(), customRecover func(err interface{})) {
 }
 
 func defaultRecoverGoroutine(err interface{}) {
-	logger := log.WithoutContext()
-	logger.Errorf("Error in Go routine: %s", err)
-	logger.Errorf("Stack: %s", debug.Stack())
+	log.Error().Interface("error", err).Msg("Error in Go routine")
+	log.Error().Msgf("Stack: %s", debug.Stack())
 }
 
 // OperationWithRecover wrap a backoff operation in a Recover.
diff --git a/pkg/server/aggregator.go b/pkg/server/aggregator.go
index 4b50829e8b..ecf332b1f9 100644
--- a/pkg/server/aggregator.go
+++ b/pkg/server/aggregator.go
@@ -2,8 +2,9 @@ package server
 
 import (
 	"github.com/go-acme/lego/v4/challenge/tlsalpn01"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/server/provider"
 	"github.com/traefik/traefik/v2/pkg/tls"
 )
@@ -40,9 +41,10 @@ func mergeConfiguration(configurations dynamic.Configurations, defaultEntryPoint
 		if configuration.HTTP != nil {
 			for routerName, router := range configuration.HTTP.Routers {
 				if len(router.EntryPoints) == 0 {
-					log.WithoutContext().
-						WithField(log.RouterName, routerName).
-						Debugf("No entryPoint defined for this router, using the default one(s) instead: %+v", defaultEntryPoints)
+					log.Debug().
+						Str(logs.RouterName, routerName).
+						Strs(logs.EntryPointName, defaultEntryPoints).
+						Msg("No entryPoint defined for this router, using the default one(s) instead")
 					router.EntryPoints = defaultEntryPoints
 				}
 
@@ -65,9 +67,9 @@ func mergeConfiguration(configurations dynamic.Configurations, defaultEntryPoint
 		if configuration.TCP != nil {
 			for routerName, router := range configuration.TCP.Routers {
 				if len(router.EntryPoints) == 0 {
-					log.WithoutContext().
-						WithField(log.RouterName, routerName).
-						Debugf("No entryPoint defined for this TCP router, using the default one(s) instead: %+v", defaultEntryPoints)
+					log.Debug().
+						Str(logs.RouterName, routerName).
+						Msgf("No entryPoint defined for this TCP router, using the default one(s) instead: %+v", defaultEntryPoints)
 					router.EntryPoints = defaultEntryPoints
 				}
 				conf.TCP.Routers[provider.MakeQualifiedName(pvd, routerName)] = router
@@ -120,14 +122,14 @@ func mergeConfiguration(configurations dynamic.Configurations, defaultEntryPoint
 	}
 
 	if len(defaultTLSStoreProviders) > 1 {
-		log.WithoutContext().Errorf("Default TLS Stores defined multiple times in %v", defaultTLSOptionProviders)
+		log.Error().Msgf("Default TLS Stores defined multiple times in %v", defaultTLSOptionProviders)
 		delete(conf.TLS.Stores, tls.DefaultTLSStoreName)
 	}
 
 	if len(defaultTLSOptionProviders) == 0 {
 		conf.TLS.Options[tls.DefaultTLSConfigName] = tls.DefaultTLSOptions
 	} else if len(defaultTLSOptionProviders) > 1 {
-		log.WithoutContext().Errorf("Default TLS Options defined multiple times in %v", defaultTLSOptionProviders)
+		log.Error().Msgf("Default TLS Options defined multiple times in %v", defaultTLSOptionProviders)
 		// We do not set an empty tls.TLS{} as above so that we actually get a "cascading failure" later on,
 		// i.e. routers depending on this missing TLS option will fail to initialize as well.
 		delete(conf.TLS.Options, tls.DefaultTLSConfigName)
diff --git a/pkg/server/configurationwatcher.go b/pkg/server/configurationwatcher.go
index 4d6798694e..64446ef95e 100644
--- a/pkg/server/configurationwatcher.go
+++ b/pkg/server/configurationwatcher.go
@@ -5,9 +5,10 @@ import (
 	"encoding/json"
 	"reflect"
 
-	"github.com/sirupsen/logrus"
+	"github.com/rs/zerolog"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/provider"
 	"github.com/traefik/traefik/v2/pkg/safe"
 	"github.com/traefik/traefik/v2/pkg/tls"
@@ -68,14 +69,12 @@ func (c *ConfigurationWatcher) AddListener(listener func(dynamic.Configuration))
 }
 
 func (c *ConfigurationWatcher) startProviderAggregator() {
-	logger := log.WithoutContext()
-
-	logger.Infof("Starting provider aggregator %T", c.providerAggregator)
+	log.Info().Msgf("Starting provider aggregator %T", c.providerAggregator)
 
 	safe.Go(func() {
 		err := c.providerAggregator.Provide(c.allProvidersConfigs, c.routinesPool)
 		if err != nil {
-			logger.Errorf("Error starting provider aggregator %T: %s", c.providerAggregator, err)
+			log.Error().Err(err).Msgf("Error starting provider aggregator %T", c.providerAggregator)
 		}
 	})
 }
@@ -108,15 +107,15 @@ func (c *ConfigurationWatcher) receiveConfigurations(ctx context.Context) {
 					return
 				}
 
-				logger := log.WithoutContext().WithField(log.ProviderName, configMsg.ProviderName)
+				logger := log.Ctx(ctx).With().Str(logs.ProviderName, configMsg.ProviderName).Logger()
 
 				if configMsg.Configuration == nil {
-					logger.Debug("Skipping nil configuration.")
+					logger.Debug().Msg("Skipping nil configuration")
 					continue
 				}
 
 				if isEmptyConfiguration(configMsg.Configuration) {
-					logger.Debug("Skipping empty configuration.")
+					logger.Debug().Msg("Skipping empty configuration")
 					continue
 				}
 
@@ -124,7 +123,7 @@ func (c *ConfigurationWatcher) receiveConfigurations(ctx context.Context) {
 
 				if reflect.DeepEqual(newConfigurations[configMsg.ProviderName], configMsg.Configuration) {
 					// no change, do nothing
-					logger.Debug("Skipping unchanged configuration.")
+					logger.Debug().Msg("Skipping unchanged configuration")
 					continue
 				}
 
@@ -177,8 +176,8 @@ func (c *ConfigurationWatcher) applyConfigurations(ctx context.Context) {
 	}
 }
 
-func logConfiguration(logger log.Logger, configMsg dynamic.Message) {
-	if log.GetLevel() != logrus.DebugLevel {
+func logConfiguration(logger zerolog.Logger, configMsg dynamic.Message) {
+	if logger.GetLevel() > zerolog.DebugLevel {
 		return
 	}
 
@@ -212,10 +211,10 @@ func logConfiguration(logger log.Logger, configMsg dynamic.Message) {
 
 	jsonConf, err := json.Marshal(copyConf)
 	if err != nil {
-		logger.Errorf("Could not marshal dynamic configuration: %v", err)
-		logger.Debugf("Configuration received: [struct] %#v", copyConf)
+		logger.Error().Err(err).Msg("Could not marshal dynamic configuration")
+		logger.Debug().Msgf("Configuration received: [struct] %#v", copyConf)
 	} else {
-		logger.Debugf("Configuration received: %s", string(jsonConf))
+		logger.Debug().RawJSON("config", jsonConf).Msg("Configuration received")
 	}
 }
 
diff --git a/pkg/server/cookie/cookie.go b/pkg/server/cookie/cookie.go
index 718e0ce157..44f6b59f75 100644
--- a/pkg/server/cookie/cookie.go
+++ b/pkg/server/cookie/cookie.go
@@ -5,7 +5,7 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 )
 
 const cookieNameLength = 6
@@ -27,7 +27,7 @@ func GenerateName(backendName string) string {
 	_, err := hash.Write(data)
 	if err != nil {
 		// Impossible case
-		log.WithoutContext().Errorf("Fail to create cookie name: %v", err)
+		log.Error().Err(err).Msg("Fail to create cookie name")
 	}
 
 	return fmt.Sprintf("_%x", hash.Sum(nil))[:cookieNameLength]
diff --git a/pkg/server/middleware/chainbuilder.go b/pkg/server/middleware/chainbuilder.go
index c5c8336060..57d8385c5a 100644
--- a/pkg/server/middleware/chainbuilder.go
+++ b/pkg/server/middleware/chainbuilder.go
@@ -4,7 +4,7 @@ import (
 	"context"
 
 	"github.com/containous/alice"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/metrics"
 	"github.com/traefik/traefik/v2/pkg/middlewares/accesslog"
 	"github.com/traefik/traefik/v2/pkg/middlewares/capture"
@@ -56,7 +56,7 @@ func (c *ChainBuilder) Build(ctx context.Context, entryPointName string) alice.C
 func (c *ChainBuilder) Close() {
 	if c.accessLoggerMiddleware != nil {
 		if err := c.accessLoggerMiddleware.Close(); err != nil {
-			log.WithoutContext().Errorf("Could not close the access log file: %s", err)
+			log.Error().Err(err).Msg("Could not close the access log file")
 		}
 	}
 
diff --git a/pkg/server/provider/provider.go b/pkg/server/provider/provider.go
index be46c1201c..b3ef5092f4 100644
--- a/pkg/server/provider/provider.go
+++ b/pkg/server/provider/provider.go
@@ -4,7 +4,7 @@ import (
 	"context"
 	"strings"
 
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 )
 
 type contextKey int
@@ -17,7 +17,7 @@ const (
 func AddInContext(ctx context.Context, elementName string) context.Context {
 	parts := strings.Split(elementName, "@")
 	if len(parts) == 1 {
-		log.FromContext(ctx).Debugf("Could not find a provider for %s.", elementName)
+		log.Ctx(ctx).Debug().Msgf("Could not find a provider for %s", elementName)
 		return ctx
 	}
 
diff --git a/pkg/server/router/router.go b/pkg/server/router/router.go
index e4acdef94c..e3b34d7e55 100644
--- a/pkg/server/router/router.go
+++ b/pkg/server/router/router.go
@@ -6,8 +6,9 @@ import (
 	"net/http"
 
 	"github.com/containous/alice"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/runtime"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/metrics"
 	"github.com/traefik/traefik/v2/pkg/middlewares/accesslog"
 	metricsMiddle "github.com/traefik/traefik/v2/pkg/middlewares/metrics"
@@ -63,19 +64,21 @@ func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string, t
 
 	for entryPointName, routers := range m.getHTTPRouters(rootCtx, entryPoints, tls) {
 		entryPointName := entryPointName
-		ctx := log.With(rootCtx, log.Str(log.EntryPointName, entryPointName))
+
+		logger := log.Ctx(rootCtx).With().Str(logs.EntryPointName, entryPointName).Logger()
+		ctx := logger.WithContext(rootCtx)
 
 		handler, err := m.buildEntryPointHandler(ctx, routers)
 		if err != nil {
-			log.FromContext(ctx).Error(err)
+			logger.Error().Err(err).Send()
 			continue
 		}
 
 		handlerWithAccessLog, err := alice.New(func(next http.Handler) (http.Handler, error) {
-			return accesslog.NewFieldHandler(next, log.EntryPointName, entryPointName, accesslog.AddOriginFields), nil
+			return accesslog.NewFieldHandler(next, logs.EntryPointName, entryPointName, accesslog.AddOriginFields), nil
 		}).Then(handler)
 		if err != nil {
-			log.FromContext(ctx).Error(err)
+			logger.Error().Err(err).Send()
 			entryPointHandlers[entryPointName] = handler
 		} else {
 			entryPointHandlers[entryPointName] = handlerWithAccessLog
@@ -83,7 +86,8 @@ func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string, t
 	}
 
 	for _, entryPointName := range entryPoints {
-		ctx := log.With(rootCtx, log.Str(log.EntryPointName, entryPointName))
+		logger := log.Ctx(rootCtx).With().Str(logs.EntryPointName, entryPointName).Logger()
+		ctx := logger.WithContext(rootCtx)
 
 		handler, ok := entryPointHandlers[entryPointName]
 		if !ok || handler == nil {
@@ -92,7 +96,7 @@ func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string, t
 
 		handlerWithMiddlewares, err := m.chainBuilder.Build(ctx, entryPointName).Then(handler)
 		if err != nil {
-			log.FromContext(ctx).Error(err)
+			logger.Error().Err(err).Send()
 			continue
 		}
 		entryPointHandlers[entryPointName] = handlerWithMiddlewares
@@ -108,20 +112,20 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
 	}
 
 	for routerName, routerConfig := range configs {
-		ctxRouter := log.With(provider.AddInContext(ctx, routerName), log.Str(log.RouterName, routerName))
-		logger := log.FromContext(ctxRouter)
+		logger := log.Ctx(ctx).With().Str(logs.RouterName, routerName).Logger()
+		ctxRouter := logger.WithContext(provider.AddInContext(ctx, routerName))
 
 		handler, err := m.buildRouterHandler(ctxRouter, routerName, routerConfig)
 		if err != nil {
 			routerConfig.AddError(err, true)
-			logger.Error(err)
+			logger.Error().Err(err).Send()
 			continue
 		}
 
 		err = muxer.AddRoute(routerConfig.Rule, routerConfig.Priority, handler)
 		if err != nil {
 			routerConfig.AddError(err, true)
-			logger.Error(err)
+			logger.Error().Err(err).Send()
 			continue
 		}
 	}
@@ -150,7 +154,7 @@ func (m *Manager) buildRouterHandler(ctx context.Context, routerName string, rou
 		return accesslog.NewFieldHandler(next, accesslog.RouterName, routerName, nil), nil
 	}).Then(handler)
 	if err != nil {
-		log.FromContext(ctx).Error(err)
+		log.Ctx(ctx).Error().Err(err).Send()
 		m.routerHandlers[routerName] = handler
 	} else {
 		m.routerHandlers[routerName] = handlerWithAccessLog
diff --git a/pkg/server/router/tcp/manager.go b/pkg/server/router/tcp/manager.go
index ff92f31121..2b4f1d504e 100644
--- a/pkg/server/router/tcp/manager.go
+++ b/pkg/server/router/tcp/manager.go
@@ -7,8 +7,9 @@ import (
 	"fmt"
 	"net/http"
 
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/runtime"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/middlewares/snicheck"
 	httpmuxer "github.com/traefik/traefik/v2/pkg/muxer/http"
 	tcpmuxer "github.com/traefik/traefik/v2/pkg/muxer/tcp"
@@ -77,11 +78,12 @@ func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string) m
 
 		routers := entryPointsRouters[entryPointName]
 
-		ctx := log.With(rootCtx, log.Str(log.EntryPointName, entryPointName))
+		logger := log.Ctx(rootCtx).With().Str(logs.EntryPointName, entryPointName).Logger()
+		ctx := logger.WithContext(rootCtx)
 
 		handler, err := m.buildEntryPointHandler(ctx, routers, entryPointsRoutersHTTP[entryPointName], m.httpHandlers[entryPointName], m.httpsHandlers[entryPointName])
 		if err != nil {
-			log.FromContext(ctx).Error(err)
+			logger.Error().Err(err).Send()
 			continue
 		}
 		entryPointHandlers[entryPointName] = handler
@@ -105,7 +107,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
 
 	defaultTLSConf, err := m.tlsManager.Get(traefiktls.DefaultTLSStoreName, traefiktls.DefaultTLSConfigName)
 	if err != nil {
-		log.FromContext(ctx).Errorf("Error during the build of the default TLS configuration: %v", err)
+		log.Ctx(ctx).Error().Err(err).Msg("Error during the build of the default TLS configuration")
 	}
 
 	// Keyed by domain. The source of truth for doing SNI checking, and for what TLS
@@ -125,8 +127,8 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
 			continue
 		}
 
-		ctxRouter := log.With(provider.AddInContext(ctx, routerHTTPName), log.Str(log.RouterName, routerHTTPName))
-		logger := log.FromContext(ctxRouter)
+		logger := log.Ctx(ctx).With().Str(logs.RouterName, routerHTTPName).Logger()
+		ctxRouter := logger.WithContext(provider.AddInContext(ctx, routerHTTPName))
 
 		tlsOptionsName := traefiktls.DefaultTLSConfigName
 		if len(routerHTTPConfig.TLS.Options) > 0 && routerHTTPConfig.TLS.Options != traefiktls.DefaultTLSConfigName {
@@ -137,7 +139,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
 		if err != nil {
 			routerErr := fmt.Errorf("invalid rule %s, error: %w", routerHTTPConfig.Rule, err)
 			routerHTTPConfig.AddError(routerErr, true)
-			logger.Error(routerErr)
+			logger.Error().Err(routerErr).Send()
 			continue
 		}
 
@@ -173,13 +175,13 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
 			//	# When a request for "/foo" comes, even though it won't be routed by
 			//	httpRouter2, if its SNI is set to foo.com, myTLSOptions will be used for the TLS
 			//	connection. Otherwise, it will fallback to the default TLS config.
-			logger.Warnf("No domain found in rule %v, the TLS options applied for this router will depend on the SNI of each request", routerHTTPConfig.Rule)
+			logger.Warn().Msgf("No domain found in rule %v, the TLS options applied for this router will depend on the SNI of each request", routerHTTPConfig.Rule)
 		}
 
 		tlsConf, err := m.tlsManager.Get(traefiktls.DefaultTLSStoreName, tlsOptionsName)
 		if err != nil {
 			routerHTTPConfig.AddError(err, true)
-			logger.Error(err)
+			logger.Error().Err(err).Send()
 			continue
 		}
 
@@ -206,7 +208,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
 
 	router.SetHTTPSHandler(sniCheck, defaultTLSConf)
 
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 	for hostSNI, tlsConfigs := range tlsOptionsForHostSNI {
 		if len(tlsConfigs) == 1 {
 			var optionsName string
@@ -217,7 +219,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
 				break
 			}
 
-			logger.Debugf("Adding route for %s with TLS options %s", hostSNI, optionsName)
+			logger.Debug().Msgf("Adding route for %s with TLS options %s", hostSNI, optionsName)
 
 			router.AddHTTPTLSConfig(hostSNI, config)
 		} else {
@@ -227,34 +229,34 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
 				routers = append(routers, v.routerName)
 			}
 
-			logger.Warnf("Found different TLS options for routers on the same host %v, so using the default TLS options instead for these routers: %#v", hostSNI, routers)
+			logger.Warn().Msgf("Found different TLS options for routers on the same host %v, so using the default TLS options instead for these routers: %#v", hostSNI, routers)
 
 			router.AddHTTPTLSConfig(hostSNI, defaultTLSConf)
 		}
 	}
 
 	for routerName, routerConfig := range configs {
-		ctxRouter := log.With(provider.AddInContext(ctx, routerName), log.Str(log.RouterName, routerName))
-		logger := log.FromContext(ctxRouter)
+		logger := log.Ctx(ctx).With().Str(logs.RouterName, routerName).Logger()
+		ctxRouter := logger.WithContext(provider.AddInContext(ctx, routerName))
 
 		if routerConfig.Service == "" {
 			err := errors.New("the service is missing on the router")
 			routerConfig.AddError(err, true)
-			logger.Error(err)
+			logger.Error().Err(err).Send()
 			continue
 		}
 
 		if routerConfig.Rule == "" {
 			err := errors.New("router has no rule")
 			routerConfig.AddError(err, true)
-			logger.Error(err)
+			logger.Error().Err(err).Send()
 			continue
 		}
 
 		handler, err := m.buildTCPHandler(ctxRouter, routerConfig)
 		if err != nil {
 			routerConfig.AddError(err, true)
-			logger.Error(err)
+			logger.Error().Err(err).Send()
 			continue
 		}
 
@@ -262,7 +264,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
 		if err != nil {
 			routerErr := fmt.Errorf("invalid rule: %q , %w", routerConfig.Rule, err)
 			routerConfig.AddError(routerErr, true)
-			logger.Error(routerErr)
+			logger.Error().Err(routerErr).Send()
 			continue
 		}
 
@@ -271,23 +273,23 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
 		if len(domains) > 0 && routerConfig.TLS == nil && domains[0] != "*" {
 			routerErr := fmt.Errorf("invalid rule: %q , has HostSNI matcher, but no TLS on router", routerConfig.Rule)
 			routerConfig.AddError(routerErr, true)
-			logger.Error(routerErr)
+			logger.Error().Err(routerErr).Send()
 		}
 
 		if routerConfig.TLS == nil {
-			logger.Debugf("Adding route for %q", routerConfig.Rule)
+			logger.Debug().Msgf("Adding route for %q", routerConfig.Rule)
 			if err := router.AddRoute(routerConfig.Rule, routerConfig.Priority, handler); err != nil {
 				routerConfig.AddError(err, true)
-				logger.Error(err)
+				logger.Error().Err(err).Send()
 			}
 			continue
 		}
 
 		if routerConfig.TLS.Passthrough {
-			logger.Debugf("Adding Passthrough route for %q", routerConfig.Rule)
+			logger.Debug().Msgf("Adding Passthrough route for %q", routerConfig.Rule)
 			if err := router.AddRouteTLS(routerConfig.Rule, routerConfig.Priority, handler, nil); err != nil {
 				routerConfig.AddError(err, true)
-				logger.Error(err)
+				logger.Error().Err(err).Send()
 			}
 			continue
 		}
@@ -299,7 +301,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
 
 			asciiError := fmt.Errorf("invalid domain name value %q, non-ASCII characters are not allowed", domain)
 			routerConfig.AddError(asciiError, true)
-			logger.Error(asciiError)
+			logger.Error().Err(asciiError).Send()
 		}
 
 		tlsOptionsName := routerConfig.TLS.Options
@@ -315,7 +317,7 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
 		tlsConf, err := m.tlsManager.Get(traefiktls.DefaultTLSStoreName, tlsOptionsName)
 		if err != nil {
 			routerConfig.AddError(err, true)
-			logger.Error(err)
+			logger.Error().Err(err).Send()
 			continue
 		}
 
@@ -333,10 +335,10 @@ func (m *Manager) buildEntryPointHandler(ctx context.Context, configs map[string
 		// it's all good. Otherwise, we would have to do as for HTTPS, i.e. disallow
 		// different TLS configs for the same HostSNIs.
 
-		logger.Debugf("Adding TLS route for %q", routerConfig.Rule)
+		logger.Debug().Msgf("Adding TLS route for %q", routerConfig.Rule)
 		if err := router.AddRouteTLS(routerConfig.Rule, routerConfig.Priority, handler, tlsConf); err != nil {
 			routerConfig.AddError(err, true)
-			logger.Error(err)
+			logger.Error().Err(err).Send()
 		}
 	}
 
diff --git a/pkg/server/router/tcp/postgres.go b/pkg/server/router/tcp/postgres.go
index 857efb3ab5..b79c3762f1 100644
--- a/pkg/server/router/tcp/postgres.go
+++ b/pkg/server/router/tcp/postgres.go
@@ -6,7 +6,7 @@ import (
 	"errors"
 	"sync"
 
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 	tcpmuxer "github.com/traefik/traefik/v2/pkg/muxer/tcp"
 	"github.com/traefik/traefik/v2/pkg/tcp"
 )
@@ -25,7 +25,7 @@ func isPostgres(br *bufio.Reader) (bool, error) {
 	for i := 1; i < len(PostgresStartTLSMsg)+1; i++ {
 		peeked, err := br.Peek(i)
 		if err != nil {
-			log.WithoutContext().Errorf("Error while Peeking first bytes: %s", err)
+			log.Error().Err(err).Msg("Error while Peeking first bytes")
 			return false, err
 		}
 
@@ -67,7 +67,7 @@ func (r *Router) servePostgres(conn tcp.WriteCloser) {
 
 	connData, err := tcpmuxer.NewConnData(hello.serverName, conn, hello.protos)
 	if err != nil {
-		log.WithoutContext().Errorf("Error while reading TCP connection data: %v", err)
+		log.Error().Err(err).Msg("Error while reading TCP connection data")
 		conn.Close()
 		return
 	}
diff --git a/pkg/server/router/tcp/router.go b/pkg/server/router/tcp/router.go
index 841d52ae82..8951245231 100644
--- a/pkg/server/router/tcp/router.go
+++ b/pkg/server/router/tcp/router.go
@@ -10,7 +10,7 @@ import (
 	"net/http"
 	"time"
 
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 	tcpmuxer "github.com/traefik/traefik/v2/pkg/muxer/tcp"
 	"github.com/traefik/traefik/v2/pkg/tcp"
 )
@@ -88,7 +88,7 @@ func (r *Router) ServeTCP(conn tcp.WriteCloser) {
 	if r.muxerTCP.HasRoutes() && !r.muxerTCPTLS.HasRoutes() && !r.muxerHTTPS.HasRoutes() {
 		connData, err := tcpmuxer.NewConnData("", conn, nil)
 		if err != nil {
-			log.WithoutContext().Errorf("Error while reading TCP connection data: %v", err)
+			log.Error().Err(err).Msg("Error while reading TCP connection data")
 			conn.Close()
 			return
 		}
@@ -128,17 +128,17 @@ func (r *Router) ServeTCP(conn tcp.WriteCloser) {
 	// Remove read/write deadline and delegate this to underlying tcp server (for now only handled by HTTP Server)
 	err = conn.SetReadDeadline(time.Time{})
 	if err != nil {
-		log.WithoutContext().Errorf("Error while setting read deadline: %v", err)
+		log.Error().Err(err).Msg("Error while setting read deadline")
 	}
 
 	err = conn.SetWriteDeadline(time.Time{})
 	if err != nil {
-		log.WithoutContext().Errorf("Error while setting write deadline: %v", err)
+		log.Error().Err(err).Msg("Error while setting write deadline")
 	}
 
 	connData, err := tcpmuxer.NewConnData(hello.serverName, conn, hello.protos)
 	if err != nil {
-		log.WithoutContext().Errorf("Error while reading TCP connection data: %v", err)
+		log.Error().Err(err).Msg("Error while reading TCP connection data")
 		conn.Close()
 		return
 	}
@@ -263,7 +263,7 @@ func (r *Router) SetHTTPSForwarder(handler tcp.Handler) {
 			Config: tlsConf,
 		})
 		if err != nil {
-			log.WithoutContext().Errorf("Error while adding route for host: %v", err)
+			log.Error().Err(err).Msg("Error while adding route for host")
 		}
 	}
 
@@ -326,7 +326,7 @@ func clientHelloInfo(br *bufio.Reader) (*clientHello, error) {
 	if err != nil {
 		var opErr *net.OpError
 		if !errors.Is(err, io.EOF) && (!errors.As(err, &opErr) || opErr.Timeout()) {
-			log.WithoutContext().Errorf("Error while Peeking first byte: %s", err)
+			log.Error().Err(err).Msg("Error while Peeking first byte")
 		}
 		return nil, err
 	}
@@ -353,7 +353,7 @@ func clientHelloInfo(br *bufio.Reader) (*clientHello, error) {
 	const recordHeaderLen = 5
 	hdr, err = br.Peek(recordHeaderLen)
 	if err != nil {
-		log.Errorf("Error while Peeking hello: %s", err)
+		log.Error().Err(err).Msg("Error while Peeking hello")
 		return &clientHello{
 			peeked: getPeeked(br),
 		}, nil
@@ -367,7 +367,7 @@ func clientHelloInfo(br *bufio.Reader) (*clientHello, error) {
 
 	helloBytes, err := br.Peek(recordHeaderLen + recLen)
 	if err != nil {
-		log.Errorf("Error while Hello: %s", err)
+		log.Error().Err(err).Msg("Error while Hello")
 		return &clientHello{
 			isTLS:  true,
 			peeked: getPeeked(br),
@@ -396,7 +396,7 @@ func clientHelloInfo(br *bufio.Reader) (*clientHello, error) {
 func getPeeked(br *bufio.Reader) string {
 	peeked, err := br.Peek(br.Buffered())
 	if err != nil {
-		log.Errorf("Could not get anything: %s", err)
+		log.Error().Err(err).Msg("Could not get anything")
 		return ""
 	}
 	return string(peeked)
diff --git a/pkg/server/router/udp/router.go b/pkg/server/router/udp/router.go
index 75bea883a9..0a5303effe 100644
--- a/pkg/server/router/udp/router.go
+++ b/pkg/server/router/udp/router.go
@@ -5,8 +5,9 @@ import (
 	"errors"
 	"sort"
 
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/runtime"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/server/provider"
 	udpservice "github.com/traefik/traefik/v2/pkg/server/service/udp"
 	"github.com/traefik/traefik/v2/pkg/udp"
@@ -46,10 +47,11 @@ func (m *Manager) BuildHandlers(rootCtx context.Context, entryPoints []string) m
 
 		routers := entryPointsRouters[entryPointName]
 
-		ctx := log.With(rootCtx, log.Str(log.EntryPointName, entryPointName))
+		logger := log.Ctx(rootCtx).With().Str(logs.EntryPointName, entryPointName).Logger()
+		ctx := logger.WithContext(rootCtx)
 
 		if len(routers) > 1 {
-			log.FromContext(ctx).Warn("Config has more than one udp router for a given entrypoint.")
+			logger.Warn().Msg("Config has more than one udp router for a given entrypoint.")
 		}
 
 		handlers := m.buildEntryPointHandlers(ctx, routers)
@@ -76,21 +78,20 @@ func (m *Manager) buildEntryPointHandlers(ctx context.Context, configs map[strin
 
 	for _, routerName := range rtNames {
 		routerConfig := configs[routerName]
-
-		ctxRouter := log.With(provider.AddInContext(ctx, routerName), log.Str(log.RouterName, routerName))
-		logger := log.FromContext(ctxRouter)
+		logger := log.Ctx(ctx).With().Str(logs.RouterName, routerName).Logger()
+		ctxRouter := logger.WithContext(provider.AddInContext(ctx, routerName))
 
 		if routerConfig.Service == "" {
 			err := errors.New("the service is missing on the udp router")
 			routerConfig.AddError(err, true)
-			logger.Error(err)
+			logger.Error().Err(err).Send()
 			continue
 		}
 
 		handler, err := m.serviceManager.BuildUDP(ctxRouter, routerConfig.Service)
 		if err != nil {
 			routerConfig.AddError(err, true)
-			logger.Error(err)
+			logger.Error().Err(err).Send()
 			continue
 		}
 
diff --git a/pkg/server/routerfactory.go b/pkg/server/routerfactory.go
index d2f0420f41..d3e0f877b0 100644
--- a/pkg/server/routerfactory.go
+++ b/pkg/server/routerfactory.go
@@ -3,9 +3,9 @@ package server
 import (
 	"context"
 
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/runtime"
 	"github.com/traefik/traefik/v2/pkg/config/static"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/metrics"
 	"github.com/traefik/traefik/v2/pkg/server/middleware"
 	tcpmiddleware "github.com/traefik/traefik/v2/pkg/server/middleware/tcp"
@@ -44,7 +44,7 @@ func NewRouterFactory(staticConfiguration static.Configuration, managerFactory *
 		protocol, err := cfg.GetProtocol()
 		if err != nil {
 			// Should never happen because Traefik should not start if protocol is invalid.
-			log.WithoutContext().Errorf("Invalid protocol: %v", err)
+			log.Error().Err(err).Msg("Invalid protocol")
 		}
 
 		if protocol == "udp" {
diff --git a/pkg/server/server.go b/pkg/server/server.go
index 722258db6f..edd0cdb027 100644
--- a/pkg/server/server.go
+++ b/pkg/server/server.go
@@ -7,7 +7,7 @@ import (
 	"os/signal"
 	"time"
 
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/metrics"
 	"github.com/traefik/traefik/v2/pkg/middlewares/accesslog"
 	"github.com/traefik/traefik/v2/pkg/safe"
@@ -53,9 +53,9 @@ func NewServer(routinesPool *safe.Pool, entryPoints TCPEntryPoints, entryPointsU
 func (s *Server) Start(ctx context.Context) {
 	go func() {
 		<-ctx.Done()
-		logger := log.FromContext(ctx)
-		logger.Info("I have to go...")
-		logger.Info("Stopping server gracefully")
+		logger := log.Ctx(ctx)
+		logger.Info().Msg("I have to go...")
+		logger.Info().Msg("Stopping server gracefully")
 		s.Stop()
 	}()
 
@@ -73,7 +73,7 @@ func (s *Server) Wait() {
 
 // Stop stops the server.
 func (s *Server) Stop() {
-	defer log.WithoutContext().Info("Server stopped")
+	defer log.Info().Msg("Server stopped")
 
 	s.tcpEntryPoints.Stop()
 	s.udpEntryPoints.Stop()
diff --git a/pkg/server/server_entrypoint_tcp.go b/pkg/server/server_entrypoint_tcp.go
index 5d70957683..2fd4380f89 100644
--- a/pkg/server/server_entrypoint_tcp.go
+++ b/pkg/server/server_entrypoint_tcp.go
@@ -16,10 +16,11 @@ import (
 
 	"github.com/containous/alice"
 	"github.com/pires/go-proxyproto"
-	"github.com/sirupsen/logrus"
+	"github.com/rs/zerolog"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/static"
 	"github.com/traefik/traefik/v2/pkg/ip"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/middlewares"
 	"github.com/traefik/traefik/v2/pkg/middlewares/forwardedheaders"
 	"github.com/traefik/traefik/v2/pkg/middlewares/requestdecorator"
@@ -32,8 +33,6 @@ import (
 	"golang.org/x/net/http2/h2c"
 )
 
-var httpServerLogger = stdlog.New(log.WithoutContext().WriterLevel(logrus.DebugLevel), "", 0)
-
 type httpForwarder struct {
 	net.Listener
 	connChan chan net.Conn
@@ -79,7 +78,7 @@ func NewTCPEntryPoints(entryPointsConfig static.EntryPoints, hostResolverConfig
 			continue
 		}
 
-		ctx := log.With(context.Background(), log.Str(log.EntryPointName, entryPointName))
+		ctx := log.With().Str(logs.EntryPointName, entryPointName).Logger().WithContext(context.Background())
 
 		serverEntryPointsTCP[entryPointName], err = NewTCPEntryPoint(ctx, config, hostResolverConfig)
 		if err != nil {
@@ -92,7 +91,7 @@ func NewTCPEntryPoints(entryPointsConfig static.EntryPoints, hostResolverConfig
 // Start the server entry points.
 func (eps TCPEntryPoints) Start() {
 	for entryPointName, serverEntryPoint := range eps {
-		ctx := log.With(context.Background(), log.Str(log.EntryPointName, entryPointName))
+		ctx := log.With().Str(logs.EntryPointName, entryPointName).Logger().WithContext(context.Background())
 		go serverEntryPoint.Start(ctx)
 	}
 }
@@ -107,10 +106,10 @@ func (eps TCPEntryPoints) Stop() {
 		go func(entryPointName string, entryPoint *TCPEntryPoint) {
 			defer wg.Done()
 
-			ctx := log.With(context.Background(), log.Str(log.EntryPointName, entryPointName))
-			entryPoint.Shutdown(ctx)
+			logger := log.With().Str(logs.EntryPointName, entryPointName).Logger()
+			entryPoint.Shutdown(logger.WithContext(context.Background()))
 
-			log.FromContext(ctx).Debugf("Entry point %s closed", entryPointName)
+			logger.Debug().Msg("Entrypoint closed")
 		}(epn, ep)
 	}
 
@@ -184,8 +183,8 @@ func NewTCPEntryPoint(ctx context.Context, configuration *static.EntryPoint, hos
 
 // Start starts the TCP server.
 func (e *TCPEntryPoint) Start(ctx context.Context) {
-	logger := log.FromContext(ctx)
-	logger.Debug("Starting TCP Server")
+	logger := log.Ctx(ctx)
+	logger.Debug().Msg("Starting TCP Server")
 
 	if e.http3Server != nil {
 		go func() { _ = e.http3Server.Start() }()
@@ -194,7 +193,7 @@ func (e *TCPEntryPoint) Start(ctx context.Context) {
 	for {
 		conn, err := e.listener.Accept()
 		if err != nil {
-			logger.Error(err)
+			logger.Error().Err(err).Send()
 
 			var opErr *net.OpError
 			if errors.As(err, &opErr) && opErr.Temporary() {
@@ -224,14 +223,14 @@ func (e *TCPEntryPoint) Start(ctx context.Context) {
 			if e.transportConfiguration.RespondingTimeouts.ReadTimeout > 0 {
 				err := writeCloser.SetReadDeadline(time.Now().Add(time.Duration(e.transportConfiguration.RespondingTimeouts.ReadTimeout)))
 				if err != nil {
-					logger.Errorf("Error while setting read deadline: %v", err)
+					logger.Error().Err(err).Msg("Error while setting read deadline")
 				}
 			}
 
 			if e.transportConfiguration.RespondingTimeouts.WriteTimeout > 0 {
 				err = writeCloser.SetWriteDeadline(time.Now().Add(time.Duration(e.transportConfiguration.RespondingTimeouts.WriteTimeout)))
 				if err != nil {
-					logger.Errorf("Error while setting write deadline: %v", err)
+					logger.Error().Err(err).Msg("Error while setting write deadline")
 				}
 			}
 
@@ -242,17 +241,17 @@ func (e *TCPEntryPoint) Start(ctx context.Context) {
 
 // Shutdown stops the TCP connections.
 func (e *TCPEntryPoint) Shutdown(ctx context.Context) {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
 	reqAcceptGraceTimeOut := time.Duration(e.transportConfiguration.LifeCycle.RequestAcceptGraceTimeout)
 	if reqAcceptGraceTimeOut > 0 {
-		logger.Infof("Waiting %s for incoming requests to cease", reqAcceptGraceTimeOut)
+		logger.Info().Msgf("Waiting %s for incoming requests to cease", reqAcceptGraceTimeOut)
 		time.Sleep(reqAcceptGraceTimeOut)
 	}
 
 	graceTimeOut := time.Duration(e.transportConfiguration.LifeCycle.GraceTimeOut)
 	ctx, cancel := context.WithTimeout(ctx, graceTimeOut)
-	logger.Debugf("Waiting %s seconds before killing connections.", graceTimeOut)
+	logger.Debug().Msgf("Waiting %s seconds before killing connections", graceTimeOut)
 
 	var wg sync.WaitGroup
 
@@ -263,13 +262,15 @@ func (e *TCPEntryPoint) Shutdown(ctx context.Context) {
 			return
 		}
 		if errors.Is(ctx.Err(), context.DeadlineExceeded) {
-			logger.Debugf("Server failed to shutdown within deadline because: %s", err)
+			logger.Debug().Err(err).Msg("Server failed to shutdown within deadline")
 			if err = server.Close(); err != nil {
-				logger.Error(err)
+				logger.Error().Err(err).Send()
 			}
 			return
 		}
-		logger.Error(err)
+
+		logger.Error().Err(err).Send()
+
 		// We expect Close to fail again because Shutdown most likely failed when trying to close a listener.
 		// We still call it however, to make sure that all connections get closed as well.
 		server.Close()
@@ -299,7 +300,7 @@ func (e *TCPEntryPoint) Shutdown(ctx context.Context) {
 				return
 			}
 			if errors.Is(ctx.Err(), context.DeadlineExceeded) {
-				logger.Debugf("Server failed to shutdown before deadline because: %s", err)
+				logger.Debug().Err(err).Msg("Server failed to shutdown before deadline")
 			}
 			e.tracker.Close()
 		}()
@@ -381,8 +382,7 @@ func (ln tcpKeepAliveListener) Accept() (net.Conn, error) {
 	}
 
 	if err := tc.SetKeepAlivePeriod(3 * time.Minute); err != nil {
-		// Some systems, such as OpenBSD, have no user-settable per-socket TCP
-		// keepalive options.
+		// Some systems, such as OpenBSD, have no user-settable per-socket TCP keepalive options.
 		if !errors.Is(err, syscall.ENOPROTOOPT) {
 			return nil, err
 		}
@@ -395,7 +395,7 @@ func buildProxyProtocolListener(ctx context.Context, entryPoint *static.EntryPoi
 	proxyListener := &proxyproto.Listener{Listener: listener}
 
 	if entryPoint.ProxyProtocol.Insecure {
-		log.FromContext(ctx).Infof("Enabling ProxyProtocol without trusted IPs: Insecure")
+		log.Ctx(ctx).Info().Msg("Enabling ProxyProtocol without trusted IPs: Insecure")
 		return proxyListener, nil
 	}
 
@@ -411,13 +411,13 @@ func buildProxyProtocolListener(ctx context.Context, entryPoint *static.EntryPoi
 		}
 
 		if !checker.ContainsIP(ipAddr.IP) {
-			log.FromContext(ctx).Debugf("IP %s is not in trusted IPs list, ignoring ProxyProtocol Headers and bypass connection", ipAddr.IP)
+			log.Ctx(ctx).Debug().Msgf("IP %s is not in trusted IPs list, ignoring ProxyProtocol Headers and bypass connection", ipAddr.IP)
 			return proxyproto.IGNORE, nil
 		}
 		return proxyproto.USE, nil
 	}
 
-	log.FromContext(ctx).Infof("Enabling ProxyProtocol for trusted IPs %v", entryPoint.ProxyProtocol.TrustedIPs)
+	log.Ctx(ctx).Info().Msgf("Enabling ProxyProtocol for trusted IPs %v", entryPoint.ProxyProtocol.TrustedIPs)
 
 	return proxyListener, nil
 }
@@ -492,7 +492,7 @@ func (c *connectionTracker) Close() {
 	defer c.lock.Unlock()
 	for conn := range c.conns {
 		if err := conn.Close(); err != nil {
-			log.WithoutContext().Errorf("Error while closing connection: %v", err)
+			log.Error().Err(err).Msg("Error while closing connection")
 		}
 		delete(c.conns, conn)
 	}
@@ -545,7 +545,7 @@ func createHTTPServer(ctx context.Context, ln net.Listener, configuration *stati
 
 	serverHTTP := &http.Server{
 		Handler:      handler,
-		ErrorLog:     httpServerLogger,
+		ErrorLog:     stdlog.New(logs.NoLevel(log.Logger, zerolog.DebugLevel), "", 0),
 		ReadTimeout:  time.Duration(configuration.Transport.RespondingTimeouts.ReadTimeout),
 		WriteTimeout: time.Duration(configuration.Transport.RespondingTimeouts.WriteTimeout),
 		IdleTimeout:  time.Duration(configuration.Transport.RespondingTimeouts.IdleTimeout),
@@ -569,7 +569,7 @@ func createHTTPServer(ctx context.Context, ln net.Listener, configuration *stati
 	go func() {
 		err := serverHTTP.Serve(listener)
 		if err != nil && !errors.Is(err, http.ErrServerClosed) {
-			log.FromContext(ctx).Errorf("Error while starting server: %v", err)
+			log.Ctx(ctx).Error().Err(err).Msg("Error while starting server")
 		}
 	}()
 	return &httpServer{
diff --git a/pkg/server/server_entrypoint_tcp_http3.go b/pkg/server/server_entrypoint_tcp_http3.go
index 0f11b82b95..99755d6d77 100644
--- a/pkg/server/server_entrypoint_tcp_http3.go
+++ b/pkg/server/server_entrypoint_tcp_http3.go
@@ -10,8 +10,8 @@ import (
 	"sync"
 
 	"github.com/lucas-clemente/quic-go/http3"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/static"
-	"github.com/traefik/traefik/v2/pkg/log"
 	tcprouter "github.com/traefik/traefik/v2/pkg/server/router/tcp"
 )
 
@@ -56,7 +56,7 @@ func newHTTP3Server(ctx context.Context, configuration *static.EntryPoint, https
 
 	httpsServer.Server.(*http.Server).Handler = http.HandlerFunc(func(rw http.ResponseWriter, req *http.Request) {
 		if err := h3.Server.SetQuicHeaders(rw.Header()); err != nil {
-			log.FromContext(ctx).Errorf("Failed to set HTTP3 headers: %v", err)
+			log.Ctx(ctx).Error().Err(err).Msg("Failed to set HTTP3 headers")
 		}
 
 		previousHandler.ServeHTTP(rw, req)
diff --git a/pkg/server/server_entrypoint_udp.go b/pkg/server/server_entrypoint_udp.go
index 163a719afc..1aa4c38c31 100644
--- a/pkg/server/server_entrypoint_udp.go
+++ b/pkg/server/server_entrypoint_udp.go
@@ -7,8 +7,9 @@ import (
 	"sync"
 	"time"
 
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/static"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/udp"
 )
 
@@ -40,7 +41,7 @@ func NewUDPEntryPoints(cfg static.EntryPoints) (UDPEntryPoints, error) {
 // Start commences the listening for all the entry points.
 func (eps UDPEntryPoints) Start() {
 	for entryPointName, ep := range eps {
-		ctx := log.With(context.Background(), log.Str(log.EntryPointName, entryPointName))
+		ctx := log.With().Str(logs.EntryPointName, entryPointName).Logger().WithContext(context.Background())
 		go ep.Start(ctx)
 	}
 }
@@ -55,10 +56,10 @@ func (eps UDPEntryPoints) Stop() {
 		go func(entryPointName string, entryPoint *UDPEntryPoint) {
 			defer wg.Done()
 
-			ctx := log.With(context.Background(), log.Str(log.EntryPointName, entryPointName))
-			entryPoint.Shutdown(ctx)
+			logger := log.With().Str(logs.EntryPointName, entryPointName).Logger()
+			entryPoint.Shutdown(logger.WithContext(context.Background()))
 
-			log.FromContext(ctx).Debugf("Entry point %s closed", entryPointName)
+			logger.Debug().Msg("Entry point closed")
 		}(epn, ep)
 	}
 
@@ -72,7 +73,8 @@ func (eps UDPEntryPoints) Switch(handlers map[string]udp.Handler) {
 			ep.Switch(handler)
 			continue
 		}
-		log.WithoutContext().Errorf("EntryPoint %q does not exist", epName)
+
+		log.Error().Str(logs.EntryPointName, epName).Msg("EntryPoint does not exist")
 	}
 }
 
@@ -100,7 +102,7 @@ func NewUDPEntryPoint(cfg *static.EntryPoint) (*UDPEntryPoint, error) {
 
 // Start commences the listening for ep.
 func (ep *UDPEntryPoint) Start(ctx context.Context) {
-	log.FromContext(ctx).Debug("Start UDP Server")
+	log.Ctx(ctx).Debug().Msg("Start UDP Server")
 	for {
 		conn, err := ep.listener.Accept()
 		if err != nil {
@@ -116,17 +118,17 @@ func (ep *UDPEntryPoint) Start(ctx context.Context) {
 // releases associated resources, but only after it has waited for a graceTimeout,
 // if any was configured.
 func (ep *UDPEntryPoint) Shutdown(ctx context.Context) {
-	logger := log.FromContext(ctx)
+	logger := log.Ctx(ctx)
 
 	reqAcceptGraceTimeOut := time.Duration(ep.transportConfiguration.LifeCycle.RequestAcceptGraceTimeout)
 	if reqAcceptGraceTimeOut > 0 {
-		logger.Infof("Waiting %s for incoming requests to cease", reqAcceptGraceTimeOut)
+		logger.Info().Msgf("Waiting %s for incoming requests to cease", reqAcceptGraceTimeOut)
 		time.Sleep(reqAcceptGraceTimeOut)
 	}
 
 	graceTimeOut := time.Duration(ep.transportConfiguration.LifeCycle.GraceTimeOut)
 	if err := ep.listener.Shutdown(graceTimeOut); err != nil {
-		logger.Error(err)
+		logger.Error().Err(err).Send()
 	}
 }
 
diff --git a/pkg/server/server_signals.go b/pkg/server/server_signals.go
index d1ad8d51a0..0987abb301 100644
--- a/pkg/server/server_signals.go
+++ b/pkg/server/server_signals.go
@@ -8,7 +8,7 @@ import (
 	"os/signal"
 	"syscall"
 
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 )
 
 func (s *Server) configureSignals() {
@@ -22,17 +22,13 @@ func (s *Server) listenSignals(ctx context.Context) {
 			return
 		case sig := <-s.signals:
 			if sig == syscall.SIGUSR1 {
-				log.WithoutContext().Infof("Closing and re-opening log files for rotation: %+v", sig)
+				log.Info().Msgf("Closing and re-opening log files for rotation: %+v", sig)
 
 				if s.accessLoggerMiddleware != nil {
 					if err := s.accessLoggerMiddleware.Rotate(); err != nil {
-						log.WithoutContext().Errorf("Error rotating access log: %v", err)
+						log.Error().Err(err).Msg("Error rotating access log")
 					}
 				}
-
-				if err := log.RotateFile(); err != nil {
-					log.WithoutContext().Errorf("Error rotating traefik log: %v", err)
-				}
 			}
 		}
 	}
diff --git a/pkg/server/service/loadbalancer/failover/failover.go b/pkg/server/service/loadbalancer/failover/failover.go
index 06d9fecd78..972d386727 100644
--- a/pkg/server/service/loadbalancer/failover/failover.go
+++ b/pkg/server/service/loadbalancer/failover/failover.go
@@ -6,8 +6,8 @@ import (
 	"net/http"
 	"sync"
 
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
 )
 
 // Failover is an http.Handler that can forward requests to the fallback handler
@@ -90,11 +90,11 @@ func (f *Failover) SetHandlerStatus(ctx context.Context, up bool) {
 
 	if up == f.handlerStatus {
 		// We're still with the same status, no need to propagate.
-		log.FromContext(ctx).Debugf("Still %s, no need to propagate", status)
+		log.Ctx(ctx).Debug().Msgf("Still %s, no need to propagate", status)
 		return
 	}
 
-	log.FromContext(ctx).Debugf("Propagating new %s status", status)
+	log.Ctx(ctx).Debug().Msgf("Propagating new %s status", status)
 	f.handlerStatus = up
 
 	for _, fn := range f.updaters {
@@ -125,11 +125,11 @@ func (f *Failover) SetFallbackHandlerStatus(ctx context.Context, up bool) {
 
 	if up == f.fallbackStatus {
 		// We're still with the same status, no need to propagate.
-		log.FromContext(ctx).Debugf("Still %s, no need to propagate", status)
+		log.Ctx(ctx).Debug().Msgf("Still %s, no need to propagate", status)
 		return
 	}
 
-	log.FromContext(ctx).Debugf("Propagating new %s status", status)
+	log.Ctx(ctx).Debug().Msgf("Propagating new %s status", status)
 	f.fallbackStatus = up
 
 	for _, fn := range f.updaters {
diff --git a/pkg/server/service/loadbalancer/mirror/mirror.go b/pkg/server/service/loadbalancer/mirror/mirror.go
index f0d12a9525..cb77b72808 100644
--- a/pkg/server/service/loadbalancer/mirror/mirror.go
+++ b/pkg/server/service/loadbalancer/mirror/mirror.go
@@ -11,9 +11,9 @@ import (
 	"net/http"
 	"sync"
 
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
 	"github.com/traefik/traefik/v2/pkg/healthcheck"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/middlewares/accesslog"
 	"github.com/traefik/traefik/v2/pkg/safe"
 )
@@ -82,18 +82,18 @@ func (m *Mirroring) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 		return
 	}
 
-	logger := log.FromContext(req.Context())
+	logger := log.Ctx(req.Context())
 	rr, bytesRead, err := newReusableRequest(req, m.maxBodySize)
 	if err != nil && !errors.Is(err, errBodyTooLarge) {
-		http.Error(rw, http.StatusText(http.StatusInternalServerError)+
-			fmt.Sprintf("error creating reusable request: %v", err), http.StatusInternalServerError)
+		http.Error(rw, fmt.Sprintf("%s: creating reusable request: %v",
+			http.StatusText(http.StatusInternalServerError), err), http.StatusInternalServerError)
 		return
 	}
 
 	if errors.Is(err, errBodyTooLarge) {
 		req.Body = io.NopCloser(io.MultiReader(bytes.NewReader(bytesRead), req.Body))
 		m.handler.ServeHTTP(rw, req)
-		logger.Debug("no mirroring, request body larger than allowed size")
+		logger.Debug().Msg("No mirroring, request body larger than allowed size")
 		return
 	}
 
@@ -102,7 +102,7 @@ func (m *Mirroring) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
 	select {
 	case <-req.Context().Done():
 		// No mirroring if request has been canceled during main handler ServeHTTP
-		logger.Warn("no mirroring, request has been canceled during main handler ServeHTTP")
+		logger.Warn().Msg("No mirroring, request has been canceled during main handler ServeHTTP")
 		return
 	default:
 	}
@@ -179,7 +179,7 @@ func (b blackHoleResponseWriter) Write(data []byte) (int, error) {
 	return len(data), nil
 }
 
-func (b blackHoleResponseWriter) WriteHeader(statusCode int) {}
+func (b blackHoleResponseWriter) WriteHeader(_ int) {}
 
 type contextStopPropagation struct {
 	context.Context
diff --git a/pkg/server/service/loadbalancer/wrr/wrr.go b/pkg/server/service/loadbalancer/wrr/wrr.go
index 803764f49c..ed89dfb314 100644
--- a/pkg/server/service/loadbalancer/wrr/wrr.go
+++ b/pkg/server/service/loadbalancer/wrr/wrr.go
@@ -7,8 +7,8 @@ import (
 	"net/http"
 	"sync"
 
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
 )
 
 type namedHandler struct {
@@ -105,7 +105,9 @@ func (b *Balancer) SetStatus(ctx context.Context, childName string, up bool) {
 	if up {
 		status = "UP"
 	}
-	log.FromContext(ctx).Debugf("Setting status of %s to %v", childName, status)
+
+	log.Ctx(ctx).Debug().Msgf("Setting status of %s to %v", childName, status)
+
 	if up {
 		b.status[childName] = struct{}{}
 	} else {
@@ -121,12 +123,12 @@ func (b *Balancer) SetStatus(ctx context.Context, childName string, up bool) {
 	// No Status Change
 	if upBefore == upAfter {
 		// We're still with the same status, no need to propagate
-		log.FromContext(ctx).Debugf("Still %s, no need to propagate", status)
+		log.Ctx(ctx).Debug().Msgf("Still %s, no need to propagate", status)
 		return
 	}
 
 	// Status Change
-	log.FromContext(ctx).Debugf("Propagating new %s status", status)
+	log.Ctx(ctx).Debug().Msgf("Propagating new %s status", status)
 	for _, fn := range b.updaters {
 		fn(upAfter)
 	}
@@ -168,7 +170,7 @@ func (b *Balancer) nextServer() (*namedHandler, error) {
 		}
 	}
 
-	log.WithoutContext().Debugf("Service selected by WRR: %s", handler.name)
+	log.Debug().Msgf("Service selected by WRR: %s", handler.name)
 	return handler, nil
 }
 
@@ -177,7 +179,7 @@ func (b *Balancer) ServeHTTP(w http.ResponseWriter, req *http.Request) {
 		cookie, err := req.Cookie(b.stickyCookie.name)
 
 		if err != nil && !errors.Is(err, http.ErrNoCookie) {
-			log.WithoutContext().Warnf("Error while reading cookie: %v", err)
+			log.Warn().Err(err).Msg("Error while reading cookie")
 		}
 
 		if err == nil && cookie != nil {
diff --git a/pkg/server/service/proxy.go b/pkg/server/service/proxy.go
index 43a2c0f288..58f5e70813 100644
--- a/pkg/server/service/proxy.go
+++ b/pkg/server/service/proxy.go
@@ -11,7 +11,7 @@ import (
 	"strings"
 	"time"
 
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 	"golang.org/x/net/http/httpguts"
 )
 
@@ -115,12 +115,12 @@ func errorHandler(w http.ResponseWriter, req *http.Request, err error) {
 		}
 	}
 
-	logger := log.FromContext(req.Context())
-	logger.Debugf("'%d %s' caused by: %v", statusCode, statusText(statusCode), err)
+	logger := log.Ctx(req.Context())
+	logger.Debug().Err(err).Msgf("%d %s", statusCode, statusText(statusCode))
 
 	w.WriteHeader(statusCode)
 	if _, werr := w.Write([]byte(statusText(statusCode))); werr != nil {
-		logger.Debugf("Error while writing status code", werr)
+		logger.Debug().Err(werr).Msg("Error while writing status code")
 	}
 }
 
diff --git a/pkg/server/service/roundtripper.go b/pkg/server/service/roundtripper.go
index b155a5632f..a4c01ef060 100644
--- a/pkg/server/service/roundtripper.go
+++ b/pkg/server/service/roundtripper.go
@@ -11,12 +11,12 @@ import (
 	"sync"
 	"time"
 
+	"github.com/rs/zerolog/log"
 	"github.com/spiffe/go-spiffe/v2/bundle/x509bundle"
 	"github.com/spiffe/go-spiffe/v2/spiffeid"
 	"github.com/spiffe/go-spiffe/v2/spiffetls/tlsconfig"
 	"github.com/spiffe/go-spiffe/v2/svid/x509svid"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
 	traefiktls "github.com/traefik/traefik/v2/pkg/tls"
 	"golang.org/x/net/http2"
 )
@@ -74,7 +74,7 @@ func (r *RoundTripperManager) Update(newConfigs map[string]*dynamic.ServersTrans
 		var err error
 		r.roundTrippers[configName], err = r.createRoundTripper(newConfig)
 		if err != nil {
-			log.WithoutContext().Errorf("Could not configure HTTP Transport %s, fallback on default transport: %v", configName, err)
+			log.Error().Err(err).Msgf("Could not configure HTTP Transport %s, fallback on default transport", configName)
 			r.roundTrippers[configName] = http.DefaultTransport
 		}
 	}
@@ -87,7 +87,7 @@ func (r *RoundTripperManager) Update(newConfigs map[string]*dynamic.ServersTrans
 		var err error
 		r.roundTrippers[newConfigName], err = r.createRoundTripper(newConfig)
 		if err != nil {
-			log.WithoutContext().Errorf("Could not configure HTTP Transport %s, fallback on default transport: %v", newConfigName, err)
+			log.Error().Err(err).Msgf("Could not configure HTTP Transport %s, fallback on default transport", newConfigName)
 			r.roundTrippers[newConfigName] = http.DefaultTransport
 		}
 	}
@@ -95,7 +95,7 @@ func (r *RoundTripperManager) Update(newConfigs map[string]*dynamic.ServersTrans
 	r.configs = newConfigs
 }
 
-// Get get a roundtripper by name.
+// Get gets a roundtripper by name.
 func (r *RoundTripperManager) Get(name string) (http.RoundTripper, error) {
 	if len(name) == 0 {
 		name = "default@internal"
@@ -195,7 +195,7 @@ func createRootCACertPool(rootCAs []traefiktls.FileOrContent) *x509.CertPool {
 	for _, cert := range rootCAs {
 		certContent, err := cert.Read()
 		if err != nil {
-			log.WithoutContext().Error("Error while read RootCAs", err)
+			log.Error().Err(err).Msg("Error while read RootCAs")
 			continue
 		}
 		roots.AppendCertsFromPEM(certContent)
diff --git a/pkg/server/service/service.go b/pkg/server/service/service.go
index d872a3607d..d4295b9c6a 100644
--- a/pkg/server/service/service.go
+++ b/pkg/server/service/service.go
@@ -13,10 +13,11 @@ import (
 	"strings"
 	"time"
 
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
 	"github.com/traefik/traefik/v2/pkg/config/runtime"
 	"github.com/traefik/traefik/v2/pkg/healthcheck"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/metrics"
 	"github.com/traefik/traefik/v2/pkg/middlewares/accesslog"
 	metricsMiddle "github.com/traefik/traefik/v2/pkg/middlewares/metrics"
@@ -64,10 +65,10 @@ func NewManager(configs map[string]*runtime.ServiceInfo, metricsRegistry metrics
 
 // BuildHTTP Creates a http.Handler for a service configuration.
 func (m *Manager) BuildHTTP(rootCtx context.Context, serviceName string) (http.Handler, error) {
-	ctx := log.With(rootCtx, log.Str(log.ServiceName, serviceName))
+	serviceName = provider.GetQualifiedName(rootCtx, serviceName)
 
-	serviceName = provider.GetQualifiedName(ctx, serviceName)
-	ctx = provider.AddInContext(ctx, serviceName)
+	ctx := log.Ctx(rootCtx).With().Str(logs.ServiceName, serviceName).Logger().
+		WithContext(provider.AddInContext(rootCtx, serviceName))
 
 	handler, ok := m.services[serviceName]
 	if ok {
@@ -241,7 +242,8 @@ func (m *Manager) getWRRServiceHandler(ctx context.Context, serviceName string,
 			return nil, fmt.Errorf("cannot register %v as updater for %v: %w", childName, serviceName, err)
 		}
 
-		log.FromContext(ctx).Debugf("Child service %v will update parent %v on status change", childName, serviceName)
+		log.Ctx(ctx).Debug().Str("parent", serviceName).Str("child", childName).
+			Msg("Child service will update parent on status change")
 	}
 
 	return balancer, nil
@@ -250,8 +252,8 @@ func (m *Manager) getWRRServiceHandler(ctx context.Context, serviceName string,
 func (m *Manager) getLoadBalancerServiceHandler(ctx context.Context, serviceName string, info *runtime.ServiceInfo) (http.Handler, error) {
 	service := info.LoadBalancer
 
-	logger := log.FromContext(ctx)
-	logger.Debug("Creating load-balancer")
+	logger := log.Ctx(ctx)
+	logger.Debug().Msg("Creating load-balancer")
 
 	// TODO: should we keep this config value as Go is now handling stream response correctly?
 	flushInterval := dynamic.DefaultFlushInterval
@@ -292,7 +294,8 @@ func (m *Manager) getLoadBalancerServiceHandler(ctx context.Context, serviceName
 			return nil, fmt.Errorf("error parsing server URL %s: %w", server.URL, err)
 		}
 
-		logger.WithField(log.ServerName, proxyName).Debugf("Creating server %s", target)
+		logger.Debug().Str(logs.ServerName, proxyName).Stringer("target", target).
+			Msg("Creating server")
 
 		proxy := buildSingleHostProxy(target, passHostHeader, time.Duration(flushInterval), roundTripper, m.bufferPool)
 
@@ -330,8 +333,8 @@ func (m *Manager) getLoadBalancerServiceHandler(ctx context.Context, serviceName
 // LaunchHealthCheck launches the health checks.
 func (m *Manager) LaunchHealthCheck(ctx context.Context) {
 	for serviceName, hc := range m.healthCheckers {
-		ctx = log.With(ctx, log.Str(log.ServiceName, serviceName))
-		go hc.Launch(ctx)
+		logger := log.Ctx(ctx).With().Str(logs.ServiceName, serviceName).Logger()
+		go hc.Launch(logger.WithContext(ctx))
 	}
 }
 
diff --git a/pkg/server/service/tcp/service.go b/pkg/server/service/tcp/service.go
index e7dc4dbc68..d099af5010 100644
--- a/pkg/server/service/tcp/service.go
+++ b/pkg/server/service/tcp/service.go
@@ -8,8 +8,9 @@ import (
 	"net"
 	"time"
 
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/runtime"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/server/provider"
 	"github.com/traefik/traefik/v2/pkg/tcp"
 )
@@ -31,8 +32,9 @@ func NewManager(conf *runtime.Configuration) *Manager {
 // BuildTCP Creates a tcp.Handler for a service configuration.
 func (m *Manager) BuildTCP(rootCtx context.Context, serviceName string) (tcp.Handler, error) {
 	serviceQualifiedName := provider.GetQualifiedName(rootCtx, serviceName)
+
+	logger := log.Ctx(rootCtx).With().Str(logs.ServiceName, serviceQualifiedName).Logger()
 	ctx := provider.AddInContext(rootCtx, serviceQualifiedName)
-	ctx = log.With(ctx, log.Str(log.ServiceName, serviceName))
 
 	conf, ok := m.configs[serviceQualifiedName]
 	if !ok {
@@ -45,7 +47,6 @@ func (m *Manager) BuildTCP(rootCtx context.Context, serviceName string) (tcp.Han
 		return nil, err
 	}
 
-	logger := log.FromContext(ctx)
 	switch {
 	case conf.LoadBalancer != nil:
 		loadBalancer := tcp.NewWRRLoadBalancer()
@@ -56,34 +57,43 @@ func (m *Manager) BuildTCP(rootCtx context.Context, serviceName string) (tcp.Han
 		}
 		duration := time.Duration(*conf.LoadBalancer.TerminationDelay) * time.Millisecond
 
-		for name, server := range shuffle(conf.LoadBalancer.Servers, m.rand) {
+		for index, server := range shuffle(conf.LoadBalancer.Servers, m.rand) {
+			srvLogger := logger.With().
+				Int(logs.ServerIndex, index).
+				Str("serverAddress", server.Address).Logger()
+
 			if _, _, err := net.SplitHostPort(server.Address); err != nil {
-				logger.Errorf("In service %q: %v", serviceQualifiedName, err)
+				srvLogger.Error().Err(err).Msg("Failed to split host port")
 				continue
 			}
 
 			handler, err := tcp.NewProxy(server.Address, duration, conf.LoadBalancer.ProxyProtocol)
 			if err != nil {
-				logger.Errorf("In service %q server %q: %v", serviceQualifiedName, server.Address, err)
+				srvLogger.Error().Err(err).Msg("Failed to create server")
 				continue
 			}
 
 			loadBalancer.AddServer(handler)
-			logger.WithField(log.ServerName, name).Debugf("Creating TCP server %d at %s", name, server.Address)
+			logger.Debug().Msg("Creating TCP server")
 		}
+
 		return loadBalancer, nil
+
 	case conf.Weighted != nil:
 		loadBalancer := tcp.NewWRRLoadBalancer()
 
 		for _, service := range shuffle(conf.Weighted.Services, m.rand) {
-			handler, err := m.BuildTCP(rootCtx, service.Name)
+			handler, err := m.BuildTCP(ctx, service.Name)
 			if err != nil {
-				logger.Errorf("In service %q: %v", serviceQualifiedName, err)
+				logger.Error().Err(err).Msg("Failed to build TCP handler")
 				return nil, err
 			}
+
 			loadBalancer.AddWeightServer(handler, service.Weight)
 		}
+
 		return loadBalancer, nil
+
 	default:
 		err := fmt.Errorf("the service %q does not have any type defined", serviceQualifiedName)
 		conf.AddError(err, true)
diff --git a/pkg/server/service/udp/service.go b/pkg/server/service/udp/service.go
index f388a7824b..7d15c19ff6 100644
--- a/pkg/server/service/udp/service.go
+++ b/pkg/server/service/udp/service.go
@@ -8,8 +8,9 @@ import (
 	"net"
 	"time"
 
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/runtime"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/server/provider"
 	"github.com/traefik/traefik/v2/pkg/udp"
 )
@@ -31,12 +32,13 @@ func NewManager(conf *runtime.Configuration) *Manager {
 // BuildUDP creates the UDP handler for the given service name.
 func (m *Manager) BuildUDP(rootCtx context.Context, serviceName string) (udp.Handler, error) {
 	serviceQualifiedName := provider.GetQualifiedName(rootCtx, serviceName)
+
+	logger := log.Ctx(rootCtx).With().Str(logs.ServiceName, serviceQualifiedName).Logger()
 	ctx := provider.AddInContext(rootCtx, serviceQualifiedName)
-	ctx = log.With(ctx, log.Str(log.ServiceName, serviceName))
 
 	conf, ok := m.configs[serviceQualifiedName]
 	if !ok {
-		return nil, fmt.Errorf("the udp service %q does not exist", serviceQualifiedName)
+		return nil, fmt.Errorf("the UDP service %q does not exist", serviceQualifiedName)
 	}
 
 	if conf.LoadBalancer != nil && conf.Weighted != nil {
@@ -45,41 +47,49 @@ func (m *Manager) BuildUDP(rootCtx context.Context, serviceName string) (udp.Han
 		return nil, err
 	}
 
-	logger := log.FromContext(ctx)
 	switch {
 	case conf.LoadBalancer != nil:
 		loadBalancer := udp.NewWRRLoadBalancer()
 
-		for name, server := range shuffle(conf.LoadBalancer.Servers, m.rand) {
+		for index, server := range shuffle(conf.LoadBalancer.Servers, m.rand) {
+			srvLogger := logger.With().
+				Int(logs.ServerIndex, index).
+				Str("serverAddress", server.Address).Logger()
+
 			if _, _, err := net.SplitHostPort(server.Address); err != nil {
-				logger.Errorf("In udp service %q: %v", serviceQualifiedName, err)
+				srvLogger.Error().Err(err).Msg("Failed to split host port")
 				continue
 			}
 
 			handler, err := udp.NewProxy(server.Address)
 			if err != nil {
-				logger.Errorf("In udp service %q server %q: %v", serviceQualifiedName, server.Address, err)
+				srvLogger.Error().Err(err).Msg("Failed to create server")
 				continue
 			}
 
 			loadBalancer.AddServer(handler)
-			logger.WithField(log.ServerName, name).Debugf("Creating UDP server %d at %s", name, server.Address)
+			srvLogger.Debug().Msg("Creating UDP server")
 		}
+
 		return loadBalancer, nil
+
 	case conf.Weighted != nil:
 		loadBalancer := udp.NewWRRLoadBalancer()
 
 		for _, service := range shuffle(conf.Weighted.Services, m.rand) {
-			handler, err := m.BuildUDP(rootCtx, service.Name)
+			handler, err := m.BuildUDP(ctx, service.Name)
 			if err != nil {
-				logger.Errorf("In udp service %q: %v", serviceQualifiedName, err)
+				logger.Error().Err(err).Msg("Failed to build UDP handler")
 				return nil, err
 			}
+
 			loadBalancer.AddWeightedServer(handler, service.Weight)
 		}
+
 		return loadBalancer, nil
+
 	default:
-		err := fmt.Errorf("the udp service %q does not have any type defined", serviceQualifiedName)
+		err := fmt.Errorf("the UDP service %q does not have any type defined", serviceQualifiedName)
 		conf.AddError(err, true)
 		return nil, err
 	}
diff --git a/pkg/tcp/proxy.go b/pkg/tcp/proxy.go
index 8887bb91d4..525c43c4b1 100644
--- a/pkg/tcp/proxy.go
+++ b/pkg/tcp/proxy.go
@@ -9,8 +9,8 @@ import (
 	"time"
 
 	"github.com/pires/go-proxyproto"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
-	"github.com/traefik/traefik/v2/pkg/log"
 )
 
 // Proxy forwards a TCP request to a TCP service.
@@ -48,14 +48,14 @@ func NewProxy(address string, terminationDelay time.Duration, proxyProtocol *dyn
 
 // ServeTCP forwards the connection to a service.
 func (p *Proxy) ServeTCP(conn WriteCloser) {
-	log.WithoutContext().Debugf("Handling connection from %s to %s", conn.RemoteAddr(), p.address)
+	log.Debug().Msgf("Handling connection from %s to %s", conn.RemoteAddr(), p.address)
 
 	// needed because of e.g. server.trackedConnection
 	defer conn.Close()
 
 	connBackend, err := p.dialBackend()
 	if err != nil {
-		log.WithoutContext().Errorf("Error while connecting to backend: %v", err)
+		log.Error().Err(err).Msg("Error while connecting to backend")
 		return
 	}
 
@@ -66,7 +66,7 @@ func (p *Proxy) ServeTCP(conn WriteCloser) {
 	if p.proxyProtocol != nil && p.proxyProtocol.Version > 0 && p.proxyProtocol.Version < 3 {
 		header := proxyproto.HeaderProxyFromAddrs(byte(p.proxyProtocol.Version), conn.RemoteAddr(), conn.LocalAddr())
 		if _, err := header.WriteTo(connBackend); err != nil {
-			log.WithoutContext().Errorf("Error while writing proxy protocol headers to backend connection: %v", err)
+			log.Error().Err(err).Msg("Error while writing proxy protocol headers to backend connection")
 			return
 		}
 	}
@@ -80,9 +80,9 @@ func (p *Proxy) ServeTCP(conn WriteCloser) {
 		// This allows to not report an RST packet sent by the peer as an error,
 		// as it is an abrupt but possible end for the TCP session
 		if isReadConnResetError(err) {
-			log.WithoutContext().Debugf("Error during connection: %v", err)
+			log.Debug().Err(err).Msg("Error during connection")
 		} else {
-			log.WithoutContext().Errorf("Error during connection: %v", err)
+			log.Error().Err(err).Msg("Error during connection")
 		}
 	}
 
@@ -95,7 +95,7 @@ func (p Proxy) dialBackend() (*net.TCPConn, error) {
 		return net.DialTCP("tcp", nil, p.tcpAddr)
 	}
 
-	log.WithoutContext().Debugf("Dial with lookup to address %s", p.address)
+	log.Debug().Msgf("Dial with lookup to address %s", p.address)
 
 	// Dial with DNS lookup for host based addresses.
 	conn, err := net.Dial("tcp", p.address)
@@ -119,7 +119,7 @@ func (p Proxy) connCopy(dst, src WriteCloser, errCh chan error) {
 		// In that case, logging the error is superfluous,
 		// as in the first place we should not have needed to call CloseWrite.
 		if !isSocketNotConnectedError(errClose) {
-			log.WithoutContext().Debugf("Error while terminating connection: %v", errClose)
+			log.Debug().Err(errClose).Msg("Error while terminating connection")
 		}
 
 		return
@@ -128,7 +128,7 @@ func (p Proxy) connCopy(dst, src WriteCloser, errCh chan error) {
 	if p.terminationDelay >= 0 {
 		err := dst.SetReadDeadline(time.Now().Add(p.terminationDelay))
 		if err != nil {
-			log.WithoutContext().Debugf("Error while setting deadline: %v", err)
+			log.Debug().Err(err).Msg("Error while setting deadline")
 		}
 	}
 }
diff --git a/pkg/tcp/wrr_load_balancer.go b/pkg/tcp/wrr_load_balancer.go
index 236fd80075..d494e0abf1 100644
--- a/pkg/tcp/wrr_load_balancer.go
+++ b/pkg/tcp/wrr_load_balancer.go
@@ -4,7 +4,7 @@ import (
 	"fmt"
 	"sync"
 
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 )
 
 type server struct {
@@ -34,7 +34,7 @@ func (b *WRRLoadBalancer) ServeTCP(conn WriteCloser) {
 	b.lock.Unlock()
 
 	if err != nil {
-		log.WithoutContext().Errorf("Error during load balancing: %v", err)
+		log.Error().Err(err).Msg("Error during load balancing")
 		conn.Close()
 		return
 	}
diff --git a/pkg/tls/certificate.go b/pkg/tls/certificate.go
index 25559a22e9..7cddcd526b 100644
--- a/pkg/tls/certificate.go
+++ b/pkg/tls/certificate.go
@@ -10,7 +10,7 @@ import (
 	"sort"
 	"strings"
 
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 )
 
 var (
@@ -63,7 +63,7 @@ func (c Certificates) GetCertificates() []tls.Certificate {
 	for _, certificate := range c {
 		cert, err := certificate.GetCertificate()
 		if err != nil {
-			log.WithoutContext().Debugf("Error while getting certificate: %v", err)
+			log.Debug().Err(err).Msg("Error while getting certificate")
 			continue
 		}
 
@@ -153,9 +153,9 @@ func (c *Certificate) AppendCertificate(certs map[string]map[string]*tls.Certifi
 		}
 	}
 	if certExists {
-		log.Debugf("Skipping addition of certificate for domain(s) %q, to TLS Store %s, as it already exists for this store.", certKey, storeName)
+		log.Debug().Msgf("Skipping addition of certificate for domain(s) %q, to TLS Store %s, as it already exists for this store.", certKey, storeName)
 	} else {
-		log.Debugf("Adding certificate for domain(s) %s", certKey)
+		log.Debug().Msgf("Adding certificate for domain(s) %s", certKey)
 		certs[storeName][certKey] = &tlsCert
 	}
 
diff --git a/pkg/tls/certificate_store.go b/pkg/tls/certificate_store.go
index 1a954a0e0c..353efc95c4 100644
--- a/pkg/tls/certificate_store.go
+++ b/pkg/tls/certificate_store.go
@@ -9,7 +9,7 @@ import (
 	"time"
 
 	"github.com/patrickmn/go-cache"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/pkg/safe"
 )
 
@@ -40,7 +40,7 @@ func (c CertificateStore) getDefaultCertificateDomains() []string {
 
 	x509Cert, err := x509.ParseCertificate(c.DefaultCertificate.Certificate[0])
 	if err != nil {
-		log.WithoutContext().Errorf("Could not parse default certificate: %v", err)
+		log.Error().Err(err).Msg("Could not parse default certificate")
 		return allCerts
 	}
 
@@ -81,7 +81,7 @@ func (c *CertificateStore) GetBestCertificate(clientHello *tls.ClientHelloInfo)
 		// If no ServerName is provided, Check for local IP address matches
 		host, _, err := net.SplitHostPort(clientHello.Conn.LocalAddr().String())
 		if err != nil {
-			log.WithoutContext().Debugf("Could not split host/port: %v", err)
+			log.Debug().Err(err).Msg("Could not split host/port")
 		}
 		serverName = strings.TrimSpace(host)
 	}
diff --git a/pkg/tls/tlsmanager.go b/pkg/tls/tlsmanager.go
index 0c7daa9923..74fb9ee203 100644
--- a/pkg/tls/tlsmanager.go
+++ b/pkg/tls/tlsmanager.go
@@ -11,8 +11,8 @@ import (
 
 	"github.com/go-acme/lego/v4/challenge/dns01"
 	"github.com/go-acme/lego/v4/challenge/tlsalpn01"
-	"github.com/sirupsen/logrus"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/tls/generate"
 	"github.com/traefik/traefik/v2/pkg/types"
 )
@@ -86,15 +86,15 @@ func (m *Manager) UpdateConfigs(ctx context.Context, stores map[string]Store, co
 	storesCertificates := make(map[string]map[string]*tls.Certificate)
 	for _, conf := range certs {
 		if len(conf.Stores) == 0 {
-			if log.GetLevel() >= logrus.DebugLevel {
-				log.FromContext(ctx).Debugf("No store is defined to add the certificate %s, it will be added to the default store.",
+			log.Ctx(ctx).Debug().MsgFunc(func() string {
+				return fmt.Sprintf("No store is defined to add the certificate %s, it will be added to the default store",
 					conf.Certificate.GetTruncatedCertificateName())
-			}
+			})
 			conf.Stores = []string{DefaultTLSStoreName}
 		}
 
 		for _, store := range conf.Stores {
-			ctxStore := log.With(ctx, log.Str(log.TLSStoreName, store))
+			logger := log.Ctx(ctx).With().Str(logs.TLSStoreName, store).Logger()
 
 			if _, ok := m.storesConfig[store]; !ok {
 				m.storesConfig[store] = Store{}
@@ -102,7 +102,7 @@ func (m *Manager) UpdateConfigs(ctx context.Context, stores map[string]Store, co
 
 			err := conf.Certificate.AppendCertificate(storesCertificates, store)
 			if err != nil {
-				log.FromContext(ctxStore).Errorf("Unable to append certificate %s to store: %v", conf.Certificate.GetTruncatedCertificateName(), err)
+				logger.Error().Err(err).Msgf("Unable to append certificate %s to store", conf.Certificate.GetTruncatedCertificateName())
 			}
 		}
 	}
@@ -122,11 +122,12 @@ func (m *Manager) UpdateConfigs(ctx context.Context, stores map[string]Store, co
 			continue
 		}
 
-		ctxStore := log.With(ctx, log.Str(log.TLSStoreName, storeName))
+		logger := log.Ctx(ctx).With().Str(logs.TLSStoreName, storeName).Logger()
+		ctxStore := logger.WithContext(ctx)
 
 		certificate, err := getDefaultCertificate(ctxStore, storeConfig, st)
 		if err != nil {
-			log.FromContext(ctxStore).Errorf("Error while creating certificate store: %v", err)
+			logger.Error().Err(err).Msg("Error while creating certificate store")
 		}
 
 		st.DefaultCertificate = certificate
@@ -187,7 +188,7 @@ func (m *Manager) Get(storeName, configName string) (*tls.Config, error) {
 		if isACMETLS(clientHello) {
 			certificate := acmeTLSStore.GetBestCertificate(clientHello)
 			if certificate == nil {
-				log.WithoutContext().Debugf("TLS: no certificate for TLSALPN challenge: %s", domainToCheck)
+				log.Debug().Msgf("TLS: no certificate for TLSALPN challenge: %s", domainToCheck)
 				// We want the user to eventually get the (alertUnrecognizedName) "unrecognized
 				// name" error.
 				// Unfortunately, if we returned an error here, since we can't use
@@ -210,19 +211,19 @@ func (m *Manager) Get(storeName, configName string) (*tls.Config, error) {
 		}
 
 		if sniStrict {
-			log.WithoutContext().Debugf("TLS: strict SNI enabled - No certificate found for domain: %q, closing connection", domainToCheck)
+			log.Debug().Msgf("TLS: strict SNI enabled - No certificate found for domain: %q, closing connection", domainToCheck)
 			// Same comment as above, as in the isACMETLS case.
 			return nil, nil
 		}
 
 		if store == nil {
-			log.WithoutContext().Errorf("TLS: No certificate store found with this name: %q, closing connection", storeName)
+			log.Error().Msgf("TLS: No certificate store found with this name: %q, closing connection", storeName)
 
 			// Same comment as above, as in the isACMETLS case.
 			return nil, nil
 		}
 
-		log.WithoutContext().Debugf("Serving default certificate for request: %q", domainToCheck)
+		log.Debug().Msgf("Serving default certificate for request: %q", domainToCheck)
 		return store.DefaultCertificate, nil
 	}
 
@@ -296,7 +297,7 @@ func getDefaultCertificate(ctx context.Context, tlsStore Store, st *CertificateS
 		return defaultACMECert, nil
 	}
 
-	log.FromContext(ctx).Debug("No default certificate, fallback to the internal generated certificate")
+	log.Ctx(ctx).Debug().Msg("No default certificate, fallback to the internal generated certificate")
 	return defaultCert, nil
 }
 
diff --git a/pkg/tracing/datadog/datadog.go b/pkg/tracing/datadog/datadog.go
index f8878943fa..74b68d65c2 100644
--- a/pkg/tracing/datadog/datadog.go
+++ b/pkg/tracing/datadog/datadog.go
@@ -7,7 +7,8 @@ import (
 	"strings"
 
 	"github.com/opentracing/opentracing-go"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	ddtracer "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/opentracer"
 	datadog "gopkg.in/DataDog/dd-trace-go.v1/ddtrace/tracer"
 )
@@ -46,6 +47,8 @@ func (c *Config) SetDefaults() {
 
 // Setup sets up the tracer.
 func (c *Config) Setup(serviceName string) (opentracing.Tracer, io.Closer, error) {
+	logger := log.With().Str(logs.TracingProviderName, Name).Logger()
+
 	opts := []datadog.StartOption{
 		datadog.WithAgentAddr(c.LocalAgentHostPort),
 		datadog.WithServiceName(serviceName),
@@ -56,6 +59,7 @@ func (c *Config) Setup(serviceName string) (opentracing.Tracer, io.Closer, error
 			PriorityHeader: c.SamplingPriorityHeaderName,
 			BaggagePrefix:  c.BagagePrefixHeaderName,
 		})),
+		datadog.WithLogger(logs.NewDatadogLogger(logger)),
 	}
 
 	for k, v := range c.GlobalTags {
@@ -63,7 +67,7 @@ func (c *Config) Setup(serviceName string) (opentracing.Tracer, io.Closer, error
 	}
 
 	if c.GlobalTag != "" {
-		log.WithoutContext().Warn(`Datadog: option "globalTag" is deprecated, please use "globalTags" instead.`)
+		logger.Warn().Msg(`Datadog: option "globalTag" is deprecated, please use "globalTags" instead.`)
 
 		key, value, _ := strings.Cut(c.GlobalTag, ":")
 
@@ -76,12 +80,13 @@ func (c *Config) Setup(serviceName string) (opentracing.Tracer, io.Closer, error
 	if c.PrioritySampling {
 		opts = append(opts, datadog.WithPrioritySampling())
 	}
+
 	tracer := ddtracer.New(opts...)
 
 	// Without this, child spans are getting the NOOP tracer
 	opentracing.SetGlobalTracer(tracer)
 
-	log.WithoutContext().Debug("Datadog tracer configured")
+	logger.Debug().Msg("Datadog tracer configured")
 
 	return tracer, nil, nil
 }
diff --git a/pkg/tracing/elastic/elastic.go b/pkg/tracing/elastic/elastic.go
index fa385376b1..967f5fcb03 100644
--- a/pkg/tracing/elastic/elastic.go
+++ b/pkg/tracing/elastic/elastic.go
@@ -5,7 +5,8 @@ import (
 	"net/url"
 
 	"github.com/opentracing/opentracing-go"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	"github.com/traefik/traefik/v2/pkg/version"
 	"go.elastic.co/apm"
 	"go.elastic.co/apm/module/apmot"
@@ -59,13 +60,15 @@ func (c *Config) Setup(serviceName string) (opentracing.Tracer, io.Closer, error
 		return nil, nil, err
 	}
 
-	tracer.SetLogger(log.WithoutContext())
+	logger := log.With().Str(logs.TracingProviderName, Name).Logger()
+	tracer.SetLogger(logs.NewElasticLogger(logger))
+
 	otTracer := apmot.New(apmot.WithTracer(tracer))
 
 	// Without this, child spans are getting the NOOP tracer
 	opentracing.SetGlobalTracer(otTracer)
 
-	log.WithoutContext().Debug("Elastic tracer configured")
+	log.Debug().Msg("Elastic tracer configured")
 
 	return otTracer, nil, nil
 }
diff --git a/pkg/tracing/haystack/haystack.go b/pkg/tracing/haystack/haystack.go
index 3990e58d24..9c64dd36be 100644
--- a/pkg/tracing/haystack/haystack.go
+++ b/pkg/tracing/haystack/haystack.go
@@ -7,7 +7,8 @@ import (
 
 	"github.com/ExpediaDotCom/haystack-client-go"
 	"github.com/opentracing/opentracing-go"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 )
 
 // Name sets the name of this tracer.
@@ -48,6 +49,8 @@ func (c *Config) Setup(serviceName string) (opentracing.Tracer, io.Closer, error
 		port = c.LocalAgentPort
 	}
 
+	logger := log.With().Str(logs.TracingProviderName, Name).Logger()
+
 	tracer, closer := haystack.NewTracer(serviceName, haystack.NewAgentDispatcher(host, port, 3*time.Second, 1000),
 		haystack.TracerOptionsFactory.Tag(tag[0], value),
 		haystack.TracerOptionsFactory.Propagator(opentracing.HTTPHeaders,
@@ -57,13 +60,13 @@ func (c *Config) Setup(serviceName string) (opentracing.Tracer, io.Closer, error
 				SpanIDKEYName:        c.SpanIDHeaderName,
 				BaggagePrefixKEYName: c.BaggagePrefixHeaderName,
 			}, haystack.DefaultCodex{})),
-		haystack.TracerOptionsFactory.Logger(&haystackLogger{logger: log.WithoutContext()}),
+		haystack.TracerOptionsFactory.Logger(logs.NewHaystackLogger(logger)),
 	)
 
 	// Without this, child spans are getting the NOOP tracer
 	opentracing.SetGlobalTracer(tracer)
 
-	log.WithoutContext().Debug("haystack tracer configured")
+	log.Debug().Msg("haystack tracer configured")
 
 	return tracer, closer, nil
 }
diff --git a/pkg/tracing/haystack/logger.go b/pkg/tracing/haystack/logger.go
deleted file mode 100644
index 7635bd8218..0000000000
--- a/pkg/tracing/haystack/logger.go
+++ /dev/null
@@ -1,24 +0,0 @@
-package haystack
-
-import (
-	"github.com/traefik/traefik/v2/pkg/log"
-)
-
-type haystackLogger struct {
-	logger log.Logger
-}
-
-// Error prints the error message.
-func (l haystackLogger) Error(format string, v ...interface{}) {
-	l.logger.Errorf(format, v...)
-}
-
-// Info prints the info message.
-func (l haystackLogger) Info(format string, v ...interface{}) {
-	l.logger.Infof(format, v...)
-}
-
-// Debug prints the info message.
-func (l haystackLogger) Debug(format string, v ...interface{}) {
-	l.logger.Debugf(format, v...)
-}
diff --git a/pkg/tracing/instana/instana.go b/pkg/tracing/instana/instana.go
index c7f538426c..39c664468f 100644
--- a/pkg/tracing/instana/instana.go
+++ b/pkg/tracing/instana/instana.go
@@ -5,7 +5,8 @@ import (
 
 	instana "github.com/instana/go-sensor"
 	"github.com/opentracing/opentracing-go"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 )
 
 // Name sets the name of this tracer.
@@ -40,6 +41,9 @@ func (c *Config) Setup(serviceName string) (opentracing.Tracer, io.Closer, error
 		logLevel = instana.Debug
 	}
 
+	logger := log.With().Str(logs.TracingProviderName, Name).Logger()
+	instana.SetLogger(logs.NewInstanaLogger(logger))
+
 	tracer := instana.NewTracerWithOptions(&instana.Options{
 		Service:           serviceName,
 		LogLevel:          logLevel,
@@ -51,7 +55,7 @@ func (c *Config) Setup(serviceName string) (opentracing.Tracer, io.Closer, error
 	// Without this, child spans are getting the NOOP tracer
 	opentracing.SetGlobalTracer(tracer)
 
-	log.WithoutContext().Debug("Instana tracer configured")
+	logger.Debug().Msg("Instana tracer configured")
 
 	return tracer, nil, nil
 }
diff --git a/pkg/tracing/jaeger/jaeger.go b/pkg/tracing/jaeger/jaeger.go
index 9f5b900260..206c206de1 100644
--- a/pkg/tracing/jaeger/jaeger.go
+++ b/pkg/tracing/jaeger/jaeger.go
@@ -5,7 +5,8 @@ import (
 	"io"
 
 	"github.com/opentracing/opentracing-go"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	jaegercli "github.com/uber/jaeger-client-go"
 	jaegercfg "github.com/uber/jaeger-client-go/config"
 	"github.com/uber/jaeger-client-go/zipkin"
@@ -88,8 +89,10 @@ func (c *Config) Setup(componentName string) (opentracing.Tracer, io.Closer, err
 
 	jMetricsFactory := jaegermet.NullFactory
 
+	logger := log.With().Str(logs.TracingProviderName, Name).Logger()
+
 	opts := []jaegercfg.Option{
-		jaegercfg.Logger(newJaegerLogger()),
+		jaegercfg.Logger(logs.NewJaegerLogger(logger)),
 		jaegercfg.Metrics(jMetricsFactory),
 		jaegercfg.Gen128Bit(c.Gen128Bit),
 	}
@@ -112,10 +115,10 @@ func (c *Config) Setup(componentName string) (opentracing.Tracer, io.Closer, err
 		opts...,
 	)
 	if err != nil {
-		log.WithoutContext().Warnf("Could not initialize jaeger tracer: %v", err)
+		logger.Warn().Err(err).Msg("Could not initialize jaeger tracer")
 		return nil, nil, err
 	}
-	log.WithoutContext().Debug("Jaeger tracer configured")
+	logger.Debug().Msg("Jaeger tracer configured")
 
 	return opentracing.GlobalTracer(), closer, nil
 }
diff --git a/pkg/tracing/jaeger/logger.go b/pkg/tracing/jaeger/logger.go
deleted file mode 100644
index 4aad3de775..0000000000
--- a/pkg/tracing/jaeger/logger.go
+++ /dev/null
@@ -1,26 +0,0 @@
-package jaeger
-
-import (
-	"github.com/sirupsen/logrus"
-	"github.com/traefik/traefik/v2/pkg/log"
-)
-
-// jaegerLogger is an implementation of the Logger interface that delegates to traefik log.
-type jaegerLogger struct {
-	logger logrus.FieldLogger
-}
-
-func newJaegerLogger() *jaegerLogger {
-	return &jaegerLogger{
-		logger: log.WithoutContext().WithField(log.TracingProviderName, "jaeger"),
-	}
-}
-
-func (l *jaegerLogger) Error(msg string) {
-	l.logger.Errorf("Tracing jaeger error: %s", msg)
-}
-
-// Infof logs a message at debug priority.
-func (l *jaegerLogger) Infof(msg string, args ...interface{}) {
-	l.logger.Debugf(msg, args...)
-}
diff --git a/pkg/tracing/operation_name.go b/pkg/tracing/operation_name.go
index e6194e31ed..70694747bf 100644
--- a/pkg/tracing/operation_name.go
+++ b/pkg/tracing/operation_name.go
@@ -5,7 +5,7 @@ import (
 	"fmt"
 	"strings"
 
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 )
 
 // TraceNameHashLength defines the number of characters to use from the head of the generated hash.
@@ -22,7 +22,7 @@ func generateOperationName(prefix string, parts []string, sep string, spanLimit
 
 	if spanLimit > 0 && len(name) > spanLimit {
 		if spanLimit < maxLength {
-			log.WithoutContext().Warnf("SpanNameLimit cannot be lesser than %d: falling back on %d, maxLength, maxLength+3", maxLength)
+			log.Warn().Msgf("SpanNameLimit cannot be lesser than %d: falling back on %d", maxLength, maxLength+3)
 			spanLimit = maxLength + 3
 		}
 
@@ -58,7 +58,7 @@ func computeHash(name string) string {
 	hash := sha256.New()
 	if _, err := hash.Write(data); err != nil {
 		// Impossible case
-		log.WithoutContext().WithField("OperationName", name).Errorf("Failed to create Span name hash for %s: %v", name, err)
+		log.Error().Str("OperationName", name).Err(err).Msgf("Failed to create Span name hash for %s", name)
 	}
 
 	return fmt.Sprintf("%x", hash.Sum(nil))[:TraceNameHashLength]
diff --git a/pkg/tracing/tracing.go b/pkg/tracing/tracing.go
index f951fc1a33..d8923f510d 100644
--- a/pkg/tracing/tracing.go
+++ b/pkg/tracing/tracing.go
@@ -9,7 +9,7 @@ import (
 
 	"github.com/opentracing/opentracing-go"
 	"github.com/opentracing/opentracing-go/ext"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 )
 
 type contextKey int
@@ -99,7 +99,7 @@ func (t *Tracing) Close() {
 	if t.closer != nil {
 		err := t.closer.Close()
 		if err != nil {
-			log.WithoutContext().Warn(err)
+			log.Warn().Err(err).Send()
 		}
 	}
 }
@@ -136,7 +136,7 @@ func InjectRequestHeaders(r *http.Request) {
 			opentracing.HTTPHeaders,
 			opentracing.HTTPHeadersCarrier(r.Header))
 		if err != nil {
-			log.FromContext(r.Context()).Error(err)
+			log.Ctx(r.Context()).Error().Err(err).Send()
 		}
 	}
 }
diff --git a/pkg/tracing/zipkin/zipkin.go b/pkg/tracing/zipkin/zipkin.go
index 96974a3051..598f38037d 100644
--- a/pkg/tracing/zipkin/zipkin.go
+++ b/pkg/tracing/zipkin/zipkin.go
@@ -8,7 +8,7 @@ import (
 	zipkinot "github.com/openzipkin-contrib/zipkin-go-opentracing"
 	"github.com/openzipkin/zipkin-go"
 	"github.com/openzipkin/zipkin-go/reporter/http"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 )
 
 // Name sets the name of this tracer.
@@ -65,7 +65,7 @@ func (c *Config) Setup(serviceName string) (opentracing.Tracer, io.Closer, error
 	// Without this, child spans are getting the NOOP tracer
 	opentracing.SetGlobalTracer(tracer)
 
-	log.WithoutContext().Debug("Zipkin tracer configured")
+	log.Debug().Msg("Zipkin tracer configured")
 
 	return tracer, reporter, nil
 }
diff --git a/pkg/types/logs.go b/pkg/types/logs.go
index 2c932e487a..657e56102d 100644
--- a/pkg/types/logs.go
+++ b/pkg/types/logs.go
@@ -21,9 +21,15 @@ const (
 
 // TraefikLog holds the configuration settings for the traefik logger.
 type TraefikLog struct {
-	Level    string `description:"Log level set to traefik logs." json:"level,omitempty" toml:"level,omitempty" yaml:"level,omitempty" export:"true"`
-	FilePath string `description:"Traefik log file path. Stdout is used when omitted or empty." json:"filePath,omitempty" toml:"filePath,omitempty" yaml:"filePath,omitempty"`
-	Format   string `description:"Traefik log format: json | common" json:"format,omitempty" toml:"format,omitempty" yaml:"format,omitempty" export:"true"`
+	Level   string `description:"Log level set to traefik logs." json:"level,omitempty" toml:"level,omitempty" yaml:"level,omitempty" export:"true"`
+	Format  string `description:"Traefik log format: json | common" json:"format,omitempty" toml:"format,omitempty" yaml:"format,omitempty" export:"true"`
+	NoColor bool   `description:"When using the 'common' format, disables the colorized output." json:"noColor,omitempty" toml:"noColor,omitempty" yaml:"noColor,omitempty" export:"true"`
+
+	FilePath   string `description:"Traefik log file path. Stdout is used when omitted or empty." json:"filePath,omitempty" toml:"filePath,omitempty" yaml:"filePath,omitempty"`
+	MaxSize    int    `description:"Maximum size in megabytes of the log file before it gets rotated." json:"maxSize,omitempty" toml:"maxSize,omitempty" yaml:"maxSize,omitempty" export:"true"`
+	MaxAge     int    `description:"Maximum number of days to retain old log files based on the timestamp encoded in their filename." json:"maxAge,omitempty" toml:"maxAge,omitempty" yaml:"maxAge,omitempty" export:"true"`
+	MaxBackups int    `description:"Maximum number of old log files to retain." json:"maxBackups,omitempty" toml:"maxBackups,omitempty" yaml:"maxBackups,omitempty" export:"true"`
+	Compress   bool   `description:"Determines if the rotated log files should be compressed using gzip." json:"compress,omitempty" toml:"compress,omitempty" yaml:"compress,omitempty" export:"true"`
 }
 
 // SetDefaults sets the default values.
diff --git a/pkg/types/tls.go b/pkg/types/tls.go
index 61cf5346cd..2b5457be26 100644
--- a/pkg/types/tls.go
+++ b/pkg/types/tls.go
@@ -8,7 +8,7 @@ import (
 	"fmt"
 	"os"
 
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 )
 
 // +k8s:deepcopy-gen=true
@@ -27,12 +27,12 @@ type ClientTLS struct {
 // CreateTLSConfig creates a TLS config from ClientTLS structures.
 func (c *ClientTLS) CreateTLSConfig(ctx context.Context) (*tls.Config, error) {
 	if c == nil {
-		log.FromContext(ctx).Warnf("clientTLS is nil")
+		log.Ctx(ctx).Warn().Msg("clientTLS is nil")
 		return nil, nil
 	}
 
 	if c.CAOptional {
-		log.FromContext(ctx).Warn("CAOptional is deprecated, TLS client authentication is a server side option.")
+		log.Ctx(ctx).Warn().Msg("CAOptional is deprecated, TLS client authentication is a server side option.")
 	}
 
 	// Not initialized, to rely on system bundle.
diff --git a/pkg/udp/proxy.go b/pkg/udp/proxy.go
index 8ccbfda4bc..31f64a5ce4 100644
--- a/pkg/udp/proxy.go
+++ b/pkg/udp/proxy.go
@@ -4,7 +4,7 @@ import (
 	"io"
 	"net"
 
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 )
 
 // Proxy is a reverse-proxy implementation of the Handler interface.
@@ -20,14 +20,14 @@ func NewProxy(address string) (*Proxy, error) {
 
 // ServeUDP implements the Handler interface.
 func (p *Proxy) ServeUDP(conn *Conn) {
-	log.WithoutContext().Debugf("Handling connection from %s to %s", conn.rAddr, p.target)
+	log.Debug().Msgf("Handling connection from %s to %s", conn.rAddr, p.target)
 
 	// needed because of e.g. server.trackedConnection
 	defer conn.Close()
 
 	connBackend, err := net.Dial("udp", p.target)
 	if err != nil {
-		log.WithoutContext().Errorf("Error while connecting to backend: %v", err)
+		log.Error().Err(err).Msg("Error while connecting to backend")
 		return
 	}
 
@@ -40,7 +40,7 @@ func (p *Proxy) ServeUDP(conn *Conn) {
 
 	err = <-errChan
 	if err != nil {
-		log.WithoutContext().Errorf("Error while serving UDP: %v", err)
+		log.Error().Err(err).Msg("Error while serving UDP")
 	}
 
 	<-errChan
@@ -55,6 +55,6 @@ func connCopy(dst io.WriteCloser, src io.Reader, errCh chan error) {
 	errCh <- err
 
 	if err := dst.Close(); err != nil {
-		log.WithoutContext().Debugf("Error while terminating connection: %v", err)
+		log.Debug().Err(err).Msg("Error while terminating connection")
 	}
 }
diff --git a/pkg/udp/wrr_load_balancer.go b/pkg/udp/wrr_load_balancer.go
index 7dd2a9c075..03fe059e1c 100644
--- a/pkg/udp/wrr_load_balancer.go
+++ b/pkg/udp/wrr_load_balancer.go
@@ -4,7 +4,7 @@ import (
 	"fmt"
 	"sync"
 
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 )
 
 type server struct {
@@ -34,7 +34,7 @@ func (b *WRRLoadBalancer) ServeUDP(conn *Conn) {
 	b.lock.Unlock()
 
 	if err != nil {
-		log.WithoutContext().Errorf("Error during load balancing: %v", err)
+		log.Error().Err(err).Msg("Error during load balancing")
 		conn.Close()
 		return
 	}
diff --git a/pkg/version/version.go b/pkg/version/version.go
index 49b4f9e1ef..72472fcc98 100644
--- a/pkg/version/version.go
+++ b/pkg/version/version.go
@@ -9,7 +9,7 @@ import (
 	"github.com/google/go-github/v28/github"
 	"github.com/gorilla/mux"
 	goversion "github.com/hashicorp/go-version"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog/log"
 	"github.com/unrolled/render"
 )
 
@@ -48,7 +48,7 @@ func (v Handler) Append(router *mux.Router) {
 			}
 
 			if err := templatesRenderer.JSON(response, http.StatusOK, v); err != nil {
-				log.WithoutContext().Error(err)
+				log.Error().Err(err).Send()
 			}
 		})
 }
@@ -59,38 +59,36 @@ func CheckNewVersion() {
 		return
 	}
 
-	logger := log.WithoutContext()
-
 	client := github.NewClient(nil)
 
 	updateURL, err := url.Parse("https://update.traefik.io/")
 	if err != nil {
-		logger.Warnf("Error checking new version: %s", err)
+		log.Warn().Err(err).Msg("Error checking new version")
 		return
 	}
 	client.BaseURL = updateURL
 
 	releases, resp, err := client.Repositories.ListReleases(context.Background(), "traefik", "traefik", nil)
 	if err != nil {
-		logger.Warnf("Error checking new version: %s", err)
+		log.Warn().Err(err).Msg("Error checking new version")
 		return
 	}
 
 	if resp.StatusCode != http.StatusOK {
-		logger.Warnf("Error checking new version: status=%s", resp.Status)
+		log.Warn().Msgf("Error checking new version: status=%s", resp.Status)
 		return
 	}
 
 	currentVersion, err := goversion.NewVersion(Version)
 	if err != nil {
-		logger.Warnf("Error checking new version: %s", err)
+		log.Warn().Err(err).Msg("Error checking new version")
 		return
 	}
 
 	for _, release := range releases {
 		releaseVersion, err := goversion.NewVersion(*release.TagName)
 		if err != nil {
-			logger.Warnf("Error checking new version: %s", err)
+			log.Warn().Err(err).Msg("Error checking new version")
 			return
 		}
 
@@ -99,7 +97,7 @@ func CheckNewVersion() {
 		}
 
 		if releaseVersion.GreaterThan(currentVersion) {
-			logger.Warnf("A new release has been found: %s. Please consider updating.", releaseVersion.String())
+			log.Warn().Err(err).Msgf("A new release has been found: %s. Please consider updating.", releaseVersion.String())
 			return
 		}
 	}


Test patch:
diff --git a/integration/access_log_test.go b/integration/access_log_test.go
index ac00e47a9d..d2f5715db5 100644
--- a/integration/access_log_test.go
+++ b/integration/access_log_test.go
@@ -12,8 +12,8 @@ import (
 	"time"
 
 	"github.com/go-check/check"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/integration/try"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"github.com/traefik/traefik/v2/pkg/middlewares/accesslog"
 	checker "github.com/vdemeester/shakers"
 )
@@ -54,7 +54,7 @@ func (s *AccessLogSuite) TestAccessLog(c *check.C) {
 	defer func() {
 		traefikLog, err := os.ReadFile(traefikTestLogFile)
 		c.Assert(err, checker.IsNil)
-		log.WithoutContext().Info(string(traefikLog))
+		log.Info().Msg(string(traefikLog))
 	}()
 
 	err := cmd.Start()
@@ -262,7 +262,7 @@ func digestParts(resp *http.Response) map[string]string {
 func getMD5(data string) string {
 	digest := md5.New()
 	if _, err := digest.Write([]byte(data)); err != nil {
-		log.WithoutContext().Error(err)
+		log.Error().Err(err).Send()
 	}
 	return fmt.Sprintf("%x", digest.Sum(nil))
 }
@@ -270,7 +270,7 @@ func getMD5(data string) string {
 func getCnonce() string {
 	b := make([]byte, 8)
 	if _, err := io.ReadFull(rand.Reader, b); err != nil {
-		log.WithoutContext().Error(err)
+		log.Error().Err(err).Send()
 	}
 	return fmt.Sprintf("%x", b)[:16]
 }
diff --git a/integration/grpc_test.go b/integration/grpc_test.go
index ab1c7f899a..a18975864b 100644
--- a/integration/grpc_test.go
+++ b/integration/grpc_test.go
@@ -11,9 +11,9 @@ import (
 	"time"
 
 	"github.com/go-check/check"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/integration/helloworld"
 	"github.com/traefik/traefik/v2/integration/try"
-	"github.com/traefik/traefik/v2/pkg/log"
 	"google.golang.org/grpc"
 	"google.golang.org/grpc/credentials"
 	"google.golang.org/grpc/credentials/insecure"
@@ -52,7 +52,7 @@ func (s *myserver) StreamExample(in *helloworld.StreamExampleRequest, server hel
 	}
 
 	if err := server.Send(&helloworld.StreamExampleReply{Data: string(data)}); err != nil {
-		log.WithoutContext().Error(err)
+		log.Error().Err(err).Send()
 	}
 
 	<-s.stopStreamExample
diff --git a/integration/integration_test.go b/integration/integration_test.go
index af3d35ca1d..d71409d38d 100644
--- a/integration/integration_test.go
+++ b/integration/integration_test.go
@@ -8,6 +8,7 @@ import (
 	"flag"
 	"fmt"
 	"io/fs"
+	stdlog "log"
 	"os"
 	"os/exec"
 	"path/filepath"
@@ -27,7 +28,10 @@ import (
 	"github.com/docker/docker/client"
 	"github.com/fatih/structs"
 	"github.com/go-check/check"
-	"github.com/traefik/traefik/v2/pkg/log"
+	"github.com/rs/zerolog"
+	"github.com/rs/zerolog/log"
+	"github.com/sirupsen/logrus"
+	"github.com/traefik/traefik/v2/pkg/logs"
 	checker "github.com/vdemeester/shakers"
 )
 
@@ -38,12 +42,22 @@ var (
 
 func Test(t *testing.T) {
 	if !*integration {
-		log.WithoutContext().Info("Integration tests disabled.")
+		log.Info().Msg("Integration tests disabled.")
 		return
 	}
 
-	// TODO(mpl): very niche optimization: do not start tailscale if none of the
-	// wanted tests actually need it (e.g. KeepAliveSuite does not).
+	log.Logger = zerolog.New(zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}).
+		With().Timestamp().Caller().Logger()
+	zerolog.SetGlobalLevel(zerolog.InfoLevel)
+
+	// Global logrus replacement
+	logrus.StandardLogger().Out = logs.NoLevel(log.Logger, zerolog.DebugLevel)
+
+	// configure default standard log.
+	stdlog.SetFlags(stdlog.Lshortfile | stdlog.LstdFlags)
+	stdlog.SetOutput(logs.NoLevel(log.Logger, zerolog.DebugLevel))
+
+	// TODO(mpl): very niche optimization: do not start tailscale if none of the wanted tests actually need it (e.g. KeepAliveSuite does not).
 	var (
 		vpn    *tailscaleNotSuite
 		useVPN bool
@@ -199,7 +213,7 @@ func (s *BaseSuite) cmdTraefik(args ...string) (*exec.Cmd, *bytes.Buffer) {
 func (s *BaseSuite) killCmd(cmd *exec.Cmd) {
 	err := cmd.Process.Kill()
 	if err != nil {
-		log.WithoutContext().Errorf("Kill: %v", err)
+		log.Error().Err(err).Msg("Kill")
 	}
 
 	time.Sleep(100 * time.Millisecond)
@@ -221,40 +235,39 @@ func (s *BaseSuite) displayLogK3S() {
 	if _, err := os.Stat(filePath); err == nil {
 		content, errR := os.ReadFile(filePath)
 		if errR != nil {
-			log.WithoutContext().Error(errR)
+			log.Error().Err(errR).Send()
 		}
-		log.WithoutContext().Println(string(content))
+		log.Print(string(content))
 	}
-	log.WithoutContext().Println()
-	log.WithoutContext().Println("################################")
-	log.WithoutContext().Println()
+	log.Print()
+	log.Print("################################")
+	log.Print()
 }
 
 func (s *BaseSuite) displayLogCompose(c *check.C) {
 	if s.dockerComposeService == nil || s.composeProject == nil {
-		log.WithoutContext().Infof("%s: No docker compose logs.", c.TestName())
+		log.Info().Str("testName", c.TestName()).Msg("No docker compose logs.")
 		return
 	}
 
-	log.WithoutContext().Infof("%s: docker compose logs: ", c.TestName())
+	log.Info().Str("testName", c.TestName()).Msg("docker compose logs")
 
-	logWriter := log.WithoutContext().WriterLevel(log.GetLevel())
-	logConsumer := formatter.NewLogConsumer(context.Background(), logWriter, false, true)
+	logConsumer := formatter.NewLogConsumer(context.Background(), logs.NoLevel(log.Logger, zerolog.InfoLevel), false, true)
 
 	err := s.dockerComposeService.Logs(context.Background(), s.composeProject.Name, logConsumer, composeapi.LogOptions{})
 	c.Assert(err, checker.IsNil)
 
-	log.WithoutContext().Println()
-	log.WithoutContext().Println("################################")
-	log.WithoutContext().Println()
+	log.Print()
+	log.Print("################################")
+	log.Print()
 }
 
 func (s *BaseSuite) displayTraefikLog(c *check.C, output *bytes.Buffer) {
 	if output == nil || output.Len() == 0 {
-		log.WithoutContext().Infof("%s: No Traefik logs.", c.TestName())
+		log.Info().Str("testName", c.TestName()).Msg("No Traefik logs.")
 	} else {
-		log.WithoutContext().Infof("%s: Traefik logs: ", c.TestName())
-		log.WithoutContext().Infof(output.String())
+		log.Info().Str("testName", c.TestName()).
+			Str("logs", output.String()).Msg("Traefik logs")
 	}
 }
 
@@ -351,7 +364,7 @@ func setupVPN(c *check.C, keyFile string) *tailscaleNotSuite {
 	data, err := os.ReadFile(keyFile)
 	if err != nil {
 		if !errors.Is(err, fs.ErrNotExist) {
-			log.Fatal(err)
+			log.Fatal().Err(err).Send()
 		}
 		return nil
 	}
diff --git a/integration/k8s_test.go b/integration/k8s_test.go
index 3762bf2b26..bfb73d93cc 100644
--- a/integration/k8s_test.go
+++ b/integration/k8s_test.go
@@ -15,9 +15,9 @@ import (
 
 	"github.com/go-check/check"
 	"github.com/pmezard/go-difflib/difflib"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/integration/try"
 	"github.com/traefik/traefik/v2/pkg/api"
-	"github.com/traefik/traefik/v2/pkg/log"
 	checker "github.com/vdemeester/shakers"
 )
 
@@ -57,7 +57,7 @@ func (s *K8sSuite) TearDownSuite(c *check.C) {
 
 	for _, filename := range generatedFiles {
 		if err := os.Remove(filename); err != nil {
-			log.WithoutContext().Warning(err)
+			log.Warn().Err(err).Send()
 		}
 	}
 }
diff --git a/integration/log_rotation_test.go b/integration/log_rotation_test.go
index 27cc6f3703..241e09353c 100644
--- a/integration/log_rotation_test.go
+++ b/integration/log_rotation_test.go
@@ -12,15 +12,12 @@ import (
 	"time"
 
 	"github.com/go-check/check"
+	"github.com/rs/zerolog/log"
 	"github.com/traefik/traefik/v2/integration/try"
-	"github.com/traefik/traefik/v2/pkg/log"
 	checker "github.com/vdemeester/shakers"
 )
 
-const (
-	traefikTestLogFileRotated       = traefikTestLogFile + ".rotated"
-	traefikTestAccessLogFileRotated = traefikTestAccessLogFile + ".rotated"
-)
+const traefikTestAccessLogFileRotated = traefikTestAccessLogFile + ".rotated"
 
 // Log rotation integration test suite.
 type LogRotationSuite struct{ BaseSuite }
@@ -35,14 +32,13 @@ func (s *LogRotationSuite) TearDownSuite(c *check.C) {
 
 	generatedFiles := []string{
 		traefikTestLogFile,
-		traefikTestLogFileRotated,
 		traefikTestAccessLogFile,
 		traefikTestAccessLogFileRotated,
 	}
 
 	for _, filename := range generatedFiles {
 		if err := os.Remove(filename); err != nil {
-			log.WithoutContext().Warning(err)
+			log.Warn().Err(err).Send()
 		}
 	}
 }
@@ -104,48 +100,6 @@ func (s *LogRotationSuite) TestAccessLogRotation(c *check.C) {
 	verifyEmptyErrorLog(c, traefikTestLogFile)
 }
 
-func (s *LogRotationSuite) TestTraefikLogRotation(c *check.C) {
-	// Start Traefik
-	cmd, display := s.traefikCmd(withConfigFile("fixtures/traefik_log_config.toml"))
-	defer display(c)
-	defer displayTraefikLogFile(c, traefikTestLogFile)
-
-	err := cmd.Start()
-	c.Assert(err, checker.IsNil)
-	defer s.killCmd(cmd)
-
-	waitForTraefik(c, "server1")
-
-	// Rename traefik log
-	err = os.Rename(traefikTestLogFile, traefikTestLogFileRotated)
-	c.Assert(err, checker.IsNil)
-
-	// issue SIGUSR1 signal to server process
-	err = cmd.Process.Signal(syscall.SIGUSR1)
-	c.Assert(err, checker.IsNil)
-
-	err = cmd.Process.Signal(syscall.SIGTERM)
-	c.Assert(err, checker.IsNil)
-
-	// Allow time for switch to be processed
-	err = try.Do(3*time.Second, func() error {
-		_, err = os.Stat(traefikTestLogFile)
-		return err
-	})
-	c.Assert(err, checker.IsNil)
-
-	// we have at least 6 lines in traefik.log.rotated
-	lineCount := verifyLogLines(c, traefikTestLogFileRotated, 0, false)
-
-	// GreaterOrEqualThan used to ensure test doesn't break
-	// If more log entries are output on startup
-	c.Assert(lineCount, checker.GreaterOrEqualThan, 5)
-
-	// Verify traefik.log output as expected
-	lineCount = verifyLogLines(c, traefikTestLogFile, lineCount, false)
-	c.Assert(lineCount, checker.GreaterOrEqualThan, 7)
-}
-
 func logAccessLogFile(c *check.C, fileName string) {
 	output, err := os.ReadFile(fileName)
 	c.Assert(err, checker.IsNil)
diff --git a/integration/simple_test.go b/integration/simple_test.go
index fac915a27d..287fe72e0b 100644
--- a/integration/simple_test.go
+++ b/integration/simple_test.go
@@ -793,7 +793,7 @@ func (s *SimpleSuite) TestUDPServiceConfigErrors(c *check.C) {
 	c.Assert(err, checker.IsNil)
 	defer s.killCmd(cmd)
 
-	err = try.GetRequest("http://127.0.0.1:8080/api/udp/services", 1000*time.Millisecond, try.BodyContains(`["the udp service \"service1@file\" does not have any type defined"]`))
+	err = try.GetRequest("http://127.0.0.1:8080/api/udp/services", 1000*time.Millisecond, try.BodyContains(`["the UDP service \"service1@file\" does not have any type defined"]`))
 	c.Assert(err, checker.IsNil)
 
 	err = try.GetRequest("http://127.0.0.1:8080/api/udp/services/service1@file", 1000*time.Millisecond, try.BodyContains(`"status":"disabled"`))
diff --git a/pkg/log/log_test.go b/pkg/log/log_test.go
deleted file mode 100644
index c81724ff30..0000000000
--- a/pkg/log/log_test.go
+++ /dev/null
@@ -1,57 +0,0 @@
-package log
-
-import (
-	"bytes"
-	"context"
-	"strings"
-	"testing"
-
-	"github.com/stretchr/testify/assert"
-)
-
-func TestLog(t *testing.T) {
-	testCases := []struct {
-		desc     string
-		fields   map[string]string
-		expected string
-	}{
-		{
-			desc: "Log with one field",
-			fields: map[string]string{
-				"foo": "bar",
-			},
-			expected: ` level=error msg="message test" foo=bar$`,
-		},
-		{
-			desc: "Log with two fields",
-			fields: map[string]string{
-				"foo": "bar",
-				"oof": "rab",
-			},
-			expected: ` level=error msg="message test" foo=bar oof=rab$`,
-		},
-		{
-			desc:     "Log without field",
-			fields:   map[string]string{},
-			expected: ` level=error msg="message test"$`,
-		},
-	}
-
-	for _, test := range testCases {
-		test := test
-		t.Run(test.desc, func(t *testing.T) {
-			var buffer bytes.Buffer
-			SetOutput(&buffer)
-
-			ctx := context.Background()
-
-			for key, value := range test.fields {
-				ctx = With(ctx, Str(key, value))
-			}
-
-			FromContext(ctx).Error("message test")
-
-			assert.Regexp(t, test.expected, strings.TrimSpace(buffer.String()))
-		})
-	}
-}
diff --git a/pkg/logs/aws_test.go b/pkg/logs/aws_test.go
new file mode 100644
index 0000000000..6b38a680ac
--- /dev/null
+++ b/pkg/logs/aws_test.go
@@ -0,0 +1,17 @@
+package logs
+
+import (
+	"os"
+	"testing"
+	"time"
+
+	"github.com/rs/zerolog"
+)
+
+func TestNewAWSWrapper(t *testing.T) {
+	out := zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}
+
+	logger := NewAWSWrapper(zerolog.New(out).With().Caller().Logger())
+
+	logger.Log("foo")
+}
diff --git a/pkg/logs/datadog_test.go b/pkg/logs/datadog_test.go
new file mode 100644
index 0000000000..a5aded73e7
--- /dev/null
+++ b/pkg/logs/datadog_test.go
@@ -0,0 +1,17 @@
+package logs
+
+import (
+	"os"
+	"testing"
+	"time"
+
+	"github.com/rs/zerolog"
+)
+
+func TestNewDatadogLogger(t *testing.T) {
+	out := zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}
+
+	logger := NewDatadogLogger(zerolog.New(out).With().Caller().Logger())
+
+	logger.Log("foo")
+}
diff --git a/pkg/logs/elastic_test.go b/pkg/logs/elastic_test.go
new file mode 100644
index 0000000000..9e7a2aee3a
--- /dev/null
+++ b/pkg/logs/elastic_test.go
@@ -0,0 +1,17 @@
+package logs
+
+import (
+	"os"
+	"testing"
+	"time"
+
+	"github.com/rs/zerolog"
+)
+
+func TestNewElasticLogger(t *testing.T) {
+	out := zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}
+
+	logger := NewElasticLogger(zerolog.New(out).With().Caller().Logger())
+
+	logger.Errorf("foo")
+}
diff --git a/pkg/logs/gokit_test.go b/pkg/logs/gokit_test.go
new file mode 100644
index 0000000000..51a6398126
--- /dev/null
+++ b/pkg/logs/gokit_test.go
@@ -0,0 +1,17 @@
+package logs
+
+import (
+	"os"
+	"testing"
+	"time"
+
+	"github.com/rs/zerolog"
+)
+
+func TestNewGoKitWrapper(t *testing.T) {
+	out := zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}
+
+	logger := NewGoKitWrapper(zerolog.New(out).With().Caller().Logger())
+
+	_ = logger.Log("foo")
+}
diff --git a/pkg/logs/haystack_test.go b/pkg/logs/haystack_test.go
new file mode 100644
index 0000000000..c66a9b972f
--- /dev/null
+++ b/pkg/logs/haystack_test.go
@@ -0,0 +1,17 @@
+package logs
+
+import (
+	"os"
+	"testing"
+	"time"
+
+	"github.com/rs/zerolog"
+)
+
+func TestNewHaystackLogger(t *testing.T) {
+	out := zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}
+
+	logger := NewHaystackLogger(zerolog.New(out).With().Caller().Logger())
+
+	logger.Info("foo")
+}
diff --git a/pkg/logs/instana_test.go b/pkg/logs/instana_test.go
new file mode 100644
index 0000000000..827f5a45b0
--- /dev/null
+++ b/pkg/logs/instana_test.go
@@ -0,0 +1,17 @@
+package logs
+
+import (
+	"os"
+	"testing"
+	"time"
+
+	"github.com/rs/zerolog"
+)
+
+func TestNewInstanaLogger(t *testing.T) {
+	out := zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}
+
+	logger := NewInstanaLogger(zerolog.New(out).With().Caller().Logger())
+
+	logger.Info("foo")
+}
diff --git a/pkg/logs/jaeger_test.go b/pkg/logs/jaeger_test.go
new file mode 100644
index 0000000000..768e8c7779
--- /dev/null
+++ b/pkg/logs/jaeger_test.go
@@ -0,0 +1,17 @@
+package logs
+
+import (
+	"os"
+	"testing"
+	"time"
+
+	"github.com/rs/zerolog"
+)
+
+func TestNewJaegerLogger(t *testing.T) {
+	out := zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}
+
+	logger := NewJaegerLogger(zerolog.New(out).With().Caller().Logger())
+
+	logger.Infof("foo")
+}
diff --git a/pkg/logs/log_test.go b/pkg/logs/log_test.go
new file mode 100644
index 0000000000..14d749fad7
--- /dev/null
+++ b/pkg/logs/log_test.go
@@ -0,0 +1,17 @@
+package logs
+
+import (
+	"os"
+	"testing"
+	"time"
+
+	"github.com/rs/zerolog"
+)
+
+func TestNoLevel(t *testing.T) {
+	out := zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}
+
+	logger := NoLevel(zerolog.New(out).With().Caller().Logger(), zerolog.DebugLevel)
+
+	logger.Info().Msg("foo")
+}
diff --git a/pkg/logs/logrus_test.go b/pkg/logs/logrus_test.go
new file mode 100644
index 0000000000..cc3155044d
--- /dev/null
+++ b/pkg/logs/logrus_test.go
@@ -0,0 +1,17 @@
+package logs
+
+import (
+	"os"
+	"testing"
+	"time"
+
+	"github.com/rs/zerolog"
+)
+
+func TestNewLogrusStdWrapper(t *testing.T) {
+	out := zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}
+
+	logger := NewLogrusWrapper(zerolog.New(out).With().Caller().Logger())
+
+	logger.Println("foo")
+}
diff --git a/pkg/logs/oxy_test.go b/pkg/logs/oxy_test.go
new file mode 100644
index 0000000000..4ce1028933
--- /dev/null
+++ b/pkg/logs/oxy_test.go
@@ -0,0 +1,17 @@
+package logs
+
+import (
+	"os"
+	"testing"
+	"time"
+
+	"github.com/rs/zerolog"
+)
+
+func TestNewOxyWrapper(t *testing.T) {
+	out := zerolog.ConsoleWriter{Out: os.Stderr, TimeFormat: time.RFC3339}
+
+	logger := NewOxyWrapper(zerolog.New(out).With().Caller().Logger())
+
+	logger.Info("foo")
+}
diff --git a/pkg/middlewares/auth/forward_test.go b/pkg/middlewares/auth/forward_test.go
index 1aeb5ef9aa..4e17f7ff5d 100644
--- a/pkg/middlewares/auth/forward_test.go
+++ b/pkg/middlewares/auth/forward_test.go
@@ -16,7 +16,7 @@ import (
 	tracingMiddleware "github.com/traefik/traefik/v2/pkg/middlewares/tracing"
 	"github.com/traefik/traefik/v2/pkg/testhelpers"
 	"github.com/traefik/traefik/v2/pkg/tracing"
-	"github.com/vulcand/oxy/forward"
+	"github.com/vulcand/oxy/v2/forward"
 )
 
 func TestForwardAuthFail(t *testing.T) {
diff --git a/pkg/middlewares/ratelimiter/rate_limiter_test.go b/pkg/middlewares/ratelimiter/rate_limiter_test.go
index 26d18580be..ac73fc2ecc 100644
--- a/pkg/middlewares/ratelimiter/rate_limiter_test.go
+++ b/pkg/middlewares/ratelimiter/rate_limiter_test.go
@@ -14,7 +14,7 @@ import (
 	ptypes "github.com/traefik/paerser/types"
 	"github.com/traefik/traefik/v2/pkg/config/dynamic"
 	"github.com/traefik/traefik/v2/pkg/testhelpers"
-	"github.com/vulcand/oxy/utils"
+	"github.com/vulcand/oxy/v2/utils"
 )
 
 func TestNewRateLimiter(t *testing.T) {
diff --git a/pkg/redactor/redactor_config_test.go b/pkg/redactor/redactor_config_test.go
index 3ae6c03034..33f2152320 100644
--- a/pkg/redactor/redactor_config_test.go
+++ b/pkg/redactor/redactor_config_test.go
@@ -845,9 +845,13 @@ func TestDo_staticConfiguration(t *testing.T) {
 	}
 
 	config.Log = &types.TraefikLog{
-		Level:    "Level",
-		FilePath: "/foo/path",
-		Format:   "json",
+		Level:      "Level",
+		Format:     "json",
+		FilePath:   "/foo/path",
+		MaxSize:    5,
+		MaxAge:     3,
+		MaxBackups: 4,
+		Compress:   true,
 	}
 
 	config.AccessLog = &types.AccessLog{
diff --git a/pkg/redactor/testdata/anonymized-static-config.json b/pkg/redactor/testdata/anonymized-static-config.json
index 79ca15a67f..8810d16c83 100644
--- a/pkg/redactor/testdata/anonymized-static-config.json
+++ b/pkg/redactor/testdata/anonymized-static-config.json
@@ -336,8 +336,12 @@
   },
   "log": {
     "level": "Level",
+    "format": "json",
     "filePath": "xxxx",
-    "format": "json"
+    "maxSize": 5,
+    "maxAge": 3,
+    "maxBackups": 4,
+    "compress": true
   },
   "accessLog": {
     "filePath": "xxxx",
@@ -466,4 +470,4 @@
       }
     }
   }
-}
+}
\ No newline at end of file
diff --git a/pkg/server/service/udp/service_test.go b/pkg/server/service/udp/service_test.go
index 0f06c1feb4..335a13a24a 100644
--- a/pkg/server/service/udp/service_test.go
+++ b/pkg/server/service/udp/service_test.go
@@ -23,7 +23,7 @@ func TestManager_BuildUDP(t *testing.T) {
 			desc:          "without configuration",
 			serviceName:   "test",
 			configs:       nil,
-			expectedError: `the udp service "test" does not exist`,
+			expectedError: `the UDP service "test" does not exist`,
 		},
 		{
 			desc:        "missing lb configuration",
@@ -33,7 +33,7 @@ func TestManager_BuildUDP(t *testing.T) {
 					UDPService: &dynamic.UDPService{},
 				},
 			},
-			expectedError: `the udp service "test" does not have any type defined`,
+			expectedError: `the UDP service "test" does not have any type defined`,
 		},
 		{
 			desc:        "no such host, server is skipped, error is logged",