feat(core)!: Update all spans to use url.full, url.fragment and url.query - #22547
Conversation
url, http.query and http.fragment are directly added
size-limit report 📦
|
isaacs
left a comment
There was a problem hiding this comment.
Some suggestions for cleanup and I think the clanker comment is likely valid.
It might be good to add a helper utility that can strip fragment/search strings, since we have url.search?.strip(1) || undefined in a bunch of places now? Something like:
function stripUrlPart(part: string || undefined || null, prefix: '?' : '#') {
return (part && part.charAt(0) === prefix) ? part.strip(1) : undefined;
}| ...(parsedUrl.search ? { 'http.query': parsedUrl.search.slice(1) || undefined } : {}), | ||
| ...(parsedUrl.hash ? { 'http.fragment': parsedUrl.hash.slice(1) || undefined } : {}), |
There was a problem hiding this comment.
These get serialized with JSON.stringify, so undefined values are safe. Not a blocker, but worth cleaning up where we encounter it, imo. Also, these can probably pull in the costs to cut down on bundle size ever so slightly:
| ...(parsedUrl.search ? { 'http.query': parsedUrl.search.slice(1) || undefined } : {}), | |
| ...(parsedUrl.hash ? { 'http.fragment': parsedUrl.hash.slice(1) || undefined } : {}), | |
| [HTTP_FRAGMENT]: parsedUrl.hash?.slice(1) || undefined, | |
| [HTTP_QUERY]: parsedUrl?.search?.slice(1) || undefined, |
| const query = urlObj?.search ? urlObj.search.slice(1) || undefined : undefined; | ||
| const fragment = urlObj?.hash ? urlObj.hash.slice(1) || undefined : undefined; |
There was a problem hiding this comment.
Same here
| const query = urlObj?.search ? urlObj.search.slice(1) || undefined : undefined; | |
| const fragment = urlObj?.hash ? urlObj.hash.slice(1) || undefined : undefined; | |
| const query = urlObj?.search?.slice(1) || undefined; | |
| const fragment = urlObj?.hash?.slice(1) || undefined; |
There was a problem hiding this comment.
Should this be requestUrl.search?.strip(1) || undefined?
url, http.query and http.fragment are directly addedurl.full, url.fragment and url.query
There was a problem hiding this comment.
same q for this test: shouldn't url no longer be sent?
| * unconditionally — setting an attribute to `undefined` is a no-op. | ||
| */ | ||
| export function getUrlQuery(query: string | undefined): string | undefined { | ||
| return stripUrlPartPrefix(query, '?'); |
There was a problem hiding this comment.
l: instead of the helper function, could we simply inline this?
| return stripUrlPartPrefix(query, '?'); | |
| return query?.replace(/^\?/, '') |
might save a couple of bytes. Same for the fragment below
| } | ||
| if (parsedUrl.hash) { | ||
| attributes['http.fragment'] = parsedUrl.hash; | ||
| attributes[SERVER_ADDRESS] = parsedUrl.host; |
There was a problem hiding this comment.
Bug: The code incorrectly assigns parsedUrl.host to the SERVER_ADDRESS attribute, which includes the port number for non-standard ports, violating OpenTelemetry semantic conventions.
Severity: MEDIUM
Suggested Fix
To conform to OpenTelemetry standards, change the assignment for SERVER_ADDRESS from parsedUrl.host to parsedUrl.hostname. This will correctly exclude the port number from the server.address attribute.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location: packages/core/src/fetch.ts#L401
Potential issue: The code sets the `SERVER_ADDRESS` attribute using `parsedUrl.host`.
According to the JavaScript URL API, `URL.host` includes the port number if it's
non-standard (e.g., `example.com:8080`). This violates the OpenTelemetry semantic
convention for `server.address`, which should only contain the domain name or IP
address, with the port stored in a separate `server.port` attribute. This bug is
triggered for any HTTP request made to a non-standard port, resulting in semantically
incorrect telemetry data that can confuse downstream monitoring and analysis tools.
Also affects:
packages/browser/src/tracing/request.ts:394~394
url.full, url.fragment and url.queryurl.full, url.fragment and url.query
| 'url.fragment'?: string; | ||
| 'url.query'?: string; |
There was a problem hiding this comment.
Is it possible to use a template literal type here? Then we could import from sentry conventions.
There was a problem hiding this comment.
yeah should be, there are some leftovers I suppose that should be updated!
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 2aefe20. Configure here.
| attributes[URL_FULL] = isURLObjectRelative(urlObject) | ||
| ? getSanitizedUrlStringFromUrlObject(urlObject) | ||
| : urlObject.href; |
There was a problem hiding this comment.
Bug: The url.full attribute is set to a relative pathname for relative URLs, which violates the OpenTelemetry semantic convention requiring an absolute URL.
Severity: MEDIUM
Suggested Fix
To comply with the OpenTelemetry specification, the url.full attribute should only be set when an absolute URL is available. For relative URLs, this attribute should be omitted entirely instead of being set to a relative path.
Prompt for AI Agent
Review the code at the location below. A potential bug has been identified by an AI
agent. Verify if this is a real issue. If it is, propose a fix; if not, explain why it's
not valid.
Location: packages/core/src/utils/url.ts#L211-L213
Potential issue: The code sets the OpenTelemetry attribute `url.full` to a relative path
(e.g., `/api/users`) when processing relative URLs. This violates the OpenTelemetry
semantic convention, which mandates that `url.full` must be an absolute URL. While this
may not cause an immediate crash, it produces non-compliant span data. Downstream
consumers and backend systems that expect a full, parseable URL may fail to process
these spans correctly, leading to issues like incorrect span grouping or data processing
errors.
There was a problem hiding this comment.
this is true, but I think the best we can do in this case.

This updates all spans we emit to:
a.
http.urlandurlbecomeurl.fullb.
http.fragmentbecomesurl.fragment(with unified semantic omitting the#)c.
http.searchbecomesurl.search(with unified semantic omitting the?)For this we introduce new utility helpers to ensure consistent formatting of search/fragment.