Repository: atlassian/gostatsd
Pull request #324 (2020-07-02 03:13:24)

Inject logger
This strips out the rest of `log` aliased to `logrus`, as well as injecting a logger mostly through the system.  `stdout` backend skips it still.


Patch:
diff --git a/backend.go b/backend.go
index 9d880f4..51192a9 100644
--- a/backend.go
+++ b/backend.go
@@ -3,13 +3,14 @@ package gostatsd
 import (
 	"context"
 
+	"github.com/sirupsen/logrus"
 	"github.com/spf13/viper"
 
 	"github.com/atlassian/gostatsd/pkg/transport"
 )
 
 // BackendFactory is a function that returns a Backend.
-type BackendFactory func(config *viper.Viper, pool *transport.TransportPool) (Backend, error)
+type BackendFactory func(config *viper.Viper, logger logrus.FieldLogger, pool *transport.TransportPool) (Backend, error)
 
 // SendCallback is called by Backend.SendMetricsAsync() to notify about the result of operation.
 // A list of errors is passed to the callback. It may be empty or contain nil values. Every non-nil value is an error
diff --git a/cmd/gostatsd/main.go b/cmd/gostatsd/main.go
index b944e8d..4c5f42d 100644
--- a/cmd/gostatsd/main.go
+++ b/cmd/gostatsd/main.go
@@ -120,7 +120,7 @@ func constructServer(v *viper.Viper) (*statsd.Server, error) {
 	backendNames := v.GetStringSlice(gostatsd.ParamBackends)
 	backendsList := make([]gostatsd.Backend, 0, len(backendNames))
 	for _, backendName := range backendNames {
-		backend, errBackend := backends.InitBackend(backendName, v, pool)
+		backend, errBackend := backends.InitBackend(backendName, v, logger, pool)
 		if errBackend != nil {
 			return nil, errBackend
 		}
diff --git a/pkg/backends/backends.go b/pkg/backends/backends.go
index 4233aec..10244e3 100644
--- a/pkg/backends/backends.go
+++ b/pkg/backends/backends.go
@@ -3,7 +3,7 @@ package backends
 import (
 	"fmt"
 
-	log "github.com/sirupsen/logrus"
+	"github.com/sirupsen/logrus"
 	"github.com/spf13/viper"
 
 	"github.com/atlassian/gostatsd"
@@ -31,29 +31,31 @@ var backends = map[string]gostatsd.BackendFactory{
 // GetBackend creates an instance of the named backend, or nil if
 // the name is not known. The error return is only used if the named backend
 // was known but failed to initialize.
-func GetBackend(name string, v *viper.Viper, pool *transport.TransportPool) (gostatsd.Backend, error) {
+func GetBackend(name string, v *viper.Viper, logger logrus.FieldLogger, pool *transport.TransportPool) (gostatsd.Backend, error) {
 	f, found := backends[name]
 	if !found {
 		return nil, nil
 	}
-	return f(v, pool)
+	return f(v, logger, pool)
 }
 
 // InitBackend creates an instance of the named backend.
-func InitBackend(name string, v *viper.Viper, pool *transport.TransportPool) (gostatsd.Backend, error) {
+func InitBackend(name string, v *viper.Viper, logger logrus.FieldLogger, pool *transport.TransportPool) (gostatsd.Backend, error) {
 	if name == "" {
-		log.Info("No backend specified")
+		logger.Info("No backend specified")
 		return nil, nil
 	}
 
-	backend, err := GetBackend(name, v, pool)
+	logger = logger.WithField("backend", name)
+
+	backend, err := GetBackend(name, v, logger, pool)
 	if err != nil {
 		return nil, fmt.Errorf("could not init backend %q: %v", name, err)
 	}
 	if backend == nil {
 		return nil, fmt.Errorf("unknown backend %q", name)
 	}
-	log.Infof("Initialised backend %q", name)
+	logger.Info("Initialised backend")
 
 	return backend, nil
 }
diff --git a/pkg/backends/cloudwatch/cloudwatch.go b/pkg/backends/cloudwatch/cloudwatch.go
index c4a72c5..e040b35 100644
--- a/pkg/backends/cloudwatch/cloudwatch.go
+++ b/pkg/backends/cloudwatch/cloudwatch.go
@@ -11,7 +11,7 @@ import (
 	"github.com/aws/aws-sdk-go/aws/session"
 	"github.com/aws/aws-sdk-go/service/cloudwatch"
 	"github.com/aws/aws-sdk-go/service/cloudwatch/cloudwatchiface"
-	log "github.com/sirupsen/logrus"
+	"github.com/sirupsen/logrus"
 	"github.com/spf13/viper"
 
 	"github.com/atlassian/gostatsd"
@@ -28,6 +28,8 @@ const BackendName = "cloudwatch"
 
 // Client is an object that is used to send messages to AWS CloudWatch.
 type Client struct {
+	logger logrus.FieldLogger
+
 	cloudwatch cloudwatchiface.CloudWatchAPI
 	namespace  string
 
@@ -35,7 +37,7 @@ type Client struct {
 }
 
 // NewClientFromViper constructs a Cloudwatch backend.
-func NewClientFromViper(v *viper.Viper, pool *transport.TransportPool) (gostatsd.Backend, error) {
+func NewClientFromViper(v *viper.Viper, logger logrus.FieldLogger, pool *transport.TransportPool) (gostatsd.Backend, error) {
 	g := util.GetSubViper(v, "cloudwatch")
 	g.SetDefault("namespace", "StatsD")
 	g.SetDefault("transport", "default")
@@ -44,12 +46,13 @@ func NewClientFromViper(v *viper.Viper, pool *transport.TransportPool) (gostatsd
 		g.GetString("namespace"),
 		g.GetString("transport"),
 		gostatsd.DisabledSubMetrics(v),
+		logger,
 		pool,
 	)
 }
 
 // NewClient constructs a AWS Cloudwatch backend.
-func NewClient(namespace, transport string, disabled gostatsd.TimerSubtypes, pool *transport.TransportPool) (*Client, error) {
+func NewClient(namespace, transport string, disabled gostatsd.TimerSubtypes, logger logrus.FieldLogger, pool *transport.TransportPool) (*Client, error) {
 	httpClient, err := pool.Get(transport)
 	if err != nil {
 		return nil, err
@@ -62,14 +65,16 @@ func NewClient(namespace, transport string, disabled gostatsd.TimerSubtypes, poo
 	}
 
 	return &Client{
+		logger: logger,
+
 		cloudwatch: cloudwatch.New(sess),
+		namespace:  namespace,
 
-		namespace:        namespace,
 		disabledSubtypes: disabled,
 	}, nil
 }
 
-func extractDimensions(tags gostatsd.Tags) (dimensions []*cloudwatch.Dimension) {
+func (client *Client) extractDimensions(tags gostatsd.Tags) (dimensions []*cloudwatch.Dimension) {
 	dimensions = []*cloudwatch.Dimension{}
 
 	for _, tag := range tags {
@@ -91,14 +96,17 @@ func extractDimensions(tags gostatsd.Tags) (dimensions []*cloudwatch.Dimension)
 	// Check that there are not too many dimensions
 	dimensionCount := len(dimensions)
 	if dimensionCount > MAX_DIMENSIONS {
-		log.Warnf("[%s] Too many dimensions (%d) specified, truncating to %d", BackendName, dimensionCount, MAX_DIMENSIONS)
+		client.logger.WithFields(logrus.Fields{
+			"count": dimensionCount,
+			"limit": MAX_DIMENSIONS,
+		}).Warn("too many dimensions, truncated")
 		return dimensions[:MAX_DIMENSIONS]
 	}
 
 	return dimensions
 }
 
-func (client Client) buildMetricData(metrics *gostatsd.MetricMap) (metricData []*cloudwatch.MetricDatum) {
+func (client *Client) buildMetricData(metrics *gostatsd.MetricMap) (metricData []*cloudwatch.MetricDatum) {
 	disabled := client.disabledSubtypes
 
 	metricData = []*cloudwatch.MetricDatum{}
@@ -106,7 +114,7 @@ func (client Client) buildMetricData(metrics *gostatsd.MetricMap) (metricData []
 	prefix := ""
 
 	addMetricData := func(key string, unit string, value float64, tags gostatsd.Tags) {
-		dimensions := extractDimensions(tags)
+		dimensions := client.extractDimensions(tags)
 		key = prefix + key
 
 		metricData = append(metricData, &cloudwatch.MetricDatum{
@@ -184,7 +192,7 @@ func (client Client) buildMetricData(metrics *gostatsd.MetricMap) (metricData []
 
 // SendMetricsAsync sends the metrics in a MetricsMap to AWS Cloudwatch,
 // preparing payload synchronously but doing the send asynchronously.
-func (client Client) SendMetricsAsync(ctx context.Context, metrics *gostatsd.MetricMap, cb gostatsd.SendCallback) {
+func (client *Client) SendMetricsAsync(ctx context.Context, metrics *gostatsd.MetricMap, cb gostatsd.SendCallback) {
 	api := client.cloudwatch
 	metricData := client.buildMetricData(metrics)
 	length := len(metricData)
@@ -229,7 +237,7 @@ func (client Client) SendMetricsAsync(ctx context.Context, metrics *gostatsd.Met
 }
 
 // Events currently not supported.
-func (client Client) SendEvent(ctx context.Context, e *gostatsd.Event) (retErr error) {
+func (client *Client) SendEvent(ctx context.Context, e *gostatsd.Event) (retErr error) {
 	return nil
 }
 
diff --git a/pkg/backends/datadog/datadog.go b/pkg/backends/datadog/datadog.go
index 01d62e6..52bd2f1 100644
--- a/pkg/backends/datadog/datadog.go
+++ b/pkg/backends/datadog/datadog.go
@@ -18,7 +18,7 @@ import (
 
 	"github.com/cenkalti/backoff"
 	jsoniter "github.com/json-iterator/go"
-	log "github.com/sirupsen/logrus"
+	"github.com/sirupsen/logrus"
 	"github.com/spf13/viper"
 	"github.com/tilinna/clock"
 
@@ -38,7 +38,7 @@ const (
 	// defaultMetricsPerBatch is the default number of metrics to send in a single batch.
 	defaultMetricsPerBatch = 1000
 	// maxResponseSize is the maximum response size we are willing to read.
-	maxResponseSize     = 10 * 1024
+	maxResponseSize     = 1024
 	maxConcurrentEvents = 20
 )
 
@@ -62,6 +62,7 @@ type Client struct {
 	batchesSent    uint64 // Accumulated number of batches successfully sent
 	seriesSent     uint64 // Accumulated number of series successfully sent
 
+	logger                logrus.FieldLogger
 	apiKey                string
 	apiEndpoint           string
 	userAgent             string
@@ -291,7 +292,11 @@ func (d *Client) post(ctx context.Context, buffer *bytes.Buffer, path, typeOfPos
 			return fmt.Errorf("[%s] %v", BackendName, err)
 		}
 
-		log.Warnf("[%s] failed to send %s, sleeping for %s: %v", BackendName, typeOfPost, next, err)
+		d.logger.WithFields(logrus.Fields{
+			"type":  typeOfPost,
+			"sleep": next,
+			"error": err,
+		}).Warn("failed to send")
 
 		timer := clck.NewTimer(next)
 		select {
@@ -352,7 +357,10 @@ func (d *Client) constructPost(ctx context.Context, buffer *bytes.Buffer, path,
 		body := io.LimitReader(resp.Body, maxResponseSize)
 		if resp.StatusCode < http.StatusOK || resp.StatusCode > http.StatusNoContent {
 			b, _ := ioutil.ReadAll(body)
-			log.Infof("[%s] failed request status: %d\n%s", BackendName, resp.StatusCode, b)
+			d.logger.WithFields(logrus.Fields{
+				"status": resp.StatusCode,
+				"body":   string(b),
+			}).Info("request failed")
 			return fmt.Errorf("received bad status code %d", resp.StatusCode)
 		}
 		_, _ = io.Copy(ioutil.Discard, body)
@@ -368,7 +376,7 @@ func (d *Client) authenticatedURL(path string) string {
 }
 
 // NewClientFromViper returns a new Datadog API client.
-func NewClientFromViper(v *viper.Viper, pool *transport.TransportPool) (gostatsd.Backend, error) {
+func NewClientFromViper(v *viper.Viper, logger logrus.FieldLogger, pool *transport.TransportPool) (gostatsd.Backend, error) {
 	dd := util.GetSubViper(v, "datadog")
 	dd.SetDefault("api_endpoint", apiURL)
 	dd.SetDefault("metrics_per_batch", defaultMetricsPerBatch)
@@ -389,6 +397,7 @@ func NewClientFromViper(v *viper.Viper, pool *transport.TransportPool) (gostatsd
 		dd.GetDuration("max_request_elapsed_time"),
 		v.GetDuration("flush-interval"), // Main viper, not sub-viper
 		gostatsd.DisabledSubMetrics(v),
+		logger,
 		pool,
 	)
 }
@@ -405,6 +414,7 @@ func NewClient(
 	maxRequestElapsedTime,
 	flushInterval time.Duration,
 	disabled gostatsd.TimerSubtypes,
+	logger logrus.FieldLogger,
 	pool *transport.TransportPool,
 ) (*Client, error) {
 	if apiEndpoint == "" {
@@ -423,13 +433,12 @@ func NewClient(
 		return nil, fmt.Errorf("[%s] maxRequestElapsedTime must be positive", BackendName)
 	}
 
-	logger := log.WithField("backend", BackendName)
 	httpClient, err := pool.Get(transport)
 	if err != nil {
 		logger.WithError(err).Error("failed to create http client")
 		return nil, err
 	}
-	logger.WithFields(log.Fields{
+	logger.WithFields(logrus.Fields{
 		"max-request-elapsed-time": maxRequestElapsedTime,
 		"max-requests":             maxRequests,
 		"metrics-per-batch":        metricsPerBatch,
@@ -445,6 +454,7 @@ func NewClient(
 		eventsBufferSem <- &bytes.Buffer{}
 	}
 	return &Client{
+		logger:                logger,
 		apiKey:                apiKey,
 		apiEndpoint:           apiEndpoint,
 		userAgent:             userAgent,
diff --git a/pkg/backends/graphite/graphite.go b/pkg/backends/graphite/graphite.go
index b4de9ee..34a2720 100644
--- a/pkg/backends/graphite/graphite.go
+++ b/pkg/backends/graphite/graphite.go
@@ -12,7 +12,7 @@ import (
 	"sync"
 	"time"
 
-	log "github.com/sirupsen/logrus"
+	"github.com/sirupsen/logrus"
 	"github.com/spf13/viper"
 
 	"github.com/atlassian/gostatsd"
@@ -223,7 +223,7 @@ func (client *Client) Name() string {
 }
 
 // NewClientFromViper constructs a Client object using configuration provided by Viper
-func NewClientFromViper(v *viper.Viper, pool *transport.TransportPool) (gostatsd.Backend, error) {
+func NewClientFromViper(v *viper.Viper, logger logrus.FieldLogger, pool *transport.TransportPool) (gostatsd.Backend, error) {
 	g := util.GetSubViper(v, "graphite")
 	g.SetDefault("address", DefaultAddress)
 	g.SetDefault("dial_timeout", DefaultDialTimeout)
@@ -247,6 +247,7 @@ func NewClientFromViper(v *viper.Viper, pool *transport.TransportPool) (gostatsd
 		g.GetString("global_suffix"),
 		g.GetString("mode"),
 		gostatsd.DisabledSubMetrics(v),
+		logger,
 	)
 }
 
@@ -263,6 +264,7 @@ func NewClient(
 	globalSuffix string,
 	mode string,
 	disabled gostatsd.TimerSubtypes,
+	logger logrus.FieldLogger,
 ) (*Client, error) {
 	if address == "" {
 		return nil, fmt.Errorf("[%s] address is required", BackendName)
@@ -311,21 +313,21 @@ func NewClient(
 	setsNamespace = normalizeMetricName(setsNamespace)
 	globalSuffix = normalizeMetricName(globalSuffix)
 
-	log.Infof("[%s] address=%s dialTimeout=%s writeTimeout=%s counterNamespace=%s timerNamespace=%s gaugesNamespace=%s setsNamespace=%s globalSuffix=%s mode=%s",
-		BackendName,
-		address,
-		dialTimeout,
-		writeTimeout,
-		counterNamespace,
-		timerNamespace,
-		gaugesNamespace,
-		setsNamespace,
-		globalSuffix,
-		mode,
-	)
+	logger.WithFields(logrus.Fields{
+		"address":           address,
+		"dial-timeout":      dialTimeout,
+		"write-timeout":     writeTimeout,
+		"counter-namespace": counterNamespace,
+		"timer-namespace":   timerNamespace,
+		"gauges-namespace":  gaugesNamespace,
+		"sets-namespace":    setsNamespace,
+		"global-suffix":     globalSuffix,
+		"mode":              mode,
+	}).Info("created backend")
 
 	return &Client{
 		sender: sender.Sender{
+			Logger: logger,
 			ConnFactory: func() (net.Conn, error) {
 				return net.DialTimeout("tcp", address, dialTimeout)
 			},
diff --git a/pkg/backends/newrelic/newrelic.go b/pkg/backends/newrelic/newrelic.go
index fd9f364..e33346a 100644
--- a/pkg/backends/newrelic/newrelic.go
+++ b/pkg/backends/newrelic/newrelic.go
@@ -17,7 +17,7 @@ import (
 	"time"
 
 	"github.com/cenkalti/backoff"
-	log "github.com/sirupsen/logrus"
+	"github.com/sirupsen/logrus"
 	"github.com/spf13/viper"
 	"github.com/tilinna/clock"
 
@@ -38,7 +38,7 @@ const (
 	// defaultMetricsPerBatch is the default number of metrics to send in a single batch.
 	defaultMetricsPerBatch = 1000
 	// maxResponseSize is the maximum response size we are willing to read.
-	maxResponseSize = 10 * 1024
+	maxResponseSize = 1024
 
 	flushTypeInsights = "insights"
 	flushTypeInfra    = "infra"
@@ -56,6 +56,7 @@ var (
 
 // Client represents a New Relic client.
 type Client struct {
+	logger         logrus.FieldLogger
 	address        string
 	addressMetrics string
 	eventType      string
@@ -314,7 +315,10 @@ func (n *Client) post(ctx context.Context, buffer *bytes.Buffer, data interface{
 			return fmt.Errorf("[%s] %v", BackendName, err)
 		}
 
-		log.Warnf("[%s] failed to send, sleeping for %s: %v", BackendName, next, err)
+		n.logger.WithFields(logrus.Fields{
+			"sleep": next,
+			"error": err,
+		}).Warn("failed to send, sleeping")
 
 		timer := clck.NewTimer(next)
 		select {
@@ -403,10 +407,10 @@ func (n *Client) postWrapper(ctx context.Context, json []byte, dataType string)
 		body := io.LimitReader(resp.Body, maxResponseSize)
 		if resp.StatusCode < http.StatusOK || resp.StatusCode > http.StatusNoContent {
 			b, _ := ioutil.ReadAll(body)
-			log.WithFields(log.Fields{
+			n.logger.WithFields(logrus.Fields{
 				"status": resp.StatusCode,
-				"body":   b,
-			}).Infof("[%s] failed request", BackendName)
+				"body":   string(b),
+			}).Info("request failed")
 			return fmt.Errorf("received bad status code %d", resp.StatusCode)
 		}
 		_, _ = io.Copy(ioutil.Discard, body)
@@ -416,7 +420,7 @@ func (n *Client) postWrapper(ctx context.Context, json []byte, dataType string)
 }
 
 // NewClientFromViper returns a new New Relic client.
-func NewClientFromViper(v *viper.Viper, pool *transport.TransportPool) (gostatsd.Backend, error) {
+func NewClientFromViper(v *viper.Viper, logger logrus.FieldLogger, pool *transport.TransportPool) (gostatsd.Backend, error) {
 	nr := util.GetSubViper(v, "newrelic")
 	nr.SetDefault("transport", "default")
 	nr.SetDefault("address", "http://localhost:8001/v1/data")
@@ -454,7 +458,7 @@ func NewClientFromViper(v *viper.Viper, pool *transport.TransportPool) (gostatsd
 	v.SetDefault("statser-type", "null")
 	v.SetDefault("flush-interval", "10s")
 	if v.GetString("statser-type") == "null" {
-		log.Infof("[%s] internal metrics OFF, to enable set 'statser-type' to 'logging' or 'internal'", BackendName)
+		logger.Info("internal metrics OFF, to enable set 'statser-type' to 'logging' or 'internal'")
 	}
 
 	return NewClient(
@@ -483,6 +487,7 @@ func NewClientFromViper(v *viper.Viper, pool *transport.TransportPool) (gostatsd
 		nr.GetDuration("max-request-elapsed-time"),
 		v.GetDuration("flush-interval"), // Main viper, not sub-viper
 		gostatsd.DisabledSubMetrics(v),
+		logger,
 		pool,
 	)
 }
@@ -493,9 +498,7 @@ func NewClient(transport, address, addressMetrics, eventType, flushType, apiKey,
 	timerMin, timerMax, timerCount, timerMean, timerMedian, timerStdDev, timerSum, timerSumSquares,
 	userAgent string, metricsPerBatch int, maxRequests uint,
 	maxRequestElapsedTime, flushInterval time.Duration,
-	disabled gostatsd.TimerSubtypes, pool *transport.TransportPool) (*Client, error) {
-
-	logger := log.WithField("backend", BackendName)
+	disabled gostatsd.TimerSubtypes, logger logrus.FieldLogger, pool *transport.TransportPool) (*Client, error) {
 
 	if metricsPerBatch <= 0 {
 		return nil, fmt.Errorf("[%s] metricsPerBatch must be positive", BackendName)
@@ -523,7 +526,7 @@ func NewClient(transport, address, addressMetrics, eventType, flushType, apiKey,
 		logger.WithError(err).Error("failed to create http client")
 		return nil, err
 	}
-	logger.WithFields(log.Fields{
+	logger.WithFields(logrus.Fields{
 		"max-request-elapsed-time": maxRequestElapsedTime,
 		"max-requests":             maxRequests,
 		"metrics-per-batch":        metricsPerBatch,
@@ -535,6 +538,7 @@ func NewClient(transport, address, addressMetrics, eventType, flushType, apiKey,
 		metricsBufferSem <- &bytes.Buffer{}
 	}
 	return &Client{
+		logger:                logger,
 		address:               address,
 		addressMetrics:        addressMetrics,
 		eventType:             eventType,
diff --git a/pkg/backends/null/null.go b/pkg/backends/null/null.go
index 9540b79..f295dd0 100644
--- a/pkg/backends/null/null.go
+++ b/pkg/backends/null/null.go
@@ -3,6 +3,7 @@ package null
 import (
 	"context"
 
+	"github.com/sirupsen/logrus"
 	"github.com/spf13/viper"
 
 	"github.com/atlassian/gostatsd"
@@ -16,7 +17,7 @@ const BackendName = "null"
 type Client struct{}
 
 // NewClientFromViper constructs a GraphiteClient object by connecting to an address.
-func NewClientFromViper(v *viper.Viper, pool *transport.TransportPool) (gostatsd.Backend, error) {
+func NewClientFromViper(v *viper.Viper, logger logrus.FieldLogger, pool *transport.TransportPool) (gostatsd.Backend, error) {
 	return NewClient()
 }
 
diff --git a/pkg/backends/sender/sender.go b/pkg/backends/sender/sender.go
index 3f29059..430daf5 100644
--- a/pkg/backends/sender/sender.go
+++ b/pkg/backends/sender/sender.go
@@ -7,7 +7,7 @@ import (
 	"sync"
 	"time"
 
-	log "github.com/sirupsen/logrus"
+	"github.com/sirupsen/logrus"
 
 	"github.com/atlassian/gostatsd"
 )
@@ -23,6 +23,8 @@ type Stream struct {
 }
 
 type Sender struct {
+	Logger logrus.FieldLogger
+
 	ConnFactory  ConnFactory
 	Sink         chan Stream
 	BufPool      sync.Pool
@@ -43,7 +45,7 @@ func (s *Sender) Run(ctx context.Context) {
 	for {
 		w, err := s.ConnFactory()
 		if err != nil {
-			log.Warnf("Failed to connect: %v", err)
+			s.Logger.WithError(err).Warn("failed to connect")
 			// TODO do backoff
 			timer := time.NewTimer(1 * time.Second)
 			for {
@@ -85,7 +87,7 @@ func (s *Sender) Run(ctx context.Context) {
 func (s *Sender) innerRun(ctx context.Context, conn net.Conn, stream *Stream, errs []error) (*Stream, []error, error) {
 	defer func() {
 		if err := conn.Close(); err != nil {
-			log.Warnf("Close failed: %v", err)
+			s.Logger.WithError(err).Warn("close failed")
 		}
 	}()
 	var err error
@@ -103,7 +105,7 @@ loop:
 		for buf := range stream.Buf {
 			if s.WriteTimeout > 0 {
 				if e := conn.SetWriteDeadline(time.Now().Add(s.WriteTimeout)); e != nil {
-					log.Warnf("Failed to set write deadline: %v", e)
+					s.Logger.WithError(e).Warn("failed to set write deadline")
 				}
 			}
 			_, err = conn.Write(buf.Bytes())
diff --git a/pkg/backends/statsdaemon/statsdaemon.go b/pkg/backends/statsdaemon/statsdaemon.go
index 773f2a0..c02cea9 100644
--- a/pkg/backends/statsdaemon/statsdaemon.go
+++ b/pkg/backends/statsdaemon/statsdaemon.go
@@ -11,7 +11,7 @@ import (
 	"sync"
 	"time"
 
-	log "github.com/sirupsen/logrus"
+	"github.com/sirupsen/logrus"
 	"github.com/spf13/viper"
 
 	"github.com/atlassian/gostatsd"
@@ -197,7 +197,7 @@ func constructEventMessage(e *gostatsd.Event) *bytes.Buffer {
 }
 
 // NewClient constructs a new statsd backend client.
-func NewClient(address string, dialTimeout, writeTimeout time.Duration, disableTags, tcpTransport bool, tlsConfig *tls.Config) (*Client, error) {
+func NewClient(address string, dialTimeout, writeTimeout time.Duration, disableTags, tcpTransport bool, tlsConfig *tls.Config, logger logrus.FieldLogger) (*Client, error) {
 	if address == "" {
 		return nil, fmt.Errorf("[%s] address is required", BackendName)
 	}
@@ -207,7 +207,12 @@ func NewClient(address string, dialTimeout, writeTimeout time.Duration, disableT
 	if writeTimeout < 0 {
 		return nil, fmt.Errorf("[%s] writeTimeout should be non-negative", BackendName)
 	}
-	log.Infof("[%s] address=%s dialTimeout=%s writeTimeout=%s", BackendName, address, dialTimeout, writeTimeout)
+	logger.WithFields(logrus.Fields{
+		"address":       address,
+		"dial-timeout":  dialTimeout,
+		"write-timeout": writeTimeout,
+	}).Info("created backend")
+
 	var packetSize int
 	var connFactory func() (net.Conn, error)
 
@@ -237,6 +242,7 @@ func NewClient(address string, dialTimeout, writeTimeout time.Duration, disableT
 		packetSize:  packetSize,
 		disableTags: disableTags,
 		sender: sender.Sender{
+			Logger:      logger,
 			ConnFactory: connFactory,
 			Sink:        make(chan sender.Stream, maxConcurrentSends),
 			BufPool: sync.Pool{
@@ -252,7 +258,7 @@ func NewClient(address string, dialTimeout, writeTimeout time.Duration, disableT
 }
 
 // NewClientFromViper constructs a statsd client by connecting to an address.
-func NewClientFromViper(v *viper.Viper, pool *transport.TransportPool) (gostatsd.Backend, error) {
+func NewClientFromViper(v *viper.Viper, logger logrus.FieldLogger, pool *transport.TransportPool) (gostatsd.Backend, error) {
 	g := util.GetSubViper(v, "statsdaemon")
 	g.SetDefault("dial_timeout", DefaultDialTimeout)
 	g.SetDefault("write_timeout", DefaultWriteTimeout)
@@ -274,6 +280,7 @@ func NewClientFromViper(v *viper.Viper, pool *transport.TransportPool) (gostatsd
 		g.GetBool("disable_tags"),
 		g.GetBool("tcp_transport"),
 		maybeTLSConfig,
+		logger,
 	)
 }
 
diff --git a/pkg/backends/stdout/stdout.go b/pkg/backends/stdout/stdout.go
index 5655820..575646d 100644
--- a/pkg/backends/stdout/stdout.go
+++ b/pkg/backends/stdout/stdout.go
@@ -9,7 +9,7 @@ import (
 	"strings"
 	"time"
 
-	log "github.com/sirupsen/logrus"
+	"github.com/sirupsen/logrus"
 	"github.com/spf13/viper"
 
 	"github.com/atlassian/gostatsd"
@@ -25,7 +25,7 @@ type Client struct {
 }
 
 // NewClientFromViper constructs a stdout backend.
-func NewClientFromViper(v *viper.Viper, pool *transport.TransportPool) (gostatsd.Backend, error) {
+func NewClientFromViper(v *viper.Viper, logger logrus.FieldLogger, pool *transport.TransportPool) (gostatsd.Backend, error) {
 	return NewClient(
 		gostatsd.DisabledSubMetrics(v),
 	)
@@ -63,7 +63,7 @@ func (client Client) SendMetricsAsync(ctx context.Context, metrics *gostatsd.Met
 }
 
 func writePayload(buf *bytes.Buffer) (retErr error) {
-	writer := log.StandardLogger().Writer()
+	writer := logrus.StandardLogger().Writer()
 	defer func() {
 		if err := writer.Close(); err != nil && retErr == nil {
 			retErr = err
@@ -138,7 +138,7 @@ func preparePayload(metrics *gostatsd.MetricMap, disabled *gostatsd.TimerSubtype
 
 // SendEvent prints events to the stdout.
 func (client Client) SendEvent(ctx context.Context, e *gostatsd.Event) (retErr error) {
-	writer := log.StandardLogger().Writer()
+	writer := logrus.StandardLogger().Writer()
 	defer func() {
 		if err := writer.Close(); err != nil && retErr == nil {
 			retErr = err
diff --git a/pkg/stats/statser_logging.go b/pkg/stats/statser_logging.go
index 981ab45..928b440 100644
--- a/pkg/stats/statser_logging.go
+++ b/pkg/stats/statser_logging.go
@@ -3,7 +3,7 @@ package stats
 import (
 	"time"
 
-	log "github.com/sirupsen/logrus"
+	"github.com/sirupsen/logrus"
 
 	"github.com/atlassian/gostatsd"
 )
@@ -13,12 +13,12 @@ type LoggingStatser struct {
 	flushNotifier
 
 	tags   gostatsd.Tags
-	logger *log.Entry
+	logger logrus.FieldLogger
 }
 
 // NewLoggingStatser creates a new Statser which sends metrics to the
-// supplied log.Entry
-func NewLoggingStatser(tags gostatsd.Tags, logger *log.Entry) Statser {
+// supplied logger.
+func NewLoggingStatser(tags gostatsd.Tags, logger logrus.FieldLogger) Statser {
 	return &LoggingStatser{
 		tags:   tags,
 		logger: logger,
@@ -27,7 +27,7 @@ func NewLoggingStatser(tags gostatsd.Tags, logger *log.Entry) Statser {
 
 // Gauge sends a gauge metric
 func (ls *LoggingStatser) Gauge(name string, value float64, tags gostatsd.Tags) {
-	ls.logger.WithFields(log.Fields{
+	ls.logger.WithFields(logrus.Fields{
 		"name":  name,
 		"tags":  ls.tags.Concat(tags),
 		"value": value,
@@ -36,7 +36,7 @@ func (ls *LoggingStatser) Gauge(name string, value float64, tags gostatsd.Tags)
 
 // Count sends a counter metric
 func (ls *LoggingStatser) Count(name string, amount float64, tags gostatsd.Tags) {
-	ls.logger.WithFields(log.Fields{
+	ls.logger.WithFields(logrus.Fields{
 		"name":   name,
 		"tags":   ls.tags.Concat(tags),
 		"amount": amount,
@@ -45,7 +45,7 @@ func (ls *LoggingStatser) Count(name string, amount float64, tags gostatsd.Tags)
 
 // Increment sends a counter metric with a value of 1
 func (ls *LoggingStatser) Increment(name string, tags gostatsd.Tags) {
-	ls.logger.WithFields(log.Fields{
+	ls.logger.WithFields(logrus.Fields{
 		"name": name,
 		"tags": ls.tags.Concat(tags),
 	}).Infof("increment")
@@ -53,7 +53,7 @@ func (ls *LoggingStatser) Increment(name string, tags gostatsd.Tags) {
 
 // TimingMS sends a timing metric from a millisecond value
 func (ls *LoggingStatser) TimingMS(name string, ms float64, tags gostatsd.Tags) {
-	ls.logger.WithFields(log.Fields{
+	ls.logger.WithFields(logrus.Fields{
 		"name": name,
 		"tags": ls.tags.Concat(tags),
 		"ms":   ms,
diff --git a/pkg/statsd/flusher.go b/pkg/statsd/flusher.go
index c49a1c4..42dd6e6 100644
--- a/pkg/statsd/flusher.go
+++ b/pkg/statsd/flusher.go
@@ -7,7 +7,7 @@ import (
 	"sync/atomic"
 	"time"
 
-	log "github.com/sirupsen/logrus"
+	"github.com/sirupsen/logrus"
 
 	"github.com/atlassian/gostatsd"
 	"github.com/atlassian/gostatsd/pkg/stats"
@@ -100,7 +100,7 @@ func (f *MetricFlusher) handleSendResult(flushResults []error) {
 		if err != nil {
 			timestampPointer = &f.lastFlushError
 			if err != context.DeadlineExceeded && err != context.Canceled {
-				log.Errorf("Sending metrics to backend failed: %v", err)
+				logrus.WithError(err).Error("Sending metrics to backend failed")
 			}
 		}
 	}
diff --git a/pkg/statsd/parser.go b/pkg/statsd/parser.go
index 3b801d1..7fff7c0 100644
--- a/pkg/statsd/parser.go
+++ b/pkg/statsd/parser.go
@@ -8,7 +8,7 @@ import (
 	"sync/atomic"
 	"time"
 
-	log "github.com/sirupsen/logrus"
+	"github.com/sirupsen/logrus"
 	"golang.org/x/time/rate"
 
 	"github.com/atlassian/gostatsd"
@@ -29,6 +29,8 @@ type DatagramParser struct {
 	metricsReceived uint64
 	eventsReceived  uint64
 
+	logger logrus.FieldLogger
+
 	ignoreHost bool
 	handler    gostatsd.PipelineHandler
 	namespace  string // Namespace to prefix all metrics
@@ -45,13 +47,23 @@ type DatagramParser struct {
 }
 
 // NewDatagramParser initialises a new DatagramParser.
-func NewDatagramParser(in <-chan []*Datagram, ns string, ignoreHost bool, estimatedTags int, handler gostatsd.PipelineHandler, badLineRateLimitPerSecond rate.Limit, logRawMetric bool) *DatagramParser {
+func NewDatagramParser(
+	in <-chan []*Datagram,
+	ns string,
+	ignoreHost bool,
+	estimatedTags int,
+	handler gostatsd.PipelineHandler,
+	badLineRateLimitPerSecond rate.Limit,
+	logRawMetric bool,
+	logger logrus.FieldLogger,
+) *DatagramParser {
 	limiter := &rate.Limiter{}
 	if badLineRateLimitPerSecond > 0 {
 		limiter = rate.NewLimiter(badLineRateLimitPerSecond, 1)
 	}
 
 	return &DatagramParser{
+		logger:         logger,
 		in:             in,
 		ignoreHost:     ignoreHost,
 		handler:        handler,
@@ -113,7 +125,11 @@ func (dp *DatagramParser) Run(ctx context.Context) {
 // logBadLineRateLimited will log a line which failed to decode, if the current rate limit has not been exceeded.
 func (dp *DatagramParser) logBadLineRateLimited(line []byte, ip gostatsd.IP, err error) {
 	if dp.badLineLimiter.Allow() {
-		log.Infof("Error parsing line %q from %s: %v", line, ip, err)
+		logrus.WithFields(logrus.Fields{
+			"line":  string(line),
+			"ip":    ip,
+			"error": err,
+		}).Info("error parsing line")
 	}
 }
 
@@ -170,7 +186,7 @@ func (dp *DatagramParser) handleDatagram(ctx context.Context, now gostatsd.Nanot
 			dp.handler.DispatchEvent(ctx, event)
 		} else {
 			// Should never happen.
-			log.Panic("Both event and metric are nil")
+			dp.logger.Panic("Both event and metric are nil")
 		}
 	}
 	return metrics, numEvents, numBad
@@ -193,13 +209,12 @@ func (dp *DatagramParser) initLogRawMetric(ctx context.Context) {
 				// The `StandardLogger` returns the singleton log instance which is used elsewhere in the application.
 				// And the `log` package makes sure that the logging interface are thread-safe so that we wouldn't
 				// mess up the contents of stdout/stderr.
-				lg := log.StandardLogger()
 				for {
 					select {
 					case <-ctx.Done():
 						return
 					case metrics := <-dp.logRawMetricChan:
-						lg.Info(metrics)
+						dp.logger.Info(metrics)
 					}
 				}
 			}()
diff --git a/pkg/statsd/statsd.go b/pkg/statsd/statsd.go
index f1d864a..2f30be3 100644
--- a/pkg/statsd/statsd.go
+++ b/pkg/statsd/statsd.go
@@ -9,7 +9,7 @@ import (
 
 	"github.com/ash2k/stager"
 	"github.com/libp2p/go-reuseport"
-	log "github.com/sirupsen/logrus"
+	"github.com/sirupsen/logrus"
 	"github.com/spf13/viper"
 	"golang.org/x/time/rate"
 
@@ -112,9 +112,9 @@ func (s *Server) createStandaloneSink() (gostatsd.PipelineHandler, []gostatsd.Ru
 	return backendHandler, runnables, nil
 }
 
-func (s *Server) createForwarderSink() (gostatsd.PipelineHandler, []gostatsd.Runnable, error) {
+func (s *Server) createForwarderSink(logger logrus.FieldLogger) (gostatsd.PipelineHandler, []gostatsd.Runnable, error) {
 	forwarderHandler, err := NewHttpForwarderHandlerV2FromViper(
-		log.StandardLogger(),
+		logger,
 		s.Viper,
 		s.TransportPool,
 	)
@@ -128,11 +128,11 @@ func (s *Server) createForwarderSink() (gostatsd.PipelineHandler, []gostatsd.Run
 	return forwarderHandler, []gostatsd.Runnable{forwarderHandler.Run, forwarderHandler.RunMetricsContext, flusher.Run}, nil
 }
 
-func (s *Server) createFinalSink() (gostatsd.PipelineHandler, []gostatsd.Runnable, error) {
+func (s *Server) createFinalSink(logger logrus.FieldLogger) (gostatsd.PipelineHandler, []gostatsd.Runnable, error) {
 	if s.ServerMode == "standalone" {
 		return s.createStandaloneSink()
 	} else if s.ServerMode == "forwarder" {
-		return s.createForwarderSink()
+		return s.createForwarderSink(logger)
 	}
 	return nil, nil, errors.New("invalid server-mode, must be standalone, or forwarder")
 }
@@ -140,7 +140,9 @@ func (s *Server) createFinalSink() (gostatsd.PipelineHandler, []gostatsd.Runnabl
 // RunWithCustomSocket runs the server until context signals done.
 // Listening socket is created using sf.
 func (s *Server) RunWithCustomSocket(ctx context.Context, sf SocketFactory) error {
-	handler, runnables, err := s.createFinalSink()
+	logger := logrus.StandardLogger()
+
+	handler, runnables, err := s.createFinalSink(logger)
 	if err != nil {
 		return err
 	}
@@ -167,7 +169,7 @@ func (s *Server) RunWithCustomSocket(ctx context.Context, sf SocketFactory) erro
 	datagrams := make(chan []*Datagram)
 
 	// Create the Parser
-	parser := NewDatagramParser(datagrams, s.Namespace, s.IgnoreHost, s.EstimatedTags, handler, s.BadLineRateLimitPerSecond, s.LogRawMetric)
+	parser := NewDatagramParser(datagrams, s.Namespace, s.IgnoreHost, s.EstimatedTags, handler, s.BadLineRateLimitPerSecond, s.LogRawMetric, logger)
 	runnables = append(runnables, parser.RunMetricsContext)
 	for i := 0; i < s.MaxParsers; i++ {
 		runnables = append(runnables, parser.Run)
@@ -179,11 +181,11 @@ func (s *Server) RunWithCustomSocket(ctx context.Context, sf SocketFactory) erro
 
 	// Create the Statser
 	hostname := s.Hostname
-	statser := s.createStatser(hostname, handler)
+	statser := s.createStatser(hostname, handler, logger)
 	runnables = gostatsd.MaybeAppendRunnable(runnables, statser)
 
 	// Create any http servers
-	httpServers, err := web.NewHttpServersFromViper(s.Viper, log.StandardLogger(), handler)
+	httpServers, err := web.NewHttpServersFromViper(s.Viper, logger, handler)
 	if err != nil {
 		return err
 	}
@@ -209,12 +211,12 @@ func (s *Server) RunWithCustomSocket(ctx context.Context, sf SocketFactory) erro
 	return ctx.Err()
 }
 
-func (s *Server) createStatser(hostname string, handler gostatsd.PipelineHandler) stats.Statser {
+func (s *Server) createStatser(hostname string, handler gostatsd.PipelineHandler, logger logrus.FieldLogger) stats.Statser {
 	switch s.StatserType {
 	case gostatsd.StatserNull:
 		return stats.NewNullStatser()
 	case gostatsd.StatserLogging:
-		return stats.NewLoggingStatser(s.InternalTags, log.NewEntry(log.New()))
+		return stats.NewLoggingStatser(s.InternalTags, logger)
 	default:
 		namespace := s.Namespace
 		if s.InternalNamespace != "" {


Test patch:
diff --git a/pkg/backends/cloudwatch/cloudwatch_test.go b/pkg/backends/cloudwatch/cloudwatch_test.go
index 290e6e2..cef7f46 100644
--- a/pkg/backends/cloudwatch/cloudwatch_test.go
+++ b/pkg/backends/cloudwatch/cloudwatch_test.go
@@ -30,7 +30,7 @@ func TestSendMetrics(t *testing.T) {
 	t.Parallel()
 
 	p := transport.NewTransportPool(logrus.New(), viper.New())
-	cli, err := NewClient("ns", "default", gostatsd.TimerSubtypes{}, p)
+	cli, err := NewClient("ns", "default", gostatsd.TimerSubtypes{}, logrus.New(), p)
 	require.NoError(t, err)
 
 	expected := []struct {
@@ -87,7 +87,7 @@ func TestSendMetricDimensions(t *testing.T) {
 	t.Parallel()
 
 	p := transport.NewTransportPool(logrus.New(), viper.New())
-	cli, err := NewClient("ns", "default", gostatsd.TimerSubtypes{}, p)
+	cli, err := NewClient("ns", "default", gostatsd.TimerSubtypes{}, logrus.New(), p)
 	require.NoError(t, err)
 
 	metricMap := &gostatsd.MetricMap{
@@ -193,7 +193,7 @@ func TestSendHistogram(t *testing.T) {
 	t.Parallel()
 
 	p := transport.NewTransportPool(logrus.New(), viper.New())
-	cli, err := NewClient("ns", "default", gostatsd.TimerSubtypes{}, p)
+	cli, err := NewClient("ns", "default", gostatsd.TimerSubtypes{}, logrus.New(), p)
 	require.NoError(t, err)
 
 	metricMap := &gostatsd.MetricMap{
diff --git a/pkg/backends/datadog/datadog_test.go b/pkg/backends/datadog/datadog_test.go
index 995f9b7..13eae10 100644
--- a/pkg/backends/datadog/datadog_test.go
+++ b/pkg/backends/datadog/datadog_test.go
@@ -57,7 +57,7 @@ func TestRetries(t *testing.T) {
 	v := viper.New()
 	v.Set("transport.default.client-timeout", 1*time.Second)
 	p := transport.NewTransportPool(logrus.New(), v)
-	client, err := NewClient(ts.URL, "apiKey123", "agent", "default", defaultMetricsPerBatch, defaultMaxRequests, true, 2*time.Second, 1*time.Second, gostatsd.TimerSubtypes{}, p)
+	client, err := NewClient(ts.URL, "apiKey123", "agent", "default", defaultMetricsPerBatch, defaultMaxRequests, true, 2*time.Second, 1*time.Second, gostatsd.TimerSubtypes{}, logrus.New(), p)
 	require.NoError(t, err)
 	res := make(chan []error, 1)
 	clck := clock.NewMock(time.Unix(0, 0))
@@ -94,7 +94,7 @@ func TestSendMetricsInMultipleBatches(t *testing.T) {
 	v := viper.New()
 	v.Set("transport.default.client-timeout", 1*time.Second)
 	p := transport.NewTransportPool(logrus.New(), v)
-	client, err := NewClient(ts.URL, "apiKey123", "agent", "default", 1, defaultMaxRequests, true, 2*time.Second, 1*time.Second, gostatsd.TimerSubtypes{}, p)
+	client, err := NewClient(ts.URL, "apiKey123", "agent", "default", 1, defaultMaxRequests, true, 2*time.Second, 1*time.Second, gostatsd.TimerSubtypes{}, logrus.New(), p)
 	require.NoError(t, err)
 	res := make(chan []error, 1)
 	client.SendMetricsAsync(context.Background(), twoCounters(), func(errs []error) {
@@ -147,7 +147,7 @@ func TestSendMetrics(t *testing.T) {
 	v := viper.New()
 	v.Set("transport.default.client-timeout", 1*time.Second)
 	p := transport.NewTransportPool(logrus.New(), v)
-	cli, err := NewClient(ts.URL, "apiKey123", "agent", "default", 1000, defaultMaxRequests, true, 2*time.Second, 1100*time.Millisecond, gostatsd.TimerSubtypes{}, p)
+	cli, err := NewClient(ts.URL, "apiKey123", "agent", "default", 1000, defaultMaxRequests, true, 2*time.Second, 1100*time.Millisecond, gostatsd.TimerSubtypes{}, logrus.New(), p)
 	require.NoError(t, err)
 
 	c := clock.NewMock(time.Unix(100, 0))
@@ -265,7 +265,7 @@ func TestSendHistogram(t *testing.T) {
 	v := viper.New()
 	v.Set("transport.default.client-timeout", 1*time.Second)
 	p := transport.NewTransportPool(logrus.New(), v)
-	client, err := NewClient(ts.URL, "apiKey123", "agent", "default", 1000, defaultMaxRequests, true, 2*time.Second, 1100*time.Millisecond, gostatsd.TimerSubtypes{}, p)
+	client, err := NewClient(ts.URL, "apiKey123", "agent", "default", 1000, defaultMaxRequests, true, 2*time.Second, 1100*time.Millisecond, gostatsd.TimerSubtypes{}, logrus.New(), p)
 	require.NoError(t, err)
 	ctx := clock.Context(context.Background(), clock.NewMock(time.Unix(100, 0)))
 	res := make(chan []error, 1)
diff --git a/pkg/backends/graphite/graphite_test.go b/pkg/backends/graphite/graphite_test.go
index 5de2e64..2cee3d4 100644
--- a/pkg/backends/graphite/graphite_test.go
+++ b/pkg/backends/graphite/graphite_test.go
@@ -12,6 +12,7 @@ import (
 	"time"
 
 	"github.com/ash2k/stager/wait"
+	"github.com/sirupsen/logrus"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
@@ -41,7 +42,7 @@ func TestPreparePayloadLegacy(t *testing.T) {
 		"stats.timers.t1.count_90.gs 90.000000 1234\n" +
 		"stats.gauges.g1.gs 3.000000 1234\n" +
 		"stats.sets.users.gs 3 1234\n"
-	cl, err := NewClient("127.0.0.1:9", 1*time.Second, 1*time.Second, "ignored1", "ignored2", "ignored3", "ignored4", "ignored5", "gs", "legacy", gostatsd.TimerSubtypes{})
+	cl, err := NewClient("127.0.0.1:9", 1*time.Second, 1*time.Second, "ignored1", "ignored2", "ignored3", "ignored4", "ignored5", "gs", "legacy", gostatsd.TimerSubtypes{}, logrus.New())
 	require.NoError(t, err)
 	b := cl.preparePayload(metrics, time.Unix(1234, 0))
 	expected = sortLines(expected)
@@ -72,7 +73,7 @@ func TestPreparePayloadBasic(t *testing.T) {
 		"gp.pt.t1.count_90.gs 90.000000 1234\n" +
 		"gp.pg.g1.gs 3.000000 1234\n" +
 		"gp.ps.users.gs 3 1234\n"
-	cl, err := NewClient("127.0.0.1:9", 1*time.Second, 1*time.Second, "gp", "pc", "pt", "pg", "ps", "gs", "basic", gostatsd.TimerSubtypes{})
+	cl, err := NewClient("127.0.0.1:9", 1*time.Second, 1*time.Second, "gp", "pc", "pt", "pg", "ps", "gs", "basic", gostatsd.TimerSubtypes{}, logrus.New())
 	require.NoError(t, err)
 	b := cl.preparePayload(metrics, time.Unix(1234, 0))
 	expected = sortLines(expected)
@@ -103,7 +104,7 @@ func TestPreparePayloadTags(t *testing.T) {
 		"gp.pt.t1.count_90.gs 90.000000 1234\n" +
 		"gp.pg.g1.gs 3.000000 1234\n" +
 		"gp.ps.users.gs 3 1234\n"
-	cl, err := NewClient("127.0.0.1:9", 1*time.Second, 1*time.Second, "gp", "pc", "pt", "pg", "ps", "gs", "tags", gostatsd.TimerSubtypes{})
+	cl, err := NewClient("127.0.0.1:9", 1*time.Second, 1*time.Second, "gp", "pc", "pt", "pg", "ps", "gs", "tags", gostatsd.TimerSubtypes{}, logrus.New())
 	require.NoError(t, err)
 	b := cl.preparePayload(metrics, time.Unix(1234, 0))
 	expected = sortLines(expected)
@@ -122,7 +123,7 @@ func TestPreparePayloadHistogram(t *testing.T) {
 			"gp.pc.t1.histogram.gs;le=60 19 1234\n" +
 			"gp.pc.t1.histogram.gs;le=+Inf 19 1234\n"
 
-	cl, err := NewClient("127.0.0.1:9", 1*time.Second, 1*time.Second, "gp", "pc", "pt", "pg", "ps", "gs", "tags", gostatsd.TimerSubtypes{})
+	cl, err := NewClient("127.0.0.1:9", 1*time.Second, 1*time.Second, "gp", "pc", "pt", "pg", "ps", "gs", "tags", gostatsd.TimerSubtypes{}, logrus.New())
 	require.NoError(t, err)
 	b := cl.preparePayload(metrics, time.Unix(1234, 0))
 	expected = sortLines(expected)
@@ -142,7 +143,7 @@ func TestSendMetricsAsync(t *testing.T) {
 	require.NoError(t, err)
 	defer l.Close()
 	addr := l.Addr().String()
-	c, err := NewClient(addr, 1*time.Second, 10*time.Second, "", "", "", "", "", "", "basic", gostatsd.TimerSubtypes{})
+	c, err := NewClient(addr, 1*time.Second, 10*time.Second, "", "", "", "", "", "", "basic", gostatsd.TimerSubtypes{}, logrus.New())
 	require.NoError(t, err)
 
 	var acceptWg sync.WaitGroup
diff --git a/pkg/backends/newrelic/newrelic_test.go b/pkg/backends/newrelic/newrelic_test.go
index de7a70a..03cb9c7 100644
--- a/pkg/backends/newrelic/newrelic_test.go
+++ b/pkg/backends/newrelic/newrelic_test.go
@@ -61,7 +61,7 @@ func TestRetries(t *testing.T) {
 	client, err := NewClient("default", ts.URL+"/v1/data", "", "GoStatsD", "", "", "", "metric_name", "metric_type",
 		"metric_per_second", "metric_value", "samples_min", "samples_max", "samples_count",
 		"samples_mean", "samples_median", "samples_std_dev", "samples_sum", "samples_sum_squares", "agent",
-		defaultMetricsPerBatch, defaultMaxRequests, 2*time.Second, 1*time.Second, gostatsd.TimerSubtypes{}, p)
+		defaultMetricsPerBatch, defaultMaxRequests, 2*time.Second, 1*time.Second, gostatsd.TimerSubtypes{}, logrus.New(), p)
 
 	require.NoError(t, err)
 	res := make(chan []error, 1)
@@ -103,7 +103,7 @@ func TestSendMetricsInMultipleBatches(t *testing.T) {
 	client, err := NewClient("default", ts.URL+"/v1/data", "", "GoStatsD", "", "", "", "metric_name", "metric_type",
 		"metric_per_second", "metric_value", "samples_min", "samples_max", "samples_count",
 		"samples_mean", "samples_median", "samples_std_dev", "samples_sum", "samples_sum_squares", "agent",
-		1, defaultMaxRequests, 2*time.Second, 1*time.Second, gostatsd.TimerSubtypes{}, p)
+		1, defaultMaxRequests, 2*time.Second, 1*time.Second, gostatsd.TimerSubtypes{}, logrus.New(), p)
 	require.NoError(t, err)
 	res := make(chan []error, 1)
 	client.SendMetricsAsync(context.Background(), twoCounters(), func(errs []error) {
@@ -191,7 +191,7 @@ func TestSendMetrics(t *testing.T) {
 			client, err := NewClient("default", ts.URL+"/v1/data", ts.URL+"/metric/v1", "GoStatsD", tt.flushType, tt.apiKey, "", "metric_name", "metric_type",
 				"metric_per_second", "metric_value", "samples_min", "samples_max", "samples_count",
 				"samples_mean", "samples_median", "samples_std_dev", "samples_sum", "samples_sum_squares", "agent",
-				defaultMetricsPerBatch, defaultMaxRequests, 2*time.Second, 1*time.Second, gostatsd.TimerSubtypes{}, p)
+				defaultMetricsPerBatch, defaultMaxRequests, 2*time.Second, 1*time.Second, gostatsd.TimerSubtypes{}, logrus.New(), p)
 
 			require.NoError(t, err)
 			res := make(chan []error, 1)
@@ -263,7 +263,7 @@ func TestSendMetricsWithHistogram(t *testing.T) {
 	client, err := NewClient("default", ts.URL+"/v1/data", "", "GoStatsD", "", "", "", "metric_name", "metric_type",
 		"metric_per_second", "metric_value", "samples_min", "samples_max", "samples_count",
 		"samples_mean", "samples_median", "samples_std_dev", "samples_sum", "samples_sum_squares", "agent",
-		defaultMetricsPerBatch, defaultMaxRequests, 2*time.Second, 1*time.Second, gostatsd.TimerSubtypes{}, p)
+		defaultMetricsPerBatch, defaultMaxRequests, 2*time.Second, 1*time.Second, gostatsd.TimerSubtypes{}, logrus.New(), p)
 
 	require.NoError(t, err)
 	res := make(chan []error, 1)
@@ -390,7 +390,7 @@ func TestEventFormatter(t *testing.T) {
 			client, err := NewClient("default", "v1/data", "", "GoStatsD", tt.name, "api-key", "", "metric_name", "metric_type",
 				"metric_per_second", "metric_value", "samples_min", "samples_max", "samples_count",
 				"samples_mean", "samples_median", "samples_std_dev", "samples_sum", "samples_sum_squares", "agent",
-				defaultMetricsPerBatch, defaultMaxRequests, 2*time.Second, 1*time.Second, gostatsd.TimerSubtypes{}, p)
+				defaultMetricsPerBatch, defaultMaxRequests, 2*time.Second, 1*time.Second, gostatsd.TimerSubtypes{}, logrus.New(), p)
 			require.NoError(t, err)
 
 			gostatsdEvent := gostatsd.Event{Title: "EventTitle", Text: "hi", Hostname: "blah", Priority: 1}
diff --git a/pkg/backends/sender/sender_test.go b/pkg/backends/sender/sender_test.go
index 54d6121..395e45d 100644
--- a/pkg/backends/sender/sender_test.go
+++ b/pkg/backends/sender/sender_test.go
@@ -11,6 +11,7 @@ import (
 	"time"
 
 	"github.com/ash2k/stager/wait"
+	"github.com/sirupsen/logrus"
 	"github.com/stretchr/testify/assert"
 )
 
@@ -18,6 +19,7 @@ func TestSend(t *testing.T) {
 	t.Parallel()
 	dc := dummyConn{}
 	sender := Sender{
+		Logger: logrus.New(),
 		ConnFactory: func() (net.Conn, error) {
 			return &dc, nil
 		},
@@ -60,6 +62,7 @@ func TestSend(t *testing.T) {
 func TestSendCallsCallbacksOnMainCtxDone(t *testing.T) {
 	t.Parallel()
 	sender := Sender{
+		Logger: logrus.New(),
 		ConnFactory: func() (net.Conn, error) {
 			return nil, errors.New("(donotwant)")
 		},
@@ -94,6 +97,7 @@ func TestSendCallsCallbacksOnMainCtxDone(t *testing.T) {
 func TestSendCallsCallbackOnCtxDone1(t *testing.T) {
 	t.Parallel()
 	sender := Sender{
+		Logger: logrus.New(),
 		ConnFactory: func() (net.Conn, error) {
 			return nil, errors.New("(donotwant)")
 		},
@@ -142,6 +146,7 @@ func TestSendCallsCallbackOnCtxDone2(t *testing.T) {
 	t.Parallel()
 	getFail := false
 	sender := Sender{
+		Logger: logrus.New(),
 		ConnFactory: func() (net.Conn, error) {
 			if getFail {
 				return nil, errors.New("(donotwant)")
diff --git a/pkg/backends/statsdaemon/statsdaemon_test.go b/pkg/backends/statsdaemon/statsdaemon_test.go
index d3a96db..c5a41e5 100644
--- a/pkg/backends/statsdaemon/statsdaemon_test.go
+++ b/pkg/backends/statsdaemon/statsdaemon_test.go
@@ -8,6 +8,7 @@ import (
 	"testing"
 	"time"
 
+	"github.com/sirupsen/logrus"
 	"github.com/stretchr/testify/assert"
 	"github.com/stretchr/testify/require"
 
@@ -25,7 +26,7 @@ var m = gostatsd.MetricMap{
 
 func TestProcessMetricsRecover(t *testing.T) {
 	t.Parallel()
-	c, err := NewClient("localhost:8125", 1*time.Second, 1*time.Second, false, false, nil)
+	c, err := NewClient("localhost:8125", 1*time.Second, 1*time.Second, false, false, nil, logrus.New())
 	require.NoError(t, err)
 	c.processMetrics(&m, func(buf *bytes.Buffer) (*bytes.Buffer, bool) {
 		return nil, true
@@ -34,7 +35,7 @@ func TestProcessMetricsRecover(t *testing.T) {
 
 func TestProcessMetricsPanic(t *testing.T) {
 	t.Parallel()
-	c, err := NewClient("localhost:8125", 1*time.Second, 1*time.Second, false, false, nil)
+	c, err := NewClient("localhost:8125", 1*time.Second, 1*time.Second, false, false, nil, logrus.New())
 	require.NoError(t, err)
 	expectedErr := errors.New("ABC some error")
 	defer func() {
@@ -77,7 +78,7 @@ func TestProcessMetrics(t *testing.T) {
 		val := val
 		t.Run(fmt.Sprintf("disableTags: %t", val.disableTags), func(t *testing.T) {
 			t.Parallel()
-			c, err := NewClient("localhost:8125", 1*time.Second, 1*time.Second, val.disableTags, false, nil)
+			c, err := NewClient("localhost:8125", 1*time.Second, 1*time.Second, val.disableTags, false, nil, logrus.New())
 			require.NoError(t, err)
 			c.processMetrics(&gaugeMetic, func(buf *bytes.Buffer) (*bytes.Buffer, bool) {
 				assert.EqualValues(t, val.expectedValue, buf.String())
diff --git a/pkg/statsd/parser_test.go b/pkg/statsd/parser_test.go
index e9cb0bf..63084c0 100644
--- a/pkg/statsd/parser_test.go
+++ b/pkg/statsd/parser_test.go
@@ -5,6 +5,7 @@ import (
 	"strconv"
 	"testing"
 
+	"github.com/sirupsen/logrus"
 	"github.com/stretchr/testify/assert"
 	"golang.org/x/time/rate"
 
@@ -20,7 +21,7 @@ const fakeIP = gostatsd.IP("127.0.0.1")
 
 func newTestParser(ignoreHost bool) (*DatagramParser, *countingHandler) {
 	ch := &countingHandler{}
-	return NewDatagramParser(nil, "", ignoreHost, 0, ch, rate.Limit(0), false), ch
+	return NewDatagramParser(nil, "", ignoreHost, 0, ch, rate.Limit(0), false, logrus.New()), ch
 }
 
 func TestParseEmptyDatagram(t *testing.T) {