diff --git a/.chloggen/allow-http-route-on-client.yaml b/.chloggen/allow-http-route-on-client.yaml
new file mode 100644
index 0000000000..4b8a6e3a82
--- /dev/null
+++ b/.chloggen/allow-http-route-on-client.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: New `url.template` attribute added to URL, HTTP client attributes are extended with optional low-cardinality `url.template`
+
+# 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: [675]
+
+# (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/attributes-registry/url.md b/docs/attributes-registry/url.md
index 3594692af9..90c80989f9 100644
--- a/docs/attributes-registry/url.md
+++ b/docs/attributes-registry/url.md
@@ -23,6 +23,7 @@ Attributes describing URL.
| `url.registered_domain` | string | The highest registered url domain, stripped of the subdomain. [7] | `example.com`; `foo.co.uk` | ![Experimental](https://img.shields.io/badge/-experimental-blue) |
| `url.scheme` | string | The [URI scheme](https://www.rfc-editor.org/rfc/rfc3986#section-3.1) component identifying the used protocol. | `https`; `ftp`; `telnet` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) |
| `url.subdomain` | string | The subdomain portion of a fully qualified domain name includes all of the names except the host name under the registered_domain. In a partially qualified domain, or if the qualification level of the full name cannot be determined, subdomain contains all of the names below the registered domain. [8] | `east`; `sub2.sub1` | ![Experimental](https://img.shields.io/badge/-experimental-blue) |
+| `url.template` | string | The low-cardinality template of an [absolute path reference](https://www.rfc-editor.org/rfc/rfc3986#section-4.2). | `/users/{id}`; `/users/:id`; `/users?id={id}` | ![Experimental](https://img.shields.io/badge/-experimental-blue) |
| `url.top_level_domain` | string | The effective top level domain (eTLD), also known as the domain suffix, is the last part of the domain name. For example, the top level domain for example.com is `com`. [9] | `com`; `co.uk` | ![Experimental](https://img.shields.io/badge/-experimental-blue) |
**[1]:** In some cases a URL may refer to an IP and/or port directly, without a domain name. In this case, the IP address would go to the domain field. If the URL contains a [literal IPv6 address](https://www.rfc-editor.org/rfc/rfc2732#section-2) enclosed by `[` and `]`, the `[` and `]` characters should also be captured in the domain field.
diff --git a/docs/http/http-metrics.md b/docs/http/http-metrics.md
index 6f68efb10f..c696d74737 100644
--- a/docs/http/http-metrics.md
+++ b/docs/http/http-metrics.md
@@ -21,6 +21,7 @@ operations. By adding HTTP attributes to metric events it allows for finely tune
- [Metric: `http.server.response.body.size`](#metric-httpserverresponsebodysize)
- [HTTP Client](#http-client)
- [Metric: `http.client.request.duration`](#metric-httpclientrequestduration)
+ - [Experimental attributes](#experimental-attributes)
- [Metric: `http.client.request.body.size`](#metric-httpclientrequestbodysize)
- [Metric: `http.client.response.body.size`](#metric-httpclientresponsebodysize)
- [Metric: `http.client.open_connections`](#metric-httpclientopen_connections)
@@ -514,6 +515,20 @@ If the request has completed successfully, instrumentations SHOULD NOT set `erro
| `_OTHER` | A fallback error value to be used when the instrumentation doesn't define a custom value. | ![Stable](https://img.shields.io/badge/-stable-lightgreen) |
+#### Experimental attributes
+
+**Status**: [Experimental][DocumentStatus]
+
+Instrumentations MAY allow users to enable additional experimental attributes.
+
+
+| Attribute | Type | Description | Examples | [Requirement Level](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/) | Stability |
+|---|---|---|---|---|---|
+| [`url.template`](/docs/attributes-registry/url.md) | string | The low-cardinality template of an [absolute path reference](https://www.rfc-editor.org/rfc/rfc3986#section-4.2). [1] | `/users/{id}`; `/users/:id`; `/users?id={id}` | `Opt-In` | ![Experimental](https://img.shields.io/badge/-experimental-blue) |
+
+**[1]:** 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.
+
+
### Metric: `http.client.request.body.size`
This metric is optional.
@@ -535,7 +550,8 @@ This metric is optional.
| [`error.type`](/docs/attributes-registry/error.md) | string | Describes a class of error the operation ended with. [4] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | `Conditionally Required` If request has ended with an error. | ![Stable](https://img.shields.io/badge/-stable-lightgreen) |
| [`http.response.status_code`](/docs/attributes-registry/http.md) | int | [HTTP response status code](https://tools.ietf.org/html/rfc7231#section-6). | `200` | `Conditionally Required` If and only if one was received/sent. | ![Stable](https://img.shields.io/badge/-stable-lightgreen) |
| [`network.protocol.name`](/docs/attributes-registry/network.md) | string | [OSI application layer](https://osi-model.com/application-layer/) or non-OSI equivalent. [5] | `http`; `spdy` | `Conditionally Required` [6] | ![Stable](https://img.shields.io/badge/-stable-lightgreen) |
-| [`network.protocol.version`](/docs/attributes-registry/network.md) | string | The actual version of the protocol used for network communication. [7] | `1.0`; `1.1`; `2`; `3` | `Recommended` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) |
+| [`url.template`](/docs/attributes-registry/url.md) | string | The low-cardinality template of an [absolute path reference](https://www.rfc-editor.org/rfc/rfc3986#section-4.2). [7] | `/users/{id}`; `/users/:id`; `/users?id={id}` | `Conditionally Required` If available. | ![Experimental](https://img.shields.io/badge/-experimental-blue) |
+| [`network.protocol.version`](/docs/attributes-registry/network.md) | string | The actual version of the protocol used for network communication. [8] | `1.0`; `1.1`; `2`; `3` | `Recommended` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) |
| [`url.scheme`](/docs/attributes-registry/url.md) | string | The [URI scheme](https://www.rfc-editor.org/rfc/rfc3986#section-3.1) component identifying the used protocol. | `http`; `https` | `Opt-In` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) |
**[1]:** HTTP request method value SHOULD be "known" to the instrumentation.
@@ -578,7 +594,9 @@ If the request has completed successfully, instrumentations SHOULD NOT set `erro
**[6]:** If not `http` and `network.protocol.version` is set.
-**[7]:** 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.
+**[7]:** 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.
+
+**[8]:** 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.
`http.request.method` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used.
@@ -623,7 +641,8 @@ This metric is optional.
| [`error.type`](/docs/attributes-registry/error.md) | string | Describes a class of error the operation ended with. [4] | `timeout`; `java.net.UnknownHostException`; `server_certificate_invalid`; `500` | `Conditionally Required` If request has ended with an error. | ![Stable](https://img.shields.io/badge/-stable-lightgreen) |
| [`http.response.status_code`](/docs/attributes-registry/http.md) | int | [HTTP response status code](https://tools.ietf.org/html/rfc7231#section-6). | `200` | `Conditionally Required` If and only if one was received/sent. | ![Stable](https://img.shields.io/badge/-stable-lightgreen) |
| [`network.protocol.name`](/docs/attributes-registry/network.md) | string | [OSI application layer](https://osi-model.com/application-layer/) or non-OSI equivalent. [5] | `http`; `spdy` | `Conditionally Required` [6] | ![Stable](https://img.shields.io/badge/-stable-lightgreen) |
-| [`network.protocol.version`](/docs/attributes-registry/network.md) | string | The actual version of the protocol used for network communication. [7] | `1.0`; `1.1`; `2`; `3` | `Recommended` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) |
+| [`url.template`](/docs/attributes-registry/url.md) | string | The low-cardinality template of an [absolute path reference](https://www.rfc-editor.org/rfc/rfc3986#section-4.2). [7] | `/users/{id}`; `/users/:id`; `/users?id={id}` | `Conditionally Required` If available. | ![Experimental](https://img.shields.io/badge/-experimental-blue) |
+| [`network.protocol.version`](/docs/attributes-registry/network.md) | string | The actual version of the protocol used for network communication. [8] | `1.0`; `1.1`; `2`; `3` | `Recommended` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) |
| [`url.scheme`](/docs/attributes-registry/url.md) | string | The [URI scheme](https://www.rfc-editor.org/rfc/rfc3986#section-3.1) component identifying the used protocol. | `http`; `https` | `Opt-In` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) |
**[1]:** HTTP request method value SHOULD be "known" to the instrumentation.
@@ -666,7 +685,9 @@ If the request has completed successfully, instrumentations SHOULD NOT set `erro
**[6]:** If not `http` and `network.protocol.version` is set.
-**[7]:** 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.
+**[7]:** 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.
+
+**[8]:** 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.
`http.request.method` has the following list of well-known values. If one of them applies, then the respective value MUST be used; otherwise, a custom value MAY be used.
@@ -771,14 +792,17 @@ This metric is optional.
|---|---|---|---|---|---|
| [`server.address`](/docs/attributes-registry/server.md) | string | Server domain name if available without reverse DNS lookup; otherwise, IP address or Unix domain socket name. [1] | `example.com`; `10.1.2.80`; `/tmp/my.sock` | `Required` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) |
| [`server.port`](/docs/attributes-registry/server.md) | int | Port identifier of the ["URI origin"](https://www.rfc-editor.org/rfc/rfc9110.html#name-uri-origin) HTTP request is sent to. [2] | `80`; `8080`; `443` | `Required` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) |
-| [`http.request.method`](/docs/attributes-registry/http.md) | string | HTTP request method. [3] | `GET`; `POST`; `HEAD` | `Recommended` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) |
+| [`url.template`](/docs/attributes-registry/url.md) | string | The low-cardinality template of an [absolute path reference](https://www.rfc-editor.org/rfc/rfc3986#section-4.2). [3] | `/users/{id}`; `/users/:id`; `/users?id={id}` | `Conditionally Required` If available. | ![Experimental](https://img.shields.io/badge/-experimental-blue) |
+| [`http.request.method`](/docs/attributes-registry/http.md) | string | HTTP request method. [4] | `GET`; `POST`; `HEAD` | `Recommended` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) |
| [`url.scheme`](/docs/attributes-registry/url.md) | string | The [URI scheme](https://www.rfc-editor.org/rfc/rfc3986#section-3.1) component identifying the used protocol. | `http`; `https` | `Opt-In` | ![Stable](https://img.shields.io/badge/-stable-lightgreen) |
**[1]:** When observed from the client side, and when communicating through an intermediary, `server.address` SHOULD represent the server address behind any intermediaries, for example proxies, if it's available.
**[2]:** When observed from the client side, and when communicating through an intermediary, `server.port` SHOULD represent the server port behind any intermediaries, for example proxies, if it's available.
-**[3]:** HTTP request method value SHOULD be "known" to the instrumentation.
+**[3]:** 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.
+
+**[4]:** 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)
and the PATCH method defined in [RFC5789](https://www.rfc-editor.org/rfc/rfc5789.html).
diff --git a/docs/http/http-spans.md b/docs/http/http-spans.md
index 91c060240c..1c95e79d9e 100644
--- a/docs/http/http-spans.md
+++ b/docs/http/http-spans.md
@@ -68,25 +68,23 @@ and various HTTP versions like 1.1, 2 and SPDY.
HTTP spans MUST follow the overall [guidelines for span names](https://github.com/open-telemetry/opentelemetry-specification/tree/v1.31.0/specification/trace/api.md#span).
+HTTP span names SHOULD be `{method} {target}` if there is a (low-cardinality) `target` available. If there is no (low-cardinality) `{target}` available, HTTP span names SHOULD be `{method}`.
+
-HTTP server span names SHOULD be `{method} {http.route}` if there is a
-(low-cardinality) `http.route` available (see below for the exact definition of the [`{method}`](#method-placeholder) placeholder).
-
-If there is no (low-cardinality) `http.route` available, HTTP server span names
-SHOULD be [`{method}`](#method-placeholder).
-
-HTTP client spans have no `http.route` attribute since client-side instrumentation
-is not generally aware of the "route", and therefore HTTP client span names SHOULD be
-[`{method}`](#method-placeholder).
+(see below for the exact definition of the [`{method}`](#method-placeholder) and [`{target}`](#target-placeholder) placeholders).
The `{method}` MUST be `{http.request.method}` if the method represents the original method known to the instrumentation.
In other cases (when `{http.request.method}` is set to `_OTHER`), `{method}` MUST be `HTTP`.
-Instrumentation MUST NOT default to using URI
-path as span name, but MAY provide hooks to allow custom logic to override the
-default span name.
+The `{target}` SHOULD be one of the following:
+
+- [`http.route`](/docs/attributes-registry/http.md) for HTTP Server spans
+- [`url.template`](/docs/attributes-registry/url.md) for HTTP Client spans if enabled and available (![Experimental](https://img.shields.io/badge/-experimental-blue))
+- Other value MAY be provided through custom hooks at span start time or later.
+
+Instrumentation MUST NOT default to using URI path as a `{target}`.
## Status
@@ -255,6 +253,9 @@ Instrumentations MAY allow users to enable additional experimental attributes.
| [`http.request.size`](/docs/attributes-registry/http.md) | 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` | `Opt-In` | ![Experimental](https://img.shields.io/badge/-experimental-blue) |
| [`http.response.body.size`](/docs/attributes-registry/http.md) | 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` | `Opt-In` | ![Experimental](https://img.shields.io/badge/-experimental-blue) |
| [`http.response.size`](/docs/attributes-registry/http.md) | 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` | `Opt-In` | ![Experimental](https://img.shields.io/badge/-experimental-blue) |
+| [`url.template`](/docs/attributes-registry/url.md) | string | The low-cardinality template of an [absolute path reference](https://www.rfc-editor.org/rfc/rfc3986#section-4.2). [1] | `/users/{id}`; `/users/:id`; `/users?id={id}` | `Opt-In` | ![Experimental](https://img.shields.io/badge/-experimental-blue) |
+
+**[1]:** 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.
### HTTP client span duration
diff --git a/model/http-common.yaml b/model/http-common.yaml
index 2187943dda..8624dd4ce4 100644
--- a/model/http-common.yaml
+++ b/model/http-common.yaml
@@ -56,6 +56,15 @@ groups:
requirement_level: opt_in
examples: ["http", "https"]
+ - id: attributes.http.client.experimental
+ type: attribute_group
+ brief: "Experimental HTTP attributes."
+ attributes:
+ - ref: url.template
+ requirement_level: opt_in
+ note: >
+ 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.
+
- id: attributes.http.server
type: attribute_group
brief: 'HTTP Server attributes'
diff --git a/model/metrics/http.yaml b/model/metrics/http.yaml
index ba1ca5a522..78283fdf75 100644
--- a/model/metrics/http.yaml
+++ b/model/metrics/http.yaml
@@ -23,6 +23,17 @@ groups:
brief: 'HTTP client attributes'
extends: attributes.http.client
+ - id: metric_attributes.http.client.experimental
+ type: attribute_group
+ brief: 'HTTP client experimental attributes'
+ extends: metric_attributes.http.client
+ attributes:
+ - ref: url.template
+ requirement_level:
+ conditionally_required: If available.
+ note: >
+ 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.
+
- id: metric.http.server.request.duration
type: metric
metric_name: http.server.request.duration
@@ -110,7 +121,7 @@ groups:
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.
- extends: metric_attributes.http.client
+ extends: metric_attributes.http.client.experimental
- id: metric.http.client.response.body.size
type: metric
@@ -123,7 +134,7 @@ groups:
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.
- extends: metric_attributes.http.client
+ extends: metric_attributes.http.client.experimental
- id: metric.http.client.open_connections
type: metric
@@ -190,3 +201,8 @@ groups:
- ref: url.scheme
requirement_level: opt_in
examples: ["http", "https"]
+ - ref: url.template
+ requirement_level:
+ conditionally_required: If available.
+ note: >
+ 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.
diff --git a/model/registry/url.yaml b/model/registry/url.yaml
index e0bd823f1c..f979c23a5f 100644
--- a/model/registry/url.yaml
+++ b/model/registry/url.yaml
@@ -105,6 +105,12 @@ groups:
note: >
The subdomain portion of `www.east.mydomain.co.uk` is `east`. If the domain has multiple levels of subdomain,
such as `sub2.sub1.example.com`, the subdomain field should contain `sub2.sub1`, with no trailing period.
+ - id: template
+ type: string
+ stability: experimental
+ brief: >
+ The low-cardinality template of an [absolute path reference](https://www.rfc-editor.org/rfc/rfc3986#section-4.2).
+ examples: ["/users/{id}", "/users/:id", "/users?id={id}"]
- id: top_level_domain
type: string
stability: experimental
diff --git a/model/trace/http.yaml b/model/trace/http.yaml
index 9bfadac3c9..23509a2814 100644
--- a/model/trace/http.yaml
+++ b/model/trace/http.yaml
@@ -42,6 +42,7 @@ groups:
type: attribute_group
brief: 'Experimental attributes for HTTP Client spans'
stability: experimental
+ extends: attributes.http.client.experimental
attributes:
- ref: http.request.size
requirement_level: opt_in