|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
import {Runnable} from '../../../../src/index.js';
|
|
|
import {BaseCallback} from '../../../../src/utils/callbacks.js';
|
|
|
|
|
|
class MetricsTrackerCallback extends BaseCallback {
|
|
|
constructor() {
|
|
|
super();
|
|
|
|
|
|
|
|
|
this.startTimes = new Map();
|
|
|
|
|
|
|
|
|
this.metrics = {};
|
|
|
}
|
|
|
|
|
|
async onStart(runnable, input, config) {
|
|
|
const key = `${runnable.constructor.name}_${Date.now()}_${Math.random()}`;
|
|
|
this.startTimes.set(key, Date.now());
|
|
|
|
|
|
const userId = config?.metadata?.userId;
|
|
|
|
|
|
const runnableName = runnable.constructor.name;
|
|
|
if (!this.metrics[runnableName]) {
|
|
|
this.metrics[runnableName] = {
|
|
|
totalCalls: 0,
|
|
|
totalTime: 0,
|
|
|
users: new Set()
|
|
|
};
|
|
|
}
|
|
|
|
|
|
this.metrics[runnableName].totalCalls++;
|
|
|
if (userId) {
|
|
|
this.metrics[runnableName].users.add(userId);
|
|
|
}
|
|
|
|
|
|
|
|
|
config._metricsKey = key;
|
|
|
}
|
|
|
|
|
|
async onEnd(runnable, output, config) {
|
|
|
const key = config._metricsKey;
|
|
|
const startTime = this.startTimes.get(key);
|
|
|
|
|
|
if (startTime) {
|
|
|
const duration = Date.now() - startTime;
|
|
|
const runnableName = runnable.constructor.name;
|
|
|
|
|
|
if (this.metrics[runnableName]) {
|
|
|
this.metrics[runnableName].totalTime += duration;
|
|
|
}
|
|
|
|
|
|
this.startTimes.delete(key);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
async onError(runnable, error, config) {
|
|
|
const key = config._metricsKey;
|
|
|
if (key) {
|
|
|
this.startTimes.delete(key);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
getReport() {
|
|
|
const report = {};
|
|
|
|
|
|
for (const [name, data] of Object.entries(this.metrics)) {
|
|
|
report[name] = {
|
|
|
calls: data.totalCalls,
|
|
|
totalTime: data.totalTime,
|
|
|
avgTime: data.totalCalls > 0 ? Math.round(data.totalTime / data.totalCalls) : 0,
|
|
|
users: Array.from(data.users)
|
|
|
};
|
|
|
}
|
|
|
|
|
|
return report;
|
|
|
}
|
|
|
|
|
|
printReport() {
|
|
|
console.log('\nπ Metrics Report:');
|
|
|
console.log('β'.repeat(60));
|
|
|
|
|
|
const report = this.getReport();
|
|
|
|
|
|
for (const [name, data] of Object.entries(report)) {
|
|
|
console.log(`${name}:`);
|
|
|
console.log(` Calls: ${data.calls}`);
|
|
|
console.log(` Total Time: ${data.totalTime}ms`);
|
|
|
console.log(` Avg Time: ${data.avgTime}ms`);
|
|
|
console.log(` Users: ${data.users.join(', ')}`);
|
|
|
console.log('');
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
class FastRunnable extends Runnable {
|
|
|
async _call(input, config) {
|
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
|
return `Fast: ${input}`;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
class SlowRunnable extends Runnable {
|
|
|
async _call(input, config) {
|
|
|
await new Promise(resolve => setTimeout(resolve, 500));
|
|
|
return `Slow: ${input}`;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
class MediumRunnable extends Runnable {
|
|
|
async _call(input, config) {
|
|
|
await new Promise(resolve => setTimeout(resolve, 250));
|
|
|
return `Medium: ${input}`;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
async function exercise() {
|
|
|
console.log('=== Exercise 14: Metrics Tracker with Metadata ===\n');
|
|
|
|
|
|
const metrics = new MetricsTrackerCallback();
|
|
|
|
|
|
const fast = new FastRunnable();
|
|
|
const medium = new MediumRunnable();
|
|
|
const slow = new SlowRunnable();
|
|
|
|
|
|
|
|
|
console.log('--- User 1 making calls ---');
|
|
|
await fast.invoke('test1', { callbacks: [metrics], metadata: { userId: "user_123" } });
|
|
|
await medium.invoke('test2', { callbacks: [metrics], metadata: { userId: "user_123" } });
|
|
|
await fast.invoke('test3', { callbacks: [metrics], metadata: { userId: "user_123" } });
|
|
|
|
|
|
|
|
|
console.log('--- User 2 making calls ---');
|
|
|
await slow.invoke('test4', { callbacks: [metrics], metadata: { userId: "user_456" } });
|
|
|
await fast.invoke('test5', { callbacks: [metrics], metadata: { userId: "user_456" } });
|
|
|
|
|
|
|
|
|
console.log('--- User 3 making calls ---');
|
|
|
await medium.invoke('test6', { callbacks: [metrics], metadata: { userId: "user_789" } });
|
|
|
await slow.invoke('test7', { callbacks: [metrics], metadata: { userId: "user_789" } });
|
|
|
await medium.invoke('test8', { callbacks: [metrics], metadata: { userId: "user_789" } });
|
|
|
|
|
|
metrics.printReport();
|
|
|
|
|
|
console.log('\nβ Exercise 2 complete!');
|
|
|
}
|
|
|
|
|
|
|
|
|
exercise().catch(console.error);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|