diff --git a/input/elasticapm/docs/spec/v2/span.json b/input/elasticapm/docs/spec/v2/span.json index e86da9a6..c8f5f7d4 100644 --- a/input/elasticapm/docs/spec/v2/span.json +++ b/input/elasticapm/docs/spec/v2/span.json @@ -188,6 +188,13 @@ "object" ], "properties": { + "body": { + "description": "The http request body as a string", + "type": [ + "null", + "string" + ] + }, "id": { "description": "ID holds the unique identifier for the http request.", "type": [ diff --git a/input/elasticapm/internal/modeldecoder/v2/decoder.go b/input/elasticapm/internal/modeldecoder/v2/decoder.go index b3a3faad..dacc1e14 100644 --- a/input/elasticapm/internal/modeldecoder/v2/decoder.go +++ b/input/elasticapm/internal/modeldecoder/v2/decoder.go @@ -1071,14 +1071,19 @@ func mapToSpanModel(from *span, event *modelpb.APMEvent) { } event.Http.Request.Method = from.Context.HTTP.Method.Val } - if from.Context.HTTP.Request.ID.IsSet() { + if from.Context.HTTP.Request.IsSet() { if event.Http == nil { event.Http = &modelpb.HTTP{} } if event.Http.Request == nil { event.Http.Request = &modelpb.HTTPRequest{} } - event.Http.Request.Id = from.Context.HTTP.Request.ID.Val + if from.Context.HTTP.Request.ID.IsSet() { + event.Http.Request.Id = from.Context.HTTP.Request.ID.Val + } + if from.Context.HTTP.Request.Body.IsSet() { + event.Http.Request.Body = modeldecoderutil.ToValue(from.Context.HTTP.Request.Body.Val) + } } if from.Context.HTTP.Response.IsSet() { if event.Http == nil { diff --git a/input/elasticapm/internal/modeldecoder/v2/model.go b/input/elasticapm/internal/modeldecoder/v2/model.go index 46a9b99e..2ac2c2d9 100644 --- a/input/elasticapm/internal/modeldecoder/v2/model.go +++ b/input/elasticapm/internal/modeldecoder/v2/model.go @@ -845,6 +845,8 @@ type spanContextHTTP struct { type spanContextHTTPRequest struct { // ID holds the unique identifier for the http request. ID nullable.String `json:"id"` + // The http request body as a string + Body nullable.String `json:"body"` } type spanContextHTTPResponse struct { diff --git a/input/elasticapm/internal/modeldecoder/v2/model_generated.go b/input/elasticapm/internal/modeldecoder/v2/model_generated.go index f5b571f7..a1019fd5 100644 --- a/input/elasticapm/internal/modeldecoder/v2/model_generated.go +++ b/input/elasticapm/internal/modeldecoder/v2/model_generated.go @@ -2500,11 +2500,12 @@ func (val *spanContextHTTP) processNestedSource() error { } func (val *spanContextHTTPRequest) IsSet() bool { - return val.ID.IsSet() + return val.ID.IsSet() || val.Body.IsSet() } func (val *spanContextHTTPRequest) Reset() { val.ID.Reset() + val.Body.Reset() } func (val *spanContextHTTPRequest) validate() error { diff --git a/input/elasticapm/internal/modeldecoder/v2/span_test.go b/input/elasticapm/internal/modeldecoder/v2/span_test.go index 57df65d7..064ba208 100644 --- a/input/elasticapm/internal/modeldecoder/v2/span_test.go +++ b/input/elasticapm/internal/modeldecoder/v2/span_test.go @@ -19,6 +19,7 @@ package v2 import ( "encoding/json" + "google.golang.org/protobuf/types/known/structpb" "net/http" "strconv" "strings" @@ -258,7 +259,7 @@ func TestDecodeMapToSpanModel(t *testing.T) { } }) - t.Run("http-request", func(t *testing.T) { + t.Run("http-request-id", func(t *testing.T) { var input span input.Context.HTTP.Request.ID.Set("some-request-id") var out modelpb.APMEvent @@ -266,6 +267,14 @@ func TestDecodeMapToSpanModel(t *testing.T) { assert.Equal(t, "some-request-id", out.Http.Request.Id) }) + t.Run("http-request-body", func(t *testing.T) { + var input span + input.Context.HTTP.Request.Body.Set("some-request-body") + var out modelpb.APMEvent + mapToSpanModel(&input, &out) + assert.Equal(t, structpb.NewStringValue("some-request-body"), out.Http.Request.Body) + }) + t.Run("http-headers", func(t *testing.T) { var input span input.Context.HTTP.Response.Headers.Set(http.Header{"a": []string{"b", "c"}})