没有http.Request的访问上下文
I am setting X-Request-Id
in context
within a middleware (as shown below) so that I can use it where there is *http.Request
struct - e.g. req.Context().Value(middleware.ReqIdKey)
. However, there are some places in my codebase where there is no way for me to access *http.Request
struct hence reason I am not able to use context
to fetch X-Request-Id
. Is there a way in Go or am I trying to do something fundamentally wrong?
internal/middleware/requestid.go
This is middleware where I set the X-Request-Id
in context
. Currently called as http.ListenAndServe(":8080", middleware.RequestId(SomeHandler))
in my "server" package.
package middleware
import (
"context"
"github.com/google/uuid"
"net/http"
)
type val string
const ReqIdKey val = "X-Request-Id"
func RequestId(handler http.Handler) http.Handler {
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
val := req.Header.Get("X-Request-Id")
if val == "" {
val = uuid.New().String()
}
ctx1 := context.WithValue(req.Context(), ReqIdKey, val)
ctx2 := req.WithContext(ctx1)
handler.ServeHTTP(res, ctx2)
})
}
internal/logger/logger.go
This is another package where I need to access context
or just X-Request-Id
value. By the way, calling logger.Config
takes place before starting the server.
package logger
import (
"github.com/sirupsen/logrus"
"os"
)
var Log *logrus.Entry
func Config() {
logrus.SetLevel(logrus.InfoLevel)
logrus.SetOutput(os.Stdout)
logrus.SetFormatter(&logrus.JSONFormatter{})
Log = logrus.WithFields(logrus.Fields{
"request_id": ..., // I need X-Request-Id value to go here so that all logs have it
})
}
First, you should pass your context everywhere it's needed. If you need that in your Config()
function, pass it there:
func Config(ctx context.Context) {
/* ... * /
}
But you probably call Config()
once, on startup, not per request, which leads to my second point:
You should not be passing context, or request-scoped data in general, to a config function. This is completely backwards.
Rather, you should be passing your logger into your handler/middleware, and let it log with the request data:
func handleSomePath(logger *logrus.Entry) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
/* do something */
logger.WithFields(logrus.Fields{
"request_id": /* ... */
})
}
}
If you have a http.Request you can access the Context and Values in there. If you do not have a Request but need the Context: Get the Context and pass it down your calltree as an explicit parameter (by convention its the first parameter).
(There is no magic in Go and any thing not passed into a function, either directly or indirectly simply is not there.)