@drawbridge/drawbridge-telemetry 0.0.2 → 0.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 +3 -5
- package/dist/bullmq.cjs +4 -3
- package/dist/bullmq.d.cts +11 -8
- package/dist/bullmq.d.ts +11 -8
- package/dist/bullmq.js +4 -3
- package/dist/express.cjs +5 -6
- package/dist/express.d.cts +16 -11
- package/dist/express.d.ts +16 -11
- package/dist/express.js +5 -6
- package/package.json +8 -7
package/README.md
CHANGED
|
@@ -21,8 +21,6 @@ Requires `@sentry/core` >= 10 as a peer dependency. Backend services satisfy thi
|
|
|
21
21
|
|
|
22
22
|
## Naming conventions
|
|
23
23
|
|
|
24
|
-
Codified in the plan at `~/.claude/plans/yes-lets-see-it-enumerated-bumblebee.md`. Summary:
|
|
25
|
-
|
|
26
24
|
- **Event names** — `<noun>.<verb>` past tense, lowercase, dot-separated. Verbs match drawbridge-api's action status vocabulary (`processing` / `succeeded` / `failed`; never `pending` / `completed`). Nouns are singular and match Mongo collection names.
|
|
27
25
|
- **Tag keys** — camelCase. Canonical `traceId` everywhere; `X-Request-Id` header stays for wire compat.
|
|
28
26
|
- **Event envelope** — `{ name, traceId, userId, organizationId, data: {...}, createdAt }`.
|
|
@@ -52,10 +50,10 @@ Sentry events for request BullMQ worker
|
|
|
52
50
|
|
|
53
51
|
For cron-style repeatable BullMQ jobs (no parent request or change event), synthesize a `__traceId` at enqueue time — pass it via `data.__traceId` to `enqueueFromWorker`.
|
|
54
52
|
|
|
55
|
-
## Build
|
|
53
|
+
## Build & publish
|
|
56
54
|
|
|
57
55
|
```sh
|
|
58
|
-
npm run build
|
|
56
|
+
npm run build # tsup + npm publish
|
|
59
57
|
```
|
|
60
58
|
|
|
61
|
-
|
|
59
|
+
Bump `version` in `package.json` before publishing.
|
package/dist/bullmq.cjs
CHANGED
|
@@ -34,6 +34,7 @@ __export(bullmq_exports, {
|
|
|
34
34
|
wrapWorkerHandler: () => wrapWorkerHandler
|
|
35
35
|
});
|
|
36
36
|
module.exports = __toCommonJS(bullmq_exports);
|
|
37
|
+
var import_crypto = require("crypto");
|
|
37
38
|
var Sentry2 = __toESM(require("@sentry/core"), 1);
|
|
38
39
|
|
|
39
40
|
// index.js
|
|
@@ -50,9 +51,9 @@ var currentTraceId = () => {
|
|
|
50
51
|
};
|
|
51
52
|
|
|
52
53
|
// bullmq.js
|
|
53
|
-
var wrapWorkerHandler = (handler) => async (
|
|
54
|
+
var wrapWorkerHandler = (handler) => async (job) => {
|
|
54
55
|
var _a;
|
|
55
|
-
const traceId = (_a = job == null ? void 0 : job.data) == null ? void 0 : _a.__traceId;
|
|
56
|
+
const traceId = ((_a = job == null ? void 0 : job.data) == null ? void 0 : _a.__traceId) || (0, import_crypto.randomUUID)();
|
|
56
57
|
return withTraceScope(traceId, (scope) => {
|
|
57
58
|
if (job == null ? void 0 : job.queueName) {
|
|
58
59
|
scope.setTag("queue", job.queueName);
|
|
@@ -62,7 +63,7 @@ var wrapWorkerHandler = (handler) => async (jobData, job) => {
|
|
|
62
63
|
scope.setTag("jobName", job.name);
|
|
63
64
|
}
|
|
64
65
|
;
|
|
65
|
-
return handler(
|
|
66
|
+
return handler(job);
|
|
66
67
|
});
|
|
67
68
|
};
|
|
68
69
|
var enqueueFromWorker = async (queue, name, data, options = {}) => {
|
package/dist/bullmq.d.cts
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
|
+
import { randomUUID } from 'crypto';
|
|
1
2
|
import { currentTraceId, withTraceScope } from './index.cjs';
|
|
2
3
|
import '@sentry/core';
|
|
3
4
|
|
|
4
5
|
// Wrap a BullMQ worker handler so it runs inside a Sentry scope tagged
|
|
5
|
-
// with the job's traceId, queue name, and job name.
|
|
6
|
-
//
|
|
7
|
-
//
|
|
6
|
+
// with the job's traceId, queue name, and job name. Matches BullMQ's
|
|
7
|
+
// native single-arg `( job )` callback shape — pass it directly to
|
|
8
|
+
// `new Worker( name, wrapWorkerHandler( handler ), ... )`.
|
|
8
9
|
//
|
|
9
|
-
// The traceId originates from one of
|
|
10
|
+
// The traceId originates from one of three places:
|
|
10
11
|
// - HTTP request: the API's req.id is forwarded as job.data.__traceId
|
|
11
12
|
// - Change stream: the Mongo resume token (_id._data) is forwarded
|
|
12
|
-
//
|
|
13
|
+
// - Synthesized: any job arriving with no __traceId gets a fresh UUID,
|
|
14
|
+
// so cron / repeatable jobs (no parent context) still produce
|
|
15
|
+
// correlated downstream events.
|
|
13
16
|
|
|
14
|
-
const wrapWorkerHandler = ( handler ) => async (
|
|
17
|
+
const wrapWorkerHandler = ( handler ) => async ( job ) => {
|
|
15
18
|
|
|
16
|
-
const traceId = job?.data?.__traceId;
|
|
19
|
+
const traceId = job?.data?.__traceId || randomUUID();
|
|
17
20
|
|
|
18
21
|
return withTraceScope( traceId, ( scope ) => {
|
|
19
22
|
|
|
@@ -27,7 +30,7 @@ const wrapWorkerHandler = ( handler ) => async ( jobData, job ) => {
|
|
|
27
30
|
scope.setTag( 'jobName', job.name );
|
|
28
31
|
|
|
29
32
|
}
|
|
30
|
-
return handler(
|
|
33
|
+
return handler( job );
|
|
31
34
|
|
|
32
35
|
} );
|
|
33
36
|
|
package/dist/bullmq.d.ts
CHANGED
|
@@ -1,19 +1,22 @@
|
|
|
1
|
+
import { randomUUID } from 'crypto';
|
|
1
2
|
import { currentTraceId, withTraceScope } from './index.js';
|
|
2
3
|
import '@sentry/core';
|
|
3
4
|
|
|
4
5
|
// Wrap a BullMQ worker handler so it runs inside a Sentry scope tagged
|
|
5
|
-
// with the job's traceId, queue name, and job name.
|
|
6
|
-
//
|
|
7
|
-
//
|
|
6
|
+
// with the job's traceId, queue name, and job name. Matches BullMQ's
|
|
7
|
+
// native single-arg `( job )` callback shape — pass it directly to
|
|
8
|
+
// `new Worker( name, wrapWorkerHandler( handler ), ... )`.
|
|
8
9
|
//
|
|
9
|
-
// The traceId originates from one of
|
|
10
|
+
// The traceId originates from one of three places:
|
|
10
11
|
// - HTTP request: the API's req.id is forwarded as job.data.__traceId
|
|
11
12
|
// - Change stream: the Mongo resume token (_id._data) is forwarded
|
|
12
|
-
//
|
|
13
|
+
// - Synthesized: any job arriving with no __traceId gets a fresh UUID,
|
|
14
|
+
// so cron / repeatable jobs (no parent context) still produce
|
|
15
|
+
// correlated downstream events.
|
|
13
16
|
|
|
14
|
-
const wrapWorkerHandler = ( handler ) => async (
|
|
17
|
+
const wrapWorkerHandler = ( handler ) => async ( job ) => {
|
|
15
18
|
|
|
16
|
-
const traceId = job?.data?.__traceId;
|
|
19
|
+
const traceId = job?.data?.__traceId || randomUUID();
|
|
17
20
|
|
|
18
21
|
return withTraceScope( traceId, ( scope ) => {
|
|
19
22
|
|
|
@@ -27,7 +30,7 @@ const wrapWorkerHandler = ( handler ) => async ( jobData, job ) => {
|
|
|
27
30
|
scope.setTag( 'jobName', job.name );
|
|
28
31
|
|
|
29
32
|
}
|
|
30
|
-
return handler(
|
|
33
|
+
return handler( job );
|
|
31
34
|
|
|
32
35
|
} );
|
|
33
36
|
|
package/dist/bullmq.js
CHANGED
|
@@ -4,10 +4,11 @@ import {
|
|
|
4
4
|
} from "./chunk-SRV5HFQT.js";
|
|
5
5
|
|
|
6
6
|
// bullmq.js
|
|
7
|
+
import { randomUUID } from "crypto";
|
|
7
8
|
import * as Sentry from "@sentry/core";
|
|
8
|
-
var wrapWorkerHandler = (handler) => async (
|
|
9
|
+
var wrapWorkerHandler = (handler) => async (job) => {
|
|
9
10
|
var _a;
|
|
10
|
-
const traceId = (_a = job == null ? void 0 : job.data) == null ? void 0 : _a.__traceId;
|
|
11
|
+
const traceId = ((_a = job == null ? void 0 : job.data) == null ? void 0 : _a.__traceId) || randomUUID();
|
|
11
12
|
return withTraceScope(traceId, (scope) => {
|
|
12
13
|
if (job == null ? void 0 : job.queueName) {
|
|
13
14
|
scope.setTag("queue", job.queueName);
|
|
@@ -17,7 +18,7 @@ var wrapWorkerHandler = (handler) => async (jobData, job) => {
|
|
|
17
18
|
scope.setTag("jobName", job.name);
|
|
18
19
|
}
|
|
19
20
|
;
|
|
20
|
-
return handler(
|
|
21
|
+
return handler(job);
|
|
21
22
|
});
|
|
22
23
|
};
|
|
23
24
|
var enqueueFromWorker = async (queue, name, data, options = {}) => {
|
package/dist/express.cjs
CHANGED
|
@@ -35,29 +35,28 @@ __export(express_exports, {
|
|
|
35
35
|
module.exports = __toCommonJS(express_exports);
|
|
36
36
|
var Sentry = __toESM(require("@sentry/core"), 1);
|
|
37
37
|
var applyRequestContext = (req) => {
|
|
38
|
-
const scope = Sentry.getCurrentScope();
|
|
39
38
|
if (req == null ? void 0 : req.id) {
|
|
40
|
-
|
|
39
|
+
Sentry.setTag("traceId", req.id);
|
|
41
40
|
}
|
|
42
41
|
;
|
|
43
42
|
if (req == null ? void 0 : req.method) {
|
|
44
|
-
|
|
43
|
+
Sentry.setTag("method", req.method);
|
|
45
44
|
}
|
|
46
45
|
;
|
|
47
46
|
if (req == null ? void 0 : req.path) {
|
|
48
|
-
|
|
47
|
+
Sentry.setTag("route", req.path);
|
|
49
48
|
}
|
|
50
49
|
;
|
|
51
50
|
const user = (req == null ? void 0 : req.authenticated) || (req == null ? void 0 : req.user);
|
|
52
51
|
if (user) {
|
|
53
|
-
|
|
52
|
+
Sentry.setUser({
|
|
54
53
|
id: user.id || user._id,
|
|
55
54
|
email: user.email
|
|
56
55
|
});
|
|
57
56
|
}
|
|
58
57
|
;
|
|
59
58
|
if (req == null ? void 0 : req.organization) {
|
|
60
|
-
|
|
59
|
+
Sentry.setTag("organization", req.organization.id || req.organization._id);
|
|
61
60
|
}
|
|
62
61
|
;
|
|
63
62
|
};
|
package/dist/express.d.cts
CHANGED
|
@@ -1,25 +1,30 @@
|
|
|
1
1
|
import * as Sentry from '@sentry/core';
|
|
2
2
|
|
|
3
|
-
// Apply all available context tags from a request to the
|
|
4
|
-
// scope. Idempotent — safe to call multiple times as
|
|
5
|
-
// (e.g. once at request entry for
|
|
6
|
-
// middleware once req.authenticated
|
|
3
|
+
// Apply all available context tags from a request to the per-request
|
|
4
|
+
// Sentry isolation scope. Idempotent — safe to call multiple times as
|
|
5
|
+
// the request enriches (e.g. once at request entry for
|
|
6
|
+
// traceId/method/path, again from auth middleware once req.authenticated
|
|
7
|
+
// and req.organization are populated).
|
|
7
8
|
//
|
|
8
9
|
// Reads from req.authenticated first (drawbridge-api convention) and
|
|
9
10
|
// falls back to req.user for compatibility with other services.
|
|
11
|
+
//
|
|
12
|
+
// Uses Sentry.setTag / Sentry.setUser (which target the isolation scope
|
|
13
|
+
// in v8+) rather than getCurrentScope().setTag, because errors captured
|
|
14
|
+
// later in the request from a different async fork only inherit the
|
|
15
|
+
// isolation scope. Setting tags on the current scope at middleware time
|
|
16
|
+
// can leave them out of the captureException event payload.
|
|
10
17
|
|
|
11
18
|
const applyRequestContext = ( req ) => {
|
|
12
19
|
|
|
13
|
-
const scope = Sentry.getCurrentScope();
|
|
14
|
-
|
|
15
20
|
if( req?.id ){
|
|
16
21
|
|
|
17
|
-
|
|
22
|
+
Sentry.setTag( 'traceId', req.id );
|
|
18
23
|
|
|
19
24
|
}
|
|
20
25
|
if( req?.method ){
|
|
21
26
|
|
|
22
|
-
|
|
27
|
+
Sentry.setTag( 'method', req.method );
|
|
23
28
|
|
|
24
29
|
}
|
|
25
30
|
// req.route is set by Express only AFTER route matching, which hasn't
|
|
@@ -28,14 +33,14 @@ const applyRequestContext = ( req ) => {
|
|
|
28
33
|
// future res.on('finish') refinement.
|
|
29
34
|
if( req?.path ){
|
|
30
35
|
|
|
31
|
-
|
|
36
|
+
Sentry.setTag( 'route', req.path );
|
|
32
37
|
|
|
33
38
|
}
|
|
34
39
|
const user = req?.authenticated || req?.user;
|
|
35
40
|
|
|
36
41
|
if( user ){
|
|
37
42
|
|
|
38
|
-
|
|
43
|
+
Sentry.setUser({
|
|
39
44
|
id : user.id || user._id,
|
|
40
45
|
email : user.email
|
|
41
46
|
});
|
|
@@ -43,7 +48,7 @@ const applyRequestContext = ( req ) => {
|
|
|
43
48
|
}
|
|
44
49
|
if( req?.organization ){
|
|
45
50
|
|
|
46
|
-
|
|
51
|
+
Sentry.setTag( 'organization', req.organization.id || req.organization._id );
|
|
47
52
|
|
|
48
53
|
}
|
|
49
54
|
};
|
package/dist/express.d.ts
CHANGED
|
@@ -1,25 +1,30 @@
|
|
|
1
1
|
import * as Sentry from '@sentry/core';
|
|
2
2
|
|
|
3
|
-
// Apply all available context tags from a request to the
|
|
4
|
-
// scope. Idempotent — safe to call multiple times as
|
|
5
|
-
// (e.g. once at request entry for
|
|
6
|
-
// middleware once req.authenticated
|
|
3
|
+
// Apply all available context tags from a request to the per-request
|
|
4
|
+
// Sentry isolation scope. Idempotent — safe to call multiple times as
|
|
5
|
+
// the request enriches (e.g. once at request entry for
|
|
6
|
+
// traceId/method/path, again from auth middleware once req.authenticated
|
|
7
|
+
// and req.organization are populated).
|
|
7
8
|
//
|
|
8
9
|
// Reads from req.authenticated first (drawbridge-api convention) and
|
|
9
10
|
// falls back to req.user for compatibility with other services.
|
|
11
|
+
//
|
|
12
|
+
// Uses Sentry.setTag / Sentry.setUser (which target the isolation scope
|
|
13
|
+
// in v8+) rather than getCurrentScope().setTag, because errors captured
|
|
14
|
+
// later in the request from a different async fork only inherit the
|
|
15
|
+
// isolation scope. Setting tags on the current scope at middleware time
|
|
16
|
+
// can leave them out of the captureException event payload.
|
|
10
17
|
|
|
11
18
|
const applyRequestContext = ( req ) => {
|
|
12
19
|
|
|
13
|
-
const scope = Sentry.getCurrentScope();
|
|
14
|
-
|
|
15
20
|
if( req?.id ){
|
|
16
21
|
|
|
17
|
-
|
|
22
|
+
Sentry.setTag( 'traceId', req.id );
|
|
18
23
|
|
|
19
24
|
}
|
|
20
25
|
if( req?.method ){
|
|
21
26
|
|
|
22
|
-
|
|
27
|
+
Sentry.setTag( 'method', req.method );
|
|
23
28
|
|
|
24
29
|
}
|
|
25
30
|
// req.route is set by Express only AFTER route matching, which hasn't
|
|
@@ -28,14 +33,14 @@ const applyRequestContext = ( req ) => {
|
|
|
28
33
|
// future res.on('finish') refinement.
|
|
29
34
|
if( req?.path ){
|
|
30
35
|
|
|
31
|
-
|
|
36
|
+
Sentry.setTag( 'route', req.path );
|
|
32
37
|
|
|
33
38
|
}
|
|
34
39
|
const user = req?.authenticated || req?.user;
|
|
35
40
|
|
|
36
41
|
if( user ){
|
|
37
42
|
|
|
38
|
-
|
|
43
|
+
Sentry.setUser({
|
|
39
44
|
id : user.id || user._id,
|
|
40
45
|
email : user.email
|
|
41
46
|
});
|
|
@@ -43,7 +48,7 @@ const applyRequestContext = ( req ) => {
|
|
|
43
48
|
}
|
|
44
49
|
if( req?.organization ){
|
|
45
50
|
|
|
46
|
-
|
|
51
|
+
Sentry.setTag( 'organization', req.organization.id || req.organization._id );
|
|
47
52
|
|
|
48
53
|
}
|
|
49
54
|
};
|
package/dist/express.js
CHANGED
|
@@ -1,29 +1,28 @@
|
|
|
1
1
|
// express.js
|
|
2
2
|
import * as Sentry from "@sentry/core";
|
|
3
3
|
var applyRequestContext = (req) => {
|
|
4
|
-
const scope = Sentry.getCurrentScope();
|
|
5
4
|
if (req == null ? void 0 : req.id) {
|
|
6
|
-
|
|
5
|
+
Sentry.setTag("traceId", req.id);
|
|
7
6
|
}
|
|
8
7
|
;
|
|
9
8
|
if (req == null ? void 0 : req.method) {
|
|
10
|
-
|
|
9
|
+
Sentry.setTag("method", req.method);
|
|
11
10
|
}
|
|
12
11
|
;
|
|
13
12
|
if (req == null ? void 0 : req.path) {
|
|
14
|
-
|
|
13
|
+
Sentry.setTag("route", req.path);
|
|
15
14
|
}
|
|
16
15
|
;
|
|
17
16
|
const user = (req == null ? void 0 : req.authenticated) || (req == null ? void 0 : req.user);
|
|
18
17
|
if (user) {
|
|
19
|
-
|
|
18
|
+
Sentry.setUser({
|
|
20
19
|
id: user.id || user._id,
|
|
21
20
|
email: user.email
|
|
22
21
|
});
|
|
23
22
|
}
|
|
24
23
|
;
|
|
25
24
|
if (req == null ? void 0 : req.organization) {
|
|
26
|
-
|
|
25
|
+
Sentry.setTag("organization", req.organization.id || req.organization._id);
|
|
27
26
|
}
|
|
28
27
|
;
|
|
29
28
|
};
|
package/package.json
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
|
-
"dependencies": {
|
|
4
|
-
"tsup": "8.5.1",
|
|
5
|
-
"typescript": "5.9.3"
|
|
6
|
-
},
|
|
7
3
|
"peerDependencies": {
|
|
8
4
|
"@sentry/core": ">=10"
|
|
9
5
|
},
|
|
@@ -13,7 +9,9 @@
|
|
|
13
9
|
}
|
|
14
10
|
},
|
|
15
11
|
"devDependencies": {
|
|
16
|
-
"@sentry/core": "10.27.0"
|
|
12
|
+
"@sentry/core": "10.27.0",
|
|
13
|
+
"tsup": "8.5.1",
|
|
14
|
+
"typescript": "5.9.3"
|
|
17
15
|
},
|
|
18
16
|
"exports": {
|
|
19
17
|
".": {
|
|
@@ -48,9 +46,12 @@
|
|
|
48
46
|
"access": "public"
|
|
49
47
|
},
|
|
50
48
|
"scripts": {
|
|
51
|
-
"sync": ". \"$HOME/.nvm/nvm.sh\" && nvm use && npm prune && npm install",
|
|
49
|
+
"sync": ". \"$HOME/.nvm/nvm.sh\" && nvm use && npm prune && npm install && npx drawbridge-agents-sync",
|
|
52
50
|
"build": "tsup && npm publish"
|
|
53
51
|
},
|
|
54
52
|
"types": "dist/index.d.ts",
|
|
55
|
-
"version": "0.0.
|
|
53
|
+
"version": "0.0.4",
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"@drawbridge/drawbridge-agents": "0.0.4"
|
|
56
|
+
}
|
|
56
57
|
}
|