Skip to content

Commit 0438150

Browse files
author
Stephen Belanger
committed
lib: add tracing channel to diagnostics_channel
1 parent 7e09c6c commit 0438150

5 files changed

+293
-0
lines changed

lib/diagnostics_channel.js

Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ const {
77
ObjectCreate,
88
ObjectGetPrototypeOf,
99
ObjectSetPrototypeOf,
10+
PromisePrototypeThen,
11+
ReflectApply,
1012
SymbolHasInstance,
1113
} = primordials;
1214

@@ -136,10 +138,135 @@ function hasSubscribers(name) {
136138
return channel.hasSubscribers;
137139
}
138140

141+
class TracingChannel {
142+
constructor(name) {
143+
this.name = name;
144+
this.channels = {
145+
start: channel(`${name}.start`),
146+
end: channel(`${name}.end`),
147+
asyncEnd: channel(`${name}.asyncEnd`),
148+
error: channel(`${name}.error`)
149+
};
150+
}
151+
152+
get hasSubscribers() {
153+
const { channels } = this;
154+
for (const key in channels) {
155+
if (channels[key].hasSubscribers) return true;
156+
}
157+
return false;
158+
}
159+
160+
subscribe(handlers) {
161+
let subscribed = true;
162+
for (const key in handlers) {
163+
if (!subscribe(`${this.name}.${key}`, handlers[key])) {
164+
subscribed = false;
165+
}
166+
}
167+
return subscribed;
168+
}
169+
170+
unsubscribe(handlers) {
171+
let unsubscribed = true;
172+
for (const key in handlers) {
173+
if (!unsubscribe(`${this.name}.${key}`, handlers[key])) {
174+
unsubscribed = false;
175+
}
176+
}
177+
return unsubscribed;
178+
}
179+
180+
traceSync(fn, ctx = {}, thisArg, ...args) {
181+
const { start, end, error } = this.channels;
182+
start.publish(ctx);
183+
try {
184+
const result = ReflectApply(fn, thisArg, args);
185+
ctx.result = result;
186+
return result;
187+
} catch (err) {
188+
ctx.error = err;
189+
error.publish(ctx);
190+
throw err;
191+
} finally {
192+
end.publish(ctx);
193+
}
194+
}
195+
196+
tracePromise(fn, ctx = {}, thisArg, ...args) {
197+
const { asyncEnd, start, end, error } = this.channels;
198+
start.publish(ctx);
199+
200+
const reject = (err) => {
201+
ctx.error = err;
202+
error.publish(ctx);
203+
asyncEnd.publish(ctx);
204+
throw err;
205+
};
206+
207+
const resolve = (result) => {
208+
ctx.result = result;
209+
asyncEnd.publish(ctx);
210+
return result;
211+
};
212+
213+
try {
214+
const promise = ReflectApply(fn, thisArg, args);
215+
return PromisePrototypeThen(promise, resolve, reject);
216+
} catch (err) {
217+
ctx.error = err;
218+
error.publish(ctx);
219+
throw err;
220+
} finally {
221+
end.publish(ctx);
222+
}
223+
}
224+
225+
traceCallback(fn, position = 0, ctx = {}, thisArg, ...args) {
226+
const { start, end, asyncEnd, error } = this.channels;
227+
start.publish(ctx);
228+
229+
function wrap(fn) {
230+
return function wrappedCallback (err, res) {
231+
if (err) {
232+
ctx.error = err;
233+
error.publish(ctx);
234+
} else {
235+
ctx.result = res;
236+
}
237+
238+
asyncEnd.publish(ctx);
239+
if (fn) {
240+
return ReflectApply(fn, this, arguments);
241+
}
242+
}
243+
}
244+
245+
if (position >= 0) {
246+
args.splice(position, 1, wrap(args.at(position)));
247+
}
248+
249+
try {
250+
return ReflectApply(fn, thisArg, args);
251+
} catch (err) {
252+
ctx.error = err;
253+
error.publish(ctx);
254+
throw err;
255+
} finally {
256+
end.publish(ctx);
257+
}
258+
}
259+
}
260+
261+
function tracingChannel(name) {
262+
return new TracingChannel(name);
263+
}
264+
139265
module.exports = {
140266
channel,
141267
hasSubscribers,
142268
subscribe,
269+
tracingChannel,
143270
unsubscribe,
144271
Channel
145272
};
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const dc = require('diagnostics_channel');
5+
const assert = require('assert');
6+
7+
const channel = dc.tracingChannel('test');
8+
9+
const expectedError = new Error('test');
10+
const input = { foo: 'bar' };
11+
const thisArg = { baz: 'buz' };
12+
13+
function check(found) {
14+
assert.deepStrictEqual(found, input);
15+
}
16+
17+
const handlers = {
18+
start: common.mustCall(check, 2),
19+
end: common.mustCall(check, 2),
20+
asyncEnd: common.mustCall(check, 2),
21+
error: common.mustCall((found) => {
22+
check(found);
23+
assert.deepStrictEqual(found.error, expectedError);
24+
}, 2)
25+
};
26+
27+
channel.subscribe(handlers);
28+
29+
channel.traceCallback(function (cb, err) {
30+
assert.deepStrictEqual(this, thisArg);
31+
setImmediate(cb, err);
32+
}, 0, input, thisArg, common.mustCall((err, res) => {
33+
assert.strictEqual(err, expectedError);
34+
assert.deepStrictEqual(res, undefined);
35+
}), expectedError);
36+
37+
channel.tracePromise(function (value) {
38+
assert.deepStrictEqual(this, thisArg);
39+
return Promise.reject(value);
40+
}, input, thisArg, expectedError).then(
41+
common.mustNotCall(),
42+
common.mustCall(value => {
43+
assert.deepStrictEqual(value, expectedError);
44+
})
45+
);
Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const dc = require('diagnostics_channel');
5+
const assert = require('assert');
6+
7+
const channel = dc.tracingChannel('test');
8+
9+
const expectedResult = { foo: 'bar' };
10+
const input = { foo: 'bar' };
11+
const thisArg = { baz: 'buz' };
12+
13+
function check(found) {
14+
assert.deepStrictEqual(found, input);
15+
}
16+
17+
const handlers = {
18+
start: common.mustCall(check, 2),
19+
end: common.mustCall(check, 2),
20+
asyncEnd: common.mustCall((found) => {
21+
check(found);
22+
assert.strictEqual(found.error, undefined);
23+
assert.deepStrictEqual(found.result, expectedResult);
24+
}, 2),
25+
error: common.mustNotCall()
26+
};
27+
28+
channel.subscribe(handlers);
29+
30+
channel.traceCallback(function (cb, err, res) {
31+
assert.deepStrictEqual(this, thisArg);
32+
setImmediate(cb, err, res);
33+
}, 0, input, thisArg, common.mustCall((err, res) => {
34+
assert.strictEqual(err, null);
35+
assert.deepStrictEqual(res, expectedResult);
36+
}), null, expectedResult);
37+
38+
channel.tracePromise(function (value) {
39+
assert.deepStrictEqual(this, thisArg);
40+
return Promise.resolve(value);
41+
}, input, thisArg, expectedResult).then(
42+
common.mustCall(value => {
43+
assert.deepStrictEqual(value, expectedResult);
44+
}),
45+
common.mustNotCall()
46+
);
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const dc = require('diagnostics_channel');
5+
const assert = require('assert');
6+
7+
const channel = dc.tracingChannel('test');
8+
9+
const expectedError = new Error('test');
10+
const input = { foo: 'bar' };
11+
const thisArg = { baz: 'buz' };
12+
13+
function check(found) {
14+
assert.deepStrictEqual(found, input);
15+
}
16+
17+
const handlers = {
18+
start: common.mustCall(check),
19+
end: common.mustCall(check),
20+
asyncEnd: common.mustNotCall(),
21+
error: common.mustCall((found) => {
22+
check(found);
23+
assert.deepStrictEqual(found.error, expectedError);
24+
})
25+
};
26+
27+
channel.subscribe(handlers);
28+
try {
29+
channel.traceSync(function (err) {
30+
assert.deepStrictEqual(this, thisArg);
31+
assert.strictEqual(err, expectedError);
32+
throw err;
33+
}, input, thisArg, expectedError);
34+
35+
throw new Error('It should not reach this error');
36+
} catch (error) {
37+
assert.deepStrictEqual(error, expectedError);
38+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
'use strict';
2+
3+
const common = require('../common');
4+
const dc = require('diagnostics_channel');
5+
const assert = require('assert');
6+
7+
const channel = dc.tracingChannel('test');
8+
9+
const expectedResult = { foo: 'bar' };
10+
const input = { foo: 'bar' };
11+
12+
function check(found) {
13+
assert.deepStrictEqual(found, input);
14+
}
15+
16+
const handlers = {
17+
start: common.mustCall(check),
18+
end: common.mustCall((found) => {
19+
check(found);
20+
assert.deepStrictEqual(found.result, expectedResult);
21+
}),
22+
asyncEnd: common.mustNotCall(),
23+
error: common.mustNotCall()
24+
};
25+
26+
assert.strictEqual(channel.hasSubscribers, false);
27+
channel.subscribe(handlers);
28+
assert.strictEqual(channel.hasSubscribers, true);
29+
channel.traceSync(() => {
30+
return expectedResult;
31+
}, input);
32+
33+
channel.unsubscribe(handlers);
34+
assert.strictEqual(channel.hasSubscribers, false);
35+
channel.traceSync(() => {
36+
return expectedResult;
37+
}, input);

0 commit comments

Comments
 (0)