Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/google-cloud-serverless/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
"access": "public"
},
"dependencies": {
"@sentry/conventions": "^0.16.0",
"@sentry/core": "10.67.0",
"@sentry/node": "10.67.0"
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
import type * as common from '@google-cloud/common';
import { HTTP_REQUEST_METHOD, SENTRY_OP, SERVER_ADDRESS, URL_FULL } from '@sentry/conventions/attributes';
import { WEB_SERVER_HTTP_CLIENT_SPAN_OP } from '@sentry/conventions/op';
import type { Client, IntegrationFn } from '@sentry/core';
import {
defineIntegration,
fill,
getClient,
isURLObjectRelative,
parseStringToURLObject,
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
SentryNonRecordingSpan,
} from '@sentry/core';
Expand Down Expand Up @@ -54,9 +58,12 @@ function wrapRequestFunction(orig: RequestFunction): RequestFunction {
? startInactiveSpan({
name: `${httpMethod} ${reqOpts.uri}`,
onlyIfParent: true,
op: `http.client.${identifyService(this.apiEndpoint)}`,
attributes: {
[SENTRY_OP]: WEB_SERVER_HTTP_CLIENT_SPAN_OP,
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.serverless',
[HTTP_REQUEST_METHOD]: httpMethod,
[SERVER_ADDRESS]: getServerAddress(this.apiEndpoint),
Comment thread
msonnb marked this conversation as resolved.
[URL_FULL]: reqOpts.uri,
Comment thread
msonnb marked this conversation as resolved.
Comment thread
msonnb marked this conversation as resolved.
},
})
: new SentryNonRecordingSpan();
Expand All @@ -67,8 +74,8 @@ function wrapRequestFunction(orig: RequestFunction): RequestFunction {
};
}

/** Identifies service by its base url */
function identifyService(apiEndpoint: string): string {
const match = apiEndpoint.match(/^https:\/\/(\w+)\.googleapis.com$/);
return match?.[1] || apiEndpoint.replace(/^(http|https)?:\/\//, '');
/** Extracts the host of the API endpoint the request is sent to */
function getServerAddress(apiEndpoint: string): string {
const url = parseStringToURLObject(apiEndpoint);
return url && !isURLObjectRelative(url) ? url.host : apiEndpoint;
Comment on lines +78 to +80

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Bug: The code uses url.host to set the SERVER_ADDRESS attribute, which can incorrectly include the port number, violating OpenTelemetry semantic conventions.
Severity: LOW

Suggested Fix

To ensure compliance with OpenTelemetry conventions and maintain consistency within the codebase, change url.host to url.hostname. This will correctly extract only the hostname from the URL, excluding any port number.

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/google-cloud-serverless/src/integrations/google-cloud-http.ts#L77-L79

Potential issue: The code uses `url.host` to populate the `SERVER_ADDRESS` OpenTelemetry
attribute. According to OpenTelemetry semantic conventions, `server.address` should only
contain the hostname, with the port stored in a separate `server.port` attribute. Using
`url.host` can include the port if a non-standard port is present in the URL. While this
is unlikely with standard Google Cloud API endpoints, it violates the specification and
is inconsistent with other parts of the codebase that correctly use `url.hostname`. This
could lead to incorrect telemetry data in environments using custom endpoints with
non-standard ports.

Did we get this right? 👍 / 👎 to inform future reviews.

}
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
import { BigQuery } from '@google-cloud/bigquery';
import { HTTP_REQUEST_METHOD, SENTRY_OP, SERVER_ADDRESS, URL_FULL } from '@sentry/conventions/attributes';
import { WEB_SERVER_HTTP_CLIENT_SPAN_OP } from '@sentry/conventions/op';
import { SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core';
import { createTransport, NodeClient, setCurrentClient } from '@sentry/node';
import * as fs from 'fs';
Expand Down Expand Up @@ -76,19 +78,25 @@ describe('GoogleCloudHttp tracing', () => {
const resp = await bigquery.query('SELECT true AS foo');
expect(resp).toEqual([[{ foo: true }]]);
expect(mockStartInactiveSpan).toBeCalledWith({
op: 'http.client.bigquery',
name: 'POST /jobs',
onlyIfParent: true,
attributes: {
[SENTRY_OP]: WEB_SERVER_HTTP_CLIENT_SPAN_OP,
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.serverless',
[HTTP_REQUEST_METHOD]: 'POST',
[SERVER_ADDRESS]: 'bigquery.googleapis.com',
[URL_FULL]: '/jobs',
},
});
expect(mockStartInactiveSpan).toBeCalledWith({
op: 'http.client.bigquery',
name: expect.stringMatching(/^GET \/queries\/.+/),
onlyIfParent: true,
attributes: {
[SENTRY_OP]: WEB_SERVER_HTTP_CLIENT_SPAN_OP,
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.serverless',
[HTTP_REQUEST_METHOD]: 'GET',
[SERVER_ADDRESS]: 'bigquery.googleapis.com',
[URL_FULL]: expect.stringMatching(/^\/queries\/.+/),
},
});
});
Expand Down
Loading