@sentry/node 9.32.0 → 9.34.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (48) hide show
  1. package/build/cjs/index.js +2 -0
  2. package/build/cjs/index.js.map +1 -1
  3. package/build/cjs/integrations/anr/index.js +1 -1
  4. package/build/cjs/integrations/local-variables/local-variables-async.js +1 -1
  5. package/build/cjs/integrations/tracing/fastify/index.js +42 -5
  6. package/build/cjs/integrations/tracing/fastify/index.js.map +1 -1
  7. package/build/cjs/integrations/tracing/index.js +3 -0
  8. package/build/cjs/integrations/tracing/index.js.map +1 -1
  9. package/build/cjs/integrations/tracing/postgresjs.js +283 -0
  10. package/build/cjs/integrations/tracing/postgresjs.js.map +1 -0
  11. package/build/cjs/integrations/tracing/vercelai/index.js +7 -183
  12. package/build/cjs/integrations/tracing/vercelai/index.js.map +1 -1
  13. package/build/esm/index.js +1 -0
  14. package/build/esm/index.js.map +1 -1
  15. package/build/esm/integrations/anr/index.js +1 -1
  16. package/build/esm/integrations/anr/worker-script.js +2 -2
  17. package/build/esm/integrations/local-variables/local-variables-async.js +1 -1
  18. package/build/esm/integrations/local-variables/worker-script.js +2 -2
  19. package/build/esm/integrations/tracing/fastify/index.js +43 -6
  20. package/build/esm/integrations/tracing/fastify/index.js.map +1 -1
  21. package/build/esm/integrations/tracing/index.js +3 -0
  22. package/build/esm/integrations/tracing/index.js.map +1 -1
  23. package/build/esm/integrations/tracing/postgresjs.js +279 -0
  24. package/build/esm/integrations/tracing/postgresjs.js.map +1 -0
  25. package/build/esm/integrations/tracing/vercelai/index.js +8 -184
  26. package/build/esm/integrations/tracing/vercelai/index.js.map +1 -1
  27. package/build/esm/package.json +1 -1
  28. package/build/types/index.d.ts +1 -0
  29. package/build/types/index.d.ts.map +1 -1
  30. package/build/types/integrations/tracing/fastify/index.d.ts +1 -1
  31. package/build/types/integrations/tracing/fastify/index.d.ts.map +1 -1
  32. package/build/types/integrations/tracing/index.d.ts.map +1 -1
  33. package/build/types/integrations/tracing/postgresjs.d.ts +81 -0
  34. package/build/types/integrations/tracing/postgresjs.d.ts.map +1 -0
  35. package/build/types/integrations/tracing/vercelai/index.d.ts +5 -7
  36. package/build/types/integrations/tracing/vercelai/index.d.ts.map +1 -1
  37. package/build/types-ts3.8/index.d.ts +1 -0
  38. package/build/types-ts3.8/integrations/tracing/fastify/index.d.ts +1 -1
  39. package/build/types-ts3.8/integrations/tracing/postgresjs.d.ts +81 -0
  40. package/build/types-ts3.8/integrations/tracing/vercelai/index.d.ts +5 -7
  41. package/package.json +3 -3
  42. package/build/cjs/integrations/tracing/vercelai/ai_sdk_attributes.js +0 -168
  43. package/build/cjs/integrations/tracing/vercelai/ai_sdk_attributes.js.map +0 -1
  44. package/build/esm/integrations/tracing/vercelai/ai_sdk_attributes.js +0 -152
  45. package/build/esm/integrations/tracing/vercelai/ai_sdk_attributes.js.map +0 -1
  46. package/build/types/integrations/tracing/vercelai/ai_sdk_attributes.d.ts +0 -669
  47. package/build/types/integrations/tracing/vercelai/ai_sdk_attributes.d.ts.map +0 -1
  48. package/build/types-ts3.8/integrations/tracing/vercelai/ai_sdk_attributes.d.ts +0 -669
@@ -14,6 +14,7 @@ import { instrumentMongoose, mongooseIntegration } from './mongoose.js';
14
14
  import { instrumentMysql, mysqlIntegration } from './mysql.js';
15
15
  import { instrumentMysql2, mysql2Integration } from './mysql2.js';
16
16
  import { instrumentPostgres, postgresIntegration } from './postgres.js';
17
+ import { instrumentPostgresJs, postgresJsIntegration } from './postgresjs.js';
17
18
  import { prismaIntegration } from './prisma.js';
18
19
  import { instrumentRedis, redisIntegration } from './redis.js';
19
20
  import { instrumentTedious, tediousIntegration } from './tedious.js';
@@ -43,6 +44,7 @@ function getAutoPerformanceIntegrations() {
43
44
  amqplibIntegration(),
44
45
  lruMemoizerIntegration(),
45
46
  vercelAIIntegration(),
47
+ postgresJsIntegration(),
46
48
  ];
47
49
  }
48
50
 
@@ -74,6 +76,7 @@ function getOpenTelemetryInstrumentationToPreload() {
74
76
  instrumentGenericPool,
75
77
  instrumentAmqplib,
76
78
  instrumentVercelAi,
79
+ instrumentPostgresJs,
77
80
  ];
78
81
  }
79
82
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../../src/integrations/tracing/index.ts"],"sourcesContent":["import type { Integration } from '@sentry/core';\nimport { instrumentOtelHttp } from '../http';\nimport { amqplibIntegration, instrumentAmqplib } from './amqplib';\nimport { connectIntegration, instrumentConnect } from './connect';\nimport { expressIntegration, instrumentExpress, instrumentExpressV5 } from './express';\nimport { fastifyIntegration, instrumentFastify, instrumentFastifyV3 } from './fastify';\nimport { genericPoolIntegration, instrumentGenericPool } from './genericPool';\nimport { graphqlIntegration, instrumentGraphql } from './graphql';\nimport { hapiIntegration, instrumentHapi } from './hapi';\nimport { instrumentKafka, kafkaIntegration } from './kafka';\nimport { instrumentKoa, koaIntegration } from './koa';\nimport { instrumentLruMemoizer, lruMemoizerIntegration } from './lrumemoizer';\nimport { instrumentMongo, mongoIntegration } from './mongo';\nimport { instrumentMongoose, mongooseIntegration } from './mongoose';\nimport { instrumentMysql, mysqlIntegration } from './mysql';\nimport { instrumentMysql2, mysql2Integration } from './mysql2';\nimport { instrumentPostgres, postgresIntegration } from './postgres';\nimport { prismaIntegration } from './prisma';\nimport { instrumentRedis, redisIntegration } from './redis';\nimport { instrumentTedious, tediousIntegration } from './tedious';\nimport { instrumentVercelAi, vercelAIIntegration } from './vercelai';\n\n/**\n * With OTEL, all performance integrations will be added, as OTEL only initializes them when the patched package is actually required.\n */\nexport function getAutoPerformanceIntegrations(): Integration[] {\n return [\n expressIntegration(),\n fastifyIntegration(),\n graphqlIntegration(),\n mongoIntegration(),\n mongooseIntegration(),\n mysqlIntegration(),\n mysql2Integration(),\n redisIntegration(),\n postgresIntegration(),\n prismaIntegration(),\n hapiIntegration(),\n koaIntegration(),\n connectIntegration(),\n tediousIntegration(),\n genericPoolIntegration(),\n kafkaIntegration(),\n amqplibIntegration(),\n lruMemoizerIntegration(),\n vercelAIIntegration(),\n ];\n}\n\n/**\n * Get a list of methods to instrument OTEL, when preload instrumentation.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function getOpenTelemetryInstrumentationToPreload(): (((options?: any) => void) & { id: string })[] {\n return [\n instrumentOtelHttp,\n instrumentExpress,\n instrumentExpressV5,\n instrumentConnect,\n instrumentFastify,\n instrumentFastifyV3,\n instrumentHapi,\n instrumentKafka,\n instrumentKoa,\n instrumentLruMemoizer,\n instrumentMongo,\n instrumentMongoose,\n instrumentMysql,\n instrumentMysql2,\n instrumentPostgres,\n instrumentHapi,\n instrumentGraphql,\n instrumentRedis,\n instrumentTedious,\n instrumentGenericPool,\n instrumentAmqplib,\n instrumentVercelAi,\n ];\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;AAsBA;AACA;AACA;AACO,SAAS,8BAA8B,GAAkB;AAChE,EAAE,OAAO;AACT,IAAI,kBAAkB,EAAE;AACxB,IAAI,kBAAkB,EAAE;AACxB,IAAI,kBAAkB,EAAE;AACxB,IAAI,gBAAgB,EAAE;AACtB,IAAI,mBAAmB,EAAE;AACzB,IAAI,gBAAgB,EAAE;AACtB,IAAI,iBAAiB,EAAE;AACvB,IAAI,gBAAgB,EAAE;AACtB,IAAI,mBAAmB,EAAE;AACzB,IAAI,iBAAiB,EAAE;AACvB,IAAI,eAAe,EAAE;AACrB,IAAI,cAAc,EAAE;AACpB,IAAI,kBAAkB,EAAE;AACxB,IAAI,kBAAkB,EAAE;AACxB,IAAI,sBAAsB,EAAE;AAC5B,IAAI,gBAAgB,EAAE;AACtB,IAAI,kBAAkB,EAAE;AACxB,IAAI,sBAAsB,EAAE;AAC5B,IAAI,mBAAmB,EAAE;AACzB,GAAG;AACH;;AAEA;AACA;AACA;AACA;AACO,SAAS,wCAAwC,GAAmD;AAC3G,EAAE,OAAO;AACT,IAAI,kBAAkB;AACtB,IAAI,iBAAiB;AACrB,IAAI,mBAAmB;AACvB,IAAI,iBAAiB;AACrB,IAAI,iBAAiB;AACrB,IAAI,mBAAmB;AACvB,IAAI,cAAc;AAClB,IAAI,eAAe;AACnB,IAAI,aAAa;AACjB,IAAI,qBAAqB;AACzB,IAAI,eAAe;AACnB,IAAI,kBAAkB;AACtB,IAAI,eAAe;AACnB,IAAI,gBAAgB;AACpB,IAAI,kBAAkB;AACtB,IAAI,cAAc;AAClB,IAAI,iBAAiB;AACrB,IAAI,eAAe;AACnB,IAAI,iBAAiB;AACrB,IAAI,qBAAqB;AACzB,IAAI,iBAAiB;AACrB,IAAI,kBAAkB;AACtB,GAAG;AACH;;;;"}
1
+ {"version":3,"file":"index.js","sources":["../../../../src/integrations/tracing/index.ts"],"sourcesContent":["import type { Integration } from '@sentry/core';\nimport { instrumentOtelHttp } from '../http';\nimport { amqplibIntegration, instrumentAmqplib } from './amqplib';\nimport { connectIntegration, instrumentConnect } from './connect';\nimport { expressIntegration, instrumentExpress, instrumentExpressV5 } from './express';\nimport { fastifyIntegration, instrumentFastify, instrumentFastifyV3 } from './fastify';\nimport { genericPoolIntegration, instrumentGenericPool } from './genericPool';\nimport { graphqlIntegration, instrumentGraphql } from './graphql';\nimport { hapiIntegration, instrumentHapi } from './hapi';\nimport { instrumentKafka, kafkaIntegration } from './kafka';\nimport { instrumentKoa, koaIntegration } from './koa';\nimport { instrumentLruMemoizer, lruMemoizerIntegration } from './lrumemoizer';\nimport { instrumentMongo, mongoIntegration } from './mongo';\nimport { instrumentMongoose, mongooseIntegration } from './mongoose';\nimport { instrumentMysql, mysqlIntegration } from './mysql';\nimport { instrumentMysql2, mysql2Integration } from './mysql2';\nimport { instrumentPostgres, postgresIntegration } from './postgres';\nimport { instrumentPostgresJs, postgresJsIntegration } from './postgresjs';\nimport { prismaIntegration } from './prisma';\nimport { instrumentRedis, redisIntegration } from './redis';\nimport { instrumentTedious, tediousIntegration } from './tedious';\nimport { instrumentVercelAi, vercelAIIntegration } from './vercelai';\n\n/**\n * With OTEL, all performance integrations will be added, as OTEL only initializes them when the patched package is actually required.\n */\nexport function getAutoPerformanceIntegrations(): Integration[] {\n return [\n expressIntegration(),\n fastifyIntegration(),\n graphqlIntegration(),\n mongoIntegration(),\n mongooseIntegration(),\n mysqlIntegration(),\n mysql2Integration(),\n redisIntegration(),\n postgresIntegration(),\n prismaIntegration(),\n hapiIntegration(),\n koaIntegration(),\n connectIntegration(),\n tediousIntegration(),\n genericPoolIntegration(),\n kafkaIntegration(),\n amqplibIntegration(),\n lruMemoizerIntegration(),\n vercelAIIntegration(),\n postgresJsIntegration(),\n ];\n}\n\n/**\n * Get a list of methods to instrument OTEL, when preload instrumentation.\n */\n// eslint-disable-next-line @typescript-eslint/no-explicit-any\nexport function getOpenTelemetryInstrumentationToPreload(): (((options?: any) => void) & { id: string })[] {\n return [\n instrumentOtelHttp,\n instrumentExpress,\n instrumentExpressV5,\n instrumentConnect,\n instrumentFastify,\n instrumentFastifyV3,\n instrumentHapi,\n instrumentKafka,\n instrumentKoa,\n instrumentLruMemoizer,\n instrumentMongo,\n instrumentMongoose,\n instrumentMysql,\n instrumentMysql2,\n instrumentPostgres,\n instrumentHapi,\n instrumentGraphql,\n instrumentRedis,\n instrumentTedious,\n instrumentGenericPool,\n instrumentAmqplib,\n instrumentVercelAi,\n instrumentPostgresJs,\n ];\n}\n"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAuBA;AACA;AACA;AACO,SAAS,8BAA8B,GAAkB;AAChE,EAAE,OAAO;AACT,IAAI,kBAAkB,EAAE;AACxB,IAAI,kBAAkB,EAAE;AACxB,IAAI,kBAAkB,EAAE;AACxB,IAAI,gBAAgB,EAAE;AACtB,IAAI,mBAAmB,EAAE;AACzB,IAAI,gBAAgB,EAAE;AACtB,IAAI,iBAAiB,EAAE;AACvB,IAAI,gBAAgB,EAAE;AACtB,IAAI,mBAAmB,EAAE;AACzB,IAAI,iBAAiB,EAAE;AACvB,IAAI,eAAe,EAAE;AACrB,IAAI,cAAc,EAAE;AACpB,IAAI,kBAAkB,EAAE;AACxB,IAAI,kBAAkB,EAAE;AACxB,IAAI,sBAAsB,EAAE;AAC5B,IAAI,gBAAgB,EAAE;AACtB,IAAI,kBAAkB,EAAE;AACxB,IAAI,sBAAsB,EAAE;AAC5B,IAAI,mBAAmB,EAAE;AACzB,IAAI,qBAAqB,EAAE;AAC3B,GAAG;AACH;;AAEA;AACA;AACA;AACA;AACO,SAAS,wCAAwC,GAAmD;AAC3G,EAAE,OAAO;AACT,IAAI,kBAAkB;AACtB,IAAI,iBAAiB;AACrB,IAAI,mBAAmB;AACvB,IAAI,iBAAiB;AACrB,IAAI,iBAAiB;AACrB,IAAI,mBAAmB;AACvB,IAAI,cAAc;AAClB,IAAI,eAAe;AACnB,IAAI,aAAa;AACjB,IAAI,qBAAqB;AACzB,IAAI,eAAe;AACnB,IAAI,kBAAkB;AACtB,IAAI,eAAe;AACnB,IAAI,gBAAgB;AACpB,IAAI,kBAAkB;AACtB,IAAI,cAAc;AAClB,IAAI,iBAAiB;AACrB,IAAI,eAAe;AACnB,IAAI,iBAAiB;AACrB,IAAI,qBAAqB;AACzB,IAAI,iBAAiB;AACrB,IAAI,kBAAkB;AACtB,IAAI,oBAAoB;AACxB,GAAG;AACH;;;;"}
@@ -0,0 +1,279 @@
1
+ import { trace, context } from '@opentelemetry/api';
2
+ import { InstrumentationBase, InstrumentationNodeModuleDefinition, InstrumentationNodeModuleFile, safeExecuteInTheMiddle } from '@opentelemetry/instrumentation';
3
+ import { ATTR_DB_RESPONSE_STATUS_CODE, ATTR_ERROR_TYPE, ATTR_DB_OPERATION_NAME, ATTR_DB_SYSTEM_NAME, ATTR_DB_NAMESPACE, ATTR_SERVER_ADDRESS, ATTR_SERVER_PORT, ATTR_DB_QUERY_TEXT } from '@opentelemetry/semantic-conventions';
4
+ import { defineIntegration, SDK_VERSION, SPAN_STATUS_ERROR, startSpanManual, getCurrentScope, logger } from '@sentry/core';
5
+ import { generateInstrumentOnce } from '../../otel/instrument.js';
6
+ import { addOriginToSpan } from '../../utils/addOriginToSpan.js';
7
+
8
+ // Instrumentation for https://github.com/porsager/postgres
9
+
10
+ const INTEGRATION_NAME = 'PostgresJs';
11
+ const SUPPORTED_VERSIONS = ['>=3.0.0 <4'];
12
+
13
+ const instrumentPostgresJs = generateInstrumentOnce(
14
+ INTEGRATION_NAME,
15
+ (options) =>
16
+ new PostgresJsInstrumentation({
17
+ requireParentSpan: options?.requireParentSpan ?? true,
18
+ requestHook: options?.requestHook,
19
+ }),
20
+ );
21
+
22
+ /**
23
+ * Instrumentation for the [postgres](https://www.npmjs.com/package/postgres) library.
24
+ * This instrumentation captures postgresjs queries and their attributes,
25
+ */
26
+ class PostgresJsInstrumentation extends InstrumentationBase {
27
+ constructor(config) {
28
+ super('sentry-postgres-js', SDK_VERSION, config);
29
+ }
30
+
31
+ /**
32
+ * Initializes the instrumentation.
33
+ */
34
+ init() {
35
+ const instrumentationModule = new InstrumentationNodeModuleDefinition('postgres', SUPPORTED_VERSIONS);
36
+
37
+ ['src', 'cf/src', 'cjs/src'].forEach(path => {
38
+ instrumentationModule.files.push(
39
+ new InstrumentationNodeModuleFile(
40
+ `postgres/${path}/connection.js`,
41
+ ['*'],
42
+ this._patchConnection.bind(this),
43
+ this._unwrap.bind(this),
44
+ ),
45
+ );
46
+
47
+ instrumentationModule.files.push(
48
+ new InstrumentationNodeModuleFile(
49
+ `postgres/${path}/query.js`,
50
+ SUPPORTED_VERSIONS,
51
+ this._patchQuery.bind(this),
52
+ this._unwrap.bind(this),
53
+ ),
54
+ );
55
+ });
56
+
57
+ return [instrumentationModule];
58
+ }
59
+
60
+ /**
61
+ * Determines whether a span should be created based on the current context.
62
+ * If `requireParentSpan` is set to true in the configuration, a span will
63
+ * only be created if there is a parent span available.
64
+ */
65
+ _shouldCreateSpans() {
66
+ const config = this.getConfig();
67
+ const hasParentSpan = trace.getSpan(context.active()) !== undefined;
68
+ return hasParentSpan || !config.requireParentSpan;
69
+ }
70
+
71
+ /**
72
+ * Patches the reject method of the Query class to set the span status and end it
73
+ */
74
+ _patchReject(rejectTarget, span) {
75
+ return new Proxy(rejectTarget, {
76
+ apply: (
77
+ rejectTarget,
78
+ rejectThisArg,
79
+ rejectArgs
80
+
81
+ ,
82
+ ) => {
83
+ span.setStatus({
84
+ code: SPAN_STATUS_ERROR,
85
+ // This message is the error message from the rejectArgs, when available
86
+ // e.g "relation 'User' does not exist"
87
+ message: rejectArgs?.[0]?.message || 'unknown_error',
88
+ });
89
+
90
+ const result = Reflect.apply(rejectTarget, rejectThisArg, rejectArgs);
91
+
92
+ // This status code is PG error code, e.g. '42P01' for "relation does not exist"
93
+ // https://www.postgresql.org/docs/current/errcodes-appendix.html
94
+ span.setAttribute(ATTR_DB_RESPONSE_STATUS_CODE, rejectArgs?.[0]?.code || 'Unknown error');
95
+ // This is the error type, e.g. 'PostgresError' for a Postgres error
96
+ span.setAttribute(ATTR_ERROR_TYPE, rejectArgs?.[0]?.name || 'Unknown error');
97
+
98
+ span.end();
99
+ return result;
100
+ },
101
+ });
102
+ }
103
+
104
+ /**
105
+ * Patches the resolve method of the Query class to end the span when the query is resolved.
106
+ */
107
+ _patchResolve(resolveTarget, span) {
108
+ return new Proxy(resolveTarget, {
109
+ apply: (resolveTarget, resolveThisArg, resolveArgs) => {
110
+ const result = Reflect.apply(resolveTarget, resolveThisArg, resolveArgs);
111
+ const sqlCommand = resolveArgs?.[0]?.command;
112
+
113
+ if (sqlCommand) {
114
+ // SQL command is only available when the query is resolved successfully
115
+ span.setAttribute(ATTR_DB_OPERATION_NAME, sqlCommand);
116
+ }
117
+ span.end();
118
+ return result;
119
+ },
120
+ });
121
+ }
122
+
123
+ /**
124
+ * Patches the Query class to instrument the handle method.
125
+ */
126
+ _patchQuery(moduleExports
127
+
128
+ ) {
129
+ moduleExports.Query.prototype.handle = new Proxy(moduleExports.Query.prototype.handle, {
130
+ apply: async (
131
+ handleTarget,
132
+ handleThisArg
133
+
134
+ ,
135
+ handleArgs,
136
+ ) => {
137
+ if (!this._shouldCreateSpans()) {
138
+ // If we don't need to create spans, just call the original method
139
+ return Reflect.apply(handleTarget, handleThisArg, handleArgs);
140
+ }
141
+
142
+ const sanitizedSqlQuery = this._sanitizeSqlQuery(handleThisArg.strings?.[0]);
143
+
144
+ return startSpanManual(
145
+ {
146
+ name: sanitizedSqlQuery || 'postgresjs.query',
147
+ op: 'db',
148
+ },
149
+ (span) => {
150
+ const scope = getCurrentScope();
151
+ const postgresConnectionContext = scope.getScopeData().contexts['postgresjsConnection']
152
+
153
+ ;
154
+
155
+ addOriginToSpan(span, 'auto.db.otel.postgres');
156
+
157
+ const { requestHook } = this.getConfig();
158
+
159
+ if (requestHook) {
160
+ safeExecuteInTheMiddle(
161
+ () => requestHook(span, sanitizedSqlQuery, postgresConnectionContext),
162
+ error => {
163
+ if (error) {
164
+ logger.error(`Error in requestHook for ${INTEGRATION_NAME} integration:`, error);
165
+ }
166
+ },
167
+ );
168
+ }
169
+
170
+ // ATTR_DB_NAMESPACE is used to indicate the database name and the schema name
171
+ // It's only the database name as we don't have the schema information
172
+ const databaseName = postgresConnectionContext?.ATTR_DB_NAMESPACE || '<unknown database>';
173
+ const databaseHost = postgresConnectionContext?.ATTR_SERVER_ADDRESS || '<unknown host>';
174
+ const databasePort = postgresConnectionContext?.ATTR_SERVER_PORT || '<unknown port>';
175
+
176
+ span.setAttribute(ATTR_DB_SYSTEM_NAME, 'postgres');
177
+ span.setAttribute(ATTR_DB_NAMESPACE, databaseName);
178
+ span.setAttribute(ATTR_SERVER_ADDRESS, databaseHost);
179
+ span.setAttribute(ATTR_SERVER_PORT, databasePort);
180
+ span.setAttribute(ATTR_DB_QUERY_TEXT, sanitizedSqlQuery);
181
+
182
+ handleThisArg.resolve = this._patchResolve(handleThisArg.resolve, span);
183
+ handleThisArg.reject = this._patchReject(handleThisArg.reject, span);
184
+
185
+ try {
186
+ return Reflect.apply(handleTarget, handleThisArg, handleArgs);
187
+ } catch (error) {
188
+ span.setStatus({
189
+ code: SPAN_STATUS_ERROR,
190
+ });
191
+ span.end();
192
+ throw error; // Re-throw the error to propagate it
193
+ }
194
+ },
195
+ );
196
+ },
197
+ });
198
+
199
+ return moduleExports;
200
+ }
201
+
202
+ /**
203
+ * Patches the Connection class to set the database, host, and port attributes
204
+ * when a new connection is created.
205
+ */
206
+ _patchConnection(Connection) {
207
+ return new Proxy(Connection, {
208
+ apply: (connectionTarget, thisArg, connectionArgs) => {
209
+ const databaseName = connectionArgs[0]?.database || '<unknown database>';
210
+ const databaseHost = connectionArgs[0]?.host?.[0] || '<unknown host>';
211
+ const databasePort = connectionArgs[0]?.port?.[0] || '<unknown port>';
212
+
213
+ const scope = getCurrentScope();
214
+ scope.setContext('postgresjsConnection', {
215
+ ATTR_DB_NAMESPACE: databaseName,
216
+ ATTR_SERVER_ADDRESS: databaseHost,
217
+ ATTR_SERVER_PORT: databasePort,
218
+ });
219
+
220
+ return Reflect.apply(connectionTarget, thisArg, connectionArgs);
221
+ },
222
+ });
223
+ }
224
+
225
+ /**
226
+ * Sanitize SQL query as per the OTEL semantic conventions
227
+ * https://opentelemetry.io/docs/specs/semconv/database/database-spans/#sanitization-of-dbquerytext
228
+ */
229
+ _sanitizeSqlQuery(sqlQuery) {
230
+ if (!sqlQuery) {
231
+ return 'Unknown SQL Query';
232
+ }
233
+
234
+ return (
235
+ sqlQuery
236
+ .replace(/\s+/g, ' ')
237
+ .trim() // Remove extra spaces including newlines and trim
238
+ .substring(0, 1024) // Truncate to 1024 characters
239
+ .replace(/--.*?(\r?\n|$)/g, '') // Single line comments
240
+ .replace(/\/\*[\s\S]*?\*\//g, '') // Multi-line comments
241
+ .replace(/;\s*$/, '') // Remove trailing semicolons
242
+ .replace(/\b\d+\b/g, '?') // Replace standalone numbers
243
+ // Collapse whitespace to a single space
244
+ .replace(/\s+/g, ' ')
245
+ // Collapse IN and in clauses
246
+ // eg. IN (?, ?, ?, ?) to IN (?)
247
+ .replace(/\bIN\b\s*\(\s*\?(?:\s*,\s*\?)*\s*\)/g, 'IN (?)')
248
+ );
249
+ }
250
+ }
251
+
252
+ const _postgresJsIntegration = (() => {
253
+ return {
254
+ name: INTEGRATION_NAME,
255
+ setupOnce() {
256
+ instrumentPostgresJs();
257
+ },
258
+ };
259
+ }) ;
260
+
261
+ /**
262
+ * Adds Sentry tracing instrumentation for the [postgres](https://www.npmjs.com/package/postgres) library.
263
+ *
264
+ * For more information, see the [`postgresIntegration` documentation](https://docs.sentry.io/platforms/javascript/guides/node/configuration/integrations/postgres/).
265
+ *
266
+ * @example
267
+ * ```javascript
268
+ * const Sentry = require('@sentry/node');
269
+ *
270
+ * Sentry.init({
271
+ * integrations: [Sentry.postgresJsIntegration()],
272
+ * });
273
+ * ```
274
+ */
275
+
276
+ const postgresJsIntegration = defineIntegration(_postgresJsIntegration);
277
+
278
+ export { PostgresJsInstrumentation, instrumentPostgresJs, postgresJsIntegration };
279
+ //# sourceMappingURL=postgresjs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"postgresjs.js","sources":["../../../../src/integrations/tracing/postgresjs.ts"],"sourcesContent":["// Instrumentation for https://github.com/porsager/postgres\nimport { context, trace } from '@opentelemetry/api';\nimport type { InstrumentationConfig } from '@opentelemetry/instrumentation';\nimport {\n InstrumentationBase,\n InstrumentationNodeModuleDefinition,\n InstrumentationNodeModuleFile,\n safeExecuteInTheMiddle,\n} from '@opentelemetry/instrumentation';\nimport {\n ATTR_DB_NAMESPACE,\n ATTR_DB_OPERATION_NAME,\n ATTR_DB_QUERY_TEXT,\n ATTR_DB_RESPONSE_STATUS_CODE,\n ATTR_DB_SYSTEM_NAME,\n ATTR_ERROR_TYPE,\n ATTR_SERVER_ADDRESS,\n ATTR_SERVER_PORT,\n} from '@opentelemetry/semantic-conventions';\nimport type { IntegrationFn, Span } from '@sentry/core';\nimport {\n defineIntegration,\n getCurrentScope,\n logger,\n SDK_VERSION,\n SPAN_STATUS_ERROR,\n startSpanManual,\n} from '@sentry/core';\nimport { generateInstrumentOnce } from '../../otel/instrument';\nimport { addOriginToSpan } from '../../utils/addOriginToSpan';\n\nconst INTEGRATION_NAME = 'PostgresJs';\nconst SUPPORTED_VERSIONS = ['>=3.0.0 <4'];\n\ntype PostgresConnectionContext = {\n ATTR_DB_NAMESPACE?: string; // Database name\n ATTR_SERVER_ADDRESS?: string; // Hostname or IP address of the database server\n ATTR_SERVER_PORT?: string; // Port number of the database server\n};\n\ntype PostgresJsInstrumentationConfig = InstrumentationConfig & {\n /**\n * Whether to require a parent span for the instrumentation.\n * If set to true, the instrumentation will only create spans if there is a parent span\n * available in the current scope.\n * @default true\n */\n requireParentSpan?: boolean;\n /**\n * Hook to modify the span before it is started.\n * This can be used to set additional attributes or modify the span in any way.\n */\n requestHook?: (span: Span, sanitizedSqlQuery: string, postgresConnectionContext?: PostgresConnectionContext) => void;\n};\n\nexport const instrumentPostgresJs = generateInstrumentOnce(\n INTEGRATION_NAME,\n (options?: PostgresJsInstrumentationConfig) =>\n new PostgresJsInstrumentation({\n requireParentSpan: options?.requireParentSpan ?? true,\n requestHook: options?.requestHook,\n }),\n);\n\n/**\n * Instrumentation for the [postgres](https://www.npmjs.com/package/postgres) library.\n * This instrumentation captures postgresjs queries and their attributes,\n */\nexport class PostgresJsInstrumentation extends InstrumentationBase<PostgresJsInstrumentationConfig> {\n public constructor(config: PostgresJsInstrumentationConfig) {\n super('sentry-postgres-js', SDK_VERSION, config);\n }\n\n /**\n * Initializes the instrumentation.\n */\n public init(): InstrumentationNodeModuleDefinition[] {\n const instrumentationModule = new InstrumentationNodeModuleDefinition('postgres', SUPPORTED_VERSIONS);\n\n ['src', 'cf/src', 'cjs/src'].forEach(path => {\n instrumentationModule.files.push(\n new InstrumentationNodeModuleFile(\n `postgres/${path}/connection.js`,\n ['*'],\n this._patchConnection.bind(this),\n this._unwrap.bind(this),\n ),\n );\n\n instrumentationModule.files.push(\n new InstrumentationNodeModuleFile(\n `postgres/${path}/query.js`,\n SUPPORTED_VERSIONS,\n this._patchQuery.bind(this),\n this._unwrap.bind(this),\n ),\n );\n });\n\n return [instrumentationModule];\n }\n\n /**\n * Determines whether a span should be created based on the current context.\n * If `requireParentSpan` is set to true in the configuration, a span will\n * only be created if there is a parent span available.\n */\n private _shouldCreateSpans(): boolean {\n const config = this.getConfig();\n const hasParentSpan = trace.getSpan(context.active()) !== undefined;\n return hasParentSpan || !config.requireParentSpan;\n }\n\n /**\n * Patches the reject method of the Query class to set the span status and end it\n */\n private _patchReject(rejectTarget: any, span: Span): any {\n return new Proxy(rejectTarget, {\n apply: (\n rejectTarget,\n rejectThisArg,\n rejectArgs: {\n message?: string;\n code?: string;\n name?: string;\n }[],\n ) => {\n span.setStatus({\n code: SPAN_STATUS_ERROR,\n // This message is the error message from the rejectArgs, when available\n // e.g \"relation 'User' does not exist\"\n message: rejectArgs?.[0]?.message || 'unknown_error',\n });\n\n const result = Reflect.apply(rejectTarget, rejectThisArg, rejectArgs);\n\n // This status code is PG error code, e.g. '42P01' for \"relation does not exist\"\n // https://www.postgresql.org/docs/current/errcodes-appendix.html\n span.setAttribute(ATTR_DB_RESPONSE_STATUS_CODE, rejectArgs?.[0]?.code || 'Unknown error');\n // This is the error type, e.g. 'PostgresError' for a Postgres error\n span.setAttribute(ATTR_ERROR_TYPE, rejectArgs?.[0]?.name || 'Unknown error');\n\n span.end();\n return result;\n },\n });\n }\n\n /**\n * Patches the resolve method of the Query class to end the span when the query is resolved.\n */\n private _patchResolve(resolveTarget: any, span: Span): any {\n return new Proxy(resolveTarget, {\n apply: (resolveTarget, resolveThisArg, resolveArgs: [{ command?: string }]) => {\n const result = Reflect.apply(resolveTarget, resolveThisArg, resolveArgs);\n const sqlCommand = resolveArgs?.[0]?.command;\n\n if (sqlCommand) {\n // SQL command is only available when the query is resolved successfully\n span.setAttribute(ATTR_DB_OPERATION_NAME, sqlCommand);\n }\n span.end();\n return result;\n },\n });\n }\n\n /**\n * Patches the Query class to instrument the handle method.\n */\n private _patchQuery(moduleExports: {\n Query: {\n prototype: {\n handle: any;\n };\n };\n }): any {\n moduleExports.Query.prototype.handle = new Proxy(moduleExports.Query.prototype.handle, {\n apply: async (\n handleTarget,\n handleThisArg: {\n resolve: any;\n reject: any;\n strings?: string[];\n },\n handleArgs,\n ) => {\n if (!this._shouldCreateSpans()) {\n // If we don't need to create spans, just call the original method\n return Reflect.apply(handleTarget, handleThisArg, handleArgs);\n }\n\n const sanitizedSqlQuery = this._sanitizeSqlQuery(handleThisArg.strings?.[0]);\n\n return startSpanManual(\n {\n name: sanitizedSqlQuery || 'postgresjs.query',\n op: 'db',\n },\n (span: Span) => {\n const scope = getCurrentScope();\n const postgresConnectionContext = scope.getScopeData().contexts['postgresjsConnection'] as\n | PostgresConnectionContext\n | undefined;\n\n addOriginToSpan(span, 'auto.db.otel.postgres');\n\n const { requestHook } = this.getConfig();\n\n if (requestHook) {\n safeExecuteInTheMiddle(\n () => requestHook(span, sanitizedSqlQuery, postgresConnectionContext),\n error => {\n if (error) {\n logger.error(`Error in requestHook for ${INTEGRATION_NAME} integration:`, error);\n }\n },\n );\n }\n\n // ATTR_DB_NAMESPACE is used to indicate the database name and the schema name\n // It's only the database name as we don't have the schema information\n const databaseName = postgresConnectionContext?.ATTR_DB_NAMESPACE || '<unknown database>';\n const databaseHost = postgresConnectionContext?.ATTR_SERVER_ADDRESS || '<unknown host>';\n const databasePort = postgresConnectionContext?.ATTR_SERVER_PORT || '<unknown port>';\n\n span.setAttribute(ATTR_DB_SYSTEM_NAME, 'postgres');\n span.setAttribute(ATTR_DB_NAMESPACE, databaseName);\n span.setAttribute(ATTR_SERVER_ADDRESS, databaseHost);\n span.setAttribute(ATTR_SERVER_PORT, databasePort);\n span.setAttribute(ATTR_DB_QUERY_TEXT, sanitizedSqlQuery);\n\n handleThisArg.resolve = this._patchResolve(handleThisArg.resolve, span);\n handleThisArg.reject = this._patchReject(handleThisArg.reject, span);\n\n try {\n return Reflect.apply(handleTarget, handleThisArg, handleArgs);\n } catch (error) {\n span.setStatus({\n code: SPAN_STATUS_ERROR,\n });\n span.end();\n throw error; // Re-throw the error to propagate it\n }\n },\n );\n },\n });\n\n return moduleExports;\n }\n\n /**\n * Patches the Connection class to set the database, host, and port attributes\n * when a new connection is created.\n */\n private _patchConnection(Connection: any): any {\n return new Proxy(Connection, {\n apply: (connectionTarget, thisArg, connectionArgs: { database: string; host: string[]; port: number[] }[]) => {\n const databaseName = connectionArgs[0]?.database || '<unknown database>';\n const databaseHost = connectionArgs[0]?.host?.[0] || '<unknown host>';\n const databasePort = connectionArgs[0]?.port?.[0] || '<unknown port>';\n\n const scope = getCurrentScope();\n scope.setContext('postgresjsConnection', {\n ATTR_DB_NAMESPACE: databaseName,\n ATTR_SERVER_ADDRESS: databaseHost,\n ATTR_SERVER_PORT: databasePort,\n });\n\n return Reflect.apply(connectionTarget, thisArg, connectionArgs);\n },\n });\n }\n\n /**\n * Sanitize SQL query as per the OTEL semantic conventions\n * https://opentelemetry.io/docs/specs/semconv/database/database-spans/#sanitization-of-dbquerytext\n */\n private _sanitizeSqlQuery(sqlQuery: string | undefined): string {\n if (!sqlQuery) {\n return 'Unknown SQL Query';\n }\n\n return (\n sqlQuery\n .replace(/\\s+/g, ' ')\n .trim() // Remove extra spaces including newlines and trim\n .substring(0, 1024) // Truncate to 1024 characters\n .replace(/--.*?(\\r?\\n|$)/g, '') // Single line comments\n .replace(/\\/\\*[\\s\\S]*?\\*\\//g, '') // Multi-line comments\n .replace(/;\\s*$/, '') // Remove trailing semicolons\n .replace(/\\b\\d+\\b/g, '?') // Replace standalone numbers\n // Collapse whitespace to a single space\n .replace(/\\s+/g, ' ')\n // Collapse IN and in clauses\n // eg. IN (?, ?, ?, ?) to IN (?)\n .replace(/\\bIN\\b\\s*\\(\\s*\\?(?:\\s*,\\s*\\?)*\\s*\\)/g, 'IN (?)')\n );\n }\n}\n\nconst _postgresJsIntegration = (() => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n instrumentPostgresJs();\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Adds Sentry tracing instrumentation for the [postgres](https://www.npmjs.com/package/postgres) library.\n *\n * For more information, see the [`postgresIntegration` documentation](https://docs.sentry.io/platforms/javascript/guides/node/configuration/integrations/postgres/).\n *\n * @example\n * ```javascript\n * const Sentry = require('@sentry/node');\n *\n * Sentry.init({\n * integrations: [Sentry.postgresJsIntegration()],\n * });\n * ```\n */\n\nexport const postgresJsIntegration = defineIntegration(_postgresJsIntegration);\n"],"names":[],"mappings":"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"}
@@ -1,7 +1,5 @@
1
- import { defineIntegration, spanToJSON, SEMANTIC_ATTRIBUTE_SENTRY_OP } from '@sentry/core';
1
+ import { defineIntegration, addVercelAiProcessors } from '@sentry/core';
2
2
  import { generateInstrumentOnce } from '../../../otel/instrument.js';
3
- import { addOriginToSpan } from '../../../utils/addOriginToSpan.js';
4
- import { AI_TOOL_CALL_NAME_ATTRIBUTE, AI_TOOL_CALL_ID_ATTRIBUTE, AI_MODEL_ID_ATTRIBUTE, AI_MODEL_PROVIDER_ATTRIBUTE, AI_TELEMETRY_FUNCTION_ID_ATTRIBUTE, AI_PROMPT_ATTRIBUTE, GEN_AI_RESPONSE_MODEL_ATTRIBUTE, GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE, GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE, AI_USAGE_COMPLETION_TOKENS_ATTRIBUTE, AI_USAGE_PROMPT_TOKENS_ATTRIBUTE, AI_PROMPT_MESSAGES_ATTRIBUTE, AI_RESPONSE_TEXT_ATTRIBUTE, AI_RESPONSE_TOOL_CALLS_ATTRIBUTE, AI_PROMPT_TOOLS_ATTRIBUTE } from './ai_sdk_attributes.js';
5
3
  import { INTEGRATION_NAME } from './constants.js';
6
4
  import { SentryVercelAiInstrumentation } from './instrumentation.js';
7
5
 
@@ -26,175 +24,14 @@ const _vercelAIIntegration = ((options = {}) => {
26
24
  instrumentation = instrumentVercelAi();
27
25
  },
28
26
  afterAllSetup(client) {
29
- function registerProcessors() {
30
- client.on('spanStart', span => {
31
- const { data: attributes, description: name } = spanToJSON(span);
32
-
33
- if (!name) {
34
- return;
35
- }
36
-
37
- // Tool call spans
38
- // https://ai-sdk.dev/docs/ai-sdk-core/telemetry#tool-call-spans
39
- if (
40
- attributes[AI_TOOL_CALL_NAME_ATTRIBUTE] &&
41
- attributes[AI_TOOL_CALL_ID_ATTRIBUTE] &&
42
- name === 'ai.toolCall'
43
- ) {
44
- addOriginToSpan(span, 'auto.vercelai.otel');
45
- span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'gen_ai.execute_tool');
46
- span.setAttribute('gen_ai.tool.call.id', attributes[AI_TOOL_CALL_ID_ATTRIBUTE]);
47
- span.setAttribute('gen_ai.tool.name', attributes[AI_TOOL_CALL_NAME_ATTRIBUTE]);
48
- span.updateName(`execute_tool ${attributes[AI_TOOL_CALL_NAME_ATTRIBUTE]}`);
49
- return;
50
- }
51
-
52
- // The AI and Provider must be defined for generate, stream, and embed spans.
53
- // The id of the model
54
- const aiModelId = attributes[AI_MODEL_ID_ATTRIBUTE];
55
- // the provider of the model
56
- const aiModelProvider = attributes[AI_MODEL_PROVIDER_ATTRIBUTE];
57
- if (typeof aiModelId !== 'string' || typeof aiModelProvider !== 'string' || !aiModelId || !aiModelProvider) {
58
- return;
59
- }
60
-
61
- addOriginToSpan(span, 'auto.vercelai.otel');
62
-
63
- const nameWthoutAi = name.replace('ai.', '');
64
- span.setAttribute('ai.pipeline.name', nameWthoutAi);
65
- span.updateName(nameWthoutAi);
66
-
67
- // If a Telemetry name is set and it is a pipeline span, use that as the operation name
68
- const functionId = attributes[AI_TELEMETRY_FUNCTION_ID_ATTRIBUTE];
69
- if (functionId && typeof functionId === 'string' && name.split('.').length - 1 === 1) {
70
- span.updateName(`${nameWthoutAi} ${functionId}`);
71
- span.setAttribute('ai.pipeline.name', functionId);
72
- }
73
-
74
- if (attributes[AI_PROMPT_ATTRIBUTE]) {
75
- span.setAttribute('gen_ai.prompt', attributes[AI_PROMPT_ATTRIBUTE]);
76
- }
77
- if (attributes[AI_MODEL_ID_ATTRIBUTE] && !attributes[GEN_AI_RESPONSE_MODEL_ATTRIBUTE]) {
78
- span.setAttribute(GEN_AI_RESPONSE_MODEL_ATTRIBUTE, attributes[AI_MODEL_ID_ATTRIBUTE]);
79
- }
80
- span.setAttribute('ai.streaming', name.includes('stream'));
81
-
82
- // Generate Spans
83
- if (name === 'ai.generateText') {
84
- span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'gen_ai.invoke_agent');
85
- return;
86
- }
87
-
88
- if (name === 'ai.generateText.doGenerate') {
89
- span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'gen_ai.generate_text');
90
- span.updateName(`generate_text ${attributes[AI_MODEL_ID_ATTRIBUTE]}`);
91
- return;
92
- }
93
-
94
- if (name === 'ai.streamText') {
95
- span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'gen_ai.invoke_agent');
96
- return;
97
- }
98
-
99
- if (name === 'ai.streamText.doStream') {
100
- span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'gen_ai.stream_text');
101
- span.updateName(`stream_text ${attributes[AI_MODEL_ID_ATTRIBUTE]}`);
102
- return;
103
- }
104
-
105
- if (name === 'ai.generateObject') {
106
- span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'gen_ai.invoke_agent');
107
- return;
108
- }
109
-
110
- if (name === 'ai.generateObject.doGenerate') {
111
- span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'gen_ai.generate_object');
112
- span.updateName(`generate_object ${attributes[AI_MODEL_ID_ATTRIBUTE]}`);
113
- return;
114
- }
115
-
116
- if (name === 'ai.streamObject') {
117
- span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'gen_ai.invoke_agent');
118
- return;
119
- }
120
-
121
- if (name === 'ai.streamObject.doStream') {
122
- span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'gen_ai.stream_object');
123
- span.updateName(`stream_object ${attributes[AI_MODEL_ID_ATTRIBUTE]}`);
124
- return;
125
- }
126
-
127
- if (name === 'ai.embed') {
128
- span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'gen_ai.invoke_agent');
129
- return;
130
- }
131
-
132
- if (name === 'ai.embed.doEmbed') {
133
- span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'gen_ai.embed');
134
- span.updateName(`embed ${attributes[AI_MODEL_ID_ATTRIBUTE]}`);
135
- return;
136
- }
137
-
138
- if (name === 'ai.embedMany') {
139
- span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'gen_ai.invoke_agent');
140
- return;
141
- }
142
-
143
- if (name === 'ai.embedMany.doEmbed') {
144
- span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'gen_ai.embed_many');
145
- span.updateName(`embed_many ${attributes[AI_MODEL_ID_ATTRIBUTE]}`);
146
- return;
147
- }
148
-
149
- if (name.startsWith('ai.stream')) {
150
- span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'ai.run');
151
- return;
152
- }
153
- });
154
-
155
- client.addEventProcessor(event => {
156
- if (event.type === 'transaction' && event.spans?.length) {
157
- for (const span of event.spans) {
158
- const { data: attributes, description: name } = span;
159
-
160
- if (!name || span.origin !== 'auto.vercelai.otel') {
161
- continue;
162
- }
163
-
164
- renameAttributeKey(
165
- attributes,
166
- AI_USAGE_COMPLETION_TOKENS_ATTRIBUTE,
167
- GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE,
168
- );
169
- renameAttributeKey(attributes, AI_USAGE_PROMPT_TOKENS_ATTRIBUTE, GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE);
170
- if (
171
- typeof attributes[GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE] === 'number' &&
172
- typeof attributes[GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE] === 'number'
173
- ) {
174
- attributes['gen_ai.usage.total_tokens'] =
175
- attributes[GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE] + attributes[GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE];
176
- }
177
-
178
- // Rename AI SDK attributes to standardized gen_ai attributes
179
- renameAttributeKey(attributes, AI_PROMPT_MESSAGES_ATTRIBUTE, 'gen_ai.request.messages');
180
- renameAttributeKey(attributes, AI_RESPONSE_TEXT_ATTRIBUTE, 'gen_ai.response.text');
181
- renameAttributeKey(attributes, AI_RESPONSE_TOOL_CALLS_ATTRIBUTE, 'gen_ai.response.tool_calls');
182
- renameAttributeKey(attributes, AI_PROMPT_TOOLS_ATTRIBUTE, 'gen_ai.request.available_tools');
183
- }
184
- }
185
-
186
- return event;
187
- });
188
- }
189
-
190
27
  // Auto-detect if we should force the integration when running with 'ai' package available
191
28
  // Note that this can only be detected if the 'Modules' integration is available, and running in CJS mode
192
29
  const shouldForce = options.force ?? shouldForceIntegration(client);
193
30
 
194
31
  if (shouldForce) {
195
- registerProcessors();
32
+ addVercelAiProcessors(client);
196
33
  } else {
197
- instrumentation?.callWhenPatched(registerProcessors);
34
+ instrumentation?.callWhenPatched(() => addVercelAiProcessors(client));
198
35
  }
199
36
  },
200
37
  };
@@ -202,6 +39,7 @@ const _vercelAIIntegration = ((options = {}) => {
202
39
 
203
40
  /**
204
41
  * Adds Sentry tracing instrumentation for the [ai](https://www.npmjs.com/package/ai) library.
42
+ * This integration is not enabled by default, you need to manually add it.
205
43
  *
206
44
  * For more information, see the [`ai` documentation](https://sdk.vercel.ai/docs/ai-sdk-core/telemetry).
207
45
  *
@@ -214,17 +52,14 @@ const _vercelAIIntegration = ((options = {}) => {
214
52
  * });
215
53
  * ```
216
54
  *
217
- * The integration automatically detects when to force registration in CommonJS environments
218
- * when the 'ai' package is available. You can still manually set the `force` option if needed.
219
- *
220
- * By default this integration adds tracing support to all `ai` function calls. If you need to disable
221
- * collecting spans for a specific call, you can do so by setting `experimental_telemetry.isEnabled` to
222
- * `false` in the first argument of the function call.
55
+ * This integration adds tracing support to all `ai` function calls.
56
+ * You need to opt-in to collecting spans for a specific call,
57
+ * you can do so by setting `experimental_telemetry.isEnabled` to `true` in the first argument of the function call.
223
58
  *
224
59
  * ```javascript
225
60
  * const result = await generateText({
226
61
  * model: openai('gpt-4-turbo'),
227
- * experimental_telemetry: { isEnabled: false },
62
+ * experimental_telemetry: { isEnabled: true },
228
63
  * });
229
64
  * ```
230
65
  *
@@ -240,16 +75,5 @@ const _vercelAIIntegration = ((options = {}) => {
240
75
  */
241
76
  const vercelAIIntegration = defineIntegration(_vercelAIIntegration);
242
77
 
243
- /**
244
- * Renames an attribute key in the provided attributes object if the old key exists.
245
- * This function safely handles null and undefined values.
246
- */
247
- function renameAttributeKey(attributes, oldKey, newKey) {
248
- if (attributes[oldKey] != null) {
249
- attributes[newKey] = attributes[oldKey];
250
- delete attributes[oldKey];
251
- }
252
- }
253
-
254
78
  export { instrumentVercelAi, vercelAIIntegration };
255
79
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sources":["../../../../../src/integrations/tracing/vercelai/index.ts"],"sourcesContent":["/* eslint-disable @typescript-eslint/no-dynamic-delete */\n/* eslint-disable complexity */\nimport type { Client, IntegrationFn } from '@sentry/core';\nimport { defineIntegration, SEMANTIC_ATTRIBUTE_SENTRY_OP, spanToJSON } from '@sentry/core';\nimport { generateInstrumentOnce } from '../../../otel/instrument';\nimport { addOriginToSpan } from '../../../utils/addOriginToSpan';\nimport type { modulesIntegration } from '../../modules';\nimport {\n AI_MODEL_ID_ATTRIBUTE,\n AI_MODEL_PROVIDER_ATTRIBUTE,\n AI_PROMPT_ATTRIBUTE,\n AI_PROMPT_MESSAGES_ATTRIBUTE,\n AI_PROMPT_TOOLS_ATTRIBUTE,\n AI_RESPONSE_TEXT_ATTRIBUTE,\n AI_RESPONSE_TOOL_CALLS_ATTRIBUTE,\n AI_TELEMETRY_FUNCTION_ID_ATTRIBUTE,\n AI_TOOL_CALL_ID_ATTRIBUTE,\n AI_TOOL_CALL_NAME_ATTRIBUTE,\n AI_USAGE_COMPLETION_TOKENS_ATTRIBUTE,\n AI_USAGE_PROMPT_TOKENS_ATTRIBUTE,\n GEN_AI_RESPONSE_MODEL_ATTRIBUTE,\n GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE,\n GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE,\n} from './ai_sdk_attributes';\nimport { INTEGRATION_NAME } from './constants';\nimport { SentryVercelAiInstrumentation } from './instrumentation';\nimport type { VercelAiOptions } from './types';\n\nexport const instrumentVercelAi = generateInstrumentOnce(INTEGRATION_NAME, () => new SentryVercelAiInstrumentation({}));\n\n/**\n * Determines if the integration should be forced based on environment and package availability.\n * Returns true if the 'ai' package is available.\n */\nfunction shouldForceIntegration(client: Client): boolean {\n const modules = client.getIntegrationByName<ReturnType<typeof modulesIntegration>>('Modules');\n return !!modules?.getModules?.()?.ai;\n}\n\nconst _vercelAIIntegration = ((options: VercelAiOptions = {}) => {\n let instrumentation: undefined | SentryVercelAiInstrumentation;\n\n return {\n name: INTEGRATION_NAME,\n options,\n setupOnce() {\n instrumentation = instrumentVercelAi();\n },\n afterAllSetup(client) {\n function registerProcessors(): void {\n client.on('spanStart', span => {\n const { data: attributes, description: name } = spanToJSON(span);\n\n if (!name) {\n return;\n }\n\n // Tool call spans\n // https://ai-sdk.dev/docs/ai-sdk-core/telemetry#tool-call-spans\n if (\n attributes[AI_TOOL_CALL_NAME_ATTRIBUTE] &&\n attributes[AI_TOOL_CALL_ID_ATTRIBUTE] &&\n name === 'ai.toolCall'\n ) {\n addOriginToSpan(span, 'auto.vercelai.otel');\n span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'gen_ai.execute_tool');\n span.setAttribute('gen_ai.tool.call.id', attributes[AI_TOOL_CALL_ID_ATTRIBUTE]);\n span.setAttribute('gen_ai.tool.name', attributes[AI_TOOL_CALL_NAME_ATTRIBUTE]);\n span.updateName(`execute_tool ${attributes[AI_TOOL_CALL_NAME_ATTRIBUTE]}`);\n return;\n }\n\n // The AI and Provider must be defined for generate, stream, and embed spans.\n // The id of the model\n const aiModelId = attributes[AI_MODEL_ID_ATTRIBUTE];\n // the provider of the model\n const aiModelProvider = attributes[AI_MODEL_PROVIDER_ATTRIBUTE];\n if (typeof aiModelId !== 'string' || typeof aiModelProvider !== 'string' || !aiModelId || !aiModelProvider) {\n return;\n }\n\n addOriginToSpan(span, 'auto.vercelai.otel');\n\n const nameWthoutAi = name.replace('ai.', '');\n span.setAttribute('ai.pipeline.name', nameWthoutAi);\n span.updateName(nameWthoutAi);\n\n // If a Telemetry name is set and it is a pipeline span, use that as the operation name\n const functionId = attributes[AI_TELEMETRY_FUNCTION_ID_ATTRIBUTE];\n if (functionId && typeof functionId === 'string' && name.split('.').length - 1 === 1) {\n span.updateName(`${nameWthoutAi} ${functionId}`);\n span.setAttribute('ai.pipeline.name', functionId);\n }\n\n if (attributes[AI_PROMPT_ATTRIBUTE]) {\n span.setAttribute('gen_ai.prompt', attributes[AI_PROMPT_ATTRIBUTE]);\n }\n if (attributes[AI_MODEL_ID_ATTRIBUTE] && !attributes[GEN_AI_RESPONSE_MODEL_ATTRIBUTE]) {\n span.setAttribute(GEN_AI_RESPONSE_MODEL_ATTRIBUTE, attributes[AI_MODEL_ID_ATTRIBUTE]);\n }\n span.setAttribute('ai.streaming', name.includes('stream'));\n\n // Generate Spans\n if (name === 'ai.generateText') {\n span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'gen_ai.invoke_agent');\n return;\n }\n\n if (name === 'ai.generateText.doGenerate') {\n span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'gen_ai.generate_text');\n span.updateName(`generate_text ${attributes[AI_MODEL_ID_ATTRIBUTE]}`);\n return;\n }\n\n if (name === 'ai.streamText') {\n span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'gen_ai.invoke_agent');\n return;\n }\n\n if (name === 'ai.streamText.doStream') {\n span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'gen_ai.stream_text');\n span.updateName(`stream_text ${attributes[AI_MODEL_ID_ATTRIBUTE]}`);\n return;\n }\n\n if (name === 'ai.generateObject') {\n span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'gen_ai.invoke_agent');\n return;\n }\n\n if (name === 'ai.generateObject.doGenerate') {\n span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'gen_ai.generate_object');\n span.updateName(`generate_object ${attributes[AI_MODEL_ID_ATTRIBUTE]}`);\n return;\n }\n\n if (name === 'ai.streamObject') {\n span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'gen_ai.invoke_agent');\n return;\n }\n\n if (name === 'ai.streamObject.doStream') {\n span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'gen_ai.stream_object');\n span.updateName(`stream_object ${attributes[AI_MODEL_ID_ATTRIBUTE]}`);\n return;\n }\n\n if (name === 'ai.embed') {\n span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'gen_ai.invoke_agent');\n return;\n }\n\n if (name === 'ai.embed.doEmbed') {\n span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'gen_ai.embed');\n span.updateName(`embed ${attributes[AI_MODEL_ID_ATTRIBUTE]}`);\n return;\n }\n\n if (name === 'ai.embedMany') {\n span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'gen_ai.invoke_agent');\n return;\n }\n\n if (name === 'ai.embedMany.doEmbed') {\n span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'gen_ai.embed_many');\n span.updateName(`embed_many ${attributes[AI_MODEL_ID_ATTRIBUTE]}`);\n return;\n }\n\n if (name.startsWith('ai.stream')) {\n span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'ai.run');\n return;\n }\n });\n\n client.addEventProcessor(event => {\n if (event.type === 'transaction' && event.spans?.length) {\n for (const span of event.spans) {\n const { data: attributes, description: name } = span;\n\n if (!name || span.origin !== 'auto.vercelai.otel') {\n continue;\n }\n\n renameAttributeKey(\n attributes,\n AI_USAGE_COMPLETION_TOKENS_ATTRIBUTE,\n GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE,\n );\n renameAttributeKey(attributes, AI_USAGE_PROMPT_TOKENS_ATTRIBUTE, GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE);\n if (\n typeof attributes[GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE] === 'number' &&\n typeof attributes[GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE] === 'number'\n ) {\n attributes['gen_ai.usage.total_tokens'] =\n attributes[GEN_AI_USAGE_OUTPUT_TOKENS_ATTRIBUTE] + attributes[GEN_AI_USAGE_INPUT_TOKENS_ATTRIBUTE];\n }\n\n // Rename AI SDK attributes to standardized gen_ai attributes\n renameAttributeKey(attributes, AI_PROMPT_MESSAGES_ATTRIBUTE, 'gen_ai.request.messages');\n renameAttributeKey(attributes, AI_RESPONSE_TEXT_ATTRIBUTE, 'gen_ai.response.text');\n renameAttributeKey(attributes, AI_RESPONSE_TOOL_CALLS_ATTRIBUTE, 'gen_ai.response.tool_calls');\n renameAttributeKey(attributes, AI_PROMPT_TOOLS_ATTRIBUTE, 'gen_ai.request.available_tools');\n }\n }\n\n return event;\n });\n }\n\n // Auto-detect if we should force the integration when running with 'ai' package available\n // Note that this can only be detected if the 'Modules' integration is available, and running in CJS mode\n const shouldForce = options.force ?? shouldForceIntegration(client);\n\n if (shouldForce) {\n registerProcessors();\n } else {\n instrumentation?.callWhenPatched(registerProcessors);\n }\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Adds Sentry tracing instrumentation for the [ai](https://www.npmjs.com/package/ai) library.\n *\n * For more information, see the [`ai` documentation](https://sdk.vercel.ai/docs/ai-sdk-core/telemetry).\n *\n * @example\n * ```javascript\n * const Sentry = require('@sentry/node');\n *\n * Sentry.init({\n * integrations: [Sentry.vercelAIIntegration()],\n * });\n * ```\n *\n * The integration automatically detects when to force registration in CommonJS environments\n * when the 'ai' package is available. You can still manually set the `force` option if needed.\n *\n * By default this integration adds tracing support to all `ai` function calls. If you need to disable\n * collecting spans for a specific call, you can do so by setting `experimental_telemetry.isEnabled` to\n * `false` in the first argument of the function call.\n *\n * ```javascript\n * const result = await generateText({\n * model: openai('gpt-4-turbo'),\n * experimental_telemetry: { isEnabled: false },\n * });\n * ```\n *\n * If you want to collect inputs and outputs for a specific call, you must specifically opt-in to each\n * function call by setting `experimental_telemetry.recordInputs` and `experimental_telemetry.recordOutputs`\n * to `true`.\n *\n * ```javascript\n * const result = await generateText({\n * model: openai('gpt-4-turbo'),\n * experimental_telemetry: { isEnabled: true, recordInputs: true, recordOutputs: true },\n * });\n */\nexport const vercelAIIntegration = defineIntegration(_vercelAIIntegration);\n\n/**\n * Renames an attribute key in the provided attributes object if the old key exists.\n * This function safely handles null and undefined values.\n */\nfunction renameAttributeKey(attributes: Record<string, unknown>, oldKey: string, newKey: string): void {\n if (attributes[oldKey] != null) {\n attributes[newKey] = attributes[oldKey];\n delete attributes[oldKey];\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sources":["../../../../../src/integrations/tracing/vercelai/index.ts"],"sourcesContent":["import type { Client, IntegrationFn } from '@sentry/core';\nimport { addVercelAiProcessors, defineIntegration } from '@sentry/core';\nimport { generateInstrumentOnce } from '../../../otel/instrument';\nimport type { modulesIntegration } from '../../modules';\nimport { INTEGRATION_NAME } from './constants';\nimport { SentryVercelAiInstrumentation } from './instrumentation';\nimport type { VercelAiOptions } from './types';\n\nexport const instrumentVercelAi = generateInstrumentOnce(INTEGRATION_NAME, () => new SentryVercelAiInstrumentation({}));\n\n/**\n * Determines if the integration should be forced based on environment and package availability.\n * Returns true if the 'ai' package is available.\n */\nfunction shouldForceIntegration(client: Client): boolean {\n const modules = client.getIntegrationByName<ReturnType<typeof modulesIntegration>>('Modules');\n return !!modules?.getModules?.()?.ai;\n}\n\nconst _vercelAIIntegration = ((options: VercelAiOptions = {}) => {\n let instrumentation: undefined | SentryVercelAiInstrumentation;\n\n return {\n name: INTEGRATION_NAME,\n options,\n setupOnce() {\n instrumentation = instrumentVercelAi();\n },\n afterAllSetup(client) {\n // Auto-detect if we should force the integration when running with 'ai' package available\n // Note that this can only be detected if the 'Modules' integration is available, and running in CJS mode\n const shouldForce = options.force ?? shouldForceIntegration(client);\n\n if (shouldForce) {\n addVercelAiProcessors(client);\n } else {\n instrumentation?.callWhenPatched(() => addVercelAiProcessors(client));\n }\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Adds Sentry tracing instrumentation for the [ai](https://www.npmjs.com/package/ai) library.\n * This integration is not enabled by default, you need to manually add it.\n *\n * For more information, see the [`ai` documentation](https://sdk.vercel.ai/docs/ai-sdk-core/telemetry).\n *\n * @example\n * ```javascript\n * const Sentry = require('@sentry/node');\n *\n * Sentry.init({\n * integrations: [Sentry.vercelAIIntegration()],\n * });\n * ```\n *\n * This integration adds tracing support to all `ai` function calls.\n * You need to opt-in to collecting spans for a specific call,\n * you can do so by setting `experimental_telemetry.isEnabled` to `true` in the first argument of the function call.\n *\n * ```javascript\n * const result = await generateText({\n * model: openai('gpt-4-turbo'),\n * experimental_telemetry: { isEnabled: true },\n * });\n * ```\n *\n * If you want to collect inputs and outputs for a specific call, you must specifically opt-in to each\n * function call by setting `experimental_telemetry.recordInputs` and `experimental_telemetry.recordOutputs`\n * to `true`.\n *\n * ```javascript\n * const result = await generateText({\n * model: openai('gpt-4-turbo'),\n * experimental_telemetry: { isEnabled: true, recordInputs: true, recordOutputs: true },\n * });\n */\nexport const vercelAIIntegration = defineIntegration(_vercelAIIntegration);\n"],"names":[],"mappings":";;;;;MAQa,kBAAmB,GAAE,sBAAsB,CAAC,gBAAgB,EAAE,MAAM,IAAI,6BAA6B,CAAC,EAAE,CAAC;;AAEtH;AACA;AACA;AACA;AACA,SAAS,sBAAsB,CAAC,MAAM,EAAmB;AACzD,EAAE,MAAM,UAAU,MAAM,CAAC,oBAAoB,CAAwC,SAAS,CAAC;AAC/F,EAAE,OAAO,CAAC,CAAC,OAAO,EAAE,UAAU,IAAI,EAAE,EAAE;AACtC;;AAEA,MAAM,oBAAA,IAAwB,CAAC,OAAO,GAAoB,EAAE,KAAK;AACjE,EAAE,IAAI,eAAe;;AAErB,EAAE,OAAO;AACT,IAAI,IAAI,EAAE,gBAAgB;AAC1B,IAAI,OAAO;AACX,IAAI,SAAS,GAAG;AAChB,MAAM,eAAgB,GAAE,kBAAkB,EAAE;AAC5C,KAAK;AACL,IAAI,aAAa,CAAC,MAAM,EAAE;AAC1B;AACA;AACA,MAAM,MAAM,WAAY,GAAE,OAAO,CAAC,SAAS,sBAAsB,CAAC,MAAM,CAAC;;AAEzE,MAAM,IAAI,WAAW,EAAE;AACvB,QAAQ,qBAAqB,CAAC,MAAM,CAAC;AACrC,aAAa;AACb,QAAQ,eAAe,EAAE,eAAe,CAAC,MAAM,qBAAqB,CAAC,MAAM,CAAC,CAAC;AAC7E;AACA,KAAK;AACL,GAAG;AACH,CAAC,CAAE;;AAEH;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;MACa,mBAAoB,GAAE,iBAAiB,CAAC,oBAAoB;;;;"}
@@ -1 +1 @@
1
- {"type":"module","version":"9.32.0","sideEffects":false}
1
+ {"type":"module","version":"9.34.0","sideEffects":false}