Skip to content

Commit edcc016

Browse files
committed
Add requireParentSpan, requestHook and origin
1 parent ff9248e commit edcc016

File tree

2 files changed

+90
-25
lines changed
  • dev-packages/node-integration-tests/suites/tracing/postgresjs
  • packages/node/src/integrations/tracing

2 files changed

+90
-25
lines changed

dev-packages/node-integration-tests/suites/tracing/postgresjs/test.ts

Lines changed: 16 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -11,15 +11,15 @@ describe('postgresjs auto instrumentation', () => {
1111
'db.namespace': 'test_db',
1212
'db.system.name': 'postgres',
1313
'sentry.op': 'db',
14-
'sentry.origin': 'manual',
14+
'sentry.origin': 'auto.db.otel.postgres',
1515
'server.address': 'localhost',
1616
'server.port': 5444,
1717
}),
1818
description:
1919
'CREATE TABLE "User" ("id" SERIAL NOT NULL,"createdAt" TIMESTAMP(?) NOT NULL DEFAULT CURRENT_TIMESTAMP,"email" TEXT NOT NULL,"name" TEXT,CONSTRAINT "User_pkey" PRIMARY KEY ("id"))',
2020
op: 'db',
2121
status: 'ok',
22-
origin: 'manual',
22+
origin: 'auto.db.otel.postgres',
2323
parent_span_id: expect.any(String),
2424
span_id: expect.any(String),
2525
start_timestamp: expect.any(Number),
@@ -31,15 +31,15 @@ describe('postgresjs auto instrumentation', () => {
3131
'db.namespace': 'test_db',
3232
'db.system.name': 'postgres',
3333
'sentry.op': 'db',
34-
'sentry.origin': 'manual',
34+
'sentry.origin': 'auto.db.otel.postgres',
3535
'server.address': 'localhost',
3636
'server.port': 5444,
3737
}),
3838
description:
3939
"select b.oid, b.typarray from pg_catalog.pg_type a left join pg_catalog.pg_type b on b.oid = a.typelem where a.typcategory = 'A' group by b.oid, b.typarray order by b.oid",
4040
op: 'db',
4141
status: 'ok',
42-
origin: 'manual',
42+
origin: 'auto.db.otel.postgres',
4343
parent_span_id: expect.any(String),
4444
span_id: expect.any(String),
4545
start_timestamp: expect.any(Number),
@@ -50,15 +50,15 @@ describe('postgresjs auto instrumentation', () => {
5050
data: expect.objectContaining({
5151
'db.namespace': 'test_db',
5252
'db.system.name': 'postgres',
53-
'sentry.origin': 'manual',
53+
'sentry.origin': 'auto.db.otel.postgres',
5454
'sentry.op': 'db',
5555
'server.address': 'localhost',
5656
'server.port': 5444,
5757
}),
5858
description: 'INSERT INTO "User" ("email", "name") VALUES (\'Foo\', \'[email protected]\')',
5959
op: 'db',
6060
status: 'ok',
61-
origin: 'manual',
61+
origin: 'auto.db.otel.postgres',
6262
parent_span_id: expect.any(String),
6363
span_id: expect.any(String),
6464
start_timestamp: expect.any(Number),
@@ -70,14 +70,14 @@ describe('postgresjs auto instrumentation', () => {
7070
'db.namespace': 'test_db',
7171
'db.system.name': 'postgres',
7272
'sentry.op': 'db',
73-
'sentry.origin': 'manual',
73+
'sentry.origin': 'auto.db.otel.postgres',
7474
'server.address': 'localhost',
7575
'server.port': 5444,
7676
}),
7777
description: 'UPDATE "User" SET "name" = \'Foo\' WHERE "email" = \'[email protected]\'',
7878
op: 'db',
7979
status: 'ok',
80-
origin: 'manual',
80+
origin: 'auto.db.otel.postgres',
8181
parent_span_id: expect.any(String),
8282
span_id: expect.any(String),
8383
start_timestamp: expect.any(Number),
@@ -89,14 +89,14 @@ describe('postgresjs auto instrumentation', () => {
8989
'db.namespace': 'test_db',
9090
'db.system.name': 'postgres',
9191
'sentry.op': 'db',
92-
'sentry.origin': 'manual',
92+
'sentry.origin': 'auto.db.otel.postgres',
9393
'server.address': 'localhost',
9494
'server.port': 5444,
9595
}),
9696
description: 'SELECT * FROM "User" WHERE "email" = \'[email protected]\'',
9797
op: 'db',
9898
status: 'ok',
99-
origin: 'manual',
99+
origin: 'auto.db.otel.postgres',
100100
parent_span_id: expect.any(String),
101101
span_id: expect.any(String),
102102
start_timestamp: expect.any(Number),
@@ -108,14 +108,14 @@ describe('postgresjs auto instrumentation', () => {
108108
'db.namespace': 'test_db',
109109
'db.system.name': 'postgres',
110110
'sentry.op': 'db',
111-
'sentry.origin': 'manual',
111+
'sentry.origin': 'auto.db.otel.postgres',
112112
'server.address': 'localhost',
113113
'server.port': 5444,
114114
}),
115115
description: 'SELECT * from generate_series(?,?) as x',
116116
op: 'db',
117117
status: 'ok',
118-
origin: 'manual',
118+
origin: 'auto.db.otel.postgres',
119119
parent_span_id: expect.any(String),
120120
span_id: expect.any(String),
121121
start_timestamp: expect.any(Number),
@@ -127,14 +127,14 @@ describe('postgresjs auto instrumentation', () => {
127127
'db.namespace': 'test_db',
128128
'db.system.name': 'postgres',
129129
'sentry.op': 'db',
130-
'sentry.origin': 'manual',
130+
'sentry.origin': 'auto.db.otel.postgres',
131131
'server.address': 'localhost',
132132
'server.port': 5444,
133133
}),
134134
description: 'DROP TABLE "User"',
135135
op: 'db',
136136
status: 'ok',
137-
origin: 'manual',
137+
origin: 'auto.db.otel.postgres',
138138
parent_span_id: expect.any(String),
139139
span_id: expect.any(String),
140140
start_timestamp: expect.any(Number),
@@ -146,14 +146,14 @@ describe('postgresjs auto instrumentation', () => {
146146
'db.namespace': 'test_db',
147147
'db.system.name': 'postgres',
148148
'sentry.op': 'db',
149-
'sentry.origin': 'manual',
149+
'sentry.origin': 'auto.db.otel.postgres',
150150
'server.address': 'localhost',
151151
'server.port': 5444,
152152
}),
153153
description: 'SELECT * FROM "User" WHERE "email" = \'[email protected]\'',
154154
op: 'db',
155155
status: 'unknown_error',
156-
origin: 'manual',
156+
origin: 'auto.db.otel.postgres',
157157
parent_span_id: expect.any(String),
158158
span_id: expect.any(String),
159159
start_timestamp: expect.any(Number),

packages/node/src/integrations/tracing/postgresjs.ts

Lines changed: 74 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
// Instrumentation for https://github.com/porsager/postgres
2+
import { context, trace } from '@opentelemetry/api';
23
import type { InstrumentationConfig } from '@opentelemetry/instrumentation';
34
import {
45
InstrumentationBase,
56
InstrumentationNodeModuleDefinition,
67
InstrumentationNodeModuleFile,
8+
safeExecuteInTheMiddle,
79
} from '@opentelemetry/instrumentation';
810
import {
911
ATTR_DB_NAMESPACE,
@@ -12,20 +14,56 @@ import {
1214
ATTR_SERVER_PORT,
1315
} from '@opentelemetry/semantic-conventions';
1416
import type { IntegrationFn, Span } from '@sentry/core';
15-
import { defineIntegration, getCurrentScope, SDK_VERSION, SPAN_STATUS_ERROR, startSpanManual } from '@sentry/core';
17+
import {
18+
defineIntegration,
19+
getCurrentScope,
20+
logger,
21+
SDK_VERSION,
22+
SPAN_STATUS_ERROR,
23+
startSpanManual,
24+
} from '@sentry/core';
1625
import { generateInstrumentOnce } from '../../otel/instrument';
26+
import { addOriginToSpan } from '../../utils/addOriginToSpan';
1727

1828
const INTEGRATION_NAME = 'PostgresJs';
1929
const SUPPORTED_VERSIONS = ['>=3.0.0 <4'];
2030

21-
export const instrumentPostgresJs = generateInstrumentOnce(INTEGRATION_NAME, () => new PostgresJsInstrumentation());
31+
type PostgresConnectionContext = {
32+
ATTR_DB_NAMESPACE?: string; // Database name
33+
ATTR_SERVER_ADDRESS?: string; // Hostname or IP address of the database server
34+
ATTR_SERVER_PORT?: string; // Port number of the database server
35+
};
36+
37+
type PostgresJsInstrumentationConfig = InstrumentationConfig & {
38+
/**
39+
* Whether to require a parent span for the instrumentation.
40+
* If set to true, the instrumentation will only create spans if there is a parent span
41+
* available in the current scope.
42+
* @default true
43+
*/
44+
requireParentSpan?: boolean;
45+
/**
46+
* Hook to modify the span before it is started.
47+
* This can be used to set additional attributes or modify the span in any way.
48+
*/
49+
requestHook?: (span: Span, sanitizedSqlQuery: string, postgresConnectionContext?: PostgresConnectionContext) => void;
50+
};
51+
52+
export const instrumentPostgresJs = generateInstrumentOnce(
53+
INTEGRATION_NAME,
54+
(options?: PostgresJsInstrumentationConfig) =>
55+
new PostgresJsInstrumentation({
56+
requireParentSpan: options?.requireParentSpan ?? true,
57+
requestHook: options?.requestHook,
58+
}),
59+
);
2260

2361
/**
2462
* Instrumentation for the [postgres](https://www.npmjs.com/package/postgres) library.
2563
* This instrumentation captures postgresjs queries and their attributes,
2664
*/
27-
export class PostgresJsInstrumentation extends InstrumentationBase {
28-
public constructor(config: InstrumentationConfig = {}) {
65+
export class PostgresJsInstrumentation extends InstrumentationBase<PostgresJsInstrumentationConfig> {
66+
public constructor(config: PostgresJsInstrumentationConfig) {
2967
super('sentry-postgres-js', SDK_VERSION, config);
3068
}
3169

@@ -58,6 +96,17 @@ export class PostgresJsInstrumentation extends InstrumentationBase {
5896
return [instrumentationModule];
5997
}
6098

99+
/**
100+
* Determines whether a span should be created based on the current context.
101+
* If `requireParentSpan` is set to true in the configuration, a span will
102+
* only be created if there is a parent span available.
103+
*/
104+
private _shouldCreateSpans(): boolean {
105+
const config = this.getConfig();
106+
const hasParentSpan = trace.getSpan(context.active()) !== undefined;
107+
return hasParentSpan || !config.requireParentSpan;
108+
}
109+
61110
/**
62111
* Patches the reject method of the Query class to set the span status and end it
63112
*/
@@ -114,6 +163,11 @@ export class PostgresJsInstrumentation extends InstrumentationBase {
114163
},
115164
handleArgs,
116165
) => {
166+
if (!this._shouldCreateSpans()) {
167+
// If we don't need to create spans, just call the original method
168+
return Reflect.apply(handleTarget, handleThisArg, handleArgs);
169+
}
170+
117171
const sanitizedSqlQuery = this._sanitizeSqlQuery(handleThisArg.strings?.[0]);
118172

119173
return startSpanManual(
@@ -124,13 +178,24 @@ export class PostgresJsInstrumentation extends InstrumentationBase {
124178
(span: Span) => {
125179
const scope = getCurrentScope();
126180
const postgresConnectionContext = scope.getScopeData().contexts['postgresjsConnection'] as
127-
| {
128-
ATTR_DB_NAMESPACE: string;
129-
ATTR_SERVER_ADDRESS: string;
130-
ATTR_SERVER_PORT: string;
131-
}
181+
| PostgresConnectionContext
132182
| undefined;
133183

184+
addOriginToSpan(span, 'auto.db.otel.postgres');
185+
186+
const { requestHook } = this.getConfig();
187+
188+
if (requestHook) {
189+
safeExecuteInTheMiddle(
190+
() => requestHook(span, sanitizedSqlQuery, postgresConnectionContext),
191+
error => {
192+
if (error) {
193+
logger.error(`Error in requestHook for ${INTEGRATION_NAME} integration:`, error);
194+
}
195+
},
196+
);
197+
}
198+
134199
// ATTR_DB_NAMESPACE is used to indicate the database name and the schema name
135200
// It's only the database name as we don't have the schema information
136201
const databaseName = postgresConnectionContext?.ATTR_DB_NAMESPACE || '<unknown database>';

0 commit comments

Comments
 (0)