Skip to content

Commit 77d0e56

Browse files
authored
chore: Add spans for alert processing (#1219)
Closes HDX-2506 # Summary This PR adds a span for each task (of any kind) and each alert processing loop (one per connection), with provider, team, and connection span attributes. When ported to Cloud, this will allow us to more easily determine which team logs are coming from. <img width="1492" height="105" alt="Screenshot 2025-09-29 at 1 04 20 PM" src="https://github.com/user-attachments/assets/2eed25fc-32b3-48b2-b7f0-24ffe0beab18" /> <img width="1449" height="148" alt="Screenshot 2025-09-29 at 1 04 17 PM" src="https://github.com/user-attachments/assets/1b8d6acc-df90-4048-96db-2b2dc2bc2d63" /> <img width="515" height="213" alt="Screenshot 2025-09-29 at 1 04 12 PM" src="https://github.com/user-attachments/assets/061f366f-24ac-4195-9b72-3f1b5a871e36" />
1 parent 1ed4fbf commit 77d0e56

File tree

4 files changed

+71
-35
lines changed

4 files changed

+71
-35
lines changed

.changeset/dry-wombats-join.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@hyperdx/api": patch
3+
---
4+
5+
chore: Add spans for alert processing

packages/api/src/tasks/checkAlerts.ts

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import {
99
ChartConfigWithOptDateRange,
1010
DisplayType,
1111
} from '@hyperdx/common-utils/dist/types';
12+
import { setTraceAttributes } from '@hyperdx/node-opentelemetry';
1213
import * as fns from 'date-fns';
1314
import { chunk, isString } from 'lodash';
1415
import { ObjectId } from 'mongoose';
@@ -39,6 +40,8 @@ import { CheckAlertsTaskArgs, HdxTask } from '@/tasks/types';
3940
import { roundDownToXMinutes, unflattenObject } from '@/tasks/util';
4041
import logger from '@/utils/logger';
4142

43+
import { tasksTracer } from './tracer';
44+
4245
export const doesExceedThreshold = (
4346
thresholdType: AlertThresholdType,
4447
threshold: number,
@@ -432,26 +435,37 @@ export default class CheckAlertTask implements HdxTask<CheckAlertsTaskArgs> {
432435
alertTask: AlertTask,
433436
teamWebhooksById: Map<string, IWebhook>,
434437
) {
435-
const { alerts, conn } = alertTask;
436-
logger.info({
437-
message: 'Processing alerts in batch',
438-
alertCount: alerts.length,
439-
});
438+
await tasksTracer.startActiveSpan('processAlertTask', async span => {
439+
setTraceAttributes({
440+
'hyperdx.alerts.team.id': alertTask.conn.team.toString(),
441+
'hyperdx.alerts.connection.id': alertTask.conn.id,
442+
});
440443

441-
const clickhouseClient = await this.provider.getClickHouseClient(conn);
444+
try {
445+
const { alerts, conn } = alertTask;
446+
logger.info({
447+
message: 'Processing alerts in batch',
448+
alertCount: alerts.length,
449+
});
442450

443-
for (const alert of alerts) {
444-
await this.task_queue.add(() =>
445-
processAlert(
446-
alertTask.now,
447-
alert,
448-
clickhouseClient,
449-
conn.id,
450-
this.provider,
451-
teamWebhooksById,
452-
),
453-
);
454-
}
451+
const clickhouseClient = await this.provider.getClickHouseClient(conn);
452+
453+
for (const alert of alerts) {
454+
await this.task_queue.add(() =>
455+
processAlert(
456+
alertTask.now,
457+
alert,
458+
clickhouseClient,
459+
conn.id,
460+
this.provider,
461+
teamWebhooksById,
462+
),
463+
);
464+
}
465+
} finally {
466+
span.end();
467+
}
468+
});
455469
}
456470

457471
async execute(): Promise<void> {
@@ -469,6 +483,10 @@ export default class CheckAlertTask implements HdxTask<CheckAlertsTaskArgs> {
469483
args: this.args,
470484
});
471485

486+
setTraceAttributes({
487+
'hyperdx.alerts.provider': this.provider.constructor.name,
488+
});
489+
472490
const alertTasks = await this.provider.getAlertTasks();
473491
const taskCount = alertTasks.length;
474492
logger.debug({

packages/api/src/tasks/index.ts

Lines changed: 22 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@ import PingPongTask from '@/tasks/pingPongTask';
99
import { asTaskArgs, HdxTask, TaskArgs } from '@/tasks/types';
1010
import logger from '@/utils/logger';
1111

12+
import { tasksTracer } from './tracer';
13+
1214
function createTask(argv: TaskArgs): HdxTask<TaskArgs> {
1315
const taskName = argv.taskName;
1416
switch (taskName) {
@@ -22,23 +24,26 @@ function createTask(argv: TaskArgs): HdxTask<TaskArgs> {
2224
}
2325

2426
const main = async (argv: TaskArgs) => {
25-
const task: HdxTask<TaskArgs> = createTask(argv);
26-
try {
27-
const t0 = performance.now();
28-
logger.info(`Task [${task.name()}] started at ${new Date()}`);
29-
await task.execute();
30-
logger.info(
31-
`Task [${task.name()}] finished in ${(performance.now() - t0).toFixed(2)} ms`,
32-
);
33-
} catch (e: unknown) {
34-
logger.error({
35-
message: `Task [${task.name()}] failed: ${serializeError(e)}`,
36-
cause: e,
37-
task,
38-
});
39-
} finally {
40-
await task.asyncDispose();
41-
}
27+
await tasksTracer.startActiveSpan(argv.taskName || 'task', async span => {
28+
const task: HdxTask<TaskArgs> = createTask(argv);
29+
try {
30+
const t0 = performance.now();
31+
logger.info(`Task [${task.name()}] started at ${new Date()}`);
32+
await task.execute();
33+
logger.info(
34+
`Task [${task.name()}] finished in ${(performance.now() - t0).toFixed(2)} ms`,
35+
);
36+
} catch (e: unknown) {
37+
logger.error({
38+
message: `Task [${task.name()}] failed: ${serializeError(e)}`,
39+
cause: e,
40+
task,
41+
});
42+
} finally {
43+
await task.asyncDispose();
44+
span.end();
45+
}
46+
});
4247
};
4348

4449
// Entry point

packages/api/src/tasks/tracer.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import opentelemetry from '@opentelemetry/api';
2+
3+
import { CODE_VERSION } from '@/config';
4+
5+
export const tasksTracer = opentelemetry.trace.getTracer(
6+
'hyperdx-tasks',
7+
CODE_VERSION,
8+
);

0 commit comments

Comments
 (0)