@devskin/agent 1.0.3 → 1.0.4
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 +132 -1
- package/dist/agent.d.ts.map +1 -1
- package/dist/agent.js +2 -0
- package/dist/agent.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/instrumentation/prisma.d.ts +3 -0
- package/dist/instrumentation/prisma.d.ts.map +1 -0
- package/dist/instrumentation/prisma.js +109 -0
- package/dist/instrumentation/prisma.js.map +1 -0
- package/package.json +1 -1
- package/src/agent.ts +4 -0
- package/src/index.ts +1 -0
- package/src/instrumentation/prisma.ts +154 -0
package/README.md
CHANGED
|
@@ -109,6 +109,7 @@ async function processOrder(orderId) {
|
|
|
109
109
|
| `sampleRate` | number | 1.0 | Sample rate (0.0 to 1.0) |
|
|
110
110
|
| `instrumentHttp` | boolean | true | Auto-instrument HTTP |
|
|
111
111
|
| `instrumentExpress` | boolean | true | Auto-instrument Express |
|
|
112
|
+
| `instrumentDatabase` | boolean | false | 🔥 **Auto-instrument MySQL/MySQL2/Prisma/Postgres/MongoDB** |
|
|
112
113
|
| `batchSize` | number | 100 | Batch size before flushing |
|
|
113
114
|
| `flushInterval` | number | 10000 | Flush interval in ms |
|
|
114
115
|
| `debug` | boolean | false | Enable debug logging |
|
|
@@ -117,6 +118,7 @@ async function processOrder(orderId) {
|
|
|
117
118
|
|
|
118
119
|
- ✅ Automatic HTTP request/response tracing
|
|
119
120
|
- ✅ Express middleware for automatic route tracing
|
|
121
|
+
- ✅ **Database query instrumentation (MySQL, MySQL2, TypeORM, Prisma, Postgres, MongoDB)**
|
|
120
122
|
- ✅ Manual span creation
|
|
121
123
|
- ✅ Distributed tracing with trace ID propagation
|
|
122
124
|
- ✅ Error tracking and reporting
|
|
@@ -124,13 +126,142 @@ async function processOrder(orderId) {
|
|
|
124
126
|
- ✅ Async context propagation
|
|
125
127
|
- ✅ Service discovery
|
|
126
128
|
|
|
129
|
+
## Database Monitoring
|
|
130
|
+
|
|
131
|
+
Enable automatic database query instrumentation to track slow queries, errors, and performance:
|
|
132
|
+
|
|
133
|
+
### MySQL/MySQL2 (Including TypeORM)
|
|
134
|
+
|
|
135
|
+
```javascript
|
|
136
|
+
const { init, startAgent } = require('@devskin/agent');
|
|
137
|
+
|
|
138
|
+
// Initialize BEFORE requiring database modules
|
|
139
|
+
init({
|
|
140
|
+
serverUrl: 'http://localhost:3060',
|
|
141
|
+
apiKey: 'your-api-key',
|
|
142
|
+
applicationId: 'your-app-id',
|
|
143
|
+
serviceName: 'my-api',
|
|
144
|
+
instrumentDatabase: true, // 🔥 Enable database instrumentation
|
|
145
|
+
debug: false
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
startAgent();
|
|
149
|
+
|
|
150
|
+
// Now require your database modules
|
|
151
|
+
const mysql = require('mysql2/promise');
|
|
152
|
+
const { DataSource } = require('typeorm');
|
|
153
|
+
|
|
154
|
+
// All queries will be automatically traced
|
|
155
|
+
const connection = await mysql.createConnection({
|
|
156
|
+
host: 'localhost',
|
|
157
|
+
user: 'root',
|
|
158
|
+
database: 'mydb'
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
const [rows] = await connection.execute('SELECT * FROM users WHERE id = ?', [123]);
|
|
162
|
+
// ✅ Query automatically captured with execution time, errors, row count
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
### With TypeORM
|
|
166
|
+
|
|
167
|
+
```javascript
|
|
168
|
+
const { init, startAgent } = require('@devskin/agent');
|
|
169
|
+
|
|
170
|
+
// IMPORTANT: Initialize agent BEFORE TypeORM
|
|
171
|
+
init({
|
|
172
|
+
serverUrl: 'http://localhost:3060',
|
|
173
|
+
apiKey: 'your-api-key',
|
|
174
|
+
applicationId: 'your-app-id',
|
|
175
|
+
serviceName: 'my-api',
|
|
176
|
+
instrumentDatabase: true,
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
startAgent();
|
|
180
|
+
|
|
181
|
+
// Now initialize TypeORM
|
|
182
|
+
const { DataSource } = require('typeorm');
|
|
183
|
+
|
|
184
|
+
const AppDataSource = new DataSource({
|
|
185
|
+
type: 'mysql',
|
|
186
|
+
host: 'localhost',
|
|
187
|
+
port: 3306,
|
|
188
|
+
username: 'root',
|
|
189
|
+
password: 'password',
|
|
190
|
+
database: 'mydb',
|
|
191
|
+
entities: [User],
|
|
192
|
+
synchronize: false,
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
await AppDataSource.initialize();
|
|
196
|
+
|
|
197
|
+
// All TypeORM queries are automatically traced
|
|
198
|
+
const users = await AppDataSource.getRepository(User).find();
|
|
199
|
+
// ✅ Captured: query text, execution time, rows affected, errors
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
### With Prisma
|
|
203
|
+
|
|
204
|
+
```javascript
|
|
205
|
+
const { init, startAgent } = require('@devskin/agent');
|
|
206
|
+
|
|
207
|
+
// IMPORTANT: Initialize agent BEFORE creating PrismaClient
|
|
208
|
+
init({
|
|
209
|
+
serverUrl: 'http://localhost:3060',
|
|
210
|
+
apiKey: 'your-api-key',
|
|
211
|
+
applicationId: 'your-app-id',
|
|
212
|
+
serviceName: 'my-api',
|
|
213
|
+
instrumentDatabase: true, // 🔥 Enable Prisma instrumentation
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
startAgent();
|
|
217
|
+
|
|
218
|
+
// Now create PrismaClient
|
|
219
|
+
const { PrismaClient } = require('@prisma/client');
|
|
220
|
+
const prisma = new PrismaClient();
|
|
221
|
+
|
|
222
|
+
// All Prisma queries are automatically traced via middleware
|
|
223
|
+
const users = await prisma.user.findMany({
|
|
224
|
+
where: { status: 'active' },
|
|
225
|
+
include: { posts: true }
|
|
226
|
+
});
|
|
227
|
+
// ✅ Captured: operation name (findMany), model (User), args, execution time, errors
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
**Important**: The agent automatically adds middleware to ALL PrismaClient instances created after `init()`. You don't need to manually add `$use()` middleware.
|
|
231
|
+
|
|
232
|
+
### What Gets Tracked
|
|
233
|
+
|
|
234
|
+
Each database query span includes:
|
|
235
|
+
- **Query text**: Full SQL statement (truncated for security)
|
|
236
|
+
- **Query type**: SELECT, INSERT, UPDATE, DELETE, etc.
|
|
237
|
+
- **Database name**: Which database was queried
|
|
238
|
+
- **Execution time**: How long the query took (in ms)
|
|
239
|
+
- **Rows affected**: Number of rows returned/modified
|
|
240
|
+
- **Errors**: Stack trace if query failed
|
|
241
|
+
- **Connection info**: Host, port, user (password excluded)
|
|
242
|
+
|
|
243
|
+
### View Database Monitoring
|
|
244
|
+
|
|
245
|
+
After deploying your instrumented app, view query analytics at:
|
|
246
|
+
```
|
|
247
|
+
http://localhost:5173/apm/database
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
Features:
|
|
251
|
+
- 📊 Slowest queries with P95/P99 percentiles
|
|
252
|
+
- 🔍 Query grouping and normalization
|
|
253
|
+
- ⚠️ Error tracking per query
|
|
254
|
+
- 📈 Query execution trends over time
|
|
255
|
+
- 🔥 Highlighting queries slower than 1 second
|
|
256
|
+
|
|
127
257
|
## Best Practices
|
|
128
258
|
|
|
129
|
-
1. **Initialize early**: Call `init()` and `startAgent()`
|
|
259
|
+
1. **Initialize early**: Call `init()` and `startAgent()` BEFORE requiring database modules
|
|
130
260
|
2. **Use Express middleware**: For Express apps, use the provided middleware for automatic instrumentation
|
|
131
261
|
3. **Sample in production**: Set `sampleRate` to a value < 1.0 in high-traffic production environments
|
|
132
262
|
4. **Add context**: Use `span.setAttribute()` to add business context to your traces
|
|
133
263
|
5. **Graceful shutdown**: Call `stopAgent()` before your application exits
|
|
264
|
+
6. **Database instrumentation**: Enable `instrumentDatabase: true` to track slow queries and errors
|
|
134
265
|
|
|
135
266
|
## Environment Variables
|
|
136
267
|
|
package/dist/agent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAQ9E,qBAAa,KAAK;IAChB,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,UAAU,CAAc;IAChC,OAAO,CAAC,iBAAiB,CAAqB;IAC9C,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,WAAW,CAAmB;IACtC,OAAO,CAAC,UAAU,CAAC,CAAiB;IACpC,OAAO,CAAC,WAAW,CAAS;gBAEhB,MAAM,EAAE,WAAW;IAkCzB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAmCtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAuBb,uBAAuB;YAYvB,2BAA2B;
|
|
1
|
+
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,WAAW,EAAE,QAAQ,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAQ9E,qBAAa,KAAK;IAChB,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,SAAS,CAAY;IAC7B,OAAO,CAAC,UAAU,CAAc;IAChC,OAAO,CAAC,iBAAiB,CAAqB;IAC9C,OAAO,CAAC,SAAS,CAAkB;IACnC,OAAO,CAAC,WAAW,CAAmB;IACtC,OAAO,CAAC,UAAU,CAAC,CAAiB;IACpC,OAAO,CAAC,WAAW,CAAS;gBAEhB,MAAM,EAAE,WAAW;IAkCzB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAmCtB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAuBb,uBAAuB;YAYvB,2BAA2B;YAoC3B,mBAAmB;IAuBjC,UAAU,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAa5B,iBAAiB,CAAC,WAAW,EAAE,WAAW,GAAG,IAAI;IAajD,SAAS,CAAC,GAAG,EAAE,QAAQ,GAAG,IAAI;IAa9B,WAAW,CAAC,KAAK,EAAE,SAAS,GAAG,IAAI;IAa7B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IA8B5B,SAAS,IAAI,WAAW;IAOxB,YAAY,IAAI,OAAO;CAGxB;AAUD,wBAAgB,IAAI,CAAC,MAAM,EAAE,WAAW,GAAG,KAAK,CAQ/C;AAKD,wBAAgB,QAAQ,IAAI,KAAK,GAAG,IAAI,CAEvC;AAKD,wBAAsB,UAAU,IAAI,OAAO,CAAC,IAAI,CAAC,CAKhD;AAKD,wBAAsB,SAAS,IAAI,OAAO,CAAC,IAAI,CAAC,CAI/C"}
|
package/dist/agent.js
CHANGED
|
@@ -120,6 +120,8 @@ class Agent {
|
|
|
120
120
|
instrumentMysql(this);
|
|
121
121
|
const { instrumentPostgres } = require('./instrumentation/postgres');
|
|
122
122
|
instrumentPostgres(this);
|
|
123
|
+
const { instrumentPrisma } = require('./instrumentation/prisma');
|
|
124
|
+
instrumentPrisma(this);
|
|
123
125
|
const { instrumentMongoDB } = require('./instrumentation/mongodb');
|
|
124
126
|
instrumentMongoDB(this);
|
|
125
127
|
const { instrumentRedis } = require('./instrumentation/redis');
|
package/dist/agent.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"agent.js","sourceRoot":"","sources":["../src/agent.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
CHANGED
|
@@ -7,6 +7,7 @@ export * from './utils/id-generator';
|
|
|
7
7
|
export { expressMiddleware, expressErrorHandler } from './instrumentation/express';
|
|
8
8
|
export { instrumentMysql } from './instrumentation/mysql';
|
|
9
9
|
export { instrumentPostgres } from './instrumentation/postgres';
|
|
10
|
+
export { instrumentPrisma } from './instrumentation/prisma';
|
|
10
11
|
export { init, getAgent, startAgent, stopAgent } from './agent';
|
|
11
12
|
export { SpanBuilder, TransactionBuilder } from './span';
|
|
12
13
|
export { Context } from './utils/context';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAqBA,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAqBA,cAAc,SAAS,CAAC;AACxB,cAAc,SAAS,CAAC;AACxB,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,iBAAiB,CAAC;AAChC,cAAc,sBAAsB,CAAC;AACrC,OAAO,EAAE,iBAAiB,EAAE,mBAAmB,EAAE,MAAM,2BAA2B,CAAC;AACnF,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAC1D,OAAO,EAAE,kBAAkB,EAAE,MAAM,4BAA4B,CAAC;AAChE,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAG5D,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAChE,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,MAAM,QAAQ,CAAC;AACzD,OAAO,EAAE,OAAO,EAAE,MAAM,iBAAiB,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -14,7 +14,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
14
14
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
15
15
|
};
|
|
16
16
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
17
|
-
exports.Context = exports.TransactionBuilder = exports.SpanBuilder = exports.stopAgent = exports.startAgent = exports.getAgent = exports.init = exports.instrumentPostgres = exports.instrumentMysql = exports.expressErrorHandler = exports.expressMiddleware = void 0;
|
|
17
|
+
exports.Context = exports.TransactionBuilder = exports.SpanBuilder = exports.stopAgent = exports.startAgent = exports.getAgent = exports.init = exports.instrumentPrisma = exports.instrumentPostgres = exports.instrumentMysql = exports.expressErrorHandler = exports.expressMiddleware = void 0;
|
|
18
18
|
__exportStar(require("./types"), exports);
|
|
19
19
|
__exportStar(require("./agent"), exports);
|
|
20
20
|
__exportStar(require("./span"), exports);
|
|
@@ -28,6 +28,8 @@ var mysql_1 = require("./instrumentation/mysql");
|
|
|
28
28
|
Object.defineProperty(exports, "instrumentMysql", { enumerable: true, get: function () { return mysql_1.instrumentMysql; } });
|
|
29
29
|
var postgres_1 = require("./instrumentation/postgres");
|
|
30
30
|
Object.defineProperty(exports, "instrumentPostgres", { enumerable: true, get: function () { return postgres_1.instrumentPostgres; } });
|
|
31
|
+
var prisma_1 = require("./instrumentation/prisma");
|
|
32
|
+
Object.defineProperty(exports, "instrumentPrisma", { enumerable: true, get: function () { return prisma_1.instrumentPrisma; } });
|
|
31
33
|
var agent_1 = require("./agent");
|
|
32
34
|
Object.defineProperty(exports, "init", { enumerable: true, get: function () { return agent_1.init; } });
|
|
33
35
|
Object.defineProperty(exports, "getAgent", { enumerable: true, get: function () { return agent_1.getAgent; } });
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAqBA,0CAAwB;AACxB,0CAAwB;AACxB,yCAAuB;AACvB,+CAA6B;AAC7B,kDAAgC;AAChC,uDAAqC;AACrC,qDAAmF;AAA1E,4GAAA,iBAAiB,OAAA;AAAE,8GAAA,mBAAmB,OAAA;AAC/C,iDAA0D;AAAjD,wGAAA,eAAe,OAAA;AACxB,uDAAgE;AAAvD,8GAAA,kBAAkB,OAAA;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";;;;;;;;;;;;;;;;;AAqBA,0CAAwB;AACxB,0CAAwB;AACxB,yCAAuB;AACvB,+CAA6B;AAC7B,kDAAgC;AAChC,uDAAqC;AACrC,qDAAmF;AAA1E,4GAAA,iBAAiB,OAAA;AAAE,8GAAA,mBAAmB,OAAA;AAC/C,iDAA0D;AAAjD,wGAAA,eAAe,OAAA;AACxB,uDAAgE;AAAvD,8GAAA,kBAAkB,OAAA;AAC3B,mDAA4D;AAAnD,0GAAA,gBAAgB,OAAA;AAGzB,iCAAgE;AAAvD,6FAAA,IAAI,OAAA;AAAE,iGAAA,QAAQ,OAAA;AAAE,mGAAA,UAAU,OAAA;AAAE,kGAAA,SAAS,OAAA;AAC9C,+BAAyD;AAAhD,mGAAA,WAAW,OAAA;AAAE,0GAAA,kBAAkB,OAAA;AACxC,2CAA0C;AAAjC,kGAAA,OAAO,OAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma.d.ts","sourceRoot":"","sources":["../../src/instrumentation/prisma.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,UAAU,CAAC;AAOjC,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,KAAK,GAAG,IAAI,CAiCnD"}
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.instrumentPrisma = instrumentPrisma;
|
|
4
|
+
const span_1 = require("../span");
|
|
5
|
+
const types_1 = require("../types");
|
|
6
|
+
function instrumentPrisma(agent) {
|
|
7
|
+
try {
|
|
8
|
+
const Module = require('module');
|
|
9
|
+
const originalRequire = Module.prototype.require;
|
|
10
|
+
Module.prototype.require = function (id) {
|
|
11
|
+
const module = originalRequire.apply(this, arguments);
|
|
12
|
+
if (id === '@prisma/client' && module.PrismaClient && !module.__devskin_instrumented) {
|
|
13
|
+
instrumentPrismaClient(agent, module);
|
|
14
|
+
module.__devskin_instrumented = true;
|
|
15
|
+
}
|
|
16
|
+
return module;
|
|
17
|
+
};
|
|
18
|
+
try {
|
|
19
|
+
const prismaModule = require('@prisma/client');
|
|
20
|
+
if (prismaModule && prismaModule.PrismaClient && !prismaModule.__devskin_instrumented) {
|
|
21
|
+
instrumentPrismaClient(agent, prismaModule);
|
|
22
|
+
prismaModule.__devskin_instrumented = true;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
catch (e) {
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
catch (error) {
|
|
29
|
+
if (agent.getConfig().debug) {
|
|
30
|
+
console.error('[DevSkin Agent] Error instrumenting Prisma:', error.message);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
function instrumentPrismaClient(agent, prismaModule) {
|
|
35
|
+
const originalPrismaClient = prismaModule.PrismaClient;
|
|
36
|
+
prismaModule.PrismaClient = function (...args) {
|
|
37
|
+
const client = new originalPrismaClient(...args);
|
|
38
|
+
client.$use(async (params, next) => {
|
|
39
|
+
const config = agent.getConfig();
|
|
40
|
+
const span = new span_1.SpanBuilder(`prisma.${params.model}.${params.action}`, types_1.SpanKind.CLIENT, config.serviceName, config.serviceVersion, config.environment, agent);
|
|
41
|
+
span.setAttribute('db.system', 'prisma');
|
|
42
|
+
span.setAttribute('db.operation', params.action);
|
|
43
|
+
if (params.model) {
|
|
44
|
+
span.setAttribute('db.sql.table', params.model);
|
|
45
|
+
}
|
|
46
|
+
if (params.args) {
|
|
47
|
+
const safeArgs = sanitizePrismaArgs(params.args);
|
|
48
|
+
if (Object.keys(safeArgs).length > 0) {
|
|
49
|
+
span.setAttribute('db.prisma.args', JSON.stringify(safeArgs).substring(0, 1000));
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
try {
|
|
53
|
+
const result = await next(params);
|
|
54
|
+
span.setStatus(types_1.SpanStatus.OK);
|
|
55
|
+
if (Array.isArray(result)) {
|
|
56
|
+
span.setAttribute('db.rows_affected', result.length);
|
|
57
|
+
}
|
|
58
|
+
return result;
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
span.setStatus(types_1.SpanStatus.ERROR);
|
|
62
|
+
span.recordError(error);
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
span.end();
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
return client;
|
|
70
|
+
};
|
|
71
|
+
Object.setPrototypeOf(prismaModule.PrismaClient, originalPrismaClient);
|
|
72
|
+
Object.setPrototypeOf(prismaModule.PrismaClient.prototype, originalPrismaClient.prototype);
|
|
73
|
+
}
|
|
74
|
+
function sanitizePrismaArgs(args) {
|
|
75
|
+
if (!args || typeof args !== 'object') {
|
|
76
|
+
return {};
|
|
77
|
+
}
|
|
78
|
+
const sanitized = {};
|
|
79
|
+
const safeFields = ['where', 'select', 'include', 'orderBy', 'take', 'skip'];
|
|
80
|
+
for (const field of safeFields) {
|
|
81
|
+
if (args[field] !== undefined) {
|
|
82
|
+
sanitized[field] = sanitizeValue(args[field]);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return sanitized;
|
|
86
|
+
}
|
|
87
|
+
function sanitizeValue(value, depth = 0) {
|
|
88
|
+
if (depth > 3)
|
|
89
|
+
return '[nested]';
|
|
90
|
+
if (value === null || value === undefined)
|
|
91
|
+
return value;
|
|
92
|
+
if (Array.isArray(value)) {
|
|
93
|
+
return value.slice(0, 5).map(v => sanitizeValue(v, depth + 1));
|
|
94
|
+
}
|
|
95
|
+
if (typeof value === 'object') {
|
|
96
|
+
const sanitized = {};
|
|
97
|
+
for (const key in value) {
|
|
98
|
+
if (key.toLowerCase().includes('password') || key.toLowerCase().includes('token') || key.toLowerCase().includes('secret')) {
|
|
99
|
+
sanitized[key] = '[REDACTED]';
|
|
100
|
+
}
|
|
101
|
+
else {
|
|
102
|
+
sanitized[key] = sanitizeValue(value[key], depth + 1);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return sanitized;
|
|
106
|
+
}
|
|
107
|
+
return value;
|
|
108
|
+
}
|
|
109
|
+
//# sourceMappingURL=prisma.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"prisma.js","sourceRoot":"","sources":["../../src/instrumentation/prisma.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/src/agent.ts
CHANGED
|
@@ -129,6 +129,10 @@ export class Agent {
|
|
|
129
129
|
const { instrumentPostgres } = require('./instrumentation/postgres');
|
|
130
130
|
instrumentPostgres(this);
|
|
131
131
|
|
|
132
|
+
// ORM Instrumentation
|
|
133
|
+
const { instrumentPrisma } = require('./instrumentation/prisma');
|
|
134
|
+
instrumentPrisma(this);
|
|
135
|
+
|
|
132
136
|
// NoSQL Databases
|
|
133
137
|
const { instrumentMongoDB } = require('./instrumentation/mongodb');
|
|
134
138
|
instrumentMongoDB(this);
|
package/src/index.ts
CHANGED
|
@@ -28,6 +28,7 @@ export * from './utils/id-generator';
|
|
|
28
28
|
export { expressMiddleware, expressErrorHandler } from './instrumentation/express';
|
|
29
29
|
export { instrumentMysql } from './instrumentation/mysql';
|
|
30
30
|
export { instrumentPostgres } from './instrumentation/postgres';
|
|
31
|
+
export { instrumentPrisma } from './instrumentation/prisma';
|
|
31
32
|
|
|
32
33
|
// Re-export commonly used functions
|
|
33
34
|
export { init, getAgent, startAgent, stopAgent } from './agent';
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
import { Agent } from '../agent';
|
|
2
|
+
import { SpanBuilder } from '../span';
|
|
3
|
+
import { SpanKind, SpanStatus } from '../types';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Instrument Prisma Client for database monitoring
|
|
7
|
+
*/
|
|
8
|
+
export function instrumentPrisma(agent: Agent): void {
|
|
9
|
+
try {
|
|
10
|
+
// Hook into Node's require system to intercept @prisma/client loads
|
|
11
|
+
const Module = require('module');
|
|
12
|
+
const originalRequire = Module.prototype.require;
|
|
13
|
+
|
|
14
|
+
Module.prototype.require = function (id: string) {
|
|
15
|
+
const module = originalRequire.apply(this, arguments);
|
|
16
|
+
|
|
17
|
+
// Instrument @prisma/client when it's loaded
|
|
18
|
+
if (id === '@prisma/client' && module.PrismaClient && !module.__devskin_instrumented) {
|
|
19
|
+
instrumentPrismaClient(agent, module);
|
|
20
|
+
module.__devskin_instrumented = true;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return module;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
// Also try to instrument if already loaded
|
|
27
|
+
try {
|
|
28
|
+
const prismaModule = require('@prisma/client');
|
|
29
|
+
if (prismaModule && prismaModule.PrismaClient && !prismaModule.__devskin_instrumented) {
|
|
30
|
+
instrumentPrismaClient(agent, prismaModule);
|
|
31
|
+
prismaModule.__devskin_instrumented = true;
|
|
32
|
+
}
|
|
33
|
+
} catch (e) {
|
|
34
|
+
// @prisma/client not yet loaded or not installed
|
|
35
|
+
}
|
|
36
|
+
} catch (error: any) {
|
|
37
|
+
if (agent.getConfig().debug) {
|
|
38
|
+
console.error('[DevSkin Agent] Error instrumenting Prisma:', error.message);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Instrument Prisma Client middleware
|
|
45
|
+
*/
|
|
46
|
+
function instrumentPrismaClient(agent: Agent, prismaModule: any): void {
|
|
47
|
+
const originalPrismaClient = prismaModule.PrismaClient;
|
|
48
|
+
|
|
49
|
+
// Wrap PrismaClient constructor to add middleware automatically
|
|
50
|
+
prismaModule.PrismaClient = function (...args: any[]) {
|
|
51
|
+
const client = new originalPrismaClient(...args);
|
|
52
|
+
|
|
53
|
+
// Add middleware to intercept queries
|
|
54
|
+
client.$use(async (params: any, next: any) => {
|
|
55
|
+
const config = agent.getConfig();
|
|
56
|
+
const span = new SpanBuilder(
|
|
57
|
+
`prisma.${params.model}.${params.action}`,
|
|
58
|
+
SpanKind.CLIENT,
|
|
59
|
+
config.serviceName,
|
|
60
|
+
config.serviceVersion,
|
|
61
|
+
config.environment,
|
|
62
|
+
agent
|
|
63
|
+
);
|
|
64
|
+
|
|
65
|
+
// Add database attributes
|
|
66
|
+
span.setAttribute('db.system', 'prisma');
|
|
67
|
+
span.setAttribute('db.operation', params.action);
|
|
68
|
+
if (params.model) {
|
|
69
|
+
span.setAttribute('db.sql.table', params.model);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// Add query parameters (safely, without sensitive data)
|
|
73
|
+
if (params.args) {
|
|
74
|
+
const safeArgs = sanitizePrismaArgs(params.args);
|
|
75
|
+
if (Object.keys(safeArgs).length > 0) {
|
|
76
|
+
span.setAttribute('db.prisma.args', JSON.stringify(safeArgs).substring(0, 1000));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
try {
|
|
81
|
+
const result = await next(params);
|
|
82
|
+
span.setStatus(SpanStatus.OK);
|
|
83
|
+
|
|
84
|
+
// Add result metadata if available
|
|
85
|
+
if (Array.isArray(result)) {
|
|
86
|
+
span.setAttribute('db.rows_affected', result.length);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
return result;
|
|
90
|
+
} catch (error: any) {
|
|
91
|
+
span.setStatus(SpanStatus.ERROR);
|
|
92
|
+
span.recordError(error);
|
|
93
|
+
throw error;
|
|
94
|
+
} finally {
|
|
95
|
+
span.end();
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
return client;
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
// Copy over static properties
|
|
103
|
+
Object.setPrototypeOf(prismaModule.PrismaClient, originalPrismaClient);
|
|
104
|
+
Object.setPrototypeOf(prismaModule.PrismaClient.prototype, originalPrismaClient.prototype);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Sanitize Prisma args to remove sensitive data
|
|
109
|
+
*/
|
|
110
|
+
function sanitizePrismaArgs(args: any): any {
|
|
111
|
+
if (!args || typeof args !== 'object') {
|
|
112
|
+
return {};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
const sanitized: any = {};
|
|
116
|
+
|
|
117
|
+
// Include safe fields like where, select, include, orderBy
|
|
118
|
+
const safeFields = ['where', 'select', 'include', 'orderBy', 'take', 'skip'];
|
|
119
|
+
for (const field of safeFields) {
|
|
120
|
+
if (args[field] !== undefined) {
|
|
121
|
+
sanitized[field] = sanitizeValue(args[field]);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return sanitized;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Sanitize values to prevent sensitive data leakage
|
|
130
|
+
*/
|
|
131
|
+
function sanitizeValue(value: any, depth = 0): any {
|
|
132
|
+
if (depth > 3) return '[nested]';
|
|
133
|
+
|
|
134
|
+
if (value === null || value === undefined) return value;
|
|
135
|
+
|
|
136
|
+
if (Array.isArray(value)) {
|
|
137
|
+
return value.slice(0, 5).map(v => sanitizeValue(v, depth + 1));
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if (typeof value === 'object') {
|
|
141
|
+
const sanitized: any = {};
|
|
142
|
+
for (const key in value) {
|
|
143
|
+
// Skip password and sensitive fields
|
|
144
|
+
if (key.toLowerCase().includes('password') || key.toLowerCase().includes('token') || key.toLowerCase().includes('secret')) {
|
|
145
|
+
sanitized[key] = '[REDACTED]';
|
|
146
|
+
} else {
|
|
147
|
+
sanitized[key] = sanitizeValue(value[key], depth + 1);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return sanitized;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return value;
|
|
154
|
+
}
|