diff --git a/.chloggen/http-request-response-body.yaml b/.chloggen/http-request-response-body.yaml new file mode 100644 index 0000000000..f1f1ad8080 --- /dev/null +++ b/.chloggen/http-request-response-body.yaml @@ -0,0 +1,22 @@ +# Use this changelog template to create an entry for release notes. +# +# If your change doesn't affect end users you should instead start +# your pull request title with [chore] or use the "Skip Changelog" label. + +# One of 'breaking', 'deprecation', 'new_component', 'enhancement', 'bug_fix' +change_type: 'enhancement' + +# The name of the area of concern in the attributes-registry, (e.g. http, cloud, db) +component: 'http' + +# A brief description of the change. Surround your text with quotes ("") if it needs to start with a backtick (`). +note: "add `http.request.body.content` and `http.response.body.content` to capture http request and response body" + +# Mandatory: One or more tracking issues related to the change. You can use the PR number here if no issue exists. +# The values here must be integers. +issues: [ 857 ] + +# (Optional) One or more lines of additional information to render under the primary note. +# These lines will be padded with 2 spaces and then inserted directly into the document. +# Use pipe (|) for multiline entries. +subtext: diff --git a/docs/http/http-spans.md b/docs/http/http-spans.md index 51fdcdb1ef..7750006694 100644 --- a/docs/http/http-spans.md +++ b/docs/http/http-spans.md @@ -163,17 +163,19 @@ There are two ways HTTP client spans can be implemented in an instrumentation: | [`network.peer.address`](/docs/registry/attributes/network.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Recommended` | string | Peer address of the network connection - IP address or Unix domain socket name. | `10.1.2.80`; `/tmp/my.sock` | | [`network.peer.port`](/docs/registry/attributes/network.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Recommended` If `network.peer.address` is set. | int | Peer port number of the network connection. | `65123` | | [`network.protocol.version`](/docs/registry/attributes/network.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Recommended` | string | The actual version of the protocol used for network communication. [10] | `1.0`; `1.1`; `2`; `3` | +| [`http.request.body.content`](/docs/registry/attributes/http.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | any | The content of the uncompressed HTTP request body, captured as a string when the request content is textual or as binary otherwise. [11] | `Hello world!`; `{"foo: "bar"}`; `aGVsbG8=` | | [`http.request.body.size`](/docs/registry/attributes/http.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | int | The size of the request payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the [Content-Length](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length) header. For requests using transport encoding, this should be the compressed size. | `3495` | -| [`http.request.header.`](/docs/registry/attributes/http.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Opt-In` | string[] | HTTP request headers, `` being the normalized HTTP Header name (lowercase), the value being the header values. [11] | `["application/json"]`; `["1.2.3.4", "1.2.3.5"]` | +| [`http.request.header.`](/docs/registry/attributes/http.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Opt-In` | string[] | HTTP request headers, `` being the normalized HTTP Header name (lowercase), the value being the header values. [12] | `["application/json"]`; `["1.2.3.4", "1.2.3.5"]` | | [`http.request.size`](/docs/registry/attributes/http.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | int | The total size of the request in bytes. This should be the total number of bytes sent over the wire, including the request line (HTTP/1.1), framing (HTTP/2 and HTTP/3), headers, and request body if any. | `1437` | +| [`http.response.body.content`](/docs/registry/attributes/http.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | any | The content of the uncompressed HTTP response body, captured as a string when the response content is textual or as byte array otherwise. [13] | `Hello world!`; `{"foo: "bar"}`; `aGVsbG8=` | | [`http.response.body.size`](/docs/registry/attributes/http.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | int | The size of the response payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the [Content-Length](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length) header. For requests using transport encoding, this should be the compressed size. | `3495` | -| [`http.response.header.`](/docs/registry/attributes/http.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Opt-In` | string[] | HTTP response headers, `` being the normalized HTTP Header name (lowercase), the value being the header values. [12] | `["application/json"]`; `["abc", "def"]` | +| [`http.response.header.`](/docs/registry/attributes/http.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Opt-In` | string[] | HTTP response headers, `` being the normalized HTTP Header name (lowercase), the value being the header values. [14] | `["application/json"]`; `["abc", "def"]` | | [`http.response.size`](/docs/registry/attributes/http.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | int | The total size of the response in bytes. This should be the total number of bytes sent over the wire, including the status line (HTTP/1.1), framing (HTTP/2 and HTTP/3), headers, and response body and trailers if any. | `1437` | -| [`network.transport`](/docs/registry/attributes/network.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Opt-In` | string | [OSI transport layer](https://wikipedia.org/wiki/Transport_layer) or [inter-process communication method](https://wikipedia.org/wiki/Inter-process_communication). [13] | `tcp`; `udp` | +| [`network.transport`](/docs/registry/attributes/network.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Opt-In` | string | [OSI transport layer](https://wikipedia.org/wiki/Transport_layer) or [inter-process communication method](https://wikipedia.org/wiki/Inter-process_communication). [15] | `tcp`; `udp` | | [`url.scheme`](/docs/registry/attributes/url.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Opt-In` | string | The [URI scheme](https://www.rfc-editor.org/rfc/rfc3986#section-3.1) component identifying the used protocol. | `http`; `https` | -| [`url.template`](/docs/registry/attributes/url.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The low-cardinality template of an [absolute path reference](https://www.rfc-editor.org/rfc/rfc3986#section-4.2). [14] | `/users/{id}`; `/users/:id`; `/users?id={id}` | +| [`url.template`](/docs/registry/attributes/url.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | The low-cardinality template of an [absolute path reference](https://www.rfc-editor.org/rfc/rfc3986#section-4.2). [16] | `/users/{id}`; `/users/:id`; `/users?id={id}` | | [`user_agent.original`](/docs/registry/attributes/user-agent.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Opt-In` | string | Value of the [HTTP User-Agent](https://www.rfc-editor.org/rfc/rfc9110.html#field.user-agent) header sent by the client. | `CERN-LineMode/2.15 libwww/2.17b3`; `Mozilla/5.0 (iPhone; CPU iPhone OS 14_7_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.2 Mobile/15E148 Safari/604.1`; `YourApp/1.0.0 grpc-java-okhttp/1.27.2` | -| [`user_agent.synthetic.type`](/docs/registry/attributes/user-agent.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | Specifies the category of synthetic traffic, such as tests or bots. [15] | `bot`; `test` | +| [`user_agent.synthetic.type`](/docs/registry/attributes/user-agent.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | Specifies the category of synthetic traffic, such as tests or bots. [17] | `bot`; `test` | **[1] `http.request.method`:** HTTP request method value SHOULD be "known" to the instrumentation. By default, this convention defines "known" methods as the ones listed in [RFC9110](https://www.rfc-editor.org/rfc/rfc9110.html#name-methods), @@ -274,7 +276,21 @@ If the request has completed successfully, instrumentations SHOULD NOT set `erro **[10] `network.protocol.version`:** If protocol version is subject to negotiation (for example using [ALPN](https://www.rfc-editor.org/rfc/rfc7301.html)), this attribute SHOULD be set to the negotiated version. If the actual protocol version is not known, this attribute SHOULD NOT be set. -**[11] `http.request.header.`:** Instrumentations SHOULD require an explicit configuration of which headers are to be captured. +**[11] `http.request.body.content`:** Captured value MAY be limited in size and thus value is expected to be truncated in many cases. + +Instrumentations MUST disable capture by default and provide an opt-in configuration option to enable it as it can capture sensitive information and cause performance overhead. + +Instrumentations SHOULD capture the body as string whenever possible as it makes it easier to use in human-readable form, also it allows to implement sanitization if needed. + +Textual content is typically detected based on the [Content-Type](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-type) header using heuristics +such as checking if the media type starts with `text/` or ends with `/json`, `+json`, `/xml`, `+xml`, `/yaml` or `+yaml` or if the media type contains an explicit charset with `;charset=`. + +Instrumentations that implement request body recording MUST NOT intentionally introduce side effects such as changing stream position or closing body stream independently from the +application. + +When body is recorded on HTTP client or server spans, the instrumentation SHOULD record part of the body that was sent or received at the time HTTP span has ended. + +**[12] `http.request.header.`:** Instrumentations SHOULD require an explicit configuration of which headers are to be captured. Including all request headers can be a security risk - explicit configuration helps avoid leaking sensitive information. The `User-Agent` header is already captured in the `user_agent.original` attribute. @@ -291,7 +307,21 @@ Examples: - A header `X-Forwarded-For: 1.2.3.4, 1.2.3.5` SHOULD be recorded as the `http.request.header.x-forwarded-for` attribute with value `["1.2.3.4", "1.2.3.5"]` or `["1.2.3.4, 1.2.3.5"]` depending on the HTTP library. -**[12] `http.response.header.`:** Instrumentations SHOULD require an explicit configuration of which headers are to be captured. +**[13] `http.response.body.content`:** Captured value MAY be limited in size and thus value is expected to be truncated in many cases. + +Instrumentations MUST disable capture by default and provide an opt-in configuration option to enable it as it can capture sensitive information and cause performance overhead. + +Instrumentations SHOULD capture the body as string whenever possible as it makes it easier to use in human-readable form, also it allows to implement sanitization if needed. + +Textual content is typically detected based on the [Content-Type](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-type) header using heuristics +such as checking if the media type starts with `text/` or ends with `/json`, `+json`, `/xml`, `+xml`, `/yaml` or `+yaml` or if the media type contains an explicit charset with `;charset=`. + +Instrumentations that implement response body recording MUST NOT intentionally introduce side effects such as changing stream position or closing body stream independently from the +application. + +When body is recorded on HTTP client or server spans, the instrumentation SHOULD record part of the body that was sent or received at the time HTTP span has ended. + +**[14] `http.response.header.`:** Instrumentations SHOULD require an explicit configuration of which headers are to be captured. Including all response headers can be a security risk - explicit configuration helps avoid leaking sensitive information. Users MAY explicitly configure instrumentations to capture them even though it is not recommended. @@ -307,11 +337,11 @@ Examples: - A header `My-custom-header: abc, def` header SHOULD be recorded as the `http.response.header.my-custom-header` attribute with value `["abc", "def"]` or `["abc, def"]` depending on the HTTP library. -**[13] `network.transport`:** Generally `tcp` for `HTTP/1.0`, `HTTP/1.1`, and `HTTP/2`. Generally `udp` for `HTTP/3`. Other obscure implementations are possible. +**[15] `network.transport`:** Generally `tcp` for `HTTP/1.0`, `HTTP/1.1`, and `HTTP/2`. Generally `udp` for `HTTP/3`. Other obscure implementations are possible. -**[14] `url.template`:** The `url.template` MUST have low cardinality. It is not usually available on HTTP clients, but may be known by the application or specialized HTTP instrumentation. +**[16] `url.template`:** The `url.template` MUST have low cardinality. It is not usually available on HTTP clients, but may be known by the application or specialized HTTP instrumentation. -**[15] `user_agent.synthetic.type`:** This attribute MAY be derived from the contents of the `user_agent.original` attribute. Components that populate the attribute are responsible for determining what they consider to be synthetic bot or test traffic. This attribute can either be set for self-identification purposes, or on telemetry detected to be generated as a result of a synthetic request. This attribute is useful for distinguishing between genuine client traffic and synthetic traffic generated by bots or tests. +**[17] `user_agent.synthetic.type`:** This attribute MAY be derived from the contents of the `user_agent.original` attribute. Components that populate the attribute are responsible for determining what they consider to be synthetic bot or test traffic. This attribute can either be set for self-identification purposes, or on telemetry detected to be generated as a result of a synthetic request. This attribute is useful for distinguishing between genuine client traffic and synthetic traffic generated by bots or tests. The following attributes can be important for making sampling decisions and SHOULD be provided **at span creation time** (if provided at all): @@ -485,16 +515,18 @@ This span represents an inbound HTTP request. | [`server.address`](/docs/registry/attributes/server.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Recommended` | string | Name of the local HTTP server that received the request. [13] | `example.com`; `10.1.2.80`; `/tmp/my.sock` | | [`user_agent.original`](/docs/registry/attributes/user-agent.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Recommended` | string | Value of the [HTTP User-Agent](https://www.rfc-editor.org/rfc/rfc9110.html#field.user-agent) header sent by the client. | `CERN-LineMode/2.15 libwww/2.17b3`; `Mozilla/5.0 (iPhone; CPU iPhone OS 14_7_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/14.1.2 Mobile/15E148 Safari/604.1`; `YourApp/1.0.0 grpc-java-okhttp/1.27.2` | | [`client.port`](/docs/registry/attributes/client.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Opt-In` | int | The port of whichever client was captured in `client.address`. [14] | `65123` | +| [`http.request.body.content`](/docs/registry/attributes/http.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | any | The content of the uncompressed HTTP request body, captured as a string when the request content is textual or as binary otherwise. [15] | `Hello world!`; `{"foo: "bar"}`; `aGVsbG8=` | | [`http.request.body.size`](/docs/registry/attributes/http.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | int | The size of the request payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the [Content-Length](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length) header. For requests using transport encoding, this should be the compressed size. | `3495` | -| [`http.request.header.`](/docs/registry/attributes/http.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Opt-In` | string[] | HTTP request headers, `` being the normalized HTTP Header name (lowercase), the value being the header values. [15] | `["application/json"]`; `["1.2.3.4", "1.2.3.5"]` | +| [`http.request.header.`](/docs/registry/attributes/http.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Opt-In` | string[] | HTTP request headers, `` being the normalized HTTP Header name (lowercase), the value being the header values. [16] | `["application/json"]`; `["1.2.3.4", "1.2.3.5"]` | | [`http.request.size`](/docs/registry/attributes/http.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | int | The total size of the request in bytes. This should be the total number of bytes sent over the wire, including the request line (HTTP/1.1), framing (HTTP/2 and HTTP/3), headers, and request body if any. | `1437` | +| [`http.response.body.content`](/docs/registry/attributes/http.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | any | The content of the uncompressed HTTP response body, captured as a string when the response content is textual or as byte array otherwise. [17] | `Hello world!`; `{"foo: "bar"}`; `aGVsbG8=` | | [`http.response.body.size`](/docs/registry/attributes/http.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | int | The size of the response payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the [Content-Length](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length) header. For requests using transport encoding, this should be the compressed size. | `3495` | -| [`http.response.header.`](/docs/registry/attributes/http.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Opt-In` | string[] | HTTP response headers, `` being the normalized HTTP Header name (lowercase), the value being the header values. [16] | `["application/json"]`; `["abc", "def"]` | +| [`http.response.header.`](/docs/registry/attributes/http.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Opt-In` | string[] | HTTP response headers, `` being the normalized HTTP Header name (lowercase), the value being the header values. [18] | `["application/json"]`; `["abc", "def"]` | | [`http.response.size`](/docs/registry/attributes/http.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | int | The total size of the response in bytes. This should be the total number of bytes sent over the wire, including the status line (HTTP/1.1), framing (HTTP/2 and HTTP/3), headers, and response body and trailers if any. | `1437` | | [`network.local.address`](/docs/registry/attributes/network.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Opt-In` | string | Local socket address. Useful in case of a multi-IP host. | `10.1.2.80`; `/tmp/my.sock` | | [`network.local.port`](/docs/registry/attributes/network.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Opt-In` | int | Local socket port. Useful in case of a multi-port host. | `65123` | -| [`network.transport`](/docs/registry/attributes/network.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Opt-In` | string | [OSI transport layer](https://wikipedia.org/wiki/Transport_layer) or [inter-process communication method](https://wikipedia.org/wiki/Inter-process_communication). [17] | `tcp`; `udp` | -| [`user_agent.synthetic.type`](/docs/registry/attributes/user-agent.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | Specifies the category of synthetic traffic, such as tests or bots. [18] | `bot`; `test` | +| [`network.transport`](/docs/registry/attributes/network.md) | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | `Opt-In` | string | [OSI transport layer](https://wikipedia.org/wiki/Transport_layer) or [inter-process communication method](https://wikipedia.org/wiki/Inter-process_communication). [19] | `tcp`; `udp` | +| [`user_agent.synthetic.type`](/docs/registry/attributes/user-agent.md) | ![Development](https://img.shields.io/badge/-development-blue) | `Opt-In` | string | Specifies the category of synthetic traffic, such as tests or bots. [20] | `bot`; `test` | **[1] `http.request.method`:** HTTP request method value SHOULD be "known" to the instrumentation. By default, this convention defines "known" methods as the ones listed in [RFC9110](https://www.rfc-editor.org/rfc/rfc9110.html#name-methods), @@ -592,7 +624,21 @@ When a query string value is redacted, the query string key SHOULD still be pres **[14] `client.port`:** When observed from the server side, and when communicating through an intermediary, `client.port` SHOULD represent the client port behind any intermediaries, for example proxies, if it's available. -**[15] `http.request.header.`:** Instrumentations SHOULD require an explicit configuration of which headers are to be captured. +**[15] `http.request.body.content`:** Captured value MAY be limited in size and thus value is expected to be truncated in many cases. + +Instrumentations MUST disable capture by default and provide an opt-in configuration option to enable it as it can capture sensitive information and cause performance overhead. + +Instrumentations SHOULD capture the body as string whenever possible as it makes it easier to use in human-readable form, also it allows to implement sanitization if needed. + +Textual content is typically detected based on the [Content-Type](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-type) header using heuristics +such as checking if the media type starts with `text/` or ends with `/json`, `+json`, `/xml`, `+xml`, `/yaml` or `+yaml` or if the media type contains an explicit charset with `;charset=`. + +Instrumentations that implement request body recording MUST NOT intentionally introduce side effects such as changing stream position or closing body stream independently from the +application. + +When body is recorded on HTTP client or server spans, the instrumentation SHOULD record part of the body that was sent or received at the time HTTP span has ended. + +**[16] `http.request.header.`:** Instrumentations SHOULD require an explicit configuration of which headers are to be captured. Including all request headers can be a security risk - explicit configuration helps avoid leaking sensitive information. The `User-Agent` header is already captured in the `user_agent.original` attribute. @@ -609,7 +655,21 @@ Examples: - A header `X-Forwarded-For: 1.2.3.4, 1.2.3.5` SHOULD be recorded as the `http.request.header.x-forwarded-for` attribute with value `["1.2.3.4", "1.2.3.5"]` or `["1.2.3.4, 1.2.3.5"]` depending on the HTTP library. -**[16] `http.response.header.`:** Instrumentations SHOULD require an explicit configuration of which headers are to be captured. +**[17] `http.response.body.content`:** Captured value MAY be limited in size and thus value is expected to be truncated in many cases. + +Instrumentations MUST disable capture by default and provide an opt-in configuration option to enable it as it can capture sensitive information and cause performance overhead. + +Instrumentations SHOULD capture the body as string whenever possible as it makes it easier to use in human-readable form, also it allows to implement sanitization if needed. + +Textual content is typically detected based on the [Content-Type](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-type) header using heuristics +such as checking if the media type starts with `text/` or ends with `/json`, `+json`, `/xml`, `+xml`, `/yaml` or `+yaml` or if the media type contains an explicit charset with `;charset=`. + +Instrumentations that implement response body recording MUST NOT intentionally introduce side effects such as changing stream position or closing body stream independently from the +application. + +When body is recorded on HTTP client or server spans, the instrumentation SHOULD record part of the body that was sent or received at the time HTTP span has ended. + +**[18] `http.response.header.`:** Instrumentations SHOULD require an explicit configuration of which headers are to be captured. Including all response headers can be a security risk - explicit configuration helps avoid leaking sensitive information. Users MAY explicitly configure instrumentations to capture them even though it is not recommended. @@ -625,9 +685,9 @@ Examples: - A header `My-custom-header: abc, def` header SHOULD be recorded as the `http.response.header.my-custom-header` attribute with value `["abc", "def"]` or `["abc, def"]` depending on the HTTP library. -**[17] `network.transport`:** Generally `tcp` for `HTTP/1.0`, `HTTP/1.1`, and `HTTP/2`. Generally `udp` for `HTTP/3`. Other obscure implementations are possible. +**[19] `network.transport`:** Generally `tcp` for `HTTP/1.0`, `HTTP/1.1`, and `HTTP/2`. Generally `udp` for `HTTP/3`. Other obscure implementations are possible. -**[18] `user_agent.synthetic.type`:** This attribute MAY be derived from the contents of the `user_agent.original` attribute. Components that populate the attribute are responsible for determining what they consider to be synthetic bot or test traffic. This attribute can either be set for self-identification purposes, or on telemetry detected to be generated as a result of a synthetic request. This attribute is useful for distinguishing between genuine client traffic and synthetic traffic generated by bots or tests. +**[20] `user_agent.synthetic.type`:** This attribute MAY be derived from the contents of the `user_agent.original` attribute. Components that populate the attribute are responsible for determining what they consider to be synthetic bot or test traffic. This attribute can either be set for self-identification purposes, or on telemetry detected to be generated as a result of a synthetic request. This attribute is useful for distinguishing between genuine client traffic and synthetic traffic generated by bots or tests. The following attributes can be important for making sampling decisions and SHOULD be provided **at span creation time** (if provided at all): diff --git a/docs/registry/attributes/http.md b/docs/registry/attributes/http.md index 5f48f1d314..858c5316d5 100644 --- a/docs/registry/attributes/http.md +++ b/docs/registry/attributes/http.md @@ -15,19 +15,35 @@ This document defines semantic convention attributes in the HTTP namespace. | Key | Stability | Value Type | Description | Example Values | | --- | --- | --- | --- | --- | | `http.connection.state` | ![Development](https://img.shields.io/badge/-development-blue) | string | State of the HTTP connection in the HTTP connection pool. | `active`; `idle` | +| `http.request.body.content` | ![Development](https://img.shields.io/badge/-development-blue) | any | The content of the uncompressed HTTP request body, captured as a string when the request content is textual or as binary otherwise. [1] | `Hello world!`; `{"foo: "bar"}`; `aGVsbG8=` | | `http.request.body.size` | ![Development](https://img.shields.io/badge/-development-blue) | int | The size of the request payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the [Content-Length](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length) header. For requests using transport encoding, this should be the compressed size. | `3495` | -| `http.request.header.` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | string[] | HTTP request headers, `` being the normalized HTTP Header name (lowercase), the value being the header values. [1] | `["application/json"]`; `["1.2.3.4", "1.2.3.5"]` | -| `http.request.method` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | string | HTTP request method. [2] | `GET`; `POST`; `HEAD` | +| `http.request.header.` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | string[] | HTTP request headers, `` being the normalized HTTP Header name (lowercase), the value being the header values. [2] | `["application/json"]`; `["1.2.3.4", "1.2.3.5"]` | +| `http.request.method` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | string | HTTP request method. [3] | `GET`; `POST`; `HEAD` | | `http.request.method_original` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | string | Original HTTP method sent by the client in the request line. | `GeT`; `ACL`; `foo` | -| `http.request.resend_count` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | The ordinal number of request resending attempt (for any reason, including redirects). [3] | `3` | +| `http.request.resend_count` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | The ordinal number of request resending attempt (for any reason, including redirects). [4] | `3` | | `http.request.size` | ![Development](https://img.shields.io/badge/-development-blue) | int | The total size of the request in bytes. This should be the total number of bytes sent over the wire, including the request line (HTTP/1.1), framing (HTTP/2 and HTTP/3), headers, and request body if any. | `1437` | +| `http.response.body.content` | ![Development](https://img.shields.io/badge/-development-blue) | any | The content of the uncompressed HTTP response body, captured as a string when the response content is textual or as byte array otherwise. [5] | `Hello world!`; `{"foo: "bar"}`; `aGVsbG8=` | | `http.response.body.size` | ![Development](https://img.shields.io/badge/-development-blue) | int | The size of the response payload body in bytes. This is the number of bytes transferred excluding headers and is often, but not always, present as the [Content-Length](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-length) header. For requests using transport encoding, this should be the compressed size. | `3495` | -| `http.response.header.` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | string[] | HTTP response headers, `` being the normalized HTTP Header name (lowercase), the value being the header values. [4] | `["application/json"]`; `["abc", "def"]` | +| `http.response.header.` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | string[] | HTTP response headers, `` being the normalized HTTP Header name (lowercase), the value being the header values. [6] | `["application/json"]`; `["abc", "def"]` | | `http.response.size` | ![Development](https://img.shields.io/badge/-development-blue) | int | The total size of the response in bytes. This should be the total number of bytes sent over the wire, including the status line (HTTP/1.1), framing (HTTP/2 and HTTP/3), headers, and response body and trailers if any. | `1437` | | `http.response.status_code` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | int | [HTTP response status code](https://tools.ietf.org/html/rfc7231#section-6). | `200` | -| `http.route` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | string | The matched route template for the request. This MUST be low-cardinality and include all static path segments, with dynamic path segments represented with placeholders. [5] | `/users/:userID?`; `my-controller/my-action/{id?}` | +| `http.route` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) | string | The matched route template for the request. This MUST be low-cardinality and include all static path segments, with dynamic path segments represented with placeholders. [7] | `/users/:userID?`; `my-controller/my-action/{id?}` | -**[1] `http.request.header.`:** Instrumentations SHOULD require an explicit configuration of which headers are to be captured. +**[1] `http.request.body.content`:** Captured value MAY be limited in size and thus value is expected to be truncated in many cases. + +Instrumentations MUST disable capture by default and provide an opt-in configuration option to enable it as it can capture sensitive information and cause performance overhead. + +Instrumentations SHOULD capture the body as string whenever possible as it makes it easier to use in human-readable form, also it allows to implement sanitization if needed. + +Textual content is typically detected based on the [Content-Type](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-type) header using heuristics +such as checking if the media type starts with `text/` or ends with `/json`, `+json`, `/xml`, `+xml`, `/yaml` or `+yaml` or if the media type contains an explicit charset with `;charset=`. + +Instrumentations that implement request body recording MUST NOT intentionally introduce side effects such as changing stream position or closing body stream independently from the +application. + +When body is recorded on HTTP client or server spans, the instrumentation SHOULD record part of the body that was sent or received at the time HTTP span has ended. + +**[2] `http.request.header.`:** Instrumentations SHOULD require an explicit configuration of which headers are to be captured. Including all request headers can be a security risk - explicit configuration helps avoid leaking sensitive information. The `User-Agent` header is already captured in the `user_agent.original` attribute. @@ -44,7 +60,7 @@ Examples: - A header `X-Forwarded-For: 1.2.3.4, 1.2.3.5` SHOULD be recorded as the `http.request.header.x-forwarded-for` attribute with value `["1.2.3.4", "1.2.3.5"]` or `["1.2.3.4, 1.2.3.5"]` depending on the HTTP library. -**[2] `http.request.method`:** HTTP request method value SHOULD be "known" to the instrumentation. +**[3] `http.request.method`:** HTTP request method value SHOULD be "known" to the instrumentation. By default, this convention defines "known" methods as the ones listed in [RFC9110](https://www.rfc-editor.org/rfc/rfc9110.html#name-methods), the PATCH method defined in [RFC5789](https://www.rfc-editor.org/rfc/rfc5789.html) and the QUERY method defined in [httpbis-safe-method-w-body](https://datatracker.ietf.org/doc/draft-ietf-httpbis-safe-method-w-body/?include_text=1). @@ -67,9 +83,23 @@ HTTP method names are case-sensitive and `http.request.method` attribute value M Instrumentations for specific web frameworks that consider HTTP methods to be case insensitive, SHOULD populate a canonical equivalent. Tracing instrumentations that do so, MUST also set `http.request.method_original` to the original value. -**[3] `http.request.resend_count`:** The resend count SHOULD be updated each time an HTTP request gets resent by the client, regardless of what was the cause of the resending (e.g. redirection, authorization failure, 503 Server Unavailable, network issues, or any other). +**[4] `http.request.resend_count`:** The resend count SHOULD be updated each time an HTTP request gets resent by the client, regardless of what was the cause of the resending (e.g. redirection, authorization failure, 503 Server Unavailable, network issues, or any other). + +**[5] `http.response.body.content`:** Captured value MAY be limited in size and thus value is expected to be truncated in many cases. + +Instrumentations MUST disable capture by default and provide an opt-in configuration option to enable it as it can capture sensitive information and cause performance overhead. + +Instrumentations SHOULD capture the body as string whenever possible as it makes it easier to use in human-readable form, also it allows to implement sanitization if needed. + +Textual content is typically detected based on the [Content-Type](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-type) header using heuristics +such as checking if the media type starts with `text/` or ends with `/json`, `+json`, `/xml`, `+xml`, `/yaml` or `+yaml` or if the media type contains an explicit charset with `;charset=`. + +Instrumentations that implement response body recording MUST NOT intentionally introduce side effects such as changing stream position or closing body stream independently from the +application. + +When body is recorded on HTTP client or server spans, the instrumentation SHOULD record part of the body that was sent or received at the time HTTP span has ended. -**[4] `http.response.header.`:** Instrumentations SHOULD require an explicit configuration of which headers are to be captured. +**[6] `http.response.header.`:** Instrumentations SHOULD require an explicit configuration of which headers are to be captured. Including all response headers can be a security risk - explicit configuration helps avoid leaking sensitive information. Users MAY explicitly configure instrumentations to capture them even though it is not recommended. @@ -85,7 +115,7 @@ Examples: - A header `My-custom-header: abc, def` header SHOULD be recorded as the `http.response.header.my-custom-header` attribute with value `["abc", "def"]` or `["abc, def"]` depending on the HTTP library. -**[5] `http.route`:** MUST NOT be populated when this is not supported by the HTTP server framework as the route attribute should have low-cardinality and the URI path can NOT substitute it. +**[7] `http.route`:** MUST NOT be populated when this is not supported by the HTTP server framework as the route attribute should have low-cardinality and the URI path can NOT substitute it. SHOULD include the [application root](/docs/http/http-spans.md#http-server-definitions) if there is one. A static path segment is a part of the route template with a fixed, low-cardinality value. This includes literal strings like `/users/` and placeholders that diff --git a/model/http/registry.yaml b/model/http/registry.yaml index d91c2d8762..8aa552b801 100644 --- a/model/http/registry.yaml +++ b/model/http/registry.yaml @@ -204,3 +204,43 @@ groups: brief: State of the HTTP connection in the HTTP connection pool. stability: development examples: ["active", "idle"] + - id: http.request.body.content + type: any + brief: > + The content of the uncompressed HTTP request body, captured as a string when the request content is textual or as binary otherwise. + note: | + Captured value MAY be limited in size and thus value is expected to be truncated in many cases. + + Instrumentations MUST disable capture by default and provide an opt-in configuration option to enable it as it can capture sensitive information and cause performance overhead. + + Instrumentations SHOULD capture the body as string whenever possible as it makes it easier to use in human-readable form, also it allows to implement sanitization if needed. + + Textual content is typically detected based on the [Content-Type](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-type) header using heuristics + such as checking if the media type starts with `text/` or ends with `/json`, `+json`, `/xml`, `+xml`, `/yaml` or `+yaml` or if the media type contains an explicit charset with `;charset=`. + + Instrumentations that implement request body recording MUST NOT intentionally introduce side effects such as changing stream position or closing body stream independently from the + application. + + When body is recorded on HTTP client or server spans, the instrumentation SHOULD record part of the body that was sent or received at the time HTTP span has ended. + examples: ['Hello world!', '{"foo: "bar"}', 'aGVsbG8='] + stability: development + - id: http.response.body.content + type: any + brief: > + The content of the uncompressed HTTP response body, captured as a string when the response content is textual or as byte array otherwise. + note: | + Captured value MAY be limited in size and thus value is expected to be truncated in many cases. + + Instrumentations MUST disable capture by default and provide an opt-in configuration option to enable it as it can capture sensitive information and cause performance overhead. + + Instrumentations SHOULD capture the body as string whenever possible as it makes it easier to use in human-readable form, also it allows to implement sanitization if needed. + + Textual content is typically detected based on the [Content-Type](https://www.rfc-editor.org/rfc/rfc9110.html#field.content-type) header using heuristics + such as checking if the media type starts with `text/` or ends with `/json`, `+json`, `/xml`, `+xml`, `/yaml` or `+yaml` or if the media type contains an explicit charset with `;charset=`. + + Instrumentations that implement response body recording MUST NOT intentionally introduce side effects such as changing stream position or closing body stream independently from the + application. + + When body is recorded on HTTP client or server spans, the instrumentation SHOULD record part of the body that was sent or received at the time HTTP span has ended. + examples: ['Hello world!', '{"foo: "bar"}', 'aGVsbG8='] + stability: development diff --git a/model/http/spans.yaml b/model/http/spans.yaml index dd49374df9..9765ba1f9a 100644 --- a/model/http/spans.yaml +++ b/model/http/spans.yaml @@ -64,6 +64,10 @@ groups: requirement_level: opt_in - ref: user_agent.synthetic.type requirement_level: opt_in + - ref: http.request.body.content + requirement_level: opt_in + - ref: http.response.body.content + requirement_level: opt_in - id: span.http.server type: span @@ -141,3 +145,7 @@ groups: requirement_level: opt_in - ref: user_agent.synthetic.type requirement_level: opt_in + - ref: http.request.body.content + requirement_level: opt_in + - ref: http.response.body.content + requirement_level: opt_in