[feature] Add a request ID and include it in logs (#1476)

This adds a lightweight form of tracing to GTS. Each incoming request is
assigned a Request ID which we then pass on and log in all our log
lines. Any function that gets called downstream from an HTTP handler
should now emit a requestID=value pair whenever it logs something.

Co-authored-by: kim <grufwub@gmail.com>
This commit is contained in:
Daenney 2023-02-17 12:02:29 +01:00 committed by GitHub
commit 68e6d08c76
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
118 changed files with 813 additions and 591 deletions

View file

@ -100,7 +100,7 @@ func (suite *MediaCreateTestSuite) SetupSuite() {
func (suite *MediaCreateTestSuite) TearDownSuite() {
if err := suite.db.Stop(context.Background()); err != nil {
log.Panicf("error closing db connection: %s", err)
log.Panicf(nil, "error closing db connection: %s", err)
}
}

View file

@ -98,7 +98,7 @@ func (suite *MediaUpdateTestSuite) SetupSuite() {
func (suite *MediaUpdateTestSuite) TearDownSuite() {
if err := suite.db.Stop(context.Background()); err != nil {
log.Panicf("error closing db connection: %s", err)
log.Panicf(nil, "error closing db connection: %s", err)
}
}

View file

@ -166,11 +166,12 @@ func (m *Module) StreamGETHandler(c *gin.Context) {
return
}
l := log.WithFields(kv.Fields{
{"account", account.Username},
{"streamID", stream.ID},
{"streamType", streamType},
}...)
l := log.WithContext(c.Request.Context()).
WithFields(kv.Fields{
{"account", account.Username},
{"streamID", stream.ID},
{"streamType", streamType},
}...)
// Upgrade the incoming HTTP request, which hijacks the underlying
// connection and reuses it for the websocket (non-http) protocol.

View file

@ -99,7 +99,7 @@ func (suite *FileserverTestSuite) SetupTest() {
func (suite *FileserverTestSuite) TearDownSuite() {
if err := suite.db.Stop(context.Background()); err != nil {
log.Panicf("error closing db connection: %s", err)
log.Panicf(nil, "error closing db connection: %s", err)
}
}

View file

@ -77,7 +77,10 @@ func (m *Module) ServeFile(c *gin.Context) {
return
}
content, errWithCode := m.processor.FileGet(c.Request.Context(), authed, &apimodel.GetContentRequestForm{
// Acquire context from gin request.
ctx := c.Request.Context()
content, errWithCode := m.processor.FileGet(ctx, authed, &apimodel.GetContentRequestForm{
AccountID: accountID,
MediaType: mediaType,
MediaSize: mediaSize,
@ -101,7 +104,7 @@ func (m *Module) ServeFile(c *gin.Context) {
defer func() {
// Close content when we're done, catch errors.
if err := content.Content.Close(); err != nil {
log.Errorf("ServeFile: error closing readcloser: %s", err)
log.Errorf(ctx, "ServeFile: error closing readcloser: %s", err)
}
}()
@ -130,15 +133,21 @@ func (m *Module) ServeFile(c *gin.Context) {
return
}
// Set known content-type and serve this file range.
// Set known content-type and serve range.
c.Header("Content-Type", format)
serveFileRange(c.Writer, content.Content, rng, content.ContentLength)
serveFileRange(
c.Writer,
c.Request,
content.Content,
rng,
content.ContentLength,
)
}
// serveFileRange serves the range of a file from a given source reader, without the
// need for implementation of io.Seeker. Instead we read the first 'start' many bytes
// into a discard reader. Code is adapted from https://codeberg.org/gruf/simplehttp.
func serveFileRange(rw http.ResponseWriter, src io.Reader, rng string, size int64) {
func serveFileRange(rw http.ResponseWriter, r *http.Request, src io.Reader, rng string, size int64) {
var i int
if i = strings.IndexByte(rng, '='); i < 0 {
@ -219,7 +228,7 @@ func serveFileRange(rw http.ResponseWriter, src io.Reader, rng string, size int6
// Dump the first 'start' many bytes into the void...
if _, err := fastcopy.CopyN(io.Discard, src, start); err != nil {
log.Errorf("error reading from source: %v", err)
log.Errorf(r.Context(), "error reading from source: %v", err)
return
}
@ -239,7 +248,7 @@ func serveFileRange(rw http.ResponseWriter, src io.Reader, rng string, size int6
// Read the "seeked" source reader into destination writer.
if _, err := fastcopy.Copy(rw, src); err != nil {
log.Errorf("error reading from source: %v", err)
log.Errorf(r.Context(), "error reading from source: %v", err)
return
}
}

View file

@ -27,6 +27,7 @@ import (
apimodel "github.com/superseriousbusiness/gotosocial/internal/api/model"
"github.com/superseriousbusiness/gotosocial/internal/gtserror"
"github.com/superseriousbusiness/gotosocial/internal/log"
"github.com/superseriousbusiness/gotosocial/internal/middleware"
)
// TODO: add more templated html pages here for different error types
@ -43,16 +44,20 @@ import (
func NotFoundHandler(c *gin.Context, instanceGet func(ctx context.Context) (*apimodel.InstanceV1, gtserror.WithCode), accept string) {
switch accept {
case string(TextHTML):
instance, err := instanceGet(c.Request.Context())
ctx := c.Request.Context()
instance, err := instanceGet(ctx)
if err != nil {
panic(err)
}
c.HTML(http.StatusNotFound, "404.tmpl", gin.H{
"instance": instance,
"instance": instance,
"requestID": middleware.RequestID(ctx),
})
default:
c.JSON(http.StatusNotFound, gin.H{"error": http.StatusText(http.StatusNotFound)})
c.JSON(http.StatusNotFound, gin.H{
"error": http.StatusText(http.StatusNotFound),
})
}
}
@ -62,15 +67,17 @@ func NotFoundHandler(c *gin.Context, instanceGet func(ctx context.Context) (*api
func genericErrorHandler(c *gin.Context, instanceGet func(ctx context.Context) (*apimodel.InstanceV1, gtserror.WithCode), accept string, errWithCode gtserror.WithCode) {
switch accept {
case string(TextHTML):
instance, err := instanceGet(c.Request.Context())
ctx := c.Request.Context()
instance, err := instanceGet(ctx)
if err != nil {
panic(err)
}
c.HTML(errWithCode.Code(), "error.tmpl", gin.H{
"instance": instance,
"code": errWithCode.Code(),
"error": errWithCode.Safe(),
"instance": instance,
"code": errWithCode.Code(),
"error": errWithCode.Safe(),
"requestID": middleware.RequestID(ctx),
})
default:
c.JSON(errWithCode.Code(), gin.H{"error": errWithCode.Safe()})
@ -108,11 +115,12 @@ func ErrorHandler(c *gin.Context, errWithCode gtserror.WithCode, instanceGet fun
// to pass any detailed errors (that might contain sensitive information) into the
// errWithCode.Error() field, since the client will see this. Use your noggin!
func OAuthErrorHandler(c *gin.Context, errWithCode gtserror.WithCode) {
l := log.WithFields(kv.Fields{
{"path", c.Request.URL.Path},
{"error", errWithCode.Error()},
{"help", errWithCode.Safe()},
}...)
l := log.WithContext(c.Request.Context()).
WithFields(kv.Fields{
{"path", c.Request.URL.Path},
{"error", errWithCode.Error()},
{"help", errWithCode.Safe()},
}...)
statusCode := errWithCode.Code()