@sentry/node 10.21.0-alpha.1 → 10.22.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.
- package/build/cjs/index.js +10 -7
- package/build/cjs/index.js.map +1 -1
- package/build/cjs/integrations/node-fetch.js +2 -0
- package/build/cjs/integrations/node-fetch.js.map +1 -1
- package/build/cjs/integrations/tracing/firebase/firebase.js +18 -0
- package/build/cjs/integrations/tracing/firebase/firebase.js.map +1 -1
- package/build/cjs/integrations/tracing/firebase/otel/firebaseInstrumentation.js +3 -0
- package/build/cjs/integrations/tracing/firebase/otel/firebaseInstrumentation.js.map +1 -1
- package/build/cjs/integrations/tracing/firebase/otel/patches/functions.js +265 -0
- package/build/cjs/integrations/tracing/firebase/otel/patches/functions.js.map +1 -0
- package/build/cjs/integrations/tracing/index.js +3 -0
- package/build/cjs/integrations/tracing/index.js.map +1 -1
- package/build/cjs/integrations/tracing/langchain/index.js +112 -0
- package/build/cjs/integrations/tracing/langchain/index.js.map +1 -0
- package/build/cjs/integrations/tracing/langchain/instrumentation.js +201 -0
- package/build/cjs/integrations/tracing/langchain/instrumentation.js.map +1 -0
- package/build/esm/index.js +2 -1
- package/build/esm/index.js.map +1 -1
- package/build/esm/integrations/node-fetch.js +2 -0
- package/build/esm/integrations/node-fetch.js.map +1 -1
- package/build/esm/integrations/tracing/firebase/firebase.js +19 -1
- package/build/esm/integrations/tracing/firebase/firebase.js.map +1 -1
- package/build/esm/integrations/tracing/firebase/otel/firebaseInstrumentation.js +3 -0
- package/build/esm/integrations/tracing/firebase/otel/firebaseInstrumentation.js.map +1 -1
- package/build/esm/integrations/tracing/firebase/otel/patches/functions.js +262 -0
- package/build/esm/integrations/tracing/firebase/otel/patches/functions.js.map +1 -0
- package/build/esm/integrations/tracing/index.js +3 -0
- package/build/esm/integrations/tracing/index.js.map +1 -1
- package/build/esm/integrations/tracing/langchain/index.js +109 -0
- package/build/esm/integrations/tracing/langchain/index.js.map +1 -0
- package/build/esm/integrations/tracing/langchain/instrumentation.js +199 -0
- package/build/esm/integrations/tracing/langchain/instrumentation.js.map +1 -0
- package/build/esm/package.json +1 -1
- package/build/types/index.d.ts +2 -1
- package/build/types/index.d.ts.map +1 -1
- package/build/types/integrations/fs.d.ts +2 -2
- package/build/types/integrations/fs.d.ts.map +1 -1
- package/build/types/integrations/http.d.ts +0 -1
- package/build/types/integrations/http.d.ts.map +1 -1
- package/build/types/integrations/node-fetch.d.ts +2 -1
- package/build/types/integrations/node-fetch.d.ts.map +1 -1
- package/build/types/integrations/tracing/connect.d.ts.map +1 -1
- package/build/types/integrations/tracing/express.d.ts +0 -1
- package/build/types/integrations/tracing/express.d.ts.map +1 -1
- package/build/types/integrations/tracing/firebase/firebase.d.ts.map +1 -1
- package/build/types/integrations/tracing/firebase/otel/firebaseInstrumentation.d.ts.map +1 -1
- package/build/types/integrations/tracing/firebase/otel/patches/functions.d.ts +23 -0
- package/build/types/integrations/tracing/firebase/otel/patches/functions.d.ts.map +1 -0
- package/build/types/integrations/tracing/firebase/otel/types.d.ts +34 -0
- package/build/types/integrations/tracing/firebase/otel/types.d.ts.map +1 -1
- package/build/types/integrations/tracing/hapi/index.d.ts.map +1 -1
- package/build/types/integrations/tracing/hapi/types.d.ts +0 -3
- package/build/types/integrations/tracing/hapi/types.d.ts.map +1 -1
- package/build/types/integrations/tracing/index.d.ts.map +1 -1
- package/build/types/integrations/tracing/koa.d.ts.map +1 -1
- package/build/types/integrations/tracing/langchain/index.d.ts +93 -0
- package/build/types/integrations/tracing/langchain/index.d.ts.map +1 -0
- package/build/types/integrations/tracing/langchain/instrumentation.d.ts +29 -0
- package/build/types/integrations/tracing/langchain/instrumentation.d.ts.map +1 -0
- package/build/types/types.d.ts +8 -5
- package/build/types/types.d.ts.map +1 -1
- package/build/types-ts3.8/index.d.ts +2 -1
- package/build/types-ts3.8/integrations/fs.d.ts +2 -2
- package/build/types-ts3.8/integrations/http.d.ts +0 -1
- package/build/types-ts3.8/integrations/node-fetch.d.ts +2 -1
- package/build/types-ts3.8/integrations/tracing/express.d.ts +0 -1
- package/build/types-ts3.8/integrations/tracing/firebase/otel/patches/functions.d.ts +23 -0
- package/build/types-ts3.8/integrations/tracing/firebase/otel/types.d.ts +34 -0
- package/build/types-ts3.8/integrations/tracing/hapi/types.d.ts +0 -1
- package/build/types-ts3.8/integrations/tracing/langchain/index.d.ts +93 -0
- package/build/types-ts3.8/integrations/tracing/langchain/instrumentation.d.ts +29 -0
- package/build/types-ts3.8/types.d.ts +8 -5
- package/package.json +4 -4
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../../../src/integrations/tracing/langchain/index.ts"],"sourcesContent":["import type { IntegrationFn, LangChainOptions } from '@sentry/core';\nimport { defineIntegration, LANGCHAIN_INTEGRATION_NAME } from '@sentry/core';\nimport { generateInstrumentOnce } from '@sentry/node-core';\nimport { SentryLangChainInstrumentation } from './instrumentation';\n\nexport const instrumentLangChain = generateInstrumentOnce<LangChainOptions>(\n LANGCHAIN_INTEGRATION_NAME,\n options => new SentryLangChainInstrumentation(options),\n);\n\nconst _langChainIntegration = ((options: LangChainOptions = {}) => {\n return {\n name: LANGCHAIN_INTEGRATION_NAME,\n setupOnce() {\n instrumentLangChain(options);\n },\n };\n}) satisfies IntegrationFn;\n\n/**\n * Adds Sentry tracing instrumentation for LangChain.\n *\n * This integration is enabled by default.\n *\n * When configured, this integration automatically instruments LangChain runnable instances\n * to capture telemetry data by injecting Sentry callback handlers into all LangChain calls.\n *\n * @example\n * ```javascript\n * import * as Sentry from '@sentry/node';\n * import { ChatOpenAI } from '@langchain/openai';\n *\n * Sentry.init({\n * integrations: [Sentry.langChainIntegration()],\n * sendDefaultPii: true, // Enable to record inputs/outputs\n * });\n *\n * // LangChain calls are automatically instrumented\n * const model = new ChatOpenAI();\n * await model.invoke(\"What is the capital of France?\");\n * ```\n *\n * ## Manual Callback Handler\n *\n * You can also manually add the Sentry callback handler alongside other callbacks:\n *\n * @example\n * ```javascript\n * import * as Sentry from '@sentry/node';\n * import { ChatOpenAI } from '@langchain/openai';\n *\n * const sentryHandler = Sentry.createLangChainCallbackHandler({\n * recordInputs: true,\n * recordOutputs: true\n * });\n *\n * const model = new ChatOpenAI();\n * await model.invoke(\n * \"What is the capital of France?\",\n * { callbacks: [sentryHandler, myOtherCallback] }\n * );\n * ```\n *\n * ## Options\n *\n * - `recordInputs`: Whether to record input messages/prompts (default: respects `sendDefaultPii` client option)\n * - `recordOutputs`: Whether to record response text (default: respects `sendDefaultPii` client option)\n *\n * ### Default Behavior\n *\n * By default, the integration will:\n * - Record inputs and outputs ONLY if `sendDefaultPii` is set to `true` in your Sentry client options\n * - Otherwise, inputs and outputs are NOT recorded unless explicitly enabled\n *\n * @example\n * ```javascript\n * // Record inputs and outputs when sendDefaultPii is false\n * Sentry.init({\n * integrations: [\n * Sentry.langChainIntegration({\n * recordInputs: true,\n * recordOutputs: true\n * })\n * ],\n * });\n *\n * // Never record inputs/outputs regardless of sendDefaultPii\n * Sentry.init({\n * sendDefaultPii: true,\n * integrations: [\n * Sentry.langChainIntegration({\n * recordInputs: false,\n * recordOutputs: false\n * })\n * ],\n * });\n * ```\n *\n * ## Supported Events\n *\n * The integration captures the following LangChain lifecycle events:\n * - LLM/Chat Model: start, end, error\n * - Chain: start, end, error\n * - Tool: start, end, error\n *\n */\nexport const langChainIntegration = defineIntegration(_langChainIntegration);\n"],"names":["generateInstrumentOnce","LANGCHAIN_INTEGRATION_NAME","SentryLangChainInstrumentation","defineIntegration"],"mappings":";;;;;;AAKO,MAAM,mBAAA,GAAsBA,+BAAsB;AACzD,EAAEC,+BAA0B;AAC5B,EAAE,WAAW,IAAIC,8CAA8B,CAAC,OAAO,CAAC;AACxD;;AAEA,MAAM,qBAAA,IAAyB,CAAC,OAAO,GAAqB,EAAE,KAAK;AACnE,EAAE,OAAO;AACT,IAAI,IAAI,EAAED,+BAA0B;AACpC,IAAI,SAAS,GAAG;AAChB,MAAM,mBAAmB,CAAC,OAAO,CAAC;AAClC,KAAK;AACL,GAAG;AACH,CAAC,CAAA;;AAED;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;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;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;MACa,oBAAA,GAAuBE,sBAAiB,CAAC,qBAAqB;;;;;"}
|
|
@@ -0,0 +1,201 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
|
|
2
|
+
|
|
3
|
+
const instrumentation = require('@opentelemetry/instrumentation');
|
|
4
|
+
const core = require('@sentry/core');
|
|
5
|
+
|
|
6
|
+
const supportedVersions = ['>=0.1.0 <1.0.0'];
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Augments a callback handler list with Sentry's handler if not already present
|
|
10
|
+
*/
|
|
11
|
+
function augmentCallbackHandlers(handlers, sentryHandler) {
|
|
12
|
+
// Handle null/undefined - return array with just our handler
|
|
13
|
+
if (!handlers) {
|
|
14
|
+
return [sentryHandler];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// If handlers is already an array
|
|
18
|
+
if (Array.isArray(handlers)) {
|
|
19
|
+
// Check if our handler is already in the list
|
|
20
|
+
if (handlers.includes(sentryHandler)) {
|
|
21
|
+
return handlers;
|
|
22
|
+
}
|
|
23
|
+
// Add our handler to the list
|
|
24
|
+
return [...handlers, sentryHandler];
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// If it's a single handler object, convert to array
|
|
28
|
+
if (typeof handlers === 'object') {
|
|
29
|
+
return [handlers, sentryHandler];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Unknown type - return original
|
|
33
|
+
return handlers;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Wraps Runnable methods (invoke, stream, batch) to inject Sentry callbacks at request time
|
|
38
|
+
* Uses a Proxy to intercept method calls and augment the options.callbacks
|
|
39
|
+
*/
|
|
40
|
+
function wrapRunnableMethod(
|
|
41
|
+
originalMethod,
|
|
42
|
+
sentryHandler,
|
|
43
|
+
_methodName,
|
|
44
|
+
) {
|
|
45
|
+
return new Proxy(originalMethod, {
|
|
46
|
+
apply(target, thisArg, args) {
|
|
47
|
+
// LangChain Runnable method signatures:
|
|
48
|
+
// invoke(input, options?) - options contains callbacks
|
|
49
|
+
// stream(input, options?) - options contains callbacks
|
|
50
|
+
// batch(inputs, options?) - options contains callbacks
|
|
51
|
+
|
|
52
|
+
// Options is typically the second argument
|
|
53
|
+
const optionsIndex = 1;
|
|
54
|
+
let options = args[optionsIndex] ;
|
|
55
|
+
|
|
56
|
+
// If options don't exist or aren't an object, create them
|
|
57
|
+
if (!options || typeof options !== 'object' || Array.isArray(options)) {
|
|
58
|
+
options = {};
|
|
59
|
+
args[optionsIndex] = options;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
// Inject our callback handler into options.callbacks (request time callbacks)
|
|
63
|
+
const existingCallbacks = options.callbacks;
|
|
64
|
+
const augmentedCallbacks = augmentCallbackHandlers(existingCallbacks, sentryHandler);
|
|
65
|
+
options.callbacks = augmentedCallbacks;
|
|
66
|
+
|
|
67
|
+
// Call original method with augmented options
|
|
68
|
+
return Reflect.apply(target, thisArg, args);
|
|
69
|
+
},
|
|
70
|
+
}) ;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Sentry LangChain instrumentation using OpenTelemetry.
|
|
75
|
+
*/
|
|
76
|
+
class SentryLangChainInstrumentation extends instrumentation.InstrumentationBase {
|
|
77
|
+
constructor(config = {}) {
|
|
78
|
+
super('@sentry/instrumentation-langchain', core.SDK_VERSION, config);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Initializes the instrumentation by defining the modules to be patched.
|
|
83
|
+
* We patch the BaseChatModel class methods to inject callbacks
|
|
84
|
+
*
|
|
85
|
+
* We hook into provider packages (@langchain/anthropic, @langchain/openai, etc.)
|
|
86
|
+
* because @langchain/core is often bundled and not loaded as a separate module
|
|
87
|
+
*/
|
|
88
|
+
init() {
|
|
89
|
+
const modules = [];
|
|
90
|
+
|
|
91
|
+
// Hook into common LangChain provider packages
|
|
92
|
+
const providerPackages = [
|
|
93
|
+
'@langchain/anthropic',
|
|
94
|
+
'@langchain/openai',
|
|
95
|
+
'@langchain/google-genai',
|
|
96
|
+
'@langchain/mistralai',
|
|
97
|
+
'@langchain/google-vertexai',
|
|
98
|
+
'@langchain/groq',
|
|
99
|
+
];
|
|
100
|
+
|
|
101
|
+
for (const packageName of providerPackages) {
|
|
102
|
+
// In CJS, LangChain packages re-export from dist/index.cjs files.
|
|
103
|
+
// Patching only the root module sometimes misses the real implementation or
|
|
104
|
+
// gets overwritten when that file is loaded. We add a file-level patch so that
|
|
105
|
+
// _patch runs again on the concrete implementation
|
|
106
|
+
modules.push(
|
|
107
|
+
new instrumentation.InstrumentationNodeModuleDefinition(
|
|
108
|
+
packageName,
|
|
109
|
+
supportedVersions,
|
|
110
|
+
this._patch.bind(this),
|
|
111
|
+
exports => exports,
|
|
112
|
+
[
|
|
113
|
+
new instrumentation.InstrumentationNodeModuleFile(
|
|
114
|
+
`${packageName}/dist/index.cjs`,
|
|
115
|
+
supportedVersions,
|
|
116
|
+
this._patch.bind(this),
|
|
117
|
+
exports => exports,
|
|
118
|
+
),
|
|
119
|
+
],
|
|
120
|
+
),
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
return modules;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
/**
|
|
128
|
+
* Core patch logic - patches chat model methods to inject Sentry callbacks
|
|
129
|
+
* This is called when a LangChain provider package is loaded
|
|
130
|
+
*/
|
|
131
|
+
_patch(exports) {
|
|
132
|
+
const client = core.getClient();
|
|
133
|
+
const defaultPii = Boolean(client?.getOptions().sendDefaultPii);
|
|
134
|
+
|
|
135
|
+
const config = this.getConfig();
|
|
136
|
+
|
|
137
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
138
|
+
const recordInputs = config?.recordInputs ?? defaultPii;
|
|
139
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
|
|
140
|
+
const recordOutputs = config?.recordOutputs ?? defaultPii;
|
|
141
|
+
|
|
142
|
+
// Create a shared handler instance
|
|
143
|
+
const sentryHandler = core.createLangChainCallbackHandler({
|
|
144
|
+
recordInputs,
|
|
145
|
+
recordOutputs,
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Patch Runnable methods to inject callbacks at request time
|
|
149
|
+
// This directly manipulates options.callbacks that LangChain uses
|
|
150
|
+
this._patchRunnableMethods(exports, sentryHandler);
|
|
151
|
+
|
|
152
|
+
return exports;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Patches chat model methods (invoke, stream, batch) to inject Sentry callbacks
|
|
157
|
+
* Finds a chat model class from the provider package exports and patches its prototype methods
|
|
158
|
+
*/
|
|
159
|
+
_patchRunnableMethods(exports, sentryHandler) {
|
|
160
|
+
// Known chat model class names for each provider
|
|
161
|
+
const knownChatModelNames = [
|
|
162
|
+
'ChatAnthropic',
|
|
163
|
+
'ChatOpenAI',
|
|
164
|
+
'ChatGoogleGenerativeAI',
|
|
165
|
+
'ChatMistralAI',
|
|
166
|
+
'ChatVertexAI',
|
|
167
|
+
'ChatGroq',
|
|
168
|
+
];
|
|
169
|
+
|
|
170
|
+
// Find a chat model class in the exports by checking known class names
|
|
171
|
+
const chatModelClass = Object.values(exports).find(exp => {
|
|
172
|
+
if (typeof exp !== 'function') {
|
|
173
|
+
return false;
|
|
174
|
+
}
|
|
175
|
+
return knownChatModelNames.includes(exp.name);
|
|
176
|
+
}) ;
|
|
177
|
+
|
|
178
|
+
if (!chatModelClass) {
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
// Patch directly on chatModelClass.prototype
|
|
183
|
+
const targetProto = chatModelClass.prototype ;
|
|
184
|
+
|
|
185
|
+
// Patch the methods (invoke, stream, batch)
|
|
186
|
+
// All chat model instances will inherit these patched methods
|
|
187
|
+
const methodsToPatch = ['invoke', 'stream', 'batch'] ;
|
|
188
|
+
|
|
189
|
+
for (const methodName of methodsToPatch) {
|
|
190
|
+
const method = targetProto[methodName];
|
|
191
|
+
if (typeof method === 'function') {
|
|
192
|
+
targetProto[methodName] = wrapRunnableMethod(
|
|
193
|
+
method ,
|
|
194
|
+
sentryHandler);
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
exports.SentryLangChainInstrumentation = SentryLangChainInstrumentation;
|
|
201
|
+
//# sourceMappingURL=instrumentation.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"instrumentation.js","sources":["../../../../../src/integrations/tracing/langchain/instrumentation.ts"],"sourcesContent":["import {\n type InstrumentationConfig,\n type InstrumentationModuleDefinition,\n InstrumentationBase,\n InstrumentationNodeModuleDefinition,\n InstrumentationNodeModuleFile,\n} from '@opentelemetry/instrumentation';\nimport type { LangChainOptions } from '@sentry/core';\nimport { createLangChainCallbackHandler, getClient, SDK_VERSION } from '@sentry/core';\n\nconst supportedVersions = ['>=0.1.0 <1.0.0'];\n\ntype LangChainInstrumentationOptions = InstrumentationConfig & LangChainOptions;\n\n/**\n * Represents the patched shape of LangChain provider package exports\n */\ninterface PatchedLangChainExports {\n [key: string]: unknown;\n}\n\n/**\n * Augments a callback handler list with Sentry's handler if not already present\n */\nfunction augmentCallbackHandlers(handlers: unknown, sentryHandler: unknown): unknown {\n // Handle null/undefined - return array with just our handler\n if (!handlers) {\n return [sentryHandler];\n }\n\n // If handlers is already an array\n if (Array.isArray(handlers)) {\n // Check if our handler is already in the list\n if (handlers.includes(sentryHandler)) {\n return handlers;\n }\n // Add our handler to the list\n return [...handlers, sentryHandler];\n }\n\n // If it's a single handler object, convert to array\n if (typeof handlers === 'object') {\n return [handlers, sentryHandler];\n }\n\n // Unknown type - return original\n return handlers;\n}\n\n/**\n * Wraps Runnable methods (invoke, stream, batch) to inject Sentry callbacks at request time\n * Uses a Proxy to intercept method calls and augment the options.callbacks\n */\nfunction wrapRunnableMethod(\n originalMethod: (...args: unknown[]) => unknown,\n sentryHandler: unknown,\n _methodName: string,\n): (...args: unknown[]) => unknown {\n return new Proxy(originalMethod, {\n apply(target, thisArg, args: unknown[]): unknown {\n // LangChain Runnable method signatures:\n // invoke(input, options?) - options contains callbacks\n // stream(input, options?) - options contains callbacks\n // batch(inputs, options?) - options contains callbacks\n\n // Options is typically the second argument\n const optionsIndex = 1;\n let options = args[optionsIndex] as Record<string, unknown> | undefined;\n\n // If options don't exist or aren't an object, create them\n if (!options || typeof options !== 'object' || Array.isArray(options)) {\n options = {};\n args[optionsIndex] = options;\n }\n\n // Inject our callback handler into options.callbacks (request time callbacks)\n const existingCallbacks = options.callbacks;\n const augmentedCallbacks = augmentCallbackHandlers(existingCallbacks, sentryHandler);\n options.callbacks = augmentedCallbacks;\n\n // Call original method with augmented options\n return Reflect.apply(target, thisArg, args);\n },\n }) as (...args: unknown[]) => unknown;\n}\n\n/**\n * Sentry LangChain instrumentation using OpenTelemetry.\n */\nexport class SentryLangChainInstrumentation extends InstrumentationBase<LangChainInstrumentationOptions> {\n public constructor(config: LangChainInstrumentationOptions = {}) {\n super('@sentry/instrumentation-langchain', SDK_VERSION, config);\n }\n\n /**\n * Initializes the instrumentation by defining the modules to be patched.\n * We patch the BaseChatModel class methods to inject callbacks\n *\n * We hook into provider packages (@langchain/anthropic, @langchain/openai, etc.)\n * because @langchain/core is often bundled and not loaded as a separate module\n */\n public init(): InstrumentationModuleDefinition | InstrumentationModuleDefinition[] {\n const modules: InstrumentationModuleDefinition[] = [];\n\n // Hook into common LangChain provider packages\n const providerPackages = [\n '@langchain/anthropic',\n '@langchain/openai',\n '@langchain/google-genai',\n '@langchain/mistralai',\n '@langchain/google-vertexai',\n '@langchain/groq',\n ];\n\n for (const packageName of providerPackages) {\n // In CJS, LangChain packages re-export from dist/index.cjs files.\n // Patching only the root module sometimes misses the real implementation or\n // gets overwritten when that file is loaded. We add a file-level patch so that\n // _patch runs again on the concrete implementation\n modules.push(\n new InstrumentationNodeModuleDefinition(\n packageName,\n supportedVersions,\n this._patch.bind(this),\n exports => exports,\n [\n new InstrumentationNodeModuleFile(\n `${packageName}/dist/index.cjs`,\n supportedVersions,\n this._patch.bind(this),\n exports => exports,\n ),\n ],\n ),\n );\n }\n\n return modules;\n }\n\n /**\n * Core patch logic - patches chat model methods to inject Sentry callbacks\n * This is called when a LangChain provider package is loaded\n */\n private _patch(exports: PatchedLangChainExports): PatchedLangChainExports | void {\n const client = getClient();\n const defaultPii = Boolean(client?.getOptions().sendDefaultPii);\n\n const config = this.getConfig();\n\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n const recordInputs = config?.recordInputs ?? defaultPii;\n // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access\n const recordOutputs = config?.recordOutputs ?? defaultPii;\n\n // Create a shared handler instance\n const sentryHandler = createLangChainCallbackHandler({\n recordInputs,\n recordOutputs,\n });\n\n // Patch Runnable methods to inject callbacks at request time\n // This directly manipulates options.callbacks that LangChain uses\n this._patchRunnableMethods(exports, sentryHandler);\n\n return exports;\n }\n\n /**\n * Patches chat model methods (invoke, stream, batch) to inject Sentry callbacks\n * Finds a chat model class from the provider package exports and patches its prototype methods\n */\n private _patchRunnableMethods(exports: PatchedLangChainExports, sentryHandler: unknown): void {\n // Known chat model class names for each provider\n const knownChatModelNames = [\n 'ChatAnthropic',\n 'ChatOpenAI',\n 'ChatGoogleGenerativeAI',\n 'ChatMistralAI',\n 'ChatVertexAI',\n 'ChatGroq',\n ];\n\n // Find a chat model class in the exports by checking known class names\n const chatModelClass = Object.values(exports).find(exp => {\n if (typeof exp !== 'function') {\n return false;\n }\n return knownChatModelNames.includes(exp.name);\n }) as { prototype: unknown; name: string } | undefined;\n\n if (!chatModelClass) {\n return;\n }\n\n // Patch directly on chatModelClass.prototype\n const targetProto = chatModelClass.prototype as Record<string, unknown>;\n\n // Patch the methods (invoke, stream, batch)\n // All chat model instances will inherit these patched methods\n const methodsToPatch = ['invoke', 'stream', 'batch'] as const;\n\n for (const methodName of methodsToPatch) {\n const method = targetProto[methodName];\n if (typeof method === 'function') {\n targetProto[methodName] = wrapRunnableMethod(\n method as (...args: unknown[]) => unknown,\n sentryHandler,\n methodName,\n );\n }\n }\n }\n}\n"],"names":["InstrumentationBase","SDK_VERSION","InstrumentationNodeModuleDefinition","InstrumentationNodeModuleFile","getClient","createLangChainCallbackHandler"],"mappings":"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"}
|
package/build/esm/index.js
CHANGED
|
@@ -27,6 +27,7 @@ export { vercelAIIntegration } from './integrations/tracing/vercelai/index.js';
|
|
|
27
27
|
export { openAIIntegration } from './integrations/tracing/openai/index.js';
|
|
28
28
|
export { anthropicAIIntegration } from './integrations/tracing/anthropic-ai/index.js';
|
|
29
29
|
export { googleGenAIIntegration } from './integrations/tracing/google-genai/index.js';
|
|
30
|
+
export { langChainIntegration } from './integrations/tracing/langchain/index.js';
|
|
30
31
|
export { buildLaunchDarklyFlagUsedHandlerShim as buildLaunchDarklyFlagUsedHandler, launchDarklyIntegrationShim as launchDarklyIntegration } from './integrations/featureFlagShims/launchDarkly.js';
|
|
31
32
|
export { OpenFeatureIntegrationHookShim as OpenFeatureIntegrationHook, openFeatureIntegrationShim as openFeatureIntegration } from './integrations/featureFlagShims/openFeature.js';
|
|
32
33
|
export { statsigIntegrationShim as statsigIntegration } from './integrations/featureFlagShims/statsig.js';
|
|
@@ -37,6 +38,6 @@ export { getDefaultIntegrations, getDefaultIntegrationsWithoutPerformance, init,
|
|
|
37
38
|
export { initOpenTelemetry, preloadOpenTelemetry } from './sdk/initOtel.js';
|
|
38
39
|
export { getAutoPerformanceIntegrations } from './integrations/tracing/index.js';
|
|
39
40
|
export { setOpenTelemetryContextAsyncContextStrategy as setNodeAsyncContextStrategy } from '@sentry/opentelemetry';
|
|
40
|
-
export { SDK_VERSION, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, Scope, addBreadcrumb, addEventProcessor, addIntegration, captureCheckIn, captureConsoleIntegration, captureEvent, captureException, captureFeedback, captureMessage, captureSession, close, consoleIntegration, consoleLoggingIntegration, continueTrace, createConsolaReporter, createTransport, dedupeIntegration, endSession, eventFiltersIntegration, extraErrorDataIntegration, featureFlagsIntegration, flush, functionToStringIntegration, getActiveSpan, getClient, getCurrentScope, getGlobalScope, getIsolationScope, getRootSpan, getSpanDescendants, getSpanStatusFromHttpCode, getTraceData, getTraceMetaTags, httpHeadersToSpanAttributes, inboundFiltersIntegration, instrumentSupabaseClient, isEnabled, isInitialized, lastEventId, linkedErrorsIntegration, parameterize, profiler, requestDataIntegration, rewriteFramesIntegration, setContext, setCurrentClient, setExtra, setExtras, setHttpStatus, setMeasurement, setTag, setTags, setUser, spanToBaggageHeader, spanToJSON, spanToTraceHeader, startInactiveSpan, startNewTrace, startSession, startSpan, startSpanManual, supabaseIntegration, suppressTracing, trpcMiddleware, updateSpanName, winterCGHeadersToDict, withActiveSpan, withIsolationScope, withMonitor, withScope, wrapMcpServerWithSentry, zodErrorsIntegration } from '@sentry/core';
|
|
41
|
+
export { SDK_VERSION, SEMANTIC_ATTRIBUTE_SENTRY_OP, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SAMPLE_RATE, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, Scope, addBreadcrumb, addEventProcessor, addIntegration, captureCheckIn, captureConsoleIntegration, captureEvent, captureException, captureFeedback, captureMessage, captureSession, close, consoleIntegration, consoleLoggingIntegration, continueTrace, createConsolaReporter, createLangChainCallbackHandler, createTransport, dedupeIntegration, endSession, eventFiltersIntegration, extraErrorDataIntegration, featureFlagsIntegration, flush, functionToStringIntegration, getActiveSpan, getClient, getCurrentScope, getGlobalScope, getIsolationScope, getRootSpan, getSpanDescendants, getSpanStatusFromHttpCode, getTraceData, getTraceMetaTags, httpHeadersToSpanAttributes, inboundFiltersIntegration, instrumentSupabaseClient, isEnabled, isInitialized, lastEventId, linkedErrorsIntegration, parameterize, profiler, requestDataIntegration, rewriteFramesIntegration, setContext, setCurrentClient, setExtra, setExtras, setHttpStatus, setMeasurement, setTag, setTags, setUser, spanToBaggageHeader, spanToJSON, spanToTraceHeader, startInactiveSpan, startNewTrace, startSession, startSpan, startSpanManual, supabaseIntegration, suppressTracing, trpcMiddleware, updateSpanName, winterCGHeadersToDict, withActiveSpan, withIsolationScope, withMonitor, withScope, wrapMcpServerWithSentry, zodErrorsIntegration } from '@sentry/core';
|
|
41
42
|
export { NODE_VERSION, NodeClient, SentryContextManager, anrIntegration, childProcessIntegration, contextLinesIntegration, createGetModuleFromFilename, createSentryWinstonTransport, cron, defaultStackParser, disableAnrDetectionForCallback, generateInstrumentOnce, getSentryRelease, httpServerIntegration, httpServerSpansIntegration, localVariablesIntegration, logger, makeNodeTransport, metrics, modulesIntegration, nodeContextIntegration, onUncaughtExceptionIntegration, onUnhandledRejectionIntegration, pinoIntegration, spotlightIntegration, systemErrorIntegration, validateOpenTelemetrySetup } from '@sentry/node-core';
|
|
42
43
|
//# sourceMappingURL=index.js.map
|
package/build/esm/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.js","sources":[],"sourcesContent":[],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"node-fetch.js","sources":["../../../src/integrations/node-fetch.ts"],"sourcesContent":["import type { UndiciInstrumentationConfig } from '@opentelemetry/instrumentation-undici';\nimport { UndiciInstrumentation } from '@opentelemetry/instrumentation-undici';\nimport type { IntegrationFn } from '@sentry/core';\nimport { defineIntegration, getClient, hasSpansEnabled, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core';\nimport type { NodeClient } from '@sentry/node-core';\nimport { generateInstrumentOnce, SentryNodeFetchInstrumentation } from '@sentry/node-core';\nimport type { NodeClientOptions } from '../types';\n\nconst INTEGRATION_NAME = 'NodeFetch';\n\ninterface NodeFetchOptions {\n /**\n * Whether breadcrumbs should be recorded for requests.\n * Defaults to true\n */\n breadcrumbs?: boolean;\n\n /**\n * If set to false, do not emit any spans.\n * This will ensure that the default UndiciInstrumentation from OpenTelemetry is not setup,\n * only the Sentry-specific instrumentation for breadcrumbs & trace propagation is applied.\n *\n * If `skipOpenTelemetrySetup: true` is configured, this defaults to `false`, otherwise it defaults to `true`.\n */\n spans?: boolean;\n\n /**\n * Do not capture spans or breadcrumbs for outgoing fetch requests to URLs where the given callback returns `true`.\n * This controls both span & breadcrumb creation - spans will be non recording if tracing is disabled.\n */\n ignoreOutgoingRequests?: (url: string) => boolean;\n}\n\nconst instrumentOtelNodeFetch = generateInstrumentOnce(\n INTEGRATION_NAME,\n UndiciInstrumentation,\n (options: NodeFetchOptions) => {\n return getConfigWithDefaults(options);\n },\n);\n\nconst instrumentSentryNodeFetch = generateInstrumentOnce(\n `${INTEGRATION_NAME}.sentry`,\n SentryNodeFetchInstrumentation,\n (options: NodeFetchOptions) => {\n return options;\n },\n);\n\nconst _nativeNodeFetchIntegration = ((options: NodeFetchOptions = {}) => {\n return {\n name: 'NodeFetch',\n setupOnce() {\n const instrumentSpans = _shouldInstrumentSpans(options, getClient<NodeClient>()?.getOptions());\n\n // This is the \"regular\" OTEL instrumentation that emits spans\n if (instrumentSpans) {\n instrumentOtelNodeFetch(options);\n }\n\n // This is the Sentry-specific instrumentation that creates breadcrumbs & propagates traces\n // This must be registered after the OTEL one, to ensure that the core trace propagation logic takes presedence\n // Otherwise, the sentry-trace header may be set multiple times\n instrumentSentryNodeFetch(options);\n },\n };\n}) satisfies IntegrationFn;\n\nexport const nativeNodeFetchIntegration = defineIntegration(_nativeNodeFetchIntegration);\n\n// Matching the behavior of the base instrumentation\nfunction getAbsoluteUrl(origin: string, path: string = '/'): string {\n const url = `${origin}`;\n\n if (url.endsWith('/') && path.startsWith('/')) {\n return `${url}${path.slice(1)}`;\n }\n\n if (!url.endsWith('/') && !path.startsWith('/')) {\n return `${url}/${path.slice(1)}`;\n }\n\n return `${url}${path}`;\n}\n\nfunction _shouldInstrumentSpans(options: NodeFetchOptions, clientOptions: Partial<NodeClientOptions> = {}): boolean {\n // If `spans` is passed in, it takes precedence\n // Else, we by default emit spans, unless `skipOpenTelemetrySetup` is set to `true` or spans are not enabled\n return typeof options.spans === 'boolean'\n ? options.spans\n : !clientOptions.skipOpenTelemetrySetup && hasSpansEnabled(clientOptions);\n}\n\nfunction getConfigWithDefaults(options: Partial<NodeFetchOptions> = {}): UndiciInstrumentationConfig {\n const instrumentationConfig = {\n requireParentforSpans: false,\n ignoreRequestHook: request => {\n const url = getAbsoluteUrl(request.origin, request.path);\n const _ignoreOutgoingRequests = options.ignoreOutgoingRequests;\n const shouldIgnore = _ignoreOutgoingRequests && url && _ignoreOutgoingRequests(url);\n\n return !!shouldIgnore;\n },\n startSpanHook: () => {\n return {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.otel.node_fetch',\n };\n },\n } satisfies UndiciInstrumentationConfig;\n\n return instrumentationConfig;\n}\n"],"names":[],"mappings":";;;;AAQA,MAAM,gBAAA,GAAmB,WAAW;;AAyBpC,MAAM,uBAAA,GAA0B,sBAAsB;AACtD,EAAE,gBAAgB;AAClB,EAAE,qBAAqB;AACvB,EAAE,CAAC,OAAO,KAAuB;AACjC,IAAI,OAAO,qBAAqB,CAAC,OAAO,CAAC;AACzC,GAAG;AACH,CAAC;;AAED,MAAM,yBAAA,GAA4B,sBAAsB;AACxD,EAAE,CAAC,EAAA,gBAAA,CAAA,OAAA,CAAA;AACA,EAAA,8BAAA;AACA,EAAA,CAAA,OAAA,KAAA;AACA,IAAA,OAAA,OAAA;AACA,GAAA;AACA,CAAA;;AAEA,MAAA,2BAAA,IAAA,CAAA,OAAA,GAAA,EAAA,KAAA;AACA,EAAA,OAAA;AACA,IAAA,IAAA,EAAA,WAAA;AACA,IAAA,SAAA,GAAA;AACA,MAAA,MAAA,eAAA,GAAA,sBAAA,CAAA,OAAA,EAAA,SAAA,EAAA,EAAA,UAAA,EAAA,CAAA;;AAEA;AACA,MAAA,IAAA,eAAA,EAAA;AACA,QAAA,uBAAA,CAAA,OAAA,CAAA;AACA;;AAEA;AACA;AACA;AACA,MAAA,yBAAA,CAAA,OAAA,CAAA;AACA,KAAA;AACA,GAAA;AACA,CAAA,CAAA;;AAEA,MAAA,0BAAA,GAAA,iBAAA,CAAA,2BAAA;;AAEA;AACA,SAAA,cAAA,CAAA,MAAA,EAAA,IAAA,GAAA,GAAA,EAAA;AACA,EAAA,MAAA,GAAA,GAAA,CAAA,EAAA,MAAA,CAAA,CAAA;;AAEA,EAAA,IAAA,GAAA,CAAA,QAAA,CAAA,GAAA,CAAA,IAAA,IAAA,CAAA,UAAA,CAAA,GAAA,CAAA,EAAA;AACA,IAAA,OAAA,CAAA,EAAA,GAAA,CAAA,EAAA,IAAA,CAAA,KAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AACA;;AAEA,EAAA,IAAA,CAAA,GAAA,CAAA,QAAA,CAAA,GAAA,CAAA,IAAA,CAAA,IAAA,CAAA,UAAA,CAAA,GAAA,CAAA,EAAA;AACA,IAAA,OAAA,CAAA,EAAA,GAAA,CAAA,CAAA,EAAA,IAAA,CAAA,KAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AACA;;AAEA,EAAA,OAAA,CAAA,EAAA,GAAA,CAAA,EAAA,IAAA,CAAA,CAAA;AACA;;AAEA,SAAA,sBAAA,CAAA,OAAA,EAAA,aAAA,GAAA,EAAA,EAAA;AACA;AACA;AACA,EAAA,OAAA,OAAA,OAAA,CAAA,KAAA,KAAA;AACA,MAAA,OAAA,CAAA;AACA,MAAA,CAAA,aAAA,CAAA,sBAAA,IAAA,eAAA,CAAA,aAAA,CAAA;AACA;;AAEA,SAAA,qBAAA,CAAA,OAAA,GAAA,EAAA,EAAA;AACA,EAAA,MAAA,qBAAA,GAAA;AACA,IAAA,qBAAA,EAAA,KAAA;AACA,IAAA,iBAAA,EAAA,OAAA,IAAA;AACA,MAAA,MAAA,GAAA,GAAA,cAAA,CAAA,OAAA,CAAA,MAAA,EAAA,OAAA,CAAA,IAAA,CAAA;AACA,MAAA,MAAA,uBAAA,GAAA,OAAA,CAAA,sBAAA;AACA,MAAA,MAAA,YAAA,GAAA,uBAAA,IAAA,GAAA,IAAA,uBAAA,CAAA,GAAA,CAAA;;AAEA,MAAA,OAAA,CAAA,CAAA,YAAA;AACA,KAAA;AACA,IAAA,aAAA,EAAA,MAAA;AACA,MAAA,OAAA;AACA,QAAA,CAAA,gCAAA,GAAA,2BAAA;AACA,OAAA;AACA,KAAA;AACA,GAAA;;AAEA,EAAA,OAAA,qBAAA;AACA;;;;"}
|
|
1
|
+
{"version":3,"file":"node-fetch.js","sources":["../../../src/integrations/node-fetch.ts"],"sourcesContent":["import type { UndiciInstrumentationConfig } from '@opentelemetry/instrumentation-undici';\nimport { UndiciInstrumentation } from '@opentelemetry/instrumentation-undici';\nimport type { IntegrationFn } from '@sentry/core';\nimport { defineIntegration, getClient, hasSpansEnabled, SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN } from '@sentry/core';\nimport type { NodeClient } from '@sentry/node-core';\nimport { generateInstrumentOnce, SentryNodeFetchInstrumentation } from '@sentry/node-core';\nimport type { NodeClientOptions } from '../types';\n\nconst INTEGRATION_NAME = 'NodeFetch';\n\ninterface NodeFetchOptions extends Pick<UndiciInstrumentationConfig, 'requestHook' | 'responseHook'> {\n /**\n * Whether breadcrumbs should be recorded for requests.\n * Defaults to true\n */\n breadcrumbs?: boolean;\n\n /**\n * If set to false, do not emit any spans.\n * This will ensure that the default UndiciInstrumentation from OpenTelemetry is not setup,\n * only the Sentry-specific instrumentation for breadcrumbs & trace propagation is applied.\n *\n * If `skipOpenTelemetrySetup: true` is configured, this defaults to `false`, otherwise it defaults to `true`.\n */\n spans?: boolean;\n\n /**\n * Do not capture spans or breadcrumbs for outgoing fetch requests to URLs where the given callback returns `true`.\n * This controls both span & breadcrumb creation - spans will be non recording if tracing is disabled.\n */\n ignoreOutgoingRequests?: (url: string) => boolean;\n}\n\nconst instrumentOtelNodeFetch = generateInstrumentOnce(\n INTEGRATION_NAME,\n UndiciInstrumentation,\n (options: NodeFetchOptions) => {\n return getConfigWithDefaults(options);\n },\n);\n\nconst instrumentSentryNodeFetch = generateInstrumentOnce(\n `${INTEGRATION_NAME}.sentry`,\n SentryNodeFetchInstrumentation,\n (options: NodeFetchOptions) => {\n return options;\n },\n);\n\nconst _nativeNodeFetchIntegration = ((options: NodeFetchOptions = {}) => {\n return {\n name: 'NodeFetch',\n setupOnce() {\n const instrumentSpans = _shouldInstrumentSpans(options, getClient<NodeClient>()?.getOptions());\n\n // This is the \"regular\" OTEL instrumentation that emits spans\n if (instrumentSpans) {\n instrumentOtelNodeFetch(options);\n }\n\n // This is the Sentry-specific instrumentation that creates breadcrumbs & propagates traces\n // This must be registered after the OTEL one, to ensure that the core trace propagation logic takes presedence\n // Otherwise, the sentry-trace header may be set multiple times\n instrumentSentryNodeFetch(options);\n },\n };\n}) satisfies IntegrationFn;\n\nexport const nativeNodeFetchIntegration = defineIntegration(_nativeNodeFetchIntegration);\n\n// Matching the behavior of the base instrumentation\nfunction getAbsoluteUrl(origin: string, path: string = '/'): string {\n const url = `${origin}`;\n\n if (url.endsWith('/') && path.startsWith('/')) {\n return `${url}${path.slice(1)}`;\n }\n\n if (!url.endsWith('/') && !path.startsWith('/')) {\n return `${url}/${path.slice(1)}`;\n }\n\n return `${url}${path}`;\n}\n\nfunction _shouldInstrumentSpans(options: NodeFetchOptions, clientOptions: Partial<NodeClientOptions> = {}): boolean {\n // If `spans` is passed in, it takes precedence\n // Else, we by default emit spans, unless `skipOpenTelemetrySetup` is set to `true` or spans are not enabled\n return typeof options.spans === 'boolean'\n ? options.spans\n : !clientOptions.skipOpenTelemetrySetup && hasSpansEnabled(clientOptions);\n}\n\nfunction getConfigWithDefaults(options: Partial<NodeFetchOptions> = {}): UndiciInstrumentationConfig {\n const instrumentationConfig = {\n requireParentforSpans: false,\n ignoreRequestHook: request => {\n const url = getAbsoluteUrl(request.origin, request.path);\n const _ignoreOutgoingRequests = options.ignoreOutgoingRequests;\n const shouldIgnore = _ignoreOutgoingRequests && url && _ignoreOutgoingRequests(url);\n\n return !!shouldIgnore;\n },\n startSpanHook: () => {\n return {\n [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.http.otel.node_fetch',\n };\n },\n requestHook: options.requestHook,\n responseHook: options.responseHook,\n } satisfies UndiciInstrumentationConfig;\n\n return instrumentationConfig;\n}\n"],"names":[],"mappings":";;;;AAQA,MAAM,gBAAA,GAAmB,WAAW;;AAyBpC,MAAM,uBAAA,GAA0B,sBAAsB;AACtD,EAAE,gBAAgB;AAClB,EAAE,qBAAqB;AACvB,EAAE,CAAC,OAAO,KAAuB;AACjC,IAAI,OAAO,qBAAqB,CAAC,OAAO,CAAC;AACzC,GAAG;AACH,CAAC;;AAED,MAAM,yBAAA,GAA4B,sBAAsB;AACxD,EAAE,CAAC,EAAA,gBAAA,CAAA,OAAA,CAAA;AACA,EAAA,8BAAA;AACA,EAAA,CAAA,OAAA,KAAA;AACA,IAAA,OAAA,OAAA;AACA,GAAA;AACA,CAAA;;AAEA,MAAA,2BAAA,IAAA,CAAA,OAAA,GAAA,EAAA,KAAA;AACA,EAAA,OAAA;AACA,IAAA,IAAA,EAAA,WAAA;AACA,IAAA,SAAA,GAAA;AACA,MAAA,MAAA,eAAA,GAAA,sBAAA,CAAA,OAAA,EAAA,SAAA,EAAA,EAAA,UAAA,EAAA,CAAA;;AAEA;AACA,MAAA,IAAA,eAAA,EAAA;AACA,QAAA,uBAAA,CAAA,OAAA,CAAA;AACA;;AAEA;AACA;AACA;AACA,MAAA,yBAAA,CAAA,OAAA,CAAA;AACA,KAAA;AACA,GAAA;AACA,CAAA,CAAA;;AAEA,MAAA,0BAAA,GAAA,iBAAA,CAAA,2BAAA;;AAEA;AACA,SAAA,cAAA,CAAA,MAAA,EAAA,IAAA,GAAA,GAAA,EAAA;AACA,EAAA,MAAA,GAAA,GAAA,CAAA,EAAA,MAAA,CAAA,CAAA;;AAEA,EAAA,IAAA,GAAA,CAAA,QAAA,CAAA,GAAA,CAAA,IAAA,IAAA,CAAA,UAAA,CAAA,GAAA,CAAA,EAAA;AACA,IAAA,OAAA,CAAA,EAAA,GAAA,CAAA,EAAA,IAAA,CAAA,KAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AACA;;AAEA,EAAA,IAAA,CAAA,GAAA,CAAA,QAAA,CAAA,GAAA,CAAA,IAAA,CAAA,IAAA,CAAA,UAAA,CAAA,GAAA,CAAA,EAAA;AACA,IAAA,OAAA,CAAA,EAAA,GAAA,CAAA,CAAA,EAAA,IAAA,CAAA,KAAA,CAAA,CAAA,CAAA,CAAA,CAAA;AACA;;AAEA,EAAA,OAAA,CAAA,EAAA,GAAA,CAAA,EAAA,IAAA,CAAA,CAAA;AACA;;AAEA,SAAA,sBAAA,CAAA,OAAA,EAAA,aAAA,GAAA,EAAA,EAAA;AACA;AACA;AACA,EAAA,OAAA,OAAA,OAAA,CAAA,KAAA,KAAA;AACA,MAAA,OAAA,CAAA;AACA,MAAA,CAAA,aAAA,CAAA,sBAAA,IAAA,eAAA,CAAA,aAAA,CAAA;AACA;;AAEA,SAAA,qBAAA,CAAA,OAAA,GAAA,EAAA,EAAA;AACA,EAAA,MAAA,qBAAA,GAAA;AACA,IAAA,qBAAA,EAAA,KAAA;AACA,IAAA,iBAAA,EAAA,OAAA,IAAA;AACA,MAAA,MAAA,GAAA,GAAA,cAAA,CAAA,OAAA,CAAA,MAAA,EAAA,OAAA,CAAA,IAAA,CAAA;AACA,MAAA,MAAA,uBAAA,GAAA,OAAA,CAAA,sBAAA;AACA,MAAA,MAAA,YAAA,GAAA,uBAAA,IAAA,GAAA,IAAA,uBAAA,CAAA,GAAA,CAAA;;AAEA,MAAA,OAAA,CAAA,CAAA,YAAA;AACA,KAAA;AACA,IAAA,aAAA,EAAA,MAAA;AACA,MAAA,OAAA;AACA,QAAA,CAAA,gCAAA,GAAA,2BAAA;AACA,OAAA;AACA,KAAA;AACA,IAAA,WAAA,EAAA,OAAA,CAAA,WAAA;AACA,IAAA,YAAA,EAAA,OAAA,CAAA,YAAA;AACA,GAAA;;AAEA,EAAA,OAAA,qBAAA;AACA;;;;"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { defineIntegration, SEMANTIC_ATTRIBUTE_SENTRY_OP } from '@sentry/core';
|
|
1
|
+
import { defineIntegration, captureException, flush, SEMANTIC_ATTRIBUTE_SENTRY_OP } from '@sentry/core';
|
|
2
2
|
import { generateInstrumentOnce, addOriginToSpan } from '@sentry/node-core';
|
|
3
3
|
import { FirebaseInstrumentation } from './otel/firebaseInstrumentation.js';
|
|
4
4
|
|
|
@@ -10,6 +10,24 @@ const config = {
|
|
|
10
10
|
|
|
11
11
|
span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'db.query');
|
|
12
12
|
},
|
|
13
|
+
functions: {
|
|
14
|
+
requestHook: span => {
|
|
15
|
+
addOriginToSpan(span, 'auto.firebase.otel.functions');
|
|
16
|
+
|
|
17
|
+
span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'http.request');
|
|
18
|
+
},
|
|
19
|
+
errorHook: async (_, error) => {
|
|
20
|
+
if (error) {
|
|
21
|
+
captureException(error, {
|
|
22
|
+
mechanism: {
|
|
23
|
+
type: 'auto.firebase.otel.functions',
|
|
24
|
+
handled: false,
|
|
25
|
+
},
|
|
26
|
+
});
|
|
27
|
+
await flush(2000);
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
},
|
|
13
31
|
};
|
|
14
32
|
|
|
15
33
|
const instrumentFirebase = generateInstrumentOnce(INTEGRATION_NAME, () => new FirebaseInstrumentation(config));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"firebase.js","sources":["../../../../../src/integrations/tracing/firebase/firebase.ts"],"sourcesContent":["import type { IntegrationFn } from '@sentry/core';\nimport { defineIntegration, SEMANTIC_ATTRIBUTE_SENTRY_OP } from '@sentry/core';\nimport { addOriginToSpan, generateInstrumentOnce } from '@sentry/node-core';\nimport { type FirebaseInstrumentationConfig, FirebaseInstrumentation } from './otel';\n\nconst INTEGRATION_NAME = 'Firebase';\n\nconst config: FirebaseInstrumentationConfig = {\n firestoreSpanCreationHook: span => {\n addOriginToSpan(span, 'auto.firebase.otel.firestore');\n\n span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'db.query');\n },\n};\n\nexport const instrumentFirebase = generateInstrumentOnce(INTEGRATION_NAME, () => new FirebaseInstrumentation(config));\n\nconst _firebaseIntegration = (() => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n instrumentFirebase();\n },\n };\n}) satisfies IntegrationFn;\n\nexport const firebaseIntegration = defineIntegration(_firebaseIntegration);\n"],"names":[],"mappings":";;;;AAKA,MAAM,gBAAA,GAAmB,UAAU;;AAEnC,MAAM,MAAM,GAAkC;AAC9C,EAAE,yBAAyB,EAAE,IAAA,IAAQ;AACrC,IAAI,eAAe,CAAC,IAAI,EAAE,8BAA8B,CAAC;;AAEzD,IAAI,IAAI,CAAC,YAAY,CAAC,4BAA4B,EAAE,UAAU,CAAC;AAC/D,GAAG;AACH,CAAC;;AAEM,MAAM,kBAAA,GAAqB,sBAAsB,CAAC,gBAAgB,EAAE,MAAM,IAAI,uBAAuB,CAAC,MAAM,CAAC;;AAEpH,MAAM,oBAAA,IAAwB,MAAM;AACpC,EAAE,OAAO;AACT,IAAI,IAAI,EAAE,gBAAgB;AAC1B,IAAI,SAAS,GAAG;AAChB,MAAM,kBAAkB,EAAE;AAC1B,KAAK;AACL,GAAG;AACH,CAAC,CAAA;;MAEY,mBAAA,GAAsB,iBAAiB,CAAC,oBAAoB;;;;"}
|
|
1
|
+
{"version":3,"file":"firebase.js","sources":["../../../../../src/integrations/tracing/firebase/firebase.ts"],"sourcesContent":["import type { IntegrationFn } from '@sentry/core';\nimport { captureException, defineIntegration, flush, SEMANTIC_ATTRIBUTE_SENTRY_OP } from '@sentry/core';\nimport { addOriginToSpan, generateInstrumentOnce } from '@sentry/node-core';\nimport { type FirebaseInstrumentationConfig, FirebaseInstrumentation } from './otel';\n\nconst INTEGRATION_NAME = 'Firebase';\n\nconst config: FirebaseInstrumentationConfig = {\n firestoreSpanCreationHook: span => {\n addOriginToSpan(span, 'auto.firebase.otel.firestore');\n\n span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'db.query');\n },\n functions: {\n requestHook: span => {\n addOriginToSpan(span, 'auto.firebase.otel.functions');\n\n span.setAttribute(SEMANTIC_ATTRIBUTE_SENTRY_OP, 'http.request');\n },\n errorHook: async (_, error) => {\n if (error) {\n captureException(error, {\n mechanism: {\n type: 'auto.firebase.otel.functions',\n handled: false,\n },\n });\n await flush(2000);\n }\n },\n },\n};\n\nexport const instrumentFirebase = generateInstrumentOnce(INTEGRATION_NAME, () => new FirebaseInstrumentation(config));\n\nconst _firebaseIntegration = (() => {\n return {\n name: INTEGRATION_NAME,\n setupOnce() {\n instrumentFirebase();\n },\n };\n}) satisfies IntegrationFn;\n\nexport const firebaseIntegration = defineIntegration(_firebaseIntegration);\n"],"names":[],"mappings":";;;;AAKA,MAAM,gBAAA,GAAmB,UAAU;;AAEnC,MAAM,MAAM,GAAkC;AAC9C,EAAE,yBAAyB,EAAE,IAAA,IAAQ;AACrC,IAAI,eAAe,CAAC,IAAI,EAAE,8BAA8B,CAAC;;AAEzD,IAAI,IAAI,CAAC,YAAY,CAAC,4BAA4B,EAAE,UAAU,CAAC;AAC/D,GAAG;AACH,EAAE,SAAS,EAAE;AACb,IAAI,WAAW,EAAE,IAAA,IAAQ;AACzB,MAAM,eAAe,CAAC,IAAI,EAAE,8BAA8B,CAAC;;AAE3D,MAAM,IAAI,CAAC,YAAY,CAAC,4BAA4B,EAAE,cAAc,CAAC;AACrE,KAAK;AACL,IAAI,SAAS,EAAE,OAAO,CAAC,EAAE,KAAK,KAAK;AACnC,MAAM,IAAI,KAAK,EAAE;AACjB,QAAQ,gBAAgB,CAAC,KAAK,EAAE;AAChC,UAAU,SAAS,EAAE;AACrB,YAAY,IAAI,EAAE,8BAA8B;AAChD,YAAY,OAAO,EAAE,KAAK;AAC1B,WAAW;AACX,SAAS,CAAC;AACV,QAAQ,MAAM,KAAK,CAAC,IAAI,CAAC;AACzB;AACA,KAAK;AACL,GAAG;AACH,CAAC;;AAEM,MAAM,kBAAA,GAAqB,sBAAsB,CAAC,gBAAgB,EAAE,MAAM,IAAI,uBAAuB,CAAC,MAAM,CAAC;;AAEpH,MAAM,oBAAA,IAAwB,MAAM;AACpC,EAAE,OAAO;AACT,IAAI,IAAI,EAAE,gBAAgB;AAC1B,IAAI,SAAS,GAAG;AAChB,MAAM,kBAAkB,EAAE;AAC1B,KAAK;AACL,GAAG;AACH,CAAC,CAAA;;MAEY,mBAAA,GAAsB,iBAAiB,CAAC,oBAAoB;;;;"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { InstrumentationBase } from '@opentelemetry/instrumentation';
|
|
2
2
|
import { SDK_VERSION } from '@sentry/core';
|
|
3
3
|
import { patchFirestore } from './patches/firestore.js';
|
|
4
|
+
import { patchFunctions } from './patches/functions.js';
|
|
4
5
|
|
|
5
6
|
const DefaultFirebaseInstrumentationConfig = {};
|
|
6
7
|
const firestoreSupportedVersions = ['>=3.0.0 <5']; // firebase 9+
|
|
8
|
+
const functionsSupportedVersions = ['>=6.0.0 <7']; // firebase-functions v2
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
11
|
* Instrumentation for Firebase services, specifically Firestore.
|
|
@@ -30,6 +32,7 @@ class FirebaseInstrumentation extends InstrumentationBase {
|
|
|
30
32
|
const modules = [];
|
|
31
33
|
|
|
32
34
|
modules.push(patchFirestore(this.tracer, firestoreSupportedVersions, this._wrap, this._unwrap, this.getConfig()));
|
|
35
|
+
modules.push(patchFunctions(this.tracer, functionsSupportedVersions, this._wrap, this._unwrap, this.getConfig()));
|
|
33
36
|
|
|
34
37
|
return modules;
|
|
35
38
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"firebaseInstrumentation.js","sources":["../../../../../../src/integrations/tracing/firebase/otel/firebaseInstrumentation.ts"],"sourcesContent":["import { type InstrumentationNodeModuleDefinition, InstrumentationBase } from '@opentelemetry/instrumentation';\nimport { SDK_VERSION } from '@sentry/core';\nimport { patchFirestore } from './patches/firestore';\nimport type { FirebaseInstrumentationConfig } from './types';\n\nconst DefaultFirebaseInstrumentationConfig: FirebaseInstrumentationConfig = {};\nconst firestoreSupportedVersions = ['>=3.0.0 <5']; // firebase 9+\n\n/**\n * Instrumentation for Firebase services, specifically Firestore.\n */\nexport class FirebaseInstrumentation extends InstrumentationBase<FirebaseInstrumentationConfig> {\n public constructor(config: FirebaseInstrumentationConfig = DefaultFirebaseInstrumentationConfig) {\n super('@sentry/instrumentation-firebase', SDK_VERSION, config);\n }\n\n /**\n * sets config\n * @param config\n */\n public override setConfig(config: FirebaseInstrumentationConfig = {}): void {\n super.setConfig({ ...DefaultFirebaseInstrumentationConfig, ...config });\n }\n\n /**\n *\n * @protected\n */\n // eslint-disable-next-line @typescript-eslint/naming-convention\n protected init(): InstrumentationNodeModuleDefinition | InstrumentationNodeModuleDefinition[] | void {\n const modules: InstrumentationNodeModuleDefinition[] = [];\n\n modules.push(patchFirestore(this.tracer, firestoreSupportedVersions, this._wrap, this._unwrap, this.getConfig()));\n\n return modules;\n }\n}\n"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"firebaseInstrumentation.js","sources":["../../../../../../src/integrations/tracing/firebase/otel/firebaseInstrumentation.ts"],"sourcesContent":["import { type InstrumentationNodeModuleDefinition, InstrumentationBase } from '@opentelemetry/instrumentation';\nimport { SDK_VERSION } from '@sentry/core';\nimport { patchFirestore } from './patches/firestore';\nimport { patchFunctions } from './patches/functions';\nimport type { FirebaseInstrumentationConfig } from './types';\n\nconst DefaultFirebaseInstrumentationConfig: FirebaseInstrumentationConfig = {};\nconst firestoreSupportedVersions = ['>=3.0.0 <5']; // firebase 9+\nconst functionsSupportedVersions = ['>=6.0.0 <7']; // firebase-functions v2\n\n/**\n * Instrumentation for Firebase services, specifically Firestore.\n */\nexport class FirebaseInstrumentation extends InstrumentationBase<FirebaseInstrumentationConfig> {\n public constructor(config: FirebaseInstrumentationConfig = DefaultFirebaseInstrumentationConfig) {\n super('@sentry/instrumentation-firebase', SDK_VERSION, config);\n }\n\n /**\n * sets config\n * @param config\n */\n public override setConfig(config: FirebaseInstrumentationConfig = {}): void {\n super.setConfig({ ...DefaultFirebaseInstrumentationConfig, ...config });\n }\n\n /**\n *\n * @protected\n */\n // eslint-disable-next-line @typescript-eslint/naming-convention\n protected init(): InstrumentationNodeModuleDefinition | InstrumentationNodeModuleDefinition[] | void {\n const modules: InstrumentationNodeModuleDefinition[] = [];\n\n modules.push(patchFirestore(this.tracer, firestoreSupportedVersions, this._wrap, this._unwrap, this.getConfig()));\n modules.push(patchFunctions(this.tracer, functionsSupportedVersions, this._wrap, this._unwrap, this.getConfig()));\n\n return modules;\n }\n}\n"],"names":[],"mappings":";;;;;AAMA,MAAM,oCAAoC,GAAkC,EAAE;AAC9E,MAAM,0BAAA,GAA6B,CAAC,YAAY,CAAC,CAAA;AACjD,MAAM,0BAAA,GAA6B,CAAC,YAAY,CAAC,CAAA;;AAEjD;AACA;AACA;AACO,MAAM,uBAAA,SAAgC,mBAAmB,CAAgC;AAChG,GAAS,WAAW,CAAC,MAAM,GAAkC,oCAAoC,EAAE;AACnG,IAAI,KAAK,CAAC,kCAAkC,EAAE,WAAW,EAAE,MAAM,CAAC;AAClE;;AAEA;AACA;AACA;AACA;AACA,IAAkB,SAAS,CAAC,MAAM,GAAkC,EAAE,EAAQ;AAC9E,IAAI,KAAK,CAAC,SAAS,CAAC,EAAE,GAAG,oCAAoC,EAAE,GAAG,MAAA,EAAQ,CAAC;AAC3E;;AAEA;AACA;AACA;AACA;AACA;AACA,GAAY,IAAI,GAAuF;AACvG,IAAI,MAAM,OAAO,GAA0C,EAAE;;AAE7D,IAAI,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,0BAA0B,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;AACrH,IAAI,OAAO,CAAC,IAAI,CAAC,cAAc,CAAC,IAAI,CAAC,MAAM,EAAE,0BAA0B,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,OAAO,EAAE,IAAI,CAAC,SAAS,EAAE,CAAC,CAAC;;AAErH,IAAI,OAAO,OAAO;AAClB;AACA;;;;"}
|
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
import { SpanKind, context, trace, diag } from '@opentelemetry/api';
|
|
2
|
+
import { InstrumentationNodeModuleDefinition, InstrumentationNodeModuleFile, isWrapped, safeExecuteInTheMiddle } from '@opentelemetry/instrumentation';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Patches Firebase Functions v2 to add OpenTelemetry instrumentation
|
|
6
|
+
* @param tracer - Opentelemetry Tracer
|
|
7
|
+
* @param functionsSupportedVersions - supported versions of firebase-functions
|
|
8
|
+
* @param wrap - reference to native instrumentation wrap function
|
|
9
|
+
* @param unwrap - reference to native instrumentation unwrap function
|
|
10
|
+
* @param config - Firebase instrumentation config
|
|
11
|
+
*/
|
|
12
|
+
function patchFunctions(
|
|
13
|
+
tracer,
|
|
14
|
+
functionsSupportedVersions,
|
|
15
|
+
wrap,
|
|
16
|
+
unwrap,
|
|
17
|
+
config,
|
|
18
|
+
) {
|
|
19
|
+
let requestHook = () => {};
|
|
20
|
+
let responseHook = () => {};
|
|
21
|
+
const errorHook = config.functions?.errorHook;
|
|
22
|
+
const configRequestHook = config.functions?.requestHook;
|
|
23
|
+
const configResponseHook = config.functions?.responseHook;
|
|
24
|
+
|
|
25
|
+
if (typeof configResponseHook === 'function') {
|
|
26
|
+
responseHook = (span, err) => {
|
|
27
|
+
safeExecuteInTheMiddle(
|
|
28
|
+
() => configResponseHook(span, err),
|
|
29
|
+
error => {
|
|
30
|
+
if (!error) {
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
diag.error(error?.message);
|
|
34
|
+
},
|
|
35
|
+
true,
|
|
36
|
+
);
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
if (typeof configRequestHook === 'function') {
|
|
40
|
+
requestHook = (span) => {
|
|
41
|
+
safeExecuteInTheMiddle(
|
|
42
|
+
() => configRequestHook(span),
|
|
43
|
+
error => {
|
|
44
|
+
if (!error) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
diag.error(error?.message);
|
|
48
|
+
},
|
|
49
|
+
true,
|
|
50
|
+
);
|
|
51
|
+
};
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const moduleFunctionsCJS = new InstrumentationNodeModuleDefinition('firebase-functions', functionsSupportedVersions);
|
|
55
|
+
const modulesToInstrument = [
|
|
56
|
+
{ name: 'firebase-functions/lib/v2/providers/https.js', triggerType: 'function' },
|
|
57
|
+
{ name: 'firebase-functions/lib/v2/providers/firestore.js', triggerType: 'firestore' },
|
|
58
|
+
{ name: 'firebase-functions/lib/v2/providers/scheduler.js', triggerType: 'scheduler' },
|
|
59
|
+
{ name: 'firebase-functions/lib/v2/storage.js', triggerType: 'storage' },
|
|
60
|
+
] ;
|
|
61
|
+
|
|
62
|
+
modulesToInstrument.forEach(({ name, triggerType }) => {
|
|
63
|
+
moduleFunctionsCJS.files.push(
|
|
64
|
+
new InstrumentationNodeModuleFile(
|
|
65
|
+
name,
|
|
66
|
+
functionsSupportedVersions,
|
|
67
|
+
moduleExports =>
|
|
68
|
+
wrapCommonFunctions(
|
|
69
|
+
moduleExports,
|
|
70
|
+
wrap,
|
|
71
|
+
unwrap,
|
|
72
|
+
tracer,
|
|
73
|
+
{ requestHook, responseHook, errorHook },
|
|
74
|
+
triggerType,
|
|
75
|
+
),
|
|
76
|
+
moduleExports => unwrapCommonFunctions(moduleExports, unwrap),
|
|
77
|
+
),
|
|
78
|
+
);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
return moduleFunctionsCJS;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/**
|
|
85
|
+
* Patches Cloud Functions for Firebase (v2) to add OpenTelemetry instrumentation
|
|
86
|
+
*
|
|
87
|
+
* @param tracer - Opentelemetry Tracer
|
|
88
|
+
* @param functionsConfig - Firebase instrumentation config
|
|
89
|
+
* @param triggerType - Type of trigger
|
|
90
|
+
* @returns A function that patches the function
|
|
91
|
+
*/
|
|
92
|
+
function patchV2Functions(
|
|
93
|
+
tracer,
|
|
94
|
+
functionsConfig,
|
|
95
|
+
triggerType,
|
|
96
|
+
) {
|
|
97
|
+
return function v2FunctionsWrapper(original) {
|
|
98
|
+
return function ( ...args) {
|
|
99
|
+
const handler = typeof args[0] === 'function' ? args[0] : args[1];
|
|
100
|
+
const documentOrOptions = typeof args[0] === 'function' ? undefined : args[0];
|
|
101
|
+
|
|
102
|
+
if (!handler) {
|
|
103
|
+
return original.call(this, ...args);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const wrappedHandler = async function ( ...handlerArgs) {
|
|
107
|
+
const functionName = process.env.FUNCTION_TARGET || process.env.K_SERVICE || 'unknown';
|
|
108
|
+
const span = tracer.startSpan(`firebase.function.${triggerType}`, {
|
|
109
|
+
kind: SpanKind.SERVER,
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
const attributes = {
|
|
113
|
+
'faas.name': functionName,
|
|
114
|
+
'faas.trigger': triggerType,
|
|
115
|
+
'faas.provider': 'firebase',
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
if (process.env.GCLOUD_PROJECT) {
|
|
119
|
+
attributes['cloud.project_id'] = process.env.GCLOUD_PROJECT;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
if (process.env.EVENTARC_CLOUD_EVENT_SOURCE) {
|
|
123
|
+
attributes['cloud.event_source'] = process.env.EVENTARC_CLOUD_EVENT_SOURCE;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
span.setAttributes(attributes);
|
|
127
|
+
functionsConfig?.requestHook?.(span);
|
|
128
|
+
|
|
129
|
+
// Can be changed to safeExecuteInTheMiddleAsync once following is merged and released
|
|
130
|
+
// https://github.com/open-telemetry/opentelemetry-js/pull/6032
|
|
131
|
+
return context.with(trace.setSpan(context.active(), span), async () => {
|
|
132
|
+
let error;
|
|
133
|
+
let result;
|
|
134
|
+
|
|
135
|
+
try {
|
|
136
|
+
result = await handler.apply(this, handlerArgs);
|
|
137
|
+
} catch (e) {
|
|
138
|
+
error = e ;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
functionsConfig?.responseHook?.(span, error);
|
|
142
|
+
|
|
143
|
+
if (error) {
|
|
144
|
+
span.recordException(error);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
span.end();
|
|
148
|
+
|
|
149
|
+
if (error) {
|
|
150
|
+
await functionsConfig?.errorHook?.(span, error);
|
|
151
|
+
throw error;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return result;
|
|
155
|
+
});
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
if (documentOrOptions) {
|
|
159
|
+
return original.call(this, documentOrOptions, wrappedHandler);
|
|
160
|
+
} else {
|
|
161
|
+
return original.call(this, wrappedHandler);
|
|
162
|
+
}
|
|
163
|
+
};
|
|
164
|
+
};
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function wrapCommonFunctions(
|
|
168
|
+
moduleExports,
|
|
169
|
+
wrap,
|
|
170
|
+
unwrap,
|
|
171
|
+
tracer,
|
|
172
|
+
functionsConfig,
|
|
173
|
+
triggerType,
|
|
174
|
+
) {
|
|
175
|
+
unwrapCommonFunctions(moduleExports, unwrap);
|
|
176
|
+
|
|
177
|
+
switch (triggerType) {
|
|
178
|
+
case 'function':
|
|
179
|
+
wrap(moduleExports, 'onRequest', patchV2Functions(tracer, functionsConfig, 'http.request'));
|
|
180
|
+
wrap(moduleExports, 'onCall', patchV2Functions(tracer, functionsConfig, 'http.call'));
|
|
181
|
+
break;
|
|
182
|
+
|
|
183
|
+
case 'firestore':
|
|
184
|
+
wrap(moduleExports, 'onDocumentCreated', patchV2Functions(tracer, functionsConfig, 'firestore.document.created'));
|
|
185
|
+
wrap(moduleExports, 'onDocumentUpdated', patchV2Functions(tracer, functionsConfig, 'firestore.document.updated'));
|
|
186
|
+
wrap(moduleExports, 'onDocumentDeleted', patchV2Functions(tracer, functionsConfig, 'firestore.document.deleted'));
|
|
187
|
+
wrap(moduleExports, 'onDocumentWritten', patchV2Functions(tracer, functionsConfig, 'firestore.document.written'));
|
|
188
|
+
wrap(
|
|
189
|
+
moduleExports,
|
|
190
|
+
'onDocumentCreatedWithAuthContext',
|
|
191
|
+
patchV2Functions(tracer, functionsConfig, 'firestore.document.created'),
|
|
192
|
+
);
|
|
193
|
+
wrap(
|
|
194
|
+
moduleExports,
|
|
195
|
+
'onDocumentUpdatedWithAuthContext',
|
|
196
|
+
patchV2Functions(tracer, functionsConfig, 'firestore.document.updated'),
|
|
197
|
+
);
|
|
198
|
+
|
|
199
|
+
wrap(
|
|
200
|
+
moduleExports,
|
|
201
|
+
'onDocumentDeletedWithAuthContext',
|
|
202
|
+
patchV2Functions(tracer, functionsConfig, 'firestore.document.deleted'),
|
|
203
|
+
);
|
|
204
|
+
|
|
205
|
+
wrap(
|
|
206
|
+
moduleExports,
|
|
207
|
+
'onDocumentWrittenWithAuthContext',
|
|
208
|
+
patchV2Functions(tracer, functionsConfig, 'firestore.document.written'),
|
|
209
|
+
);
|
|
210
|
+
break;
|
|
211
|
+
|
|
212
|
+
case 'scheduler':
|
|
213
|
+
wrap(moduleExports, 'onSchedule', patchV2Functions(tracer, functionsConfig, 'scheduler.scheduled'));
|
|
214
|
+
break;
|
|
215
|
+
|
|
216
|
+
case 'storage':
|
|
217
|
+
wrap(moduleExports, 'onObjectFinalized', patchV2Functions(tracer, functionsConfig, 'storage.object.finalized'));
|
|
218
|
+
wrap(moduleExports, 'onObjectArchived', patchV2Functions(tracer, functionsConfig, 'storage.object.archived'));
|
|
219
|
+
wrap(moduleExports, 'onObjectDeleted', patchV2Functions(tracer, functionsConfig, 'storage.object.deleted'));
|
|
220
|
+
wrap(
|
|
221
|
+
moduleExports,
|
|
222
|
+
'onObjectMetadataUpdated',
|
|
223
|
+
patchV2Functions(tracer, functionsConfig, 'storage.object.metadataUpdated'),
|
|
224
|
+
);
|
|
225
|
+
break;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return moduleExports;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function unwrapCommonFunctions(
|
|
232
|
+
moduleExports,
|
|
233
|
+
unwrap,
|
|
234
|
+
) {
|
|
235
|
+
const methods = [
|
|
236
|
+
'onSchedule',
|
|
237
|
+
'onRequest',
|
|
238
|
+
'onCall',
|
|
239
|
+
'onObjectFinalized',
|
|
240
|
+
'onObjectArchived',
|
|
241
|
+
'onObjectDeleted',
|
|
242
|
+
'onObjectMetadataUpdated',
|
|
243
|
+
'onDocumentCreated',
|
|
244
|
+
'onDocumentUpdated',
|
|
245
|
+
'onDocumentDeleted',
|
|
246
|
+
'onDocumentWritten',
|
|
247
|
+
'onDocumentCreatedWithAuthContext',
|
|
248
|
+
'onDocumentUpdatedWithAuthContext',
|
|
249
|
+
'onDocumentDeletedWithAuthContext',
|
|
250
|
+
'onDocumentWrittenWithAuthContext',
|
|
251
|
+
];
|
|
252
|
+
|
|
253
|
+
for (const method of methods) {
|
|
254
|
+
if (isWrapped(moduleExports[method])) {
|
|
255
|
+
unwrap(moduleExports, method);
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return moduleExports;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
export { patchFunctions, patchV2Functions };
|
|
262
|
+
//# sourceMappingURL=functions.js.map
|