autotel-drizzle 0.0.32 → 0.0.33

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autotel-drizzle",
3
- "version": "0.0.32",
3
+ "version": "0.0.33",
4
4
  "description": "OpenTelemetry instrumentation for Drizzle ORM",
5
5
  "type": "module",
6
6
  "main": "./dist/drizzle.js",
@@ -16,7 +16,8 @@
16
16
  "files": [
17
17
  "dist",
18
18
  "src",
19
- "README.md"
19
+ "README.md",
20
+ "skills"
20
21
  ],
21
22
  "keywords": [
22
23
  "opentelemetry",
@@ -0,0 +1,185 @@
1
+ ---
2
+ name: autotel-drizzle
3
+ description: >
4
+ Use this skill when adding OpenTelemetry tracing to a Drizzle ORM database instance — the only autotel instrumentation package needed for Drizzle, since no official OTel package exists for it.
5
+ ---
6
+
7
+ # autotel-drizzle
8
+
9
+ OpenTelemetry instrumentation for Drizzle ORM. Drizzle has no official OTel package, so this package fills that gap by patching the Drizzle session and client at runtime to create spans for every query, prepared statement, and transaction.
10
+
11
+ **Only use this package for Drizzle.** For databases with working official instrumentation (PostgreSQL via `pg`, MySQL via `mysql2`, SQLite, Redis, MongoDB, Kafka), use the official `@opentelemetry/instrumentation-*` packages directly with the `--import` pattern instead.
12
+
13
+ ## Setup
14
+
15
+ ```bash
16
+ npm install autotel autotel-drizzle
17
+ ```
18
+
19
+ ```typescript
20
+ // src/instrumentation.mts
21
+ import 'autotel/register';
22
+ import { init } from 'autotel';
23
+
24
+ init({ service: 'my-app' });
25
+ ```
26
+
27
+ ```bash
28
+ node --import ./src/instrumentation.mts dist/index.js
29
+ ```
30
+
31
+ ## Core Patterns
32
+
33
+ ### Instrument a Drizzle database instance (recommended)
34
+
35
+ `instrumentDrizzleClient` patches the Drizzle `db` object returned by `drizzle()`. This is the primary API for most use cases.
36
+
37
+ ```typescript
38
+ import { drizzle } from 'drizzle-orm/postgres-js';
39
+ import postgres from 'postgres';
40
+ import { instrumentDrizzleClient } from 'autotel-drizzle';
41
+
42
+ const queryClient = postgres(process.env.DATABASE_URL!);
43
+ const db = drizzle({ client: queryClient });
44
+
45
+ instrumentDrizzleClient(db, {
46
+ dbSystem: 'postgresql',
47
+ dbName: 'myapp',
48
+ peerName: 'db.example.com',
49
+ peerPort: 5432,
50
+ });
51
+
52
+ // All queries, prepared statements, and transactions are now traced
53
+ await db.select().from(users).where(eq(users.id, 123));
54
+ ```
55
+
56
+ ### Instrument a raw database client/pool
57
+
58
+ `instrumentDrizzle` patches a lower-level client object (e.g., a `pg.Pool`). Use this when you have the raw client but not a Drizzle instance.
59
+
60
+ ```typescript
61
+ import { Pool } from 'pg';
62
+ import { instrumentDrizzle } from 'autotel-drizzle';
63
+
64
+ const pool = new Pool({ connectionString: process.env.DATABASE_URL });
65
+ instrumentDrizzle(pool, { dbSystem: 'postgresql', dbName: 'myapp' });
66
+ ```
67
+
68
+ ### Configuration reference (`InstrumentDrizzleConfig`)
69
+
70
+ ```typescript
71
+ interface InstrumentDrizzleConfig {
72
+ tracerName?: string; // Default: 'autotel-plugins/drizzle'
73
+ dbSystem?: string; // Default: 'postgresql'. Also: 'mysql', 'sqlite'
74
+ dbName?: string; // Sets db.name on every span
75
+ captureQueryText?: boolean; // Capture SQL text in db.statement (default: true)
76
+ maxQueryTextLength?: number; // Truncate SQL at this length (default: 1000)
77
+ peerName?: string; // Sets net.peer.name
78
+ peerPort?: number; // Sets net.peer.port
79
+ }
80
+ ```
81
+
82
+ ### Span attributes set on every span
83
+
84
+ | Attribute | Source |
85
+ | --------------- | ---------------------------------------------------------------- |
86
+ | `db.system` | `config.dbSystem` (default: `'postgresql'`) |
87
+ | `db.operation` | SQL keyword extracted from query text (e.g., `SELECT`, `INSERT`) |
88
+ | `db.name` | `config.dbName` (if set) |
89
+ | `db.statement` | Truncated SQL text (if `captureQueryText: true`) |
90
+ | `net.peer.name` | `config.peerName` (if set) |
91
+ | `net.peer.port` | `config.peerPort` (if set) |
92
+
93
+ Span names follow the pattern `drizzle.<operation>` (e.g., `drizzle.select`, `drizzle.insert`) or `drizzle.query` when the operation cannot be determined.
94
+
95
+ ### Disabling SQL capture (PII safety)
96
+
97
+ ```typescript
98
+ instrumentDrizzleClient(db, {
99
+ captureQueryText: false,
100
+ });
101
+ ```
102
+
103
+ SQL text may contain user-supplied values. Disable capture or truncate aggressively when PII is a concern.
104
+
105
+ ### Supported databases
106
+
107
+ - PostgreSQL (`drizzle-orm/node-postgres`, `drizzle-orm/postgres-js`)
108
+ - MySQL (`drizzle-orm/mysql2`)
109
+ - SQLite (`drizzle-orm/better-sqlite3`, `drizzle-orm/libsql`)
110
+
111
+ ### What is instrumented
112
+
113
+ - Session `query()` and `execute()` methods
114
+ - Prepared queries (`prepareQuery`) — all execution methods: `all`, `execute`, `get`, `run`, `values`
115
+ - Transactions — the callback receives an already-instrumented transaction object, spans carry `db.transaction: true`
116
+
117
+ ### Idempotency
118
+
119
+ Both functions are idempotent. Calling them more than once on the same instance is safe; the `__autotelDrizzleInstrumented` flag prevents double-wrapping.
120
+
121
+ ### Using semantic convention exports
122
+
123
+ The package re-exports OTel semconv constants for use in custom instrumentation:
124
+
125
+ ```typescript
126
+ import {
127
+ SEMATTRS_DB_SYSTEM,
128
+ SEMATTRS_DB_STATEMENT,
129
+ SEMATTRS_DB_OPERATION,
130
+ } from 'autotel-drizzle';
131
+ ```
132
+
133
+ ## Common Mistakes
134
+
135
+ ### HIGH — Calling `instrumentDrizzleClient` after queries have already run
136
+
137
+ ```typescript
138
+ // Wrong: some early queries miss instrumentation
139
+ const db = drizzle({ client });
140
+ await db.select().from(users); // not instrumented
141
+
142
+ instrumentDrizzleClient(db);
143
+
144
+ // Correct: instrument immediately after creating the db instance
145
+ const db = drizzle({ client });
146
+ instrumentDrizzleClient(db, { dbSystem: 'postgresql' });
147
+
148
+ await db.select().from(users); // instrumented
149
+ ```
150
+
151
+ Patching works by mutating method references on the live object. Any query that ran before patching has no span.
152
+
153
+ ### HIGH — Using autotel-drizzle for databases that have official OTel packages
154
+
155
+ ```typescript
156
+ // Wrong: autotel-drizzle is for Drizzle, not for pg directly
157
+ import { instrumentDrizzle } from 'autotel-drizzle';
158
+ instrumentDrizzle(pgPool); // unnecessary if you're not using Drizzle
159
+
160
+ // Correct: use the official package for pg
161
+ import { PgInstrumentation } from '@opentelemetry/instrumentation-pg';
162
+ init({ instrumentations: [new PgInstrumentation()] });
163
+ ```
164
+
165
+ For PostgreSQL (`pg`/`postgres`), MySQL (`mysql2`), SQLite, MongoDB, and Redis, official OTel instrumentation packages exist and should be preferred.
166
+
167
+ ### MEDIUM — Not setting `dbSystem` for non-PostgreSQL databases
168
+
169
+ ```typescript
170
+ // Wrong: dbSystem defaults to 'postgresql' even for SQLite
171
+ instrumentDrizzleClient(sqliteDb);
172
+
173
+ // Correct: set dbSystem to match the actual database
174
+ instrumentDrizzleClient(sqliteDb, { dbSystem: 'sqlite' });
175
+ ```
176
+
177
+ The `db.system` span attribute will be wrong if the default is not overridden.
178
+
179
+ ### MEDIUM — Passing `captureQueryText: true` without reviewing SQL for PII
180
+
181
+ SQL statements built by Drizzle may embed user-supplied values (e.g., email addresses in `WHERE` clauses). Either disable capture or review what Drizzle sends before enabling in production.
182
+
183
+ ## Version
184
+
185
+ Targets autotel-drizzle v0.0.3 with drizzle-orm >= 0.45.1 (peer dep). Requires autotel as a dependency.