autotel-drizzle 0.2.6 → 0.3.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/README.md +63 -24
- package/dist/{drizzle-BiA8DdAa.js → drizzle-BAxEDkHv.js} +181 -51
- package/dist/{drizzle-BK9eu10E.cjs → drizzle-BeUip3cx.cjs} +178 -48
- package/dist/drizzle.cjs +1 -1
- package/dist/drizzle.d.cts +7 -8
- package/dist/drizzle.d.ts +7 -8
- package/dist/drizzle.js +1 -1
- package/dist/index.cjs +1 -1
- package/dist/index.d.cts +23 -23
- package/dist/index.d.ts +23 -23
- package/dist/index.js +1 -1
- package/package.json +11 -14
package/README.md
CHANGED
|
@@ -156,8 +156,8 @@ individually slow. Group a trace's spans by it:
|
|
|
156
156
|
|
|
157
157
|
| `db.statement.hash` | `db.collection.name` | Spans |
|
|
158
158
|
| ------------------- | -------------------- | ----: |
|
|
159
|
-
| `
|
|
160
|
-
| `
|
|
159
|
+
| `16f2c9a04be7d1` | `comments` | 200 |
|
|
160
|
+
| `0a5e83d1c96b47` | `posts` | 1 |
|
|
161
161
|
|
|
162
162
|
One statement, run 200 times. Asking which span was slowest gives you a 1.7%
|
|
163
163
|
answer and sends you off to index a table that was never the problem. The
|
|
@@ -205,24 +205,28 @@ instrumentDrizzleClient(db, {
|
|
|
205
205
|
|
|
206
206
|
The same query, before and after `CREATE INDEX`:
|
|
207
207
|
|
|
208
|
-
| Attribute | Before
|
|
209
|
-
| ------------------------ |
|
|
210
|
-
| `db.statement.hash` | `
|
|
211
|
-
| `db.plan.hash` | `
|
|
212
|
-
| `db.plan.node` | `Seq Scan`
|
|
213
|
-
| `db.plan.indexes` |
|
|
214
|
-
| `db.plan.
|
|
215
|
-
| `db.plan.rows_examined` | `200199`
|
|
216
|
-
| `db.plan.rows_returned` | `1001`
|
|
217
|
-
| `db.plan.blocks` | `1861`
|
|
218
|
-
| `db.plan.cost` |
|
|
219
|
-
| `db.plan.rows_estimated` |
|
|
220
|
-
| `db.plan.execution_ms` |
|
|
208
|
+
| Attribute | Before | After |
|
|
209
|
+
| ------------------------ | ---------------- | -------------------------------- |
|
|
210
|
+
| `db.statement.hash` | `0c4a9f1e2b7d33` | `0c4a9f1e2b7d33` |
|
|
211
|
+
| `db.plan.hash` | `1d07e5a9c4b2f8` | `08f3b6e1a7c5d2` |
|
|
212
|
+
| `db.plan.node` | `Seq Scan` | `Bitmap Heap Scan` |
|
|
213
|
+
| `db.plan.indexes` | | `idx_comments_post_id` |
|
|
214
|
+
| `db.plan.full_scan` | `true` | `false` |
|
|
215
|
+
| `db.plan.rows_examined` | `200199` | `1001` |
|
|
216
|
+
| `db.plan.rows_returned` | `1001` | `1001` |
|
|
217
|
+
| `db.plan.blocks` | `1861` | `1005` |
|
|
218
|
+
| `db.plan.cost` | | planner's total cost estimate |
|
|
219
|
+
| `db.plan.rows_estimated` | | rows the planner expected |
|
|
220
|
+
| `db.plan.execution_ms` | | measured time (`'analyze'` only) |
|
|
221
221
|
|
|
222
222
|
An unchanged statement hash beside a changed plan hash is a planner decision.
|
|
223
223
|
Grouping `db.plan.indexes` by `db.statement.hash` answers which queries an index
|
|
224
224
|
is serving.
|
|
225
225
|
|
|
226
|
+
The attribute names and the hash come from `autotel-db`, which `autotel-mongodb`
|
|
227
|
+
uses too, so a Postgres `Seq Scan` and a MongoDB `COLLSCAN` both read as
|
|
228
|
+
`db.plan.full_scan: true` and one query can group them.
|
|
229
|
+
|
|
226
230
|
`rows_examined` counts at the leaves of the plan, and counts the rows a scan
|
|
227
231
|
read and discarded. A parent node reports the rows its children handed up, so
|
|
228
232
|
summing every level counts one row per level; and the rows an index saves you
|
|
@@ -237,15 +241,50 @@ second time to measure it. Both are off by default. Use them in development, in
|
|
|
237
241
|
CI, or behind a sample of production traffic.
|
|
238
242
|
|
|
239
243
|
- `'analyze'` runs on read-only statements only, so a traced insert never runs
|
|
240
|
-
twice. A
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
-
|
|
247
|
-
|
|
248
|
-
|
|
244
|
+
twice. A write is planned with a plain `EXPLAIN` instead, which runs nothing,
|
|
245
|
+
and its span records `db.plan.mode: plan`. A leading `WITH` counts as a read
|
|
246
|
+
only when no writing keyword appears anywhere in the statement, because
|
|
247
|
+
`WITH gone AS (DELETE ... RETURNING *) SELECT` opens exactly like a read.
|
|
248
|
+
- Outside a transaction, the plan is collected before the span opens, so the
|
|
249
|
+
extra round trip stays out of the duration the span reports.
|
|
250
|
+
- Only statements Postgres can explain are sent (`SELECT`, `WITH`, `INSERT`,
|
|
251
|
+
`UPDATE`, `DELETE`, `MERGE`, `VALUES`, `TABLE`). DDL and the rest record
|
|
252
|
+
`db.plan.status: unsupported` and send nothing.
|
|
253
|
+
- **Inside `db.transaction()`, nothing is ever sent on the transaction's own
|
|
254
|
+
connection, and nothing the application sends is held back.** An `EXPLAIN`
|
|
255
|
+
there could abort the transaction, and waiting for a plan before a
|
|
256
|
+
statement reorders the transaction's statements against each other and
|
|
257
|
+
against its `COMMIT` or `ROLLBACK`. So each statement goes out exactly when
|
|
258
|
+
it would without instrumentation, and its plan is fetched alongside it from
|
|
259
|
+
a separate pooled connection, inside a transaction that is always rolled
|
|
260
|
+
back, with a 100ms lock timeout (the application's transaction may hold
|
|
261
|
+
locks the `EXPLAIN` needs). The span ends at the statement's own end time.
|
|
262
|
+
- `drizzle(pool)` (node-postgres `Pool`): plans are captured this way. A
|
|
263
|
+
statement whose table the transaction has locked records
|
|
264
|
+
`db.plan.status: failed` with the lock timeout, and is not slowed.
|
|
265
|
+
- `drizzle(client)`, an embedded database, or any other driver: there is no
|
|
266
|
+
separate connection, so statements inside a transaction record
|
|
267
|
+
`db.plan.status: unsupported`. The same statements outside a transaction
|
|
268
|
+
are planned as usual.
|
|
269
|
+
- A transaction is recognised by its connection, not by which session issued
|
|
270
|
+
the statement, so this holds for `drizzle(client)` (where the transaction
|
|
271
|
+
reuses the session it was given) and for nested transactions. Open
|
|
272
|
+
transactions through `db.transaction()` when explain is on.
|
|
273
|
+
- Explains outside a transaction through a node-postgres `Pool` check a
|
|
274
|
+
connection out and hand it back clean, so a failing `EXPLAIN` (a missing
|
|
275
|
+
privilege) never costs a pooled connection.
|
|
276
|
+
- A failed `EXPLAIN` never fails the query. The span records
|
|
277
|
+
`db.plan.status: failed` and the reason in `db.plan.error`, with any quoted
|
|
278
|
+
values, documents and key details stripped from it.
|
|
279
|
+
- Postgres only. With another `dbSystem`, spans record
|
|
280
|
+
`db.plan.status: unsupported` rather than reading as if explain were off.
|
|
281
|
+
- Plans also carry `db.plan.stages`, `db.plan.mode`, and
|
|
282
|
+
`db.plan.blocking_sort` (a `Sort` node).
|
|
283
|
+
- Inside a transaction the plan comes from a separate pooled connection, so it
|
|
284
|
+
cannot see the transaction's uncommitted changes: rows it has inserted or
|
|
285
|
+
deleted, tables it has created. The plan describes the query against the
|
|
286
|
+
committed data, and a statement on a table the transaction created records
|
|
287
|
+
`db.plan.status: failed`.
|
|
249
288
|
|
|
250
289
|
## Security Considerations
|
|
251
290
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { SpanKind, trace } from "@opentelemetry/api";
|
|
1
|
+
import { SpanKind, SpanStatusCode, trace } from "@opentelemetry/api";
|
|
3
2
|
import { finalizeSpan, runWithSpan } from "autotel/trace-helpers";
|
|
3
|
+
import { hashStatement, planAttributes, planUnavailableAttributes } from "autotel-db";
|
|
4
4
|
//#region src/common/constants.ts
|
|
5
5
|
const SEMATTRS_DB_SYSTEM = "db.system";
|
|
6
6
|
const SEMATTRS_DB_SYSTEM_NAME = "db.system.name";
|
|
@@ -37,6 +37,32 @@ function hasMethod(target, name) {
|
|
|
37
37
|
function asFunction(value) {
|
|
38
38
|
return typeof value === "function" ? value : void 0;
|
|
39
39
|
}
|
|
40
|
+
function isPgPool(value) {
|
|
41
|
+
return asFunction(value.connect) !== void 0 && typeof value.totalCount === "number";
|
|
42
|
+
}
|
|
43
|
+
function isolatedExplainClient(value) {
|
|
44
|
+
if (!isDriverObject(value) || !isPgPool(value)) return void 0;
|
|
45
|
+
const connect = asFunction(value.connect);
|
|
46
|
+
return { query: async (statement, params) => {
|
|
47
|
+
const connection = asDriverObject(await connect.call(value));
|
|
48
|
+
const run = connection && asFunction(connection.query);
|
|
49
|
+
if (connection === void 0 || run === void 0) throw new Error("the pool handed back no client");
|
|
50
|
+
const release = asFunction(connection.release);
|
|
51
|
+
try {
|
|
52
|
+
await run.call(connection, "BEGIN", []);
|
|
53
|
+
await run.call(connection, "SET LOCAL lock_timeout = '100ms'", []);
|
|
54
|
+
await run.call(connection, "SET LOCAL statement_timeout = '5s'", []);
|
|
55
|
+
return await run.call(connection, statement, params);
|
|
56
|
+
} finally {
|
|
57
|
+
try {
|
|
58
|
+
await run.call(connection, "ROLLBACK", []);
|
|
59
|
+
release?.call(connection);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
release?.call(connection, error instanceof Error ? error.message : String(error));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
} };
|
|
65
|
+
}
|
|
40
66
|
function isDriverObject(value) {
|
|
41
67
|
return value !== null && typeof value === "object";
|
|
42
68
|
}
|
|
@@ -74,6 +100,17 @@ function asQueryClient(value) {
|
|
|
74
100
|
if (!isDriverObject(value)) return;
|
|
75
101
|
const run = asFunction(value.query);
|
|
76
102
|
if (run === void 0) return;
|
|
103
|
+
const connect = asFunction(value.connect);
|
|
104
|
+
if (connect !== void 0 && isPgPool(value)) return { query: async (statement, params) => {
|
|
105
|
+
const connection = asDriverObject(await connect.call(value));
|
|
106
|
+
const checkedOut = connection && asFunction(connection.query);
|
|
107
|
+
if (connection === void 0 || checkedOut === void 0) throw new Error("the pool handed back no client");
|
|
108
|
+
try {
|
|
109
|
+
return await checkedOut.call(connection, statement, params);
|
|
110
|
+
} finally {
|
|
111
|
+
asFunction(connection.release)?.call(connection);
|
|
112
|
+
}
|
|
113
|
+
} };
|
|
77
114
|
return { query: async (statement, params) => run.call(value, statement, params) };
|
|
78
115
|
}
|
|
79
116
|
function toCount(value) {
|
|
@@ -130,16 +167,6 @@ function parseExplainResponse(result) {
|
|
|
130
167
|
}
|
|
131
168
|
//#endregion
|
|
132
169
|
//#region src/drizzle/index.ts
|
|
133
|
-
const SEMATTRS_DB_PLAN_NODE = "db.plan.node";
|
|
134
|
-
const SEMATTRS_DB_PLAN_INDEXES = "db.plan.indexes";
|
|
135
|
-
const SEMATTRS_DB_PLAN_COST = "db.plan.cost";
|
|
136
|
-
const SEMATTRS_DB_PLAN_ROWS_ESTIMATED = "db.plan.rows_estimated";
|
|
137
|
-
const SEMATTRS_DB_PLAN_ROWS_EXAMINED = "db.plan.rows_examined";
|
|
138
|
-
const SEMATTRS_DB_PLAN_ROWS_RETURNED = "db.plan.rows_returned";
|
|
139
|
-
const SEMATTRS_DB_PLAN_BLOCKS = "db.plan.blocks";
|
|
140
|
-
const SEMATTRS_DB_PLAN_EXECUTION_MS = "db.plan.execution_ms";
|
|
141
|
-
const SEMATTRS_DB_PLAN_SEQ_SCAN = "db.plan.seq_scan";
|
|
142
|
-
const SEMATTRS_DB_PLAN_HASH = "db.plan.hash";
|
|
143
170
|
const TRANSACTION_ATTRIBUTE = "db.transaction";
|
|
144
171
|
const REENTRY_FLAG = `__autotelDrizzleInstrumented:running`;
|
|
145
172
|
const DEFAULT_TRACER_NAME = "autotel-plugins/drizzle";
|
|
@@ -190,7 +217,8 @@ function resolveConfig(config) {
|
|
|
190
217
|
semconv: config?.semconv ?? semconvFromEnvironment(),
|
|
191
218
|
spanNaming: config?.spanNaming ?? "drizzle",
|
|
192
219
|
traceTransactions: config?.traceTransactions ?? true,
|
|
193
|
-
explain: (config?.dbSystem ?? DEFAULT_DB_SYSTEM) === "postgresql" ? config?.explain ?? false : false
|
|
220
|
+
explain: (config?.dbSystem ?? DEFAULT_DB_SYSTEM) === "postgresql" ? config?.explain ?? false : false,
|
|
221
|
+
explainRequested: config?.explain ?? false
|
|
194
222
|
};
|
|
195
223
|
}
|
|
196
224
|
function getState(config) {
|
|
@@ -235,9 +263,6 @@ function sanitizeQueryText(queryText, maxLength) {
|
|
|
235
263
|
if (queryText.length <= maxLength) return queryText;
|
|
236
264
|
return `${queryText.slice(0, Math.max(0, maxLength))}...`;
|
|
237
265
|
}
|
|
238
|
-
function hashQueryText(queryText) {
|
|
239
|
-
return createHash("sha1").update(queryText).digest("hex").slice(0, 16);
|
|
240
|
-
}
|
|
241
266
|
function extractOperation(queryText) {
|
|
242
267
|
const trimmed = queryText.trimStart();
|
|
243
268
|
return /^(?<operation>\w+)/u.exec(trimmed)?.groups?.operation?.toUpperCase();
|
|
@@ -252,21 +277,28 @@ const WRITE_KEYWORDS = /\b(?:INSERT|UPDATE|DELETE|MERGE|TRUNCATE|DROP|ALTER|CREA
|
|
|
252
277
|
function buildExplainStatement(mode, queryText) {
|
|
253
278
|
return `EXPLAIN (${mode === "analyze" ? "ANALYZE, BUFFERS, FORMAT JSON" : "FORMAT JSON"}) ${queryText}`;
|
|
254
279
|
}
|
|
255
|
-
|
|
256
|
-
|
|
280
|
+
const SORT_NODES = new Set(["Sort", "Incremental Sort"]);
|
|
281
|
+
function planAttributes$1(payload, mode) {
|
|
257
282
|
const [root] = payload.nodes;
|
|
258
|
-
if (root === void 0) return
|
|
259
|
-
const
|
|
260
|
-
|
|
283
|
+
if (root === void 0) return {};
|
|
284
|
+
const plan = {
|
|
285
|
+
nodes: [],
|
|
286
|
+
fullScan: false,
|
|
287
|
+
blockingSort: false,
|
|
288
|
+
mode,
|
|
289
|
+
indexes: [],
|
|
290
|
+
cost: root.totalCost,
|
|
291
|
+
rowsEstimated: root.planRows
|
|
292
|
+
};
|
|
261
293
|
let rowsExamined = 0;
|
|
262
294
|
let blocks = 0;
|
|
263
|
-
let sawSeqScan = false;
|
|
264
295
|
for (const node of payload.nodes) {
|
|
265
296
|
if (node.nodeType !== void 0) {
|
|
266
|
-
|
|
267
|
-
|
|
297
|
+
plan.nodes.push(node.nodeType);
|
|
298
|
+
plan.fullScan ||= node.nodeType === "Seq Scan";
|
|
299
|
+
plan.blockingSort ||= SORT_NODES.has(node.nodeType);
|
|
268
300
|
}
|
|
269
|
-
if (node.indexName !== void 0 && !indexes.includes(node.indexName)) indexes.push(node.indexName);
|
|
301
|
+
if (node.indexName !== void 0 && !plan.indexes.includes(node.indexName)) plan.indexes.push(node.indexName);
|
|
270
302
|
if (node.isLeaf) {
|
|
271
303
|
rowsExamined += node.actualRows;
|
|
272
304
|
rowsExamined += node.rowsRemovedByFilter;
|
|
@@ -275,33 +307,86 @@ function planAttributes(payload, mode) {
|
|
|
275
307
|
blocks += node.sharedHitBlocks;
|
|
276
308
|
blocks += node.sharedReadBlocks;
|
|
277
309
|
}
|
|
278
|
-
if (root.nodeType !== void 0) attributes[SEMATTRS_DB_PLAN_NODE] = root.nodeType;
|
|
279
|
-
if (root.totalCost !== void 0) attributes[SEMATTRS_DB_PLAN_COST] = root.totalCost;
|
|
280
|
-
if (root.planRows !== void 0) attributes[SEMATTRS_DB_PLAN_ROWS_ESTIMATED] = root.planRows;
|
|
281
|
-
if (indexes.length > 0) attributes[SEMATTRS_DB_PLAN_INDEXES] = indexes.join(",");
|
|
282
|
-
attributes[SEMATTRS_DB_PLAN_SEQ_SCAN] = sawSeqScan;
|
|
283
|
-
attributes[SEMATTRS_DB_PLAN_HASH] = hashQueryText(nodeTypes.join(">"));
|
|
284
310
|
if (mode === "analyze") {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
311
|
+
plan.rowsExamined = rowsExamined;
|
|
312
|
+
plan.rowsReturned = root.actualRows;
|
|
313
|
+
plan.blocks = blocks;
|
|
314
|
+
plan.executionMs = payload.executionMs;
|
|
289
315
|
}
|
|
290
|
-
return
|
|
316
|
+
return planAttributes(plan);
|
|
291
317
|
}
|
|
292
318
|
function resolveExplainClient(target, state) {
|
|
293
|
-
|
|
319
|
+
const own = readQueryClient(target);
|
|
320
|
+
if (own && isDriverObject(target)) return {
|
|
321
|
+
client: own,
|
|
322
|
+
connection: readObject(target, "client")
|
|
323
|
+
};
|
|
324
|
+
return state.explainClient ? { client: state.explainClient } : void 0;
|
|
294
325
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
if (!
|
|
298
|
-
|
|
326
|
+
const openTransactions = new WeakMap();
|
|
327
|
+
function transactionConnections(tx) {
|
|
328
|
+
if (!isDriverObject(tx)) return [];
|
|
329
|
+
const found = [];
|
|
330
|
+
for (const holder of [tx, ...readSessions(tx)]) {
|
|
331
|
+
const connection = readObject(holder, "client");
|
|
332
|
+
if (connection !== void 0 && !found.includes(connection)) found.push(connection);
|
|
333
|
+
}
|
|
334
|
+
return found;
|
|
335
|
+
}
|
|
336
|
+
function openTransaction(connections) {
|
|
337
|
+
for (const connection of connections) openTransactions.set(connection, (openTransactions.get(connection) ?? 0) + 1);
|
|
338
|
+
return () => {
|
|
339
|
+
for (const connection of connections) {
|
|
340
|
+
const depth = (openTransactions.get(connection) ?? 1) - 1;
|
|
341
|
+
if (depth > 0) openTransactions.set(connection, depth);
|
|
342
|
+
else openTransactions.delete(connection);
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
function thenAfter(run, after) {
|
|
347
|
+
let result;
|
|
348
|
+
try {
|
|
349
|
+
result = run();
|
|
350
|
+
} catch (error) {
|
|
351
|
+
after();
|
|
352
|
+
throw error;
|
|
353
|
+
}
|
|
354
|
+
if (isThenable(result)) return Promise.resolve(result).finally(after);
|
|
355
|
+
after();
|
|
356
|
+
return result;
|
|
357
|
+
}
|
|
358
|
+
const EXPLAINABLE = new Set([
|
|
359
|
+
"SELECT",
|
|
360
|
+
"WITH",
|
|
361
|
+
"INSERT",
|
|
362
|
+
"UPDATE",
|
|
363
|
+
"DELETE",
|
|
364
|
+
"MERGE",
|
|
365
|
+
"VALUES",
|
|
366
|
+
"TABLE"
|
|
367
|
+
]);
|
|
368
|
+
async function collectPlan(state, explainClient, queryText, params, inTransaction) {
|
|
369
|
+
const requested = state.config.explain;
|
|
370
|
+
if (!requested) return;
|
|
371
|
+
if (!EXPLAINABLE.has(extractOperation(queryText) ?? "")) return planUnavailableAttributes("unsupported", { mode: requested });
|
|
372
|
+
const mode = requested === "analyze" && !isReadOnlyStatement(queryText) ? "plan" : requested;
|
|
373
|
+
const client = inTransaction ? state.isolatedExplainClient : explainClient?.client;
|
|
374
|
+
if (client === void 0) return inTransaction ? planUnavailableAttributes("unsupported", { mode }) : planUnavailableAttributes("failed", {
|
|
375
|
+
mode,
|
|
376
|
+
error: "no client to run EXPLAIN on"
|
|
377
|
+
});
|
|
299
378
|
try {
|
|
300
379
|
const payload = parseExplainResponse(await client.query(buildExplainStatement(mode, queryText), readParams(params)));
|
|
301
|
-
if (payload === void 0) return
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
380
|
+
if (payload === void 0) return planUnavailableAttributes("failed", {
|
|
381
|
+
mode,
|
|
382
|
+
error: "EXPLAIN returned no plan autotel-drizzle reads"
|
|
383
|
+
});
|
|
384
|
+
return planAttributes$1(payload, mode);
|
|
385
|
+
} catch (error) {
|
|
386
|
+
return planUnavailableAttributes("failed", {
|
|
387
|
+
mode,
|
|
388
|
+
error: error instanceof Error ? error.message : String(error)
|
|
389
|
+
});
|
|
305
390
|
}
|
|
306
391
|
}
|
|
307
392
|
function buildSpanName(mode, operation, table) {
|
|
@@ -327,7 +412,7 @@ function buildSpan(state, queryText, extraAttributes) {
|
|
|
327
412
|
if (operation) setRenamed(SEMATTRS_DB_OPERATION, SEMATTRS_DB_OPERATION_NAME, operation);
|
|
328
413
|
if (state.config.dbName !== void 0) setRenamed(SEMATTRS_DB_NAME, SEMATTRS_DB_NAMESPACE, state.config.dbName);
|
|
329
414
|
if (queryText !== void 0) {
|
|
330
|
-
span.setAttribute(SEMATTRS_DB_STATEMENT_HASH,
|
|
415
|
+
span.setAttribute(SEMATTRS_DB_STATEMENT_HASH, hashStatement(queryText));
|
|
331
416
|
if (table !== void 0) span.setAttribute(SEMATTRS_DB_COLLECTION_NAME, table);
|
|
332
417
|
}
|
|
333
418
|
if (state.config.captureQueryText && queryText !== void 0) {
|
|
@@ -339,6 +424,37 @@ function buildSpan(state, queryText, extraAttributes) {
|
|
|
339
424
|
if (extraAttributes) for (const [key, value] of Object.entries(extraAttributes)) span.setAttribute(key, value);
|
|
340
425
|
return span;
|
|
341
426
|
}
|
|
427
|
+
function runAlongsidePlan(span, planned, fn) {
|
|
428
|
+
const end = (error) => {
|
|
429
|
+
const endTime = performance.timeOrigin + performance.now();
|
|
430
|
+
planned.then((attributes) => {
|
|
431
|
+
if (attributes) span.setAttributes(attributes);
|
|
432
|
+
}).finally(() => {
|
|
433
|
+
if (error) {
|
|
434
|
+
span.recordException(error instanceof Error ? error : new Error(String(error)));
|
|
435
|
+
span.setStatus({ code: SpanStatusCode.ERROR });
|
|
436
|
+
} else span.setStatus({ code: SpanStatusCode.OK });
|
|
437
|
+
span.end(endTime);
|
|
438
|
+
});
|
|
439
|
+
};
|
|
440
|
+
return runWithSpan(span, () => {
|
|
441
|
+
try {
|
|
442
|
+
const result = fn();
|
|
443
|
+
if (isThenable(result)) return result.then((value) => {
|
|
444
|
+
end();
|
|
445
|
+
return value;
|
|
446
|
+
}, (error) => {
|
|
447
|
+
end(error);
|
|
448
|
+
throw error;
|
|
449
|
+
});
|
|
450
|
+
end();
|
|
451
|
+
return result;
|
|
452
|
+
} catch (error) {
|
|
453
|
+
end(error);
|
|
454
|
+
throw error;
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
}
|
|
342
458
|
function executeWithSpan(span, fn) {
|
|
343
459
|
return runWithSpan(span, () => {
|
|
344
460
|
try {
|
|
@@ -369,11 +485,20 @@ function instrumentMethod(target, methodName, state, options) {
|
|
|
369
485
|
const callback = trailing === void 0 ? void 0 : (args.pop(), trailing);
|
|
370
486
|
if (options.guardReentry && target[REENTRY_FLAG]) return originalMethod.call(this, ...incomingArgs);
|
|
371
487
|
const queryText = options.queryText(args);
|
|
372
|
-
if (state.config.explain && queryText !== void 0 && !callback)
|
|
488
|
+
if (state.config.explain && queryText !== void 0 && !callback) {
|
|
489
|
+
const explainClient = resolveExplainClient(readSelf(this), state);
|
|
490
|
+
const inTransaction = options.extraAttributes?.[TRANSACTION_ATTRIBUTE] === true || explainClient?.connection !== void 0 && openTransactions.has(explainClient.connection);
|
|
491
|
+
if (inTransaction) return runAlongsidePlan(buildSpan(state, queryText, options.extraAttributes), collectPlan(state, explainClient, queryText, options.explainParams?.(args), true), () => originalMethod.call(this, ...args));
|
|
492
|
+
return collectPlan(state, explainClient, queryText, options.explainParams?.(args), inTransaction).then((planAttributes) => executeWithSpan(buildSpan(state, queryText, {
|
|
493
|
+
...options.extraAttributes,
|
|
494
|
+
...planAttributes
|
|
495
|
+
}), () => originalMethod.call(this, ...args)));
|
|
496
|
+
}
|
|
497
|
+
const unexplained = state.config.explainRequested && queryText !== void 0 ? planUnavailableAttributes("unsupported", { mode: state.config.explainRequested }) : void 0;
|
|
498
|
+
const span = buildSpan(state, queryText, {
|
|
373
499
|
...options.extraAttributes,
|
|
374
|
-
...
|
|
375
|
-
})
|
|
376
|
-
const span = buildSpan(state, queryText, options.extraAttributes);
|
|
500
|
+
...unexplained
|
|
501
|
+
});
|
|
377
502
|
if (callback) return runWithSpan(span, () => {
|
|
378
503
|
const wrappedCallback = (error, result) => {
|
|
379
504
|
finalizeSpan(span, error);
|
|
@@ -479,11 +604,15 @@ function instrumentSession(session, state, allowDirectMethods = true) {
|
|
|
479
604
|
const [rawCallback, ...restArgs] = transactionArgs;
|
|
480
605
|
const callback = asFunction(rawCallback);
|
|
481
606
|
if (callback === void 0) return originalTransaction.call(this, ...transactionArgs);
|
|
607
|
+
const closers = [];
|
|
482
608
|
const wrappedCallback = (tx, ...callbackArgs) => {
|
|
483
609
|
instrumentTransactionTarget(tx, state);
|
|
610
|
+
closers.push(openTransaction(transactionConnections(tx)));
|
|
484
611
|
return callback.call(this, tx, ...callbackArgs);
|
|
485
612
|
};
|
|
486
|
-
const runTransaction = () => originalTransaction.call(this, wrappedCallback, ...restArgs)
|
|
613
|
+
const runTransaction = () => thenAfter(() => originalTransaction.call(this, wrappedCallback, ...restArgs), () => {
|
|
614
|
+
for (const close of closers) close();
|
|
615
|
+
});
|
|
487
616
|
if (!state.config.traceTransactions) return runTransaction();
|
|
488
617
|
const span = state.tracer.startSpan("drizzle.transaction", { kind: SpanKind.CLIENT });
|
|
489
618
|
span.setAttribute(TRANSACTION_ATTRIBUTE, true);
|
|
@@ -523,6 +652,7 @@ function instrumentDrizzleClient(db, config) {
|
|
|
523
652
|
if (readFlag(target, INSTRUMENTED_FLAG)) return db;
|
|
524
653
|
const state = getState(config);
|
|
525
654
|
state.explainClient = asQueryClient(target.$client);
|
|
655
|
+
state.isolatedExplainClient = isolatedExplainClient(target.$client);
|
|
526
656
|
let instrumented = false;
|
|
527
657
|
let sessionInstrumented = false;
|
|
528
658
|
for (const session of readSessions(target)) {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
let node_crypto = require("node:crypto");
|
|
2
1
|
let _opentelemetry_api = require("@opentelemetry/api");
|
|
3
2
|
let autotel_trace_helpers = require("autotel/trace-helpers");
|
|
3
|
+
let autotel_db = require("autotel-db");
|
|
4
4
|
//#region src/common/constants.ts
|
|
5
5
|
const SEMATTRS_DB_SYSTEM = "db.system";
|
|
6
6
|
const SEMATTRS_DB_SYSTEM_NAME = "db.system.name";
|
|
@@ -37,6 +37,32 @@ function hasMethod(target, name) {
|
|
|
37
37
|
function asFunction(value) {
|
|
38
38
|
return typeof value === "function" ? value : void 0;
|
|
39
39
|
}
|
|
40
|
+
function isPgPool(value) {
|
|
41
|
+
return asFunction(value.connect) !== void 0 && typeof value.totalCount === "number";
|
|
42
|
+
}
|
|
43
|
+
function isolatedExplainClient(value) {
|
|
44
|
+
if (!isDriverObject(value) || !isPgPool(value)) return void 0;
|
|
45
|
+
const connect = asFunction(value.connect);
|
|
46
|
+
return { query: async (statement, params) => {
|
|
47
|
+
const connection = asDriverObject(await connect.call(value));
|
|
48
|
+
const run = connection && asFunction(connection.query);
|
|
49
|
+
if (connection === void 0 || run === void 0) throw new Error("the pool handed back no client");
|
|
50
|
+
const release = asFunction(connection.release);
|
|
51
|
+
try {
|
|
52
|
+
await run.call(connection, "BEGIN", []);
|
|
53
|
+
await run.call(connection, "SET LOCAL lock_timeout = '100ms'", []);
|
|
54
|
+
await run.call(connection, "SET LOCAL statement_timeout = '5s'", []);
|
|
55
|
+
return await run.call(connection, statement, params);
|
|
56
|
+
} finally {
|
|
57
|
+
try {
|
|
58
|
+
await run.call(connection, "ROLLBACK", []);
|
|
59
|
+
release?.call(connection);
|
|
60
|
+
} catch (error) {
|
|
61
|
+
release?.call(connection, error instanceof Error ? error.message : String(error));
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
} };
|
|
65
|
+
}
|
|
40
66
|
function isDriverObject(value) {
|
|
41
67
|
return value !== null && typeof value === "object";
|
|
42
68
|
}
|
|
@@ -74,6 +100,17 @@ function asQueryClient(value) {
|
|
|
74
100
|
if (!isDriverObject(value)) return;
|
|
75
101
|
const run = asFunction(value.query);
|
|
76
102
|
if (run === void 0) return;
|
|
103
|
+
const connect = asFunction(value.connect);
|
|
104
|
+
if (connect !== void 0 && isPgPool(value)) return { query: async (statement, params) => {
|
|
105
|
+
const connection = asDriverObject(await connect.call(value));
|
|
106
|
+
const checkedOut = connection && asFunction(connection.query);
|
|
107
|
+
if (connection === void 0 || checkedOut === void 0) throw new Error("the pool handed back no client");
|
|
108
|
+
try {
|
|
109
|
+
return await checkedOut.call(connection, statement, params);
|
|
110
|
+
} finally {
|
|
111
|
+
asFunction(connection.release)?.call(connection);
|
|
112
|
+
}
|
|
113
|
+
} };
|
|
77
114
|
return { query: async (statement, params) => run.call(value, statement, params) };
|
|
78
115
|
}
|
|
79
116
|
function toCount(value) {
|
|
@@ -130,16 +167,6 @@ function parseExplainResponse(result) {
|
|
|
130
167
|
}
|
|
131
168
|
//#endregion
|
|
132
169
|
//#region src/drizzle/index.ts
|
|
133
|
-
const SEMATTRS_DB_PLAN_NODE = "db.plan.node";
|
|
134
|
-
const SEMATTRS_DB_PLAN_INDEXES = "db.plan.indexes";
|
|
135
|
-
const SEMATTRS_DB_PLAN_COST = "db.plan.cost";
|
|
136
|
-
const SEMATTRS_DB_PLAN_ROWS_ESTIMATED = "db.plan.rows_estimated";
|
|
137
|
-
const SEMATTRS_DB_PLAN_ROWS_EXAMINED = "db.plan.rows_examined";
|
|
138
|
-
const SEMATTRS_DB_PLAN_ROWS_RETURNED = "db.plan.rows_returned";
|
|
139
|
-
const SEMATTRS_DB_PLAN_BLOCKS = "db.plan.blocks";
|
|
140
|
-
const SEMATTRS_DB_PLAN_EXECUTION_MS = "db.plan.execution_ms";
|
|
141
|
-
const SEMATTRS_DB_PLAN_SEQ_SCAN = "db.plan.seq_scan";
|
|
142
|
-
const SEMATTRS_DB_PLAN_HASH = "db.plan.hash";
|
|
143
170
|
const TRANSACTION_ATTRIBUTE = "db.transaction";
|
|
144
171
|
const REENTRY_FLAG = `__autotelDrizzleInstrumented:running`;
|
|
145
172
|
const DEFAULT_TRACER_NAME = "autotel-plugins/drizzle";
|
|
@@ -190,7 +217,8 @@ function resolveConfig(config) {
|
|
|
190
217
|
semconv: config?.semconv ?? semconvFromEnvironment(),
|
|
191
218
|
spanNaming: config?.spanNaming ?? "drizzle",
|
|
192
219
|
traceTransactions: config?.traceTransactions ?? true,
|
|
193
|
-
explain: (config?.dbSystem ?? DEFAULT_DB_SYSTEM) === "postgresql" ? config?.explain ?? false : false
|
|
220
|
+
explain: (config?.dbSystem ?? DEFAULT_DB_SYSTEM) === "postgresql" ? config?.explain ?? false : false,
|
|
221
|
+
explainRequested: config?.explain ?? false
|
|
194
222
|
};
|
|
195
223
|
}
|
|
196
224
|
function getState(config) {
|
|
@@ -235,9 +263,6 @@ function sanitizeQueryText(queryText, maxLength) {
|
|
|
235
263
|
if (queryText.length <= maxLength) return queryText;
|
|
236
264
|
return `${queryText.slice(0, Math.max(0, maxLength))}...`;
|
|
237
265
|
}
|
|
238
|
-
function hashQueryText(queryText) {
|
|
239
|
-
return (0, node_crypto.createHash)("sha1").update(queryText).digest("hex").slice(0, 16);
|
|
240
|
-
}
|
|
241
266
|
function extractOperation(queryText) {
|
|
242
267
|
const trimmed = queryText.trimStart();
|
|
243
268
|
return /^(?<operation>\w+)/u.exec(trimmed)?.groups?.operation?.toUpperCase();
|
|
@@ -252,21 +277,28 @@ const WRITE_KEYWORDS = /\b(?:INSERT|UPDATE|DELETE|MERGE|TRUNCATE|DROP|ALTER|CREA
|
|
|
252
277
|
function buildExplainStatement(mode, queryText) {
|
|
253
278
|
return `EXPLAIN (${mode === "analyze" ? "ANALYZE, BUFFERS, FORMAT JSON" : "FORMAT JSON"}) ${queryText}`;
|
|
254
279
|
}
|
|
280
|
+
const SORT_NODES = new Set(["Sort", "Incremental Sort"]);
|
|
255
281
|
function planAttributes(payload, mode) {
|
|
256
|
-
const attributes = {};
|
|
257
282
|
const [root] = payload.nodes;
|
|
258
|
-
if (root === void 0) return
|
|
259
|
-
const
|
|
260
|
-
|
|
283
|
+
if (root === void 0) return {};
|
|
284
|
+
const plan = {
|
|
285
|
+
nodes: [],
|
|
286
|
+
fullScan: false,
|
|
287
|
+
blockingSort: false,
|
|
288
|
+
mode,
|
|
289
|
+
indexes: [],
|
|
290
|
+
cost: root.totalCost,
|
|
291
|
+
rowsEstimated: root.planRows
|
|
292
|
+
};
|
|
261
293
|
let rowsExamined = 0;
|
|
262
294
|
let blocks = 0;
|
|
263
|
-
let sawSeqScan = false;
|
|
264
295
|
for (const node of payload.nodes) {
|
|
265
296
|
if (node.nodeType !== void 0) {
|
|
266
|
-
|
|
267
|
-
|
|
297
|
+
plan.nodes.push(node.nodeType);
|
|
298
|
+
plan.fullScan ||= node.nodeType === "Seq Scan";
|
|
299
|
+
plan.blockingSort ||= SORT_NODES.has(node.nodeType);
|
|
268
300
|
}
|
|
269
|
-
if (node.indexName !== void 0 && !indexes.includes(node.indexName)) indexes.push(node.indexName);
|
|
301
|
+
if (node.indexName !== void 0 && !plan.indexes.includes(node.indexName)) plan.indexes.push(node.indexName);
|
|
270
302
|
if (node.isLeaf) {
|
|
271
303
|
rowsExamined += node.actualRows;
|
|
272
304
|
rowsExamined += node.rowsRemovedByFilter;
|
|
@@ -275,33 +307,86 @@ function planAttributes(payload, mode) {
|
|
|
275
307
|
blocks += node.sharedHitBlocks;
|
|
276
308
|
blocks += node.sharedReadBlocks;
|
|
277
309
|
}
|
|
278
|
-
if (root.nodeType !== void 0) attributes[SEMATTRS_DB_PLAN_NODE] = root.nodeType;
|
|
279
|
-
if (root.totalCost !== void 0) attributes[SEMATTRS_DB_PLAN_COST] = root.totalCost;
|
|
280
|
-
if (root.planRows !== void 0) attributes[SEMATTRS_DB_PLAN_ROWS_ESTIMATED] = root.planRows;
|
|
281
|
-
if (indexes.length > 0) attributes[SEMATTRS_DB_PLAN_INDEXES] = indexes.join(",");
|
|
282
|
-
attributes[SEMATTRS_DB_PLAN_SEQ_SCAN] = sawSeqScan;
|
|
283
|
-
attributes[SEMATTRS_DB_PLAN_HASH] = hashQueryText(nodeTypes.join(">"));
|
|
284
310
|
if (mode === "analyze") {
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
311
|
+
plan.rowsExamined = rowsExamined;
|
|
312
|
+
plan.rowsReturned = root.actualRows;
|
|
313
|
+
plan.blocks = blocks;
|
|
314
|
+
plan.executionMs = payload.executionMs;
|
|
289
315
|
}
|
|
290
|
-
return
|
|
316
|
+
return (0, autotel_db.planAttributes)(plan);
|
|
291
317
|
}
|
|
292
318
|
function resolveExplainClient(target, state) {
|
|
293
|
-
|
|
319
|
+
const own = readQueryClient(target);
|
|
320
|
+
if (own && isDriverObject(target)) return {
|
|
321
|
+
client: own,
|
|
322
|
+
connection: readObject(target, "client")
|
|
323
|
+
};
|
|
324
|
+
return state.explainClient ? { client: state.explainClient } : void 0;
|
|
294
325
|
}
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
if (!
|
|
298
|
-
|
|
326
|
+
const openTransactions = new WeakMap();
|
|
327
|
+
function transactionConnections(tx) {
|
|
328
|
+
if (!isDriverObject(tx)) return [];
|
|
329
|
+
const found = [];
|
|
330
|
+
for (const holder of [tx, ...readSessions(tx)]) {
|
|
331
|
+
const connection = readObject(holder, "client");
|
|
332
|
+
if (connection !== void 0 && !found.includes(connection)) found.push(connection);
|
|
333
|
+
}
|
|
334
|
+
return found;
|
|
335
|
+
}
|
|
336
|
+
function openTransaction(connections) {
|
|
337
|
+
for (const connection of connections) openTransactions.set(connection, (openTransactions.get(connection) ?? 0) + 1);
|
|
338
|
+
return () => {
|
|
339
|
+
for (const connection of connections) {
|
|
340
|
+
const depth = (openTransactions.get(connection) ?? 1) - 1;
|
|
341
|
+
if (depth > 0) openTransactions.set(connection, depth);
|
|
342
|
+
else openTransactions.delete(connection);
|
|
343
|
+
}
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
function thenAfter(run, after) {
|
|
347
|
+
let result;
|
|
348
|
+
try {
|
|
349
|
+
result = run();
|
|
350
|
+
} catch (error) {
|
|
351
|
+
after();
|
|
352
|
+
throw error;
|
|
353
|
+
}
|
|
354
|
+
if (isThenable(result)) return Promise.resolve(result).finally(after);
|
|
355
|
+
after();
|
|
356
|
+
return result;
|
|
357
|
+
}
|
|
358
|
+
const EXPLAINABLE = new Set([
|
|
359
|
+
"SELECT",
|
|
360
|
+
"WITH",
|
|
361
|
+
"INSERT",
|
|
362
|
+
"UPDATE",
|
|
363
|
+
"DELETE",
|
|
364
|
+
"MERGE",
|
|
365
|
+
"VALUES",
|
|
366
|
+
"TABLE"
|
|
367
|
+
]);
|
|
368
|
+
async function collectPlan(state, explainClient, queryText, params, inTransaction) {
|
|
369
|
+
const requested = state.config.explain;
|
|
370
|
+
if (!requested) return;
|
|
371
|
+
if (!EXPLAINABLE.has(extractOperation(queryText) ?? "")) return (0, autotel_db.planUnavailableAttributes)("unsupported", { mode: requested });
|
|
372
|
+
const mode = requested === "analyze" && !isReadOnlyStatement(queryText) ? "plan" : requested;
|
|
373
|
+
const client = inTransaction ? state.isolatedExplainClient : explainClient?.client;
|
|
374
|
+
if (client === void 0) return inTransaction ? (0, autotel_db.planUnavailableAttributes)("unsupported", { mode }) : (0, autotel_db.planUnavailableAttributes)("failed", {
|
|
375
|
+
mode,
|
|
376
|
+
error: "no client to run EXPLAIN on"
|
|
377
|
+
});
|
|
299
378
|
try {
|
|
300
379
|
const payload = parseExplainResponse(await client.query(buildExplainStatement(mode, queryText), readParams(params)));
|
|
301
|
-
if (payload === void 0) return
|
|
380
|
+
if (payload === void 0) return (0, autotel_db.planUnavailableAttributes)("failed", {
|
|
381
|
+
mode,
|
|
382
|
+
error: "EXPLAIN returned no plan autotel-drizzle reads"
|
|
383
|
+
});
|
|
302
384
|
return planAttributes(payload, mode);
|
|
303
|
-
} catch {
|
|
304
|
-
return
|
|
385
|
+
} catch (error) {
|
|
386
|
+
return (0, autotel_db.planUnavailableAttributes)("failed", {
|
|
387
|
+
mode,
|
|
388
|
+
error: error instanceof Error ? error.message : String(error)
|
|
389
|
+
});
|
|
305
390
|
}
|
|
306
391
|
}
|
|
307
392
|
function buildSpanName(mode, operation, table) {
|
|
@@ -327,7 +412,7 @@ function buildSpan(state, queryText, extraAttributes) {
|
|
|
327
412
|
if (operation) setRenamed(SEMATTRS_DB_OPERATION, SEMATTRS_DB_OPERATION_NAME, operation);
|
|
328
413
|
if (state.config.dbName !== void 0) setRenamed(SEMATTRS_DB_NAME, SEMATTRS_DB_NAMESPACE, state.config.dbName);
|
|
329
414
|
if (queryText !== void 0) {
|
|
330
|
-
span.setAttribute(SEMATTRS_DB_STATEMENT_HASH,
|
|
415
|
+
span.setAttribute(SEMATTRS_DB_STATEMENT_HASH, (0, autotel_db.hashStatement)(queryText));
|
|
331
416
|
if (table !== void 0) span.setAttribute(SEMATTRS_DB_COLLECTION_NAME, table);
|
|
332
417
|
}
|
|
333
418
|
if (state.config.captureQueryText && queryText !== void 0) {
|
|
@@ -339,6 +424,37 @@ function buildSpan(state, queryText, extraAttributes) {
|
|
|
339
424
|
if (extraAttributes) for (const [key, value] of Object.entries(extraAttributes)) span.setAttribute(key, value);
|
|
340
425
|
return span;
|
|
341
426
|
}
|
|
427
|
+
function runAlongsidePlan(span, planned, fn) {
|
|
428
|
+
const end = (error) => {
|
|
429
|
+
const endTime = performance.timeOrigin + performance.now();
|
|
430
|
+
planned.then((attributes) => {
|
|
431
|
+
if (attributes) span.setAttributes(attributes);
|
|
432
|
+
}).finally(() => {
|
|
433
|
+
if (error) {
|
|
434
|
+
span.recordException(error instanceof Error ? error : new Error(String(error)));
|
|
435
|
+
span.setStatus({ code: _opentelemetry_api.SpanStatusCode.ERROR });
|
|
436
|
+
} else span.setStatus({ code: _opentelemetry_api.SpanStatusCode.OK });
|
|
437
|
+
span.end(endTime);
|
|
438
|
+
});
|
|
439
|
+
};
|
|
440
|
+
return (0, autotel_trace_helpers.runWithSpan)(span, () => {
|
|
441
|
+
try {
|
|
442
|
+
const result = fn();
|
|
443
|
+
if (isThenable(result)) return result.then((value) => {
|
|
444
|
+
end();
|
|
445
|
+
return value;
|
|
446
|
+
}, (error) => {
|
|
447
|
+
end(error);
|
|
448
|
+
throw error;
|
|
449
|
+
});
|
|
450
|
+
end();
|
|
451
|
+
return result;
|
|
452
|
+
} catch (error) {
|
|
453
|
+
end(error);
|
|
454
|
+
throw error;
|
|
455
|
+
}
|
|
456
|
+
});
|
|
457
|
+
}
|
|
342
458
|
function executeWithSpan(span, fn) {
|
|
343
459
|
return (0, autotel_trace_helpers.runWithSpan)(span, () => {
|
|
344
460
|
try {
|
|
@@ -369,11 +485,20 @@ function instrumentMethod(target, methodName, state, options) {
|
|
|
369
485
|
const callback = trailing === void 0 ? void 0 : (args.pop(), trailing);
|
|
370
486
|
if (options.guardReentry && target[REENTRY_FLAG]) return originalMethod.call(this, ...incomingArgs);
|
|
371
487
|
const queryText = options.queryText(args);
|
|
372
|
-
if (state.config.explain && queryText !== void 0 && !callback)
|
|
488
|
+
if (state.config.explain && queryText !== void 0 && !callback) {
|
|
489
|
+
const explainClient = resolveExplainClient(readSelf(this), state);
|
|
490
|
+
const inTransaction = options.extraAttributes?.[TRANSACTION_ATTRIBUTE] === true || explainClient?.connection !== void 0 && openTransactions.has(explainClient.connection);
|
|
491
|
+
if (inTransaction) return runAlongsidePlan(buildSpan(state, queryText, options.extraAttributes), collectPlan(state, explainClient, queryText, options.explainParams?.(args), true), () => originalMethod.call(this, ...args));
|
|
492
|
+
return collectPlan(state, explainClient, queryText, options.explainParams?.(args), inTransaction).then((planAttributes) => executeWithSpan(buildSpan(state, queryText, {
|
|
493
|
+
...options.extraAttributes,
|
|
494
|
+
...planAttributes
|
|
495
|
+
}), () => originalMethod.call(this, ...args)));
|
|
496
|
+
}
|
|
497
|
+
const unexplained = state.config.explainRequested && queryText !== void 0 ? (0, autotel_db.planUnavailableAttributes)("unsupported", { mode: state.config.explainRequested }) : void 0;
|
|
498
|
+
const span = buildSpan(state, queryText, {
|
|
373
499
|
...options.extraAttributes,
|
|
374
|
-
...
|
|
375
|
-
})
|
|
376
|
-
const span = buildSpan(state, queryText, options.extraAttributes);
|
|
500
|
+
...unexplained
|
|
501
|
+
});
|
|
377
502
|
if (callback) return (0, autotel_trace_helpers.runWithSpan)(span, () => {
|
|
378
503
|
const wrappedCallback = (error, result) => {
|
|
379
504
|
(0, autotel_trace_helpers.finalizeSpan)(span, error);
|
|
@@ -479,11 +604,15 @@ function instrumentSession(session, state, allowDirectMethods = true) {
|
|
|
479
604
|
const [rawCallback, ...restArgs] = transactionArgs;
|
|
480
605
|
const callback = asFunction(rawCallback);
|
|
481
606
|
if (callback === void 0) return originalTransaction.call(this, ...transactionArgs);
|
|
607
|
+
const closers = [];
|
|
482
608
|
const wrappedCallback = (tx, ...callbackArgs) => {
|
|
483
609
|
instrumentTransactionTarget(tx, state);
|
|
610
|
+
closers.push(openTransaction(transactionConnections(tx)));
|
|
484
611
|
return callback.call(this, tx, ...callbackArgs);
|
|
485
612
|
};
|
|
486
|
-
const runTransaction = () => originalTransaction.call(this, wrappedCallback, ...restArgs)
|
|
613
|
+
const runTransaction = () => thenAfter(() => originalTransaction.call(this, wrappedCallback, ...restArgs), () => {
|
|
614
|
+
for (const close of closers) close();
|
|
615
|
+
});
|
|
487
616
|
if (!state.config.traceTransactions) return runTransaction();
|
|
488
617
|
const span = state.tracer.startSpan("drizzle.transaction", { kind: _opentelemetry_api.SpanKind.CLIENT });
|
|
489
618
|
span.setAttribute(TRANSACTION_ATTRIBUTE, true);
|
|
@@ -523,6 +652,7 @@ function instrumentDrizzleClient(db, config) {
|
|
|
523
652
|
if (readFlag(target, INSTRUMENTED_FLAG)) return db;
|
|
524
653
|
const state = getState(config);
|
|
525
654
|
state.explainClient = asQueryClient(target.$client);
|
|
655
|
+
state.isolatedExplainClient = isolatedExplainClient(target.$client);
|
|
526
656
|
let instrumented = false;
|
|
527
657
|
let sessionInstrumented = false;
|
|
528
658
|
for (const session of readSessions(target)) {
|
package/dist/drizzle.cjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_drizzle = require("./drizzle-
|
|
2
|
+
const require_drizzle = require("./drizzle-BeUip3cx.cjs");
|
|
3
3
|
exports.instrumentDrizzle = require_drizzle.instrumentDrizzle;
|
|
4
4
|
exports.instrumentDrizzleClient = require_drizzle.instrumentDrizzleClient;
|
package/dist/drizzle.d.cts
CHANGED
|
@@ -13,7 +13,7 @@ import { TracerProvider } from "@opentelemetry/api";
|
|
|
13
13
|
* Both modes double the round trips to the database. Turn them on in
|
|
14
14
|
* development, in CI, or behind a sample of production traffic.
|
|
15
15
|
*/
|
|
16
|
-
type ExplainMode = 'plan' | 'analyze';
|
|
16
|
+
export type ExplainMode = 'plan' | 'analyze';
|
|
17
17
|
/**
|
|
18
18
|
* Which OpenTelemetry database attribute names to emit.
|
|
19
19
|
*
|
|
@@ -29,7 +29,7 @@ type ExplainMode = 'plan' | 'analyze';
|
|
|
29
29
|
* `OTEL_SEMCONV_STABILITY_OPT_IN`: `database` selects `'stable'` and
|
|
30
30
|
* `database/dup` selects `'dup'`.
|
|
31
31
|
*/
|
|
32
|
-
type SemconvMode = 'legacy' | 'stable' | 'dup';
|
|
32
|
+
export type SemconvMode = 'legacy' | 'stable' | 'dup';
|
|
33
33
|
/**
|
|
34
34
|
* How a query span is named.
|
|
35
35
|
*
|
|
@@ -37,8 +37,8 @@ type SemconvMode = 'legacy' | 'stable' | 'dup';
|
|
|
37
37
|
* - `'semconv'` follows OpenTelemetry's database span convention,
|
|
38
38
|
* `{db.operation.name} {target}`, so `SELECT comments`.
|
|
39
39
|
*/
|
|
40
|
-
type SpanNamingMode = 'drizzle' | 'semconv';
|
|
41
|
-
interface InstrumentDrizzleConfig {
|
|
40
|
+
export type SpanNamingMode = 'drizzle' | 'semconv';
|
|
41
|
+
export interface InstrumentDrizzleConfig {
|
|
42
42
|
tracerName?: string;
|
|
43
43
|
dbSystem?: string;
|
|
44
44
|
dbName?: string;
|
|
@@ -78,7 +78,6 @@ interface InstrumentDrizzleConfig {
|
|
|
78
78
|
*/
|
|
79
79
|
traceTransactions?: boolean;
|
|
80
80
|
}
|
|
81
|
-
declare function instrumentDrizzle<TClient>(client: TClient, config?: InstrumentDrizzleConfig): TClient;
|
|
82
|
-
declare function instrumentDrizzleClient<TDb>(db: TDb, config?: InstrumentDrizzleConfig): TDb;
|
|
83
|
-
//#endregion
|
|
84
|
-
export { ExplainMode, InstrumentDrizzleConfig, SemconvMode, SpanNamingMode, instrumentDrizzle, instrumentDrizzleClient };
|
|
81
|
+
export declare function instrumentDrizzle<TClient>(client: TClient, config?: InstrumentDrizzleConfig): TClient;
|
|
82
|
+
export declare function instrumentDrizzleClient<TDb>(db: TDb, config?: InstrumentDrizzleConfig): TDb;
|
|
83
|
+
//#endregion
|
package/dist/drizzle.d.ts
CHANGED
|
@@ -13,7 +13,7 @@ import { TracerProvider } from "@opentelemetry/api";
|
|
|
13
13
|
* Both modes double the round trips to the database. Turn them on in
|
|
14
14
|
* development, in CI, or behind a sample of production traffic.
|
|
15
15
|
*/
|
|
16
|
-
type ExplainMode = 'plan' | 'analyze';
|
|
16
|
+
export type ExplainMode = 'plan' | 'analyze';
|
|
17
17
|
/**
|
|
18
18
|
* Which OpenTelemetry database attribute names to emit.
|
|
19
19
|
*
|
|
@@ -29,7 +29,7 @@ type ExplainMode = 'plan' | 'analyze';
|
|
|
29
29
|
* `OTEL_SEMCONV_STABILITY_OPT_IN`: `database` selects `'stable'` and
|
|
30
30
|
* `database/dup` selects `'dup'`.
|
|
31
31
|
*/
|
|
32
|
-
type SemconvMode = 'legacy' | 'stable' | 'dup';
|
|
32
|
+
export type SemconvMode = 'legacy' | 'stable' | 'dup';
|
|
33
33
|
/**
|
|
34
34
|
* How a query span is named.
|
|
35
35
|
*
|
|
@@ -37,8 +37,8 @@ type SemconvMode = 'legacy' | 'stable' | 'dup';
|
|
|
37
37
|
* - `'semconv'` follows OpenTelemetry's database span convention,
|
|
38
38
|
* `{db.operation.name} {target}`, so `SELECT comments`.
|
|
39
39
|
*/
|
|
40
|
-
type SpanNamingMode = 'drizzle' | 'semconv';
|
|
41
|
-
interface InstrumentDrizzleConfig {
|
|
40
|
+
export type SpanNamingMode = 'drizzle' | 'semconv';
|
|
41
|
+
export interface InstrumentDrizzleConfig {
|
|
42
42
|
tracerName?: string;
|
|
43
43
|
dbSystem?: string;
|
|
44
44
|
dbName?: string;
|
|
@@ -78,7 +78,6 @@ interface InstrumentDrizzleConfig {
|
|
|
78
78
|
*/
|
|
79
79
|
traceTransactions?: boolean;
|
|
80
80
|
}
|
|
81
|
-
declare function instrumentDrizzle<TClient>(client: TClient, config?: InstrumentDrizzleConfig): TClient;
|
|
82
|
-
declare function instrumentDrizzleClient<TDb>(db: TDb, config?: InstrumentDrizzleConfig): TDb;
|
|
83
|
-
//#endregion
|
|
84
|
-
export { ExplainMode, InstrumentDrizzleConfig, SemconvMode, SpanNamingMode, instrumentDrizzle, instrumentDrizzleClient };
|
|
81
|
+
export declare function instrumentDrizzle<TClient>(client: TClient, config?: InstrumentDrizzleConfig): TClient;
|
|
82
|
+
export declare function instrumentDrizzleClient<TDb>(db: TDb, config?: InstrumentDrizzleConfig): TDb;
|
|
83
|
+
//#endregion
|
package/dist/drizzle.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { n as instrumentDrizzleClient, t as instrumentDrizzle } from "./drizzle-
|
|
1
|
+
import { n as instrumentDrizzleClient, t as instrumentDrizzle } from "./drizzle-BAxEDkHv.js";
|
|
2
2
|
export { instrumentDrizzle, instrumentDrizzleClient };
|
package/dist/index.cjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
-
const require_drizzle = require("./drizzle-
|
|
2
|
+
const require_drizzle = require("./drizzle-BeUip3cx.cjs");
|
|
3
3
|
exports.SEMATTRS_DB_COLLECTION_NAME = require_drizzle.SEMATTRS_DB_COLLECTION_NAME;
|
|
4
4
|
exports.SEMATTRS_DB_NAME = require_drizzle.SEMATTRS_DB_NAME;
|
|
5
5
|
exports.SEMATTRS_DB_NAMESPACE = require_drizzle.SEMATTRS_DB_NAMESPACE;
|
package/dist/index.d.cts
CHANGED
|
@@ -4,27 +4,27 @@ import { ExplainMode, InstrumentDrizzleConfig, instrumentDrizzle, instrumentDriz
|
|
|
4
4
|
* OpenTelemetry semantic conventions for database operations.
|
|
5
5
|
* These constants are shared across all plugins.
|
|
6
6
|
*/
|
|
7
|
-
declare const SEMATTRS_DB_SYSTEM:
|
|
8
|
-
declare const SEMATTRS_DB_SYSTEM_NAME:
|
|
9
|
-
declare const SEMATTRS_DB_OPERATION:
|
|
10
|
-
declare const SEMATTRS_DB_STATEMENT:
|
|
11
|
-
declare const SEMATTRS_DB_NAME:
|
|
12
|
-
declare const SEMATTRS_DB_NAMESPACE:
|
|
13
|
-
declare const SEMATTRS_DB_COLLECTION_NAME:
|
|
14
|
-
declare const SEMATTRS_DB_OPERATION_NAME:
|
|
15
|
-
declare const SEMATTRS_DB_QUERY_TEXT:
|
|
16
|
-
declare const SEMATTRS_DB_QUERY_SUMMARY:
|
|
17
|
-
declare const SEMATTRS_NET_PEER_NAME:
|
|
18
|
-
declare const SEMATTRS_NET_PEER_PORT:
|
|
19
|
-
declare const SEMATTRS_GCP_BIGQUERY_JOB_ID:
|
|
20
|
-
declare const SEMATTRS_GCP_BIGQUERY_JOB_LOCATION:
|
|
21
|
-
declare const SEMATTRS_GCP_BIGQUERY_PROJECT_ID:
|
|
22
|
-
declare const SEMATTRS_GCP_BIGQUERY_DESTINATION_TABLE:
|
|
23
|
-
declare const SEMATTRS_GCP_BIGQUERY_SOURCE_TABLES:
|
|
24
|
-
declare const SEMATTRS_GCP_BIGQUERY_STATEMENT_TYPE:
|
|
25
|
-
declare const SEMATTRS_GCP_BIGQUERY_QUERY_HASH:
|
|
26
|
-
declare const SEMATTRS_GCP_BIGQUERY_ROWS_AFFECTED:
|
|
27
|
-
declare const SEMATTRS_GCP_BIGQUERY_ROWS_RETURNED:
|
|
28
|
-
declare const SEMATTRS_GCP_BIGQUERY_SCHEMA_FIELDS:
|
|
7
|
+
export declare const SEMATTRS_DB_SYSTEM: 'db.system';
|
|
8
|
+
export declare const SEMATTRS_DB_SYSTEM_NAME: 'db.system.name';
|
|
9
|
+
export declare const SEMATTRS_DB_OPERATION: 'db.operation';
|
|
10
|
+
export declare const SEMATTRS_DB_STATEMENT: 'db.statement';
|
|
11
|
+
export declare const SEMATTRS_DB_NAME: 'db.name';
|
|
12
|
+
export declare const SEMATTRS_DB_NAMESPACE: 'db.namespace';
|
|
13
|
+
export declare const SEMATTRS_DB_COLLECTION_NAME: 'db.collection.name';
|
|
14
|
+
export declare const SEMATTRS_DB_OPERATION_NAME: 'db.operation.name';
|
|
15
|
+
export declare const SEMATTRS_DB_QUERY_TEXT: 'db.query.text';
|
|
16
|
+
export declare const SEMATTRS_DB_QUERY_SUMMARY: 'db.query.summary';
|
|
17
|
+
export declare const SEMATTRS_NET_PEER_NAME: 'net.peer.name';
|
|
18
|
+
export declare const SEMATTRS_NET_PEER_PORT: 'net.peer.port';
|
|
19
|
+
export declare const SEMATTRS_GCP_BIGQUERY_JOB_ID: 'gcp.bigquery.job.id';
|
|
20
|
+
export declare const SEMATTRS_GCP_BIGQUERY_JOB_LOCATION: 'gcp.bigquery.job.location';
|
|
21
|
+
export declare const SEMATTRS_GCP_BIGQUERY_PROJECT_ID: 'gcp.bigquery.project.id';
|
|
22
|
+
export declare const SEMATTRS_GCP_BIGQUERY_DESTINATION_TABLE: 'gcp.bigquery.destination.table';
|
|
23
|
+
export declare const SEMATTRS_GCP_BIGQUERY_SOURCE_TABLES: 'gcp.bigquery.source.tables';
|
|
24
|
+
export declare const SEMATTRS_GCP_BIGQUERY_STATEMENT_TYPE: 'gcp.bigquery.statement_type';
|
|
25
|
+
export declare const SEMATTRS_GCP_BIGQUERY_QUERY_HASH: 'gcp.bigquery.query.hash';
|
|
26
|
+
export declare const SEMATTRS_GCP_BIGQUERY_ROWS_AFFECTED: 'gcp.bigquery.rows.affected';
|
|
27
|
+
export declare const SEMATTRS_GCP_BIGQUERY_ROWS_RETURNED: 'gcp.bigquery.rows.returned';
|
|
28
|
+
export declare const SEMATTRS_GCP_BIGQUERY_SCHEMA_FIELDS: 'gcp.bigquery.schema.fields';
|
|
29
29
|
//#endregion
|
|
30
|
-
export { type ExplainMode, type InstrumentDrizzleConfig,
|
|
30
|
+
export { type ExplainMode, type InstrumentDrizzleConfig, instrumentDrizzle, instrumentDrizzleClient };
|
package/dist/index.d.ts
CHANGED
|
@@ -4,27 +4,27 @@ import { ExplainMode, InstrumentDrizzleConfig, instrumentDrizzle, instrumentDriz
|
|
|
4
4
|
* OpenTelemetry semantic conventions for database operations.
|
|
5
5
|
* These constants are shared across all plugins.
|
|
6
6
|
*/
|
|
7
|
-
declare const SEMATTRS_DB_SYSTEM:
|
|
8
|
-
declare const SEMATTRS_DB_SYSTEM_NAME:
|
|
9
|
-
declare const SEMATTRS_DB_OPERATION:
|
|
10
|
-
declare const SEMATTRS_DB_STATEMENT:
|
|
11
|
-
declare const SEMATTRS_DB_NAME:
|
|
12
|
-
declare const SEMATTRS_DB_NAMESPACE:
|
|
13
|
-
declare const SEMATTRS_DB_COLLECTION_NAME:
|
|
14
|
-
declare const SEMATTRS_DB_OPERATION_NAME:
|
|
15
|
-
declare const SEMATTRS_DB_QUERY_TEXT:
|
|
16
|
-
declare const SEMATTRS_DB_QUERY_SUMMARY:
|
|
17
|
-
declare const SEMATTRS_NET_PEER_NAME:
|
|
18
|
-
declare const SEMATTRS_NET_PEER_PORT:
|
|
19
|
-
declare const SEMATTRS_GCP_BIGQUERY_JOB_ID:
|
|
20
|
-
declare const SEMATTRS_GCP_BIGQUERY_JOB_LOCATION:
|
|
21
|
-
declare const SEMATTRS_GCP_BIGQUERY_PROJECT_ID:
|
|
22
|
-
declare const SEMATTRS_GCP_BIGQUERY_DESTINATION_TABLE:
|
|
23
|
-
declare const SEMATTRS_GCP_BIGQUERY_SOURCE_TABLES:
|
|
24
|
-
declare const SEMATTRS_GCP_BIGQUERY_STATEMENT_TYPE:
|
|
25
|
-
declare const SEMATTRS_GCP_BIGQUERY_QUERY_HASH:
|
|
26
|
-
declare const SEMATTRS_GCP_BIGQUERY_ROWS_AFFECTED:
|
|
27
|
-
declare const SEMATTRS_GCP_BIGQUERY_ROWS_RETURNED:
|
|
28
|
-
declare const SEMATTRS_GCP_BIGQUERY_SCHEMA_FIELDS:
|
|
7
|
+
export declare const SEMATTRS_DB_SYSTEM: 'db.system';
|
|
8
|
+
export declare const SEMATTRS_DB_SYSTEM_NAME: 'db.system.name';
|
|
9
|
+
export declare const SEMATTRS_DB_OPERATION: 'db.operation';
|
|
10
|
+
export declare const SEMATTRS_DB_STATEMENT: 'db.statement';
|
|
11
|
+
export declare const SEMATTRS_DB_NAME: 'db.name';
|
|
12
|
+
export declare const SEMATTRS_DB_NAMESPACE: 'db.namespace';
|
|
13
|
+
export declare const SEMATTRS_DB_COLLECTION_NAME: 'db.collection.name';
|
|
14
|
+
export declare const SEMATTRS_DB_OPERATION_NAME: 'db.operation.name';
|
|
15
|
+
export declare const SEMATTRS_DB_QUERY_TEXT: 'db.query.text';
|
|
16
|
+
export declare const SEMATTRS_DB_QUERY_SUMMARY: 'db.query.summary';
|
|
17
|
+
export declare const SEMATTRS_NET_PEER_NAME: 'net.peer.name';
|
|
18
|
+
export declare const SEMATTRS_NET_PEER_PORT: 'net.peer.port';
|
|
19
|
+
export declare const SEMATTRS_GCP_BIGQUERY_JOB_ID: 'gcp.bigquery.job.id';
|
|
20
|
+
export declare const SEMATTRS_GCP_BIGQUERY_JOB_LOCATION: 'gcp.bigquery.job.location';
|
|
21
|
+
export declare const SEMATTRS_GCP_BIGQUERY_PROJECT_ID: 'gcp.bigquery.project.id';
|
|
22
|
+
export declare const SEMATTRS_GCP_BIGQUERY_DESTINATION_TABLE: 'gcp.bigquery.destination.table';
|
|
23
|
+
export declare const SEMATTRS_GCP_BIGQUERY_SOURCE_TABLES: 'gcp.bigquery.source.tables';
|
|
24
|
+
export declare const SEMATTRS_GCP_BIGQUERY_STATEMENT_TYPE: 'gcp.bigquery.statement_type';
|
|
25
|
+
export declare const SEMATTRS_GCP_BIGQUERY_QUERY_HASH: 'gcp.bigquery.query.hash';
|
|
26
|
+
export declare const SEMATTRS_GCP_BIGQUERY_ROWS_AFFECTED: 'gcp.bigquery.rows.affected';
|
|
27
|
+
export declare const SEMATTRS_GCP_BIGQUERY_ROWS_RETURNED: 'gcp.bigquery.rows.returned';
|
|
28
|
+
export declare const SEMATTRS_GCP_BIGQUERY_SCHEMA_FIELDS: 'gcp.bigquery.schema.fields';
|
|
29
29
|
//#endregion
|
|
30
|
-
export { type ExplainMode, type InstrumentDrizzleConfig,
|
|
30
|
+
export { type ExplainMode, type InstrumentDrizzleConfig, instrumentDrizzle, instrumentDrizzleClient };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { C as SEMATTRS_NET_PEER_NAME, S as SEMATTRS_GCP_BIGQUERY_STATEMENT_TYPE, _ as SEMATTRS_GCP_BIGQUERY_QUERY_HASH, a as SEMATTRS_DB_NAMESPACE, b as SEMATTRS_GCP_BIGQUERY_SCHEMA_FIELDS, c as SEMATTRS_DB_QUERY_SUMMARY, d as SEMATTRS_DB_SYSTEM, f as SEMATTRS_DB_SYSTEM_NAME, g as SEMATTRS_GCP_BIGQUERY_PROJECT_ID, h as SEMATTRS_GCP_BIGQUERY_JOB_LOCATION, i as SEMATTRS_DB_NAME, l as SEMATTRS_DB_QUERY_TEXT, m as SEMATTRS_GCP_BIGQUERY_JOB_ID, n as instrumentDrizzleClient, o as SEMATTRS_DB_OPERATION, p as SEMATTRS_GCP_BIGQUERY_DESTINATION_TABLE, r as SEMATTRS_DB_COLLECTION_NAME, s as SEMATTRS_DB_OPERATION_NAME, t as instrumentDrizzle, u as SEMATTRS_DB_STATEMENT, v as SEMATTRS_GCP_BIGQUERY_ROWS_AFFECTED, w as SEMATTRS_NET_PEER_PORT, x as SEMATTRS_GCP_BIGQUERY_SOURCE_TABLES, y as SEMATTRS_GCP_BIGQUERY_ROWS_RETURNED } from "./drizzle-
|
|
1
|
+
import { C as SEMATTRS_NET_PEER_NAME, S as SEMATTRS_GCP_BIGQUERY_STATEMENT_TYPE, _ as SEMATTRS_GCP_BIGQUERY_QUERY_HASH, a as SEMATTRS_DB_NAMESPACE, b as SEMATTRS_GCP_BIGQUERY_SCHEMA_FIELDS, c as SEMATTRS_DB_QUERY_SUMMARY, d as SEMATTRS_DB_SYSTEM, f as SEMATTRS_DB_SYSTEM_NAME, g as SEMATTRS_GCP_BIGQUERY_PROJECT_ID, h as SEMATTRS_GCP_BIGQUERY_JOB_LOCATION, i as SEMATTRS_DB_NAME, l as SEMATTRS_DB_QUERY_TEXT, m as SEMATTRS_GCP_BIGQUERY_JOB_ID, n as instrumentDrizzleClient, o as SEMATTRS_DB_OPERATION, p as SEMATTRS_GCP_BIGQUERY_DESTINATION_TABLE, r as SEMATTRS_DB_COLLECTION_NAME, s as SEMATTRS_DB_OPERATION_NAME, t as instrumentDrizzle, u as SEMATTRS_DB_STATEMENT, v as SEMATTRS_GCP_BIGQUERY_ROWS_AFFECTED, w as SEMATTRS_NET_PEER_PORT, x as SEMATTRS_GCP_BIGQUERY_SOURCE_TABLES, y as SEMATTRS_GCP_BIGQUERY_ROWS_RETURNED } from "./drizzle-BAxEDkHv.js";
|
|
2
2
|
export { SEMATTRS_DB_COLLECTION_NAME, SEMATTRS_DB_NAME, SEMATTRS_DB_NAMESPACE, SEMATTRS_DB_OPERATION, SEMATTRS_DB_OPERATION_NAME, SEMATTRS_DB_QUERY_SUMMARY, SEMATTRS_DB_QUERY_TEXT, SEMATTRS_DB_STATEMENT, SEMATTRS_DB_SYSTEM, SEMATTRS_DB_SYSTEM_NAME, SEMATTRS_GCP_BIGQUERY_DESTINATION_TABLE, SEMATTRS_GCP_BIGQUERY_JOB_ID, SEMATTRS_GCP_BIGQUERY_JOB_LOCATION, SEMATTRS_GCP_BIGQUERY_PROJECT_ID, SEMATTRS_GCP_BIGQUERY_QUERY_HASH, SEMATTRS_GCP_BIGQUERY_ROWS_AFFECTED, SEMATTRS_GCP_BIGQUERY_ROWS_RETURNED, SEMATTRS_GCP_BIGQUERY_SCHEMA_FIELDS, SEMATTRS_GCP_BIGQUERY_SOURCE_TABLES, SEMATTRS_GCP_BIGQUERY_STATEMENT_TYPE, SEMATTRS_NET_PEER_NAME, SEMATTRS_NET_PEER_PORT, instrumentDrizzle, instrumentDrizzleClient };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "autotel-drizzle",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "OpenTelemetry instrumentation for Drizzle ORM",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/drizzle.js",
|
|
@@ -37,23 +37,20 @@
|
|
|
37
37
|
"@opentelemetry/api": "^1.9.1",
|
|
38
38
|
"@opentelemetry/instrumentation": "^0.222.0",
|
|
39
39
|
"@opentelemetry/semantic-conventions": "^1.43.0",
|
|
40
|
-
"autotel": "7.
|
|
40
|
+
"autotel": "7.10.0",
|
|
41
|
+
"autotel-db": "0.1.0"
|
|
41
42
|
},
|
|
42
43
|
"peerDependencies": {
|
|
43
|
-
"drizzle-orm": ">=0.45.
|
|
44
|
+
"drizzle-orm": ">=0.45.3"
|
|
44
45
|
},
|
|
45
46
|
"devDependencies": {
|
|
46
47
|
"@arethetypeswrong/cli": "^0.18.5",
|
|
47
48
|
"@opentelemetry/core": "^2.11.0",
|
|
48
|
-
"@types/node": "^26.
|
|
49
|
-
"
|
|
50
|
-
"
|
|
51
|
-
"
|
|
52
|
-
"
|
|
53
|
-
"eslint-plugin-unicorn": "^64.0.0",
|
|
54
|
-
"tsdown": "^0.22.14",
|
|
55
|
-
"typescript": "^6.0.3",
|
|
56
|
-
"vitest": "^4.1.10"
|
|
49
|
+
"@types/node": "^26.6.3",
|
|
50
|
+
"drizzle-orm": "^0.45.3",
|
|
51
|
+
"tsdown": "^0.23.0",
|
|
52
|
+
"typescript": "^7.0.2",
|
|
53
|
+
"vitest": "^5.0.2"
|
|
57
54
|
},
|
|
58
55
|
"repository": {
|
|
59
56
|
"type": "git",
|
|
@@ -68,8 +65,8 @@
|
|
|
68
65
|
"build": "tsdown",
|
|
69
66
|
"ci": "npm run build && npm run check-format && npm run check-exports && npm run lint && npm run test",
|
|
70
67
|
"dev": "tsdown --watch",
|
|
71
|
-
"lint": "
|
|
72
|
-
"lint:fix": "
|
|
68
|
+
"lint": "oxlint -c oxlint.json --disable-nested-config src",
|
|
69
|
+
"lint:fix": "pnpm lint --fix",
|
|
73
70
|
"format:check": "prettier --check src/**/*.ts",
|
|
74
71
|
"type-check": "tsc --noEmit",
|
|
75
72
|
"test": "vitest run --config vitest.unit.config.ts",
|