@sentry/nuxt 10.45.0 → 10.47.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/module.js +46 -27
- package/build/cjs/module.js.map +1 -1
- package/build/cjs/vite/databaseConfig.js +10 -2
- package/build/cjs/vite/databaseConfig.js.map +1 -1
- package/build/cjs/vite/middlewareConfig.js +6 -1
- package/build/cjs/vite/middlewareConfig.js.map +1 -1
- package/build/cjs/vite/sentryVitePlugin.js.map +1 -1
- package/build/cjs/vite/storageConfig.js +6 -2
- package/build/cjs/vite/storageConfig.js.map +1 -1
- package/build/cjs/vite/utils.js +21 -2
- package/build/cjs/vite/utils.js.map +1 -1
- package/build/esm/module.js +48 -29
- package/build/esm/module.js.map +1 -1
- package/build/esm/package.json +1 -1
- package/build/esm/vite/databaseConfig.js +10 -2
- package/build/esm/vite/databaseConfig.js.map +1 -1
- package/build/esm/vite/middlewareConfig.js +6 -1
- package/build/esm/vite/middlewareConfig.js.map +1 -1
- package/build/esm/vite/sentryVitePlugin.js.map +1 -1
- package/build/esm/vite/storageConfig.js +6 -2
- package/build/esm/vite/storageConfig.js.map +1 -1
- package/build/esm/vite/utils.js +21 -3
- package/build/esm/vite/utils.js.map +1 -1
- package/build/module/module.json +1 -1
- package/build/module/module.mjs +68 -30
- package/build/module/runtime/hooks/updateRouteBeforeResponse.d.ts +3 -1
- package/build/module/runtime/hooks/updateRouteBeforeResponse.js +7 -2
- package/build/module/runtime/hooks/wrapMiddlewareHandler.js +25 -15
- package/build/module/runtime/plugins/database-legacy.server.d.ts +5 -0
- package/build/module/runtime/plugins/database-legacy.server.js +6 -0
- package/build/module/runtime/plugins/database.server.d.ts +1 -1
- package/build/module/runtime/plugins/database.server.js +3 -147
- package/build/module/runtime/plugins/handler-legacy.server.d.ts +2 -0
- package/build/module/runtime/plugins/handler-legacy.server.js +4 -0
- package/build/module/runtime/plugins/handler.server.d.ts +6 -0
- package/build/module/runtime/plugins/handler.server.js +6 -0
- package/build/module/runtime/plugins/route-detector-legacy.server.d.ts +2 -0
- package/build/module/runtime/plugins/route-detector-legacy.server.js +37 -0
- package/build/module/runtime/plugins/route-detector.server.js +1 -1
- package/build/module/runtime/plugins/sentry.server.js +6 -31
- package/build/module/runtime/plugins/storage-legacy.server.d.ts +5 -0
- package/build/module/runtime/plugins/storage-legacy.server.js +6 -0
- package/build/module/runtime/plugins/storage.server.d.ts +4 -2
- package/build/module/runtime/plugins/storage.server.js +3 -168
- package/build/module/runtime/plugins/update-route-name-legacy.server.d.ts +2 -0
- package/build/module/runtime/plugins/update-route-name-legacy.server.js +4 -0
- package/build/module/runtime/plugins/update-route-name.server.d.ts +4 -0
- package/build/module/runtime/plugins/update-route-name.server.js +4 -0
- package/build/module/runtime/utils/database-span-data.d.ts +16 -2
- package/build/module/runtime/utils/instrumentDatabase.d.ts +9 -0
- package/build/module/runtime/utils/instrumentDatabase.js +147 -0
- package/build/module/runtime/utils/instrumentStorage.d.ts +7 -0
- package/build/module/runtime/utils/instrumentStorage.js +169 -0
- package/build/module/runtime/utils/patchEventHandler.d.ts +6 -0
- package/build/module/runtime/utils/patchEventHandler.js +25 -0
- package/build/types/module.d.ts.map +1 -1
- package/build/types/runtime/hooks/updateRouteBeforeResponse.d.ts +3 -1
- package/build/types/runtime/hooks/updateRouteBeforeResponse.d.ts.map +1 -1
- package/build/types/runtime/hooks/wrapMiddlewareHandler.d.ts.map +1 -1
- package/build/types/runtime/plugins/database-legacy.server.d.ts +6 -0
- package/build/types/runtime/plugins/database-legacy.server.d.ts.map +1 -0
- package/build/types/runtime/plugins/database.server.d.ts +1 -1
- package/build/types/runtime/plugins/database.server.d.ts.map +1 -1
- package/build/types/runtime/plugins/handler-legacy.server.d.ts +3 -0
- package/build/types/runtime/plugins/handler-legacy.server.d.ts.map +1 -0
- package/build/types/runtime/plugins/handler.server.d.ts +7 -0
- package/build/types/runtime/plugins/handler.server.d.ts.map +1 -0
- package/build/types/runtime/plugins/route-detector-legacy.server.d.ts +3 -0
- package/build/types/runtime/plugins/route-detector-legacy.server.d.ts.map +1 -0
- package/build/types/runtime/plugins/sentry.server.d.ts.map +1 -1
- package/build/types/runtime/plugins/storage-legacy.server.d.ts +6 -0
- package/build/types/runtime/plugins/storage-legacy.server.d.ts.map +1 -0
- package/build/types/runtime/plugins/storage.server.d.ts +4 -2
- package/build/types/runtime/plugins/storage.server.d.ts.map +1 -1
- package/build/types/runtime/plugins/update-route-name-legacy.server.d.ts +3 -0
- package/build/types/runtime/plugins/update-route-name-legacy.server.d.ts.map +1 -0
- package/build/types/runtime/plugins/update-route-name.server.d.ts +5 -0
- package/build/types/runtime/plugins/update-route-name.server.d.ts.map +1 -0
- package/build/types/runtime/utils/database-span-data.d.ts +16 -2
- package/build/types/runtime/utils/database-span-data.d.ts.map +1 -1
- package/build/types/runtime/utils/instrumentDatabase.d.ts +10 -0
- package/build/types/runtime/utils/instrumentDatabase.d.ts.map +1 -0
- package/build/types/runtime/utils/instrumentStorage.d.ts +8 -0
- package/build/types/runtime/utils/instrumentStorage.d.ts.map +1 -0
- package/build/types/runtime/utils/patchEventHandler.d.ts +7 -0
- package/build/types/runtime/utils/patchEventHandler.d.ts.map +1 -0
- package/build/types/runtime/utils.d.ts.map +1 -1
- package/build/types/vite/databaseConfig.d.ts +1 -1
- package/build/types/vite/databaseConfig.d.ts.map +1 -1
- package/build/types/vite/sentryVitePlugin.d.ts.map +1 -1
- package/build/types/vite/storageConfig.d.ts +1 -1
- package/build/types/vite/storageConfig.d.ts.map +1 -1
- package/build/types/vite/utils.d.ts +6 -1
- package/build/types/vite/utils.d.ts.map +1 -1
- package/package.json +16 -8
|
@@ -1,150 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
addBreadcrumb,
|
|
3
|
-
captureException,
|
|
4
|
-
debug,
|
|
5
|
-
flushIfServerless,
|
|
6
|
-
SEMANTIC_ATTRIBUTE_SENTRY_OP,
|
|
7
|
-
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
|
|
8
|
-
SPAN_STATUS_ERROR,
|
|
9
|
-
startSpan
|
|
10
|
-
} from "@sentry/core";
|
|
11
|
-
import { useDatabase } from "nitropack/runtime";
|
|
1
|
+
import { useDatabase } from "nitro/database";
|
|
12
2
|
import { databaseConfig } from "#sentry/database-config.mjs";
|
|
13
|
-
import {
|
|
14
|
-
const patchedStatement = /* @__PURE__ */ new WeakSet();
|
|
15
|
-
const SENTRY_ORIGIN = "auto.db.nuxt";
|
|
3
|
+
import { createDatabasePlugin } from "../utils/instrumentDatabase.js";
|
|
16
4
|
export default () => {
|
|
17
|
-
|
|
18
|
-
const _databaseConfig = databaseConfig;
|
|
19
|
-
const databaseInstances = Object.keys(databaseConfig);
|
|
20
|
-
debug.log("[Nitro Database Plugin]: Instrumenting databases...");
|
|
21
|
-
for (const instance of databaseInstances) {
|
|
22
|
-
debug.log("[Nitro Database Plugin]: Instrumenting database instance:", instance);
|
|
23
|
-
const db = useDatabase(instance);
|
|
24
|
-
instrumentDatabase(db, _databaseConfig[instance]);
|
|
25
|
-
}
|
|
26
|
-
debug.log("[Nitro Database Plugin]: Databases instrumented.");
|
|
27
|
-
} catch (error) {
|
|
28
|
-
if (error instanceof Error && /Cannot access 'instances'/.test(error.message)) {
|
|
29
|
-
debug.log("[Nitro Database Plugin]: Database instrumentation skipped during build time.");
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
debug.error("[Nitro Database Plugin]: Failed to instrument database:", error);
|
|
33
|
-
}
|
|
5
|
+
createDatabasePlugin(useDatabase, databaseConfig);
|
|
34
6
|
};
|
|
35
|
-
function instrumentDatabase(db, config) {
|
|
36
|
-
if (db.__sentry_instrumented__) {
|
|
37
|
-
debug.log("[Nitro Database Plugin]: Database already instrumented. Skipping...");
|
|
38
|
-
return;
|
|
39
|
-
}
|
|
40
|
-
const metadata = {
|
|
41
|
-
"db.system.name": config?.connector ?? db.dialect,
|
|
42
|
-
...getDatabaseSpanData(config)
|
|
43
|
-
};
|
|
44
|
-
db.prepare = new Proxy(db.prepare, {
|
|
45
|
-
apply(target, thisArg, args) {
|
|
46
|
-
const [query] = args;
|
|
47
|
-
return instrumentPreparedStatement(target.apply(thisArg, args), query, metadata);
|
|
48
|
-
}
|
|
49
|
-
});
|
|
50
|
-
db.sql = new Proxy(db.sql, {
|
|
51
|
-
apply(target, thisArg, args) {
|
|
52
|
-
const query = args[0]?.[0] ?? "";
|
|
53
|
-
const opts = createStartSpanOptions(query, metadata);
|
|
54
|
-
return startSpan(
|
|
55
|
-
opts,
|
|
56
|
-
handleSpanStart(() => target.apply(thisArg, args))
|
|
57
|
-
);
|
|
58
|
-
}
|
|
59
|
-
});
|
|
60
|
-
db.exec = new Proxy(db.exec, {
|
|
61
|
-
apply(target, thisArg, args) {
|
|
62
|
-
return startSpan(
|
|
63
|
-
createStartSpanOptions(args[0], metadata),
|
|
64
|
-
handleSpanStart(() => target.apply(thisArg, args), { query: args[0] })
|
|
65
|
-
);
|
|
66
|
-
}
|
|
67
|
-
});
|
|
68
|
-
db.__sentry_instrumented__ = true;
|
|
69
|
-
}
|
|
70
|
-
function instrumentPreparedStatement(statement, query, data) {
|
|
71
|
-
statement.bind = new Proxy(statement.bind, {
|
|
72
|
-
apply(target, thisArg, args) {
|
|
73
|
-
return instrumentPreparedStatementQueries(target.apply(thisArg, args), query, data);
|
|
74
|
-
}
|
|
75
|
-
});
|
|
76
|
-
return instrumentPreparedStatementQueries(statement, query, data);
|
|
77
|
-
}
|
|
78
|
-
function instrumentPreparedStatementQueries(statement, query, data) {
|
|
79
|
-
if (patchedStatement.has(statement)) {
|
|
80
|
-
return statement;
|
|
81
|
-
}
|
|
82
|
-
statement.get = new Proxy(statement.get, {
|
|
83
|
-
apply(target, thisArg, args) {
|
|
84
|
-
return startSpan(
|
|
85
|
-
createStartSpanOptions(query, data),
|
|
86
|
-
handleSpanStart(() => target.apply(thisArg, args), { query })
|
|
87
|
-
);
|
|
88
|
-
}
|
|
89
|
-
});
|
|
90
|
-
statement.run = new Proxy(statement.run, {
|
|
91
|
-
apply(target, thisArg, args) {
|
|
92
|
-
return startSpan(
|
|
93
|
-
createStartSpanOptions(query, data),
|
|
94
|
-
handleSpanStart(() => target.apply(thisArg, args), { query })
|
|
95
|
-
);
|
|
96
|
-
}
|
|
97
|
-
});
|
|
98
|
-
statement.all = new Proxy(statement.all, {
|
|
99
|
-
apply(target, thisArg, args) {
|
|
100
|
-
return startSpan(
|
|
101
|
-
createStartSpanOptions(query, data),
|
|
102
|
-
handleSpanStart(() => target.apply(thisArg, args), { query })
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
});
|
|
106
|
-
patchedStatement.add(statement);
|
|
107
|
-
return statement;
|
|
108
|
-
}
|
|
109
|
-
function handleSpanStart(fn, breadcrumbOpts) {
|
|
110
|
-
return async (span) => {
|
|
111
|
-
try {
|
|
112
|
-
const result = await fn();
|
|
113
|
-
if (breadcrumbOpts) {
|
|
114
|
-
createBreadcrumb(breadcrumbOpts.query);
|
|
115
|
-
}
|
|
116
|
-
return result;
|
|
117
|
-
} catch (error) {
|
|
118
|
-
span.setStatus({ code: SPAN_STATUS_ERROR, message: "internal_error" });
|
|
119
|
-
captureException(error, {
|
|
120
|
-
mechanism: {
|
|
121
|
-
handled: false,
|
|
122
|
-
type: SENTRY_ORIGIN
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
throw error;
|
|
126
|
-
} finally {
|
|
127
|
-
await flushIfServerless();
|
|
128
|
-
}
|
|
129
|
-
};
|
|
130
|
-
}
|
|
131
|
-
function createBreadcrumb(query) {
|
|
132
|
-
addBreadcrumb({
|
|
133
|
-
category: "query",
|
|
134
|
-
message: query,
|
|
135
|
-
data: {
|
|
136
|
-
"db.query.text": query
|
|
137
|
-
}
|
|
138
|
-
});
|
|
139
|
-
}
|
|
140
|
-
function createStartSpanOptions(query, data) {
|
|
141
|
-
return {
|
|
142
|
-
name: query,
|
|
143
|
-
attributes: {
|
|
144
|
-
"db.query.text": query,
|
|
145
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: SENTRY_ORIGIN,
|
|
146
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: "db.query",
|
|
147
|
-
...data
|
|
148
|
-
}
|
|
149
|
-
};
|
|
150
|
-
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { debug, getActiveSpan, getRootSpan, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE } from "@sentry/core";
|
|
2
|
+
import { defineNuxtPlugin } from "nuxt/app";
|
|
3
|
+
import { extractParametrizedRouteFromContext } from "../utils/route-extraction.js";
|
|
4
|
+
export default defineNuxtPlugin((nuxtApp) => {
|
|
5
|
+
nuxtApp.hooks.hook("app:rendered", async (renderContext) => {
|
|
6
|
+
let buildTimePagesData;
|
|
7
|
+
try {
|
|
8
|
+
const { default: importedPagesData } = await import("#build/sentry--nuxt-pages-data.mjs");
|
|
9
|
+
buildTimePagesData = importedPagesData || [];
|
|
10
|
+
debug.log("Imported build-time pages data:", buildTimePagesData);
|
|
11
|
+
} catch (error) {
|
|
12
|
+
buildTimePagesData = [];
|
|
13
|
+
debug.warn("Failed to import build-time pages data:", error);
|
|
14
|
+
}
|
|
15
|
+
const ssrContext = renderContext.ssrContext;
|
|
16
|
+
const routeInfo = extractParametrizedRouteFromContext(
|
|
17
|
+
ssrContext?.modules,
|
|
18
|
+
ssrContext?.url || ssrContext?.event._path,
|
|
19
|
+
buildTimePagesData
|
|
20
|
+
);
|
|
21
|
+
if (routeInfo === null) {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
const activeSpan = getActiveSpan();
|
|
25
|
+
if (activeSpan && routeInfo.parametrizedRoute) {
|
|
26
|
+
const rootSpan = getRootSpan(activeSpan);
|
|
27
|
+
if (!rootSpan) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
debug.log("Matched parametrized server route:", routeInfo.parametrizedRoute);
|
|
31
|
+
rootSpan.setAttributes({
|
|
32
|
+
[SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: "route",
|
|
33
|
+
"http.route": routeInfo.parametrizedRoute
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
});
|
|
37
|
+
});
|
|
@@ -5,7 +5,7 @@ export default defineNuxtPlugin((nuxtApp) => {
|
|
|
5
5
|
nuxtApp.hooks.hook("app:rendered", async (renderContext) => {
|
|
6
6
|
let buildTimePagesData;
|
|
7
7
|
try {
|
|
8
|
-
const { default: importedPagesData } = await import("#
|
|
8
|
+
const { default: importedPagesData } = await import("#sentry/nuxt-pages-data.mjs");
|
|
9
9
|
buildTimePagesData = importedPagesData || [];
|
|
10
10
|
debug.log("Imported build-time pages data:", buildTimePagesData);
|
|
11
11
|
} catch (error) {
|
|
@@ -1,21 +1,14 @@
|
|
|
1
|
-
import {
|
|
2
|
-
debug,
|
|
3
|
-
flushIfServerless,
|
|
4
|
-
getDefaultIsolationScope,
|
|
5
|
-
getIsolationScope,
|
|
6
|
-
withIsolationScope
|
|
7
|
-
} from "@sentry/core";
|
|
1
|
+
import { debug } from "@sentry/core";
|
|
8
2
|
import { sentryCaptureErrorHook } from "../hooks/captureErrorHook.js";
|
|
9
|
-
import { updateRouteBeforeResponse } from "../hooks/updateRouteBeforeResponse.js";
|
|
10
3
|
import { addSentryTracingMetaTags } from "../utils.js";
|
|
11
4
|
export default (nitroApp) => {
|
|
12
|
-
nitroApp.h3App.handler = patchEventHandler(nitroApp.h3App.handler);
|
|
13
|
-
nitroApp.hooks.hook("beforeResponse", updateRouteBeforeResponse);
|
|
14
5
|
nitroApp.hooks.hook("error", sentryCaptureErrorHook);
|
|
15
6
|
nitroApp.hooks.hook("render:html", (html, { event }) => {
|
|
16
|
-
const
|
|
17
|
-
const isPreRenderedPage = Object.keys(
|
|
18
|
-
|
|
7
|
+
const nodeResHeadersH3v1 = event.node?.res?.getHeaders() || {};
|
|
8
|
+
const isPreRenderedPage = Object.keys(nodeResHeadersH3v1).includes("x-nitro-prerender") || // fix × typescript-eslint(no-unsafe-member-access): Unsafe member access .res on an `any` value.
|
|
9
|
+
// oxlint-disable-next-line typescript/no-explicit-any,typescript-oxlint/no-unsafe-member-access
|
|
10
|
+
!!event.res?.headers?.has?.("x-nitro-prerender");
|
|
11
|
+
const isSWRCachedPage = event?.context?.cache?.options?.swr;
|
|
19
12
|
if (!isPreRenderedPage && !isSWRCachedPage) {
|
|
20
13
|
addSentryTracingMetaTags(html.head);
|
|
21
14
|
} else {
|
|
@@ -26,21 +19,3 @@ export default (nitroApp) => {
|
|
|
26
19
|
}
|
|
27
20
|
});
|
|
28
21
|
};
|
|
29
|
-
function patchEventHandler(handler) {
|
|
30
|
-
return new Proxy(handler, {
|
|
31
|
-
async apply(handlerTarget, handlerThisArg, handlerArgs) {
|
|
32
|
-
const isolationScope = getIsolationScope();
|
|
33
|
-
const newIsolationScope = isolationScope === getDefaultIsolationScope() ? isolationScope.clone() : isolationScope;
|
|
34
|
-
debug.log(
|
|
35
|
-
`Patched h3 event handler. ${isolationScope === newIsolationScope ? "Using existing" : "Created new"} isolation scope.`
|
|
36
|
-
);
|
|
37
|
-
return withIsolationScope(newIsolationScope, async () => {
|
|
38
|
-
try {
|
|
39
|
-
return await handlerTarget.apply(handlerThisArg, handlerArgs);
|
|
40
|
-
} finally {
|
|
41
|
-
await flushIfServerless();
|
|
42
|
-
}
|
|
43
|
-
});
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
}
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
import { useStorage } from "nitropack/runtime";
|
|
2
|
+
import { userStorageMounts } from "#sentry/storage-config.mjs";
|
|
3
|
+
import { createStoragePlugin } from "../utils/instrumentStorage.js";
|
|
4
|
+
export default async (_nitroApp) => {
|
|
5
|
+
await createStoragePlugin(useStorage, userStorageMounts);
|
|
6
|
+
};
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Nitro plugin that instruments storage driver calls for Nuxt v5+ (Nitro v3+)
|
|
3
3
|
*/
|
|
4
|
-
declare const _default: (_nitroApp: import("
|
|
4
|
+
declare const _default: (_nitroApp: import("nitro/types").NitroApp & {
|
|
5
|
+
hooks: NonNullable<import("nitro/types").NitroApp["hooks"]>;
|
|
6
|
+
}) => Promise<void>;
|
|
5
7
|
export default _default;
|
|
@@ -1,171 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
captureException,
|
|
3
|
-
debug,
|
|
4
|
-
flushIfServerless,
|
|
5
|
-
SEMANTIC_ATTRIBUTE_CACHE_HIT,
|
|
6
|
-
SEMANTIC_ATTRIBUTE_CACHE_KEY,
|
|
7
|
-
SEMANTIC_ATTRIBUTE_SENTRY_OP,
|
|
8
|
-
SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN,
|
|
9
|
-
SPAN_STATUS_ERROR,
|
|
10
|
-
SPAN_STATUS_OK,
|
|
11
|
-
startSpan
|
|
12
|
-
} from "@sentry/core";
|
|
13
|
-
import { useStorage } from "nitropack/runtime";
|
|
1
|
+
import { useStorage } from "nitro/storage";
|
|
14
2
|
import { userStorageMounts } from "#sentry/storage-config.mjs";
|
|
15
|
-
|
|
3
|
+
import { createStoragePlugin } from "../utils/instrumentStorage.js";
|
|
16
4
|
export default async (_nitroApp) => {
|
|
17
|
-
|
|
18
|
-
const userMounts = new Set(userStorageMounts.map((m) => `${m}:`));
|
|
19
|
-
debug.log("[storage] Starting to instrument storage drivers...");
|
|
20
|
-
userMounts.add("cache:");
|
|
21
|
-
userMounts.add("");
|
|
22
|
-
const mounts = storage.getMounts();
|
|
23
|
-
for (const mount of mounts) {
|
|
24
|
-
if (!userMounts.has(mount.base)) {
|
|
25
|
-
continue;
|
|
26
|
-
}
|
|
27
|
-
instrumentDriver(mount.driver, mount.base);
|
|
28
|
-
}
|
|
29
|
-
storage.mount = wrapStorageMount(storage);
|
|
5
|
+
await createStoragePlugin(useStorage, userStorageMounts);
|
|
30
6
|
};
|
|
31
|
-
function instrumentDriver(driver, mountBase) {
|
|
32
|
-
if (driver.__sentry_instrumented__) {
|
|
33
|
-
debug.log(`[storage] Driver already instrumented: "${driver.name}". Skipping...`);
|
|
34
|
-
return driver;
|
|
35
|
-
}
|
|
36
|
-
debug.log(`[storage] Instrumenting driver: "${driver.name}" on mount: "${mountBase}"`);
|
|
37
|
-
const methodsToInstrument = [
|
|
38
|
-
"hasItem",
|
|
39
|
-
"getItem",
|
|
40
|
-
"getItemRaw",
|
|
41
|
-
"getItems",
|
|
42
|
-
"setItem",
|
|
43
|
-
"setItemRaw",
|
|
44
|
-
"setItems",
|
|
45
|
-
"removeItem",
|
|
46
|
-
"getKeys",
|
|
47
|
-
"clear"
|
|
48
|
-
];
|
|
49
|
-
for (const methodName of methodsToInstrument) {
|
|
50
|
-
const original = driver[methodName];
|
|
51
|
-
if (typeof original !== "function") {
|
|
52
|
-
continue;
|
|
53
|
-
}
|
|
54
|
-
driver[methodName] = createMethodWrapper(original, methodName, driver, mountBase);
|
|
55
|
-
}
|
|
56
|
-
driver.__sentry_instrumented__ = true;
|
|
57
|
-
return driver;
|
|
58
|
-
}
|
|
59
|
-
function createMethodWrapper(original, methodName, driver, mountBase) {
|
|
60
|
-
return new Proxy(original, {
|
|
61
|
-
async apply(target, thisArg, args) {
|
|
62
|
-
const options = createSpanStartOptions(methodName, driver, mountBase, args);
|
|
63
|
-
debug.log(`[storage] Running method: "${methodName}" on driver: "${driver.name ?? "unknown"}"`);
|
|
64
|
-
return startSpan(options, async (span) => {
|
|
65
|
-
try {
|
|
66
|
-
const result = await target.apply(thisArg, args);
|
|
67
|
-
span.setStatus({ code: SPAN_STATUS_OK });
|
|
68
|
-
if (CACHE_HIT_METHODS.has(methodName)) {
|
|
69
|
-
span.setAttribute(SEMANTIC_ATTRIBUTE_CACHE_HIT, isCacheHit(args[0], result));
|
|
70
|
-
}
|
|
71
|
-
return result;
|
|
72
|
-
} catch (error) {
|
|
73
|
-
span.setStatus({ code: SPAN_STATUS_ERROR, message: "internal_error" });
|
|
74
|
-
captureException(error, {
|
|
75
|
-
mechanism: {
|
|
76
|
-
handled: false,
|
|
77
|
-
type: options.attributes?.[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]
|
|
78
|
-
}
|
|
79
|
-
});
|
|
80
|
-
throw error;
|
|
81
|
-
} finally {
|
|
82
|
-
await flushIfServerless();
|
|
83
|
-
}
|
|
84
|
-
});
|
|
85
|
-
}
|
|
86
|
-
});
|
|
87
|
-
}
|
|
88
|
-
function wrapStorageMount(storage) {
|
|
89
|
-
const original = storage.mount;
|
|
90
|
-
if (original.__sentry_instrumented__) {
|
|
91
|
-
return original;
|
|
92
|
-
}
|
|
93
|
-
function mountWithInstrumentation(base, driver) {
|
|
94
|
-
debug.log(`[storage] Instrumenting mount: "${base}"`);
|
|
95
|
-
const instrumentedDriver = instrumentDriver(driver, base);
|
|
96
|
-
return original(base, instrumentedDriver);
|
|
97
|
-
}
|
|
98
|
-
mountWithInstrumentation.__sentry_instrumented__ = true;
|
|
99
|
-
return mountWithInstrumentation;
|
|
100
|
-
}
|
|
101
|
-
function normalizeMethodName(methodName) {
|
|
102
|
-
return methodName.replace(/[A-Z]/g, (letter) => `_${letter.toLowerCase()}`);
|
|
103
|
-
}
|
|
104
|
-
function isEmptyValue(value) {
|
|
105
|
-
return value === null || value === void 0;
|
|
106
|
-
}
|
|
107
|
-
function createSpanStartOptions(methodName, driver, mountBase, args) {
|
|
108
|
-
const keys = getCacheKeys(args?.[0], mountBase);
|
|
109
|
-
const attributes = {
|
|
110
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_OP]: `cache.${normalizeMethodName(methodName)}`,
|
|
111
|
-
[SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: "auto.cache.nuxt",
|
|
112
|
-
[SEMANTIC_ATTRIBUTE_CACHE_KEY]: keys.length > 1 ? keys : keys[0],
|
|
113
|
-
"db.operation.name": methodName,
|
|
114
|
-
"db.collection.name": mountBase.replace(/:$/, ""),
|
|
115
|
-
"db.system.name": driver.name ?? "unknown"
|
|
116
|
-
};
|
|
117
|
-
return {
|
|
118
|
-
name: keys.join(", "),
|
|
119
|
-
attributes
|
|
120
|
-
};
|
|
121
|
-
}
|
|
122
|
-
function getCacheKeys(key, prefix) {
|
|
123
|
-
if (Array.isArray(key)) {
|
|
124
|
-
return key.map((k) => normalizeKey(k, prefix));
|
|
125
|
-
}
|
|
126
|
-
return [normalizeKey(key, prefix)];
|
|
127
|
-
}
|
|
128
|
-
function normalizeKey(key, prefix) {
|
|
129
|
-
if (typeof key === "string") {
|
|
130
|
-
return `${prefix}${key}`;
|
|
131
|
-
}
|
|
132
|
-
if (typeof key === "object" && key !== null && "key" in key) {
|
|
133
|
-
return `${prefix}${key.key}`;
|
|
134
|
-
}
|
|
135
|
-
return `${prefix}${isEmptyValue(key) ? "" : String(key)}`;
|
|
136
|
-
}
|
|
137
|
-
const CACHED_FN_HANDLERS_RE = /^nitro:(functions|handlers):/i;
|
|
138
|
-
function isCacheHit(key, value) {
|
|
139
|
-
try {
|
|
140
|
-
const isEmpty = isEmptyValue(value);
|
|
141
|
-
if (isEmpty || !CACHED_FN_HANDLERS_RE.test(key)) {
|
|
142
|
-
return !isEmpty;
|
|
143
|
-
}
|
|
144
|
-
return validateCacheEntry(key, JSON.parse(String(value)));
|
|
145
|
-
} catch {
|
|
146
|
-
return false;
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
function validateCacheEntry(key, entry) {
|
|
150
|
-
if (isEmptyValue(entry.value)) {
|
|
151
|
-
return false;
|
|
152
|
-
}
|
|
153
|
-
if (Date.now() > (entry.expires || 0)) {
|
|
154
|
-
return false;
|
|
155
|
-
}
|
|
156
|
-
if (isResponseCacheEntry(key, entry)) {
|
|
157
|
-
if (entry.value.status >= 400) {
|
|
158
|
-
return false;
|
|
159
|
-
}
|
|
160
|
-
if (entry.value.body === void 0) {
|
|
161
|
-
return false;
|
|
162
|
-
}
|
|
163
|
-
if (entry.value.headers.etag === "undefined" || entry.value.headers["last-modified"] === "undefined") {
|
|
164
|
-
return false;
|
|
165
|
-
}
|
|
166
|
-
}
|
|
167
|
-
return true;
|
|
168
|
-
}
|
|
169
|
-
function isResponseCacheEntry(key, _) {
|
|
170
|
-
return key.startsWith("nitro:handlers:");
|
|
171
|
-
}
|
|
@@ -1,8 +1,22 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { ConnectorName } from 'db0';
|
|
2
2
|
export interface DatabaseSpanData {
|
|
3
3
|
[key: string]: string | number | undefined;
|
|
4
4
|
}
|
|
5
|
+
/**
|
|
6
|
+
* A minimal database connection configuration type compatible with both nitropack (Nitro v2) and nitro (Nitro v3+).
|
|
7
|
+
* Mirrors the shape of `DatabaseConnectionConfig` from both packages.
|
|
8
|
+
*/
|
|
9
|
+
export interface DatabaseConnectionConfig {
|
|
10
|
+
connector?: ConnectorName;
|
|
11
|
+
options?: {
|
|
12
|
+
host?: string;
|
|
13
|
+
port?: number;
|
|
14
|
+
dataDir?: string;
|
|
15
|
+
name?: string;
|
|
16
|
+
[key: string]: unknown;
|
|
17
|
+
};
|
|
18
|
+
}
|
|
5
19
|
/**
|
|
6
20
|
* Extracts span attributes from the database configuration.
|
|
7
21
|
*/
|
|
8
|
-
export declare function getDatabaseSpanData(config?:
|
|
22
|
+
export declare function getDatabaseSpanData(config?: DatabaseConnectionConfig): Partial<DatabaseSpanData>;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import type { Database } from 'db0';
|
|
2
|
+
import { type DatabaseConnectionConfig } from './database-span-data.js';
|
|
3
|
+
/**
|
|
4
|
+
* Creates the Nitro database plugin setup by instrumenting the configured database instances.
|
|
5
|
+
*
|
|
6
|
+
* Called from the version-specific plugin entry points (database.server.ts / database-legacy.server.ts)
|
|
7
|
+
* which supply the correct `useDatabase` import for their respective Nitro version.
|
|
8
|
+
*/
|
|
9
|
+
export declare function createDatabasePlugin(useDatabase: (name: string) => Database, databaseConfig: Record<string, DatabaseConnectionConfig>): void;
|