Rayn D
Rayn D

Reputation: 629

Use request ID in all logger

I've some web-application server using go http and I want that each request will have context with uuid, for this I can use http request context https://golang.org/pkg/net/http/#Request.Context

we are using logrus and we initiate it in one file and use the logger instance in other files.

what I need is to print request ID in all the logs but not to add new paremeters to each log print, I want do to it once in each http request (pass the req-id) and all the logs print will have it without doing anything with it

e.g. if the id=123 so log.info("foo") will print // id=123 foo

I've tried with the following but not sure it's the right way, please advice.

package main

import (
    "context"
    "errors"

    log "github.com/sirupsen/logrus"
)

type someContextKey string

var (
    keyA = someContextKey("a")
    keyB = someContextKey("b")
)

func main() {
    ctx := context.Background()
    ctx = context.WithValue(ctx, keyA, "foo")
    ctx = context.WithValue(ctx, keyB, "bar")

    logger(ctx, nil).Info("did nothing")
    err := errors.New("an error")
    logger(ctx, err).Fatal("unrecoverable error")
}

func logger(ctx context.Context, err error) *log.Entry {
    entry := log.WithField("component", "main")

    entry = entry.WithField("ReqID", "myRequestID")

    return entry
}

https://play.golang.org/p/oCW09UhTjZ5

Upvotes: 5

Views: 7985

Answers (1)

TehSphinX
TehSphinX

Reputation: 7430

Every time you call the logger function you are creating a new *log.Entry and writing the request ID to it again. From your question it sounded like you do not want that.

func main() {
    ctx := context.Background()
    ctx = context.WithValue(ctx, keyA, "foo")
    ctx = context.WithValue(ctx, keyB, "bar")

    lg := logger(ctx)
    lg.Info("did nothing")
    err := errors.New("an error")
    lg.WithError(err).Fatal("unrecoverable error")
}

func logger(ctx context.Context) *log.Entry {
    entry := log.WithField("component", "main")
    entry = entry.WithField("ReqID", "myRequestID")

    return entry
}

The downside of this is that you will have to pass the lg variable to every function this request calls and which should also log the request ID.

What we did at our company is create a thin layer around logrus that has an additional method WithRequestCtx so we could pass in the request context and it would extract the request ID itself (which we had written to the context in a middleware). If no request ID was present nothing was added to the log entry. This however did add the request ID to every log entry again as your sample code also did.

Note: our thin layer around logrus had a lot more functionality and default settings to justify the extra effort. In the long run this turned out very helpful to have one place to be able to adjust logging for all our services.

Note2: meanwhile we are in the process of replacing logrus with zerolog to be more lightweight.

Upvotes: 4

Related Questions