Skip to content

Commit d6bc9b0

Browse files
committed
feat(core): Ensure url, http.query and http.fragment are directly added
1 parent 34eb8bc commit d6bc9b0

5 files changed

Lines changed: 53 additions & 15 deletions

File tree

packages/core/src/fetch.ts

Lines changed: 12 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,11 @@
1-
import { HTTP_URL, URL_FULL } from '@sentry/conventions/attributes';
1+
import {
2+
HTTP_FRAGMENT,
3+
HTTP_METHOD,
4+
HTTP_QUERY,
5+
HTTP_URL,
6+
SERVER_ADDRESS,
7+
URL_FULL,
8+
} from '@sentry/conventions/attributes';
29
import { getClient } from './currentScopes';
310
import { SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from './semanticAttributes';
411
import { setHttpStatus, SPAN_STATUS_ERROR, spanIsIgnored, startInactiveSpan } from './tracing';
@@ -387,7 +394,7 @@ function getFetchSpanAttributes(
387394
const attributes: SpanAttributes = {
388395
url: stripDataUrlContent(url),
389396
type: 'fetch',
390-
'http.method': method,
397+
[HTTP_METHOD]: method,
391398
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: spanOrigin,
392399
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: 'http.client',
393400
};
@@ -396,13 +403,13 @@ function getFetchSpanAttributes(
396403
// oxlint-disable-next-line typescript/no-deprecated
397404
attributes[HTTP_URL] = stripDataUrlContent(parsedUrl.href);
398405
attributes[URL_FULL] = stripDataUrlContent(parsedUrl.href);
399-
attributes['server.address'] = parsedUrl.host;
406+
attributes[SERVER_ADDRESS] = parsedUrl.host;
400407
}
401408
if (parsedUrl.search) {
402-
attributes['http.query'] = parsedUrl.search;
409+
attributes[HTTP_QUERY] = parsedUrl.search.slice(1) || undefined;
403410
}
404411
if (parsedUrl.hash) {
405-
attributes['http.fragment'] = parsedUrl.hash;
412+
attributes[HTTP_FRAGMENT] = parsedUrl.hash.slice(1) || undefined;
406413
}
407414
}
408415
return attributes;

packages/core/src/integrations/http/add-outgoing-request-breadcrumb.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,8 @@ export function addOutgoingRequestBreadcrumb(
2424
status_code: statusCode,
2525
url: getSanitizedUrlString(parsedUrl),
2626
'http.method': request.method || 'GET',
27-
...(parsedUrl.search ? { 'http.query': parsedUrl.search } : {}),
28-
...(parsedUrl.hash ? { 'http.fragment': parsedUrl.hash } : {}),
27+
...(parsedUrl.search ? { 'http.query': parsedUrl.search.slice(1) || undefined } : {}),
28+
...(parsedUrl.hash ? { 'http.fragment': parsedUrl.hash.slice(1) || undefined } : {}),
2929
},
3030
type: 'http',
3131
level,

packages/core/src/utils/url.ts

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,17 @@
1-
import { URL_FULL } from '@sentry/conventions/attributes';
1+
import {
2+
HTTP_FRAGMENT,
3+
HTTP_QUERY,
4+
HTTP_ROUTE,
5+
SERVER_ADDRESS,
6+
URL_DOMAIN,
7+
URL_FRAGMENT,
8+
URL_FULL,
9+
URL_PATH,
10+
URL_PORT,
11+
URL_QUERY,
12+
URL_SCHEME,
13+
URL_TEMPLATE,
14+
} from '@sentry/conventions/attributes';
215
import {
316
SEMANTIC_ATTRIBUTE_HTTP_REQUEST_METHOD,
417
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
@@ -165,7 +178,7 @@ export function getHttpSpanDetailsFromUrlObject(
165178

166179
if (routeName) {
167180
// This is based on https://opentelemetry.io/docs/specs/semconv/http/http-spans/#name
168-
attributes[kind === 'server' ? 'http.route' : 'url.template'] = routeName;
181+
attributes[kind === 'server' ? HTTP_ROUTE : URL_TEMPLATE] = routeName;
169182
attributes[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] = 'route';
170183
}
171184

@@ -175,13 +188,19 @@ export function getHttpSpanDetailsFromUrlObject(
175188

176189
if (urlObject) {
177190
if (urlObject.search) {
178-
attributes['url.query'] = urlObject.search;
191+
const query = urlObject.search.slice(1) || undefined;
192+
attributes[URL_QUERY] = query;
193+
// legacy attribute
194+
attributes[HTTP_QUERY] = query;
179195
}
180196
if (urlObject.hash) {
181-
attributes['url.fragment'] = urlObject.hash;
197+
const fragment = urlObject.hash.slice(1) || undefined;
198+
attributes[URL_FRAGMENT] = fragment;
199+
// legacy attribute
200+
attributes[HTTP_FRAGMENT] = fragment;
182201
}
183202
if (urlObject.pathname) {
184-
attributes['url.path'] = urlObject.pathname;
203+
attributes[URL_PATH] = urlObject.pathname;
185204
if (urlObject.pathname === '/') {
186205
attributes[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE] = 'route';
187206
}
@@ -190,14 +209,15 @@ export function getHttpSpanDetailsFromUrlObject(
190209
if (!isURLObjectRelative(urlObject)) {
191210
attributes[URL_FULL] = urlObject.href;
192211
if (urlObject.port) {
193-
attributes['url.port'] = urlObject.port;
212+
attributes[URL_PORT] = urlObject.port;
194213
}
195214
if (urlObject.protocol) {
196-
attributes['url.scheme'] = urlObject.protocol;
215+
attributes[URL_SCHEME] = urlObject.protocol;
197216
}
198217
if (urlObject.hostname) {
199-
attributes[kind === 'server' ? 'server.address' : 'url.domain'] = urlObject.hostname;
218+
attributes[kind === 'server' ? SERVER_ADDRESS : URL_DOMAIN] = urlObject.hostname;
200219
}
220+
attributes['url'] = getSanitizedUrlStringFromUrlObject(urlObject);
201221
}
202222
}
203223

packages/node/src/integrations/http/httpServerSpansIntegration.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,10 @@ import { RPCType, setRPCMetadata } from '@opentelemetry/core';
77
import {
88
HTTP_CLIENT_IP,
99
HTTP_FLAVOR,
10+
HTTP_FRAGMENT,
1011
HTTP_HOST,
1112
HTTP_METHOD,
13+
HTTP_QUERY,
1214
HTTP_RESPONSE_STATUS_CODE,
1315
HTTP_ROUTE,
1416
HTTP_SCHEME,
@@ -51,6 +53,7 @@ import {
5153
bindScopeToEmitter,
5254
startInactiveSpan,
5355
withActiveSpan,
56+
getSanitizedUrlStringFromUrlObject,
5457
SPAN_KIND,
5558
} from '@sentry/core';
5659
import { DEBUG_BUILD } from '../../debug-build';
@@ -152,6 +155,7 @@ const _httpServerSpansIntegration = ((options: HttpServerSpansIntegrationOptions
152155

153156
const fullUrl = normalizedRequest.url || request.url || '/';
154157
const urlObj = parseStringToURLObject(fullUrl);
158+
const sanitizedUrl = urlObj ? getSanitizedUrlStringFromUrlObject(urlObj) : undefined;
155159

156160
const headers = request.headers;
157161
const userAgent = headers['user-agent'];
@@ -166,6 +170,9 @@ const _httpServerSpansIntegration = ((options: HttpServerSpansIntegrationOptions
166170
const httpTargetWithoutQueryFragment = urlObj ? urlObj.pathname : stripUrlQueryAndFragment(fullUrl);
167171
const bestEffortTransactionName = `${method} ${httpTargetWithoutQueryFragment}`;
168172

173+
const query = urlObj?.search ? urlObj.search.slice(1) || undefined : undefined;
174+
const fragment = urlObj?.hash ? urlObj.hash.slice(1) || undefined : undefined;
175+
169176
const span = startInactiveSpan({
170177
name: bestEffortTransactionName,
171178
kind: SPAN_KIND.SERVER,
@@ -179,7 +186,10 @@ const _httpServerSpansIntegration = ((options: HttpServerSpansIntegrationOptions
179186
// Old Semantic Conventions attributes - added for compatibility with what `@opentelemetry/instrumentation-http` output before
180187
/* eslint-disable typescript/no-deprecated */
181188
[HTTP_URL]: fullUrl,
189+
url: sanitizedUrl,
182190
[HTTP_METHOD]: normalizedRequest.method,
191+
[HTTP_QUERY]: query,
192+
[HTTP_FRAGMENT]: fragment,
183193
[HTTP_TARGET]: urlObj ? `${urlObj.pathname}${urlObj.search}` : httpTargetWithoutQueryFragment,
184194
[HTTP_HOST]: host,
185195
[NET_HOST_NAME]: hostname,

packages/node/src/integrations/node-fetch/undici-instrumentation.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,7 @@ function onRequestCreated(config: NodeFetchOptions, { request }: RequestMessage)
215215
// Skip instrumenting this request.
216216
return;
217217
}
218+
218219
const urlScheme = requestUrl.protocol.replace(':', '');
219220
const requestMethod = getRequestMethod(request.method);
220221
const attributes: SpanAttributes = {

0 commit comments

Comments
 (0)