Skip to content

Commit c47f434

Browse files
committed
refactor(observability): extract Superlog drain to shared helper
Pulls the 5 duplicate createOTLPDrain blocks in api/basket/insights/ slack/uptime into @databuddy/shared/evlog-superlog. Drops the SUPERLOG_ENDPOINT override (configurability theater, hardcoded to https://intake.superlog.sh) and dedupes the try/catch loop in insights and slack. Net -66 LOC.
1 parent 41d186c commit c47f434

11 files changed

Lines changed: 60 additions & 107 deletions

File tree

.env.example

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -87,4 +87,3 @@ NOTRA_API_KEY=
8787
# Superlog OTLP logs export. When set, evlog drains in api/basket/insights/slack/uptime
8888
# also ship wide events to https://intake.superlog.sh/v1/logs.
8989
SUPERLOG_API_KEY=""
90-
SUPERLOG_ENDPOINT=""

apps/api/src/lib/evlog-api.ts

Lines changed: 4 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { dirname, join } from "node:path";
22
import { fileURLToPath } from "node:url";
33
import { readBooleanEnv } from "@databuddy/env/boolean";
4+
import { createBatchedSuperlogDrain } from "@databuddy/shared/evlog-superlog";
45
import type { DrainContext, EnrichContext } from "evlog";
56
import { createAxiomDrain } from "evlog/axiom";
67
import {
@@ -9,29 +10,14 @@ import {
910
createUserAgentEnricher,
1011
} from "evlog/enrichers";
1112
import { createFsDrain } from "evlog/fs";
12-
import { createOTLPDrain } from "evlog/otlp";
1313
import { createDrainPipeline } from "evlog/pipeline";
1414

15-
const pipeline = createDrainPipeline<DrainContext>({
15+
const batchedAxiomDrain = createDrainPipeline<DrainContext>({
1616
batch: { size: 50, intervalMs: 5000 },
1717
maxBufferSize: 2000,
18-
});
18+
})(createAxiomDrain());
1919

20-
const axiomDrain = createAxiomDrain();
21-
22-
const batchedAxiomDrain = pipeline(axiomDrain);
23-
24-
const superlogApiKey = process.env.SUPERLOG_API_KEY;
25-
26-
const batchedSuperlogDrain = superlogApiKey
27-
? pipeline(
28-
createOTLPDrain({
29-
endpoint:
30-
process.env.SUPERLOG_ENDPOINT || "https://intake.superlog.sh",
31-
headers: { Authorization: `Bearer ${superlogApiKey}` },
32-
})
33-
)
34-
: null;
20+
const batchedSuperlogDrain = createBatchedSuperlogDrain();
3521

3622
const devFsLogsDir = join(
3723
dirname(fileURLToPath(import.meta.url)),
@@ -88,10 +74,6 @@ function parseDurationMs(duration: unknown): number | undefined {
8874
: Math.round(Number.parseFloat(match[1]));
8975
}
9076

91-
/**
92-
* In development, writes NDJSON wide events to `apps/api/.evlog/logs/` (analyze-logs skill)
93-
* and still sends to Axiom via the batched pipeline. Production: Axiom only.
94-
*/
9577
export async function apiLoggerDrain(ctx: DrainContext): Promise<void> {
9678
normalizeWideEventForAxiom(ctx.event as Record<string, unknown>);
9779

apps/basket/src/lib/evlog-basket.ts

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { dirname, join } from "node:path";
22
import { fileURLToPath } from "node:url";
33
import { readBooleanEnv } from "@databuddy/env/boolean";
4+
import { createBatchedSuperlogDrain } from "@databuddy/shared/evlog-superlog";
45
import type { DrainContext, EnrichContext } from "evlog";
56
import { createAxiomDrain } from "evlog/axiom";
67
import {
@@ -9,29 +10,14 @@ import {
910
createUserAgentEnricher,
1011
} from "evlog/enrichers";
1112
import { createFsDrain } from "evlog/fs";
12-
import { createOTLPDrain } from "evlog/otlp";
1313
import { createDrainPipeline } from "evlog/pipeline";
1414

15-
const pipeline = createDrainPipeline<DrainContext>({
15+
const batchedAxiomDrain = createDrainPipeline<DrainContext>({
1616
batch: { size: 50, intervalMs: 5000 },
1717
maxBufferSize: 2000,
18-
});
18+
})(createAxiomDrain());
1919

20-
const axiomDrain = createAxiomDrain();
21-
22-
const batchedAxiomDrain = pipeline(axiomDrain);
23-
24-
const superlogApiKey = process.env.SUPERLOG_API_KEY;
25-
26-
const batchedSuperlogDrain = superlogApiKey
27-
? pipeline(
28-
createOTLPDrain({
29-
endpoint:
30-
process.env.SUPERLOG_ENDPOINT || "https://intake.superlog.sh",
31-
headers: { Authorization: `Bearer ${superlogApiKey}` },
32-
})
33-
)
34-
: null;
20+
const batchedSuperlogDrain = createBatchedSuperlogDrain();
3521

3622
const devFsLogsDir = join(
3723
dirname(fileURLToPath(import.meta.url)),

apps/insights/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
"@databuddy/env": "workspace:*",
1616
"@databuddy/redis": "workspace:*",
1717
"@databuddy/rpc": "workspace:*",
18+
"@databuddy/shared": "workspace:*",
1819
"ai": "^6.0.188",
1920
"bullmq": "^5.66.5",
2021
"dayjs": "^1.11.19",

apps/insights/src/lib/evlog-insights.ts

Lines changed: 6 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import { dirname, join } from "node:path";
33
import { fileURLToPath } from "node:url";
44
import { readBooleanEnv } from "@databuddy/env/boolean";
55
import { env } from "@databuddy/env/insights";
6+
import { createBatchedSuperlogDrain } from "@databuddy/shared/evlog-superlog";
67
import type { DrainContext, RequestLogger } from "evlog";
78
import { createLogger, log } from "evlog";
89
import { createAxiomDrain } from "evlog/axiom";
910
import { createFsDrain } from "evlog/fs";
10-
import { createOTLPDrain } from "evlog/otlp";
1111
import { createDrainPipeline } from "evlog/pipeline";
1212

1313
type PrimitiveLogValue = string | number | boolean;
@@ -34,17 +34,7 @@ const batchedAxiomDrain = axiomApiKey
3434
)
3535
: null;
3636

37-
const superlogApiKey = process.env.SUPERLOG_API_KEY;
38-
39-
const batchedSuperlogDrain = superlogApiKey
40-
? pipeline(
41-
createOTLPDrain({
42-
endpoint:
43-
process.env.SUPERLOG_ENDPOINT || "https://intake.superlog.sh",
44-
headers: { Authorization: `Bearer ${superlogApiKey}` },
45-
})
46-
)
47-
: null;
37+
const batchedSuperlogDrain = createBatchedSuperlogDrain();
4838

4939
const fsDrain =
5040
process.env.NODE_ENV === "development" || readBooleanEnv("INSIGHTS_EVLOG_FS")
@@ -82,16 +72,12 @@ export async function insightsLoggerDrain(ctx: DrainContext): Promise<void> {
8272
if (fsDrain) {
8373
await fsDrain(ctx);
8474
}
85-
if (batchedAxiomDrain) {
86-
try {
87-
await batchedAxiomDrain(ctx);
88-
} catch {
89-
// Drain failures must not break background workers.
75+
for (const drain of [batchedAxiomDrain, batchedSuperlogDrain]) {
76+
if (!drain) {
77+
continue;
9078
}
91-
}
92-
if (batchedSuperlogDrain) {
9379
try {
94-
await batchedSuperlogDrain(ctx);
80+
await drain(ctx);
9581
} catch {
9682
// Drain failures must not break background workers.
9783
}

apps/slack/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"@databuddy/env": "workspace:*",
2020
"@databuddy/redis": "workspace:*",
2121
"@databuddy/rpc": "workspace:*",
22+
"@databuddy/shared": "workspace:*",
2223
"@slack/bolt": "^4.7.2",
2324
"@slack/web-api": "7.16.0",
2425
"evlog": "catalog:"

apps/slack/src/lib/evlog-slack.ts

Lines changed: 15 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -3,11 +3,11 @@ import { dirname, join } from "node:path";
33
import { fileURLToPath } from "node:url";
44
import { readBooleanEnv } from "@databuddy/env/boolean";
55
import { env } from "@databuddy/env/slack";
6+
import { createBatchedSuperlogDrain } from "@databuddy/shared/evlog-superlog";
67
import type { DrainContext, RequestLogger } from "evlog";
78
import { createLogger, log } from "evlog";
89
import { createAxiomDrain } from "evlog/axiom";
910
import { createFsDrain } from "evlog/fs";
10-
import { createOTLPDrain } from "evlog/otlp";
1111
import { createDrainPipeline } from "evlog/pipeline";
1212

1313
type SlackLogValue = string | number | boolean;
@@ -17,13 +17,11 @@ const activeSlackLog = new AsyncLocalStorage<RequestLogger>();
1717

1818
const axiomApiKey = env.AXIOM_API_KEY ?? env.AXIOM_TOKEN;
1919

20-
const pipeline = createDrainPipeline<DrainContext>({
21-
batch: { size: 50, intervalMs: 5000 },
22-
maxBufferSize: 2000,
23-
});
24-
2520
const batchedAxiomDrain = axiomApiKey
26-
? pipeline(
21+
? createDrainPipeline<DrainContext>({
22+
batch: { size: 50, intervalMs: 5000 },
23+
maxBufferSize: 2000,
24+
})(
2725
createAxiomDrain({
2826
apiKey: axiomApiKey,
2927
dataset: env.SLACK_AXIOM_DATASET,
@@ -32,17 +30,7 @@ const batchedAxiomDrain = axiomApiKey
3230
)
3331
: null;
3432

35-
const superlogApiKey = process.env.SUPERLOG_API_KEY;
36-
37-
const batchedSuperlogDrain = superlogApiKey
38-
? pipeline(
39-
createOTLPDrain({
40-
endpoint:
41-
process.env.SUPERLOG_ENDPOINT || "https://intake.superlog.sh",
42-
headers: { Authorization: `Bearer ${superlogApiKey}` },
43-
})
44-
)
45-
: null;
33+
const batchedSuperlogDrain = createBatchedSuperlogDrain();
4634

4735
const fsDrain =
4836
env.NODE_ENV === "development" || readBooleanEnv("SLACK_EVLOG_FS")
@@ -68,15 +56,15 @@ export async function slackLoggerDrain(ctx: DrainContext): Promise<void> {
6856
if (fsDrain) {
6957
await fsDrain(ctx);
7058
}
71-
try {
72-
await batchedAxiomDrain?.(ctx);
73-
} catch {
74-
// Drain failures must not break Slack event handling.
75-
}
76-
try {
77-
await batchedSuperlogDrain?.(ctx);
78-
} catch {
79-
// Drain failures must not break Slack event handling.
59+
for (const drain of [batchedAxiomDrain, batchedSuperlogDrain]) {
60+
if (!drain) {
61+
continue;
62+
}
63+
try {
64+
await drain(ctx);
65+
} catch {
66+
// Drain failures must not break Slack event handling.
67+
}
8068
}
8169
}
8270

apps/uptime/src/lib/evlog-uptime.ts

Lines changed: 4 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
import { dirname, join } from "node:path";
22
import { fileURLToPath } from "node:url";
33
import { readBooleanEnv } from "@databuddy/env/boolean";
4+
import { createBatchedSuperlogDrain } from "@databuddy/shared/evlog-superlog";
45
import type { DrainContext, EnrichContext } from "evlog";
56
import { createAxiomDrain } from "evlog/axiom";
67
import {
@@ -9,29 +10,14 @@ import {
910
createUserAgentEnricher,
1011
} from "evlog/enrichers";
1112
import { createFsDrain } from "evlog/fs";
12-
import { createOTLPDrain } from "evlog/otlp";
1313
import { createDrainPipeline } from "evlog/pipeline";
1414

15-
const pipeline = createDrainPipeline<DrainContext>({
15+
const batchedAxiomDrain = createDrainPipeline<DrainContext>({
1616
batch: { size: 50, intervalMs: 5000 },
1717
maxBufferSize: 2000,
18-
});
18+
})(createAxiomDrain());
1919

20-
const axiomDrain = createAxiomDrain();
21-
22-
const batchedAxiomDrain = pipeline(axiomDrain);
23-
24-
const superlogApiKey = process.env.SUPERLOG_API_KEY;
25-
26-
const batchedSuperlogDrain = superlogApiKey
27-
? pipeline(
28-
createOTLPDrain({
29-
endpoint:
30-
process.env.SUPERLOG_ENDPOINT || "https://intake.superlog.sh",
31-
headers: { Authorization: `Bearer ${superlogApiKey}` },
32-
})
33-
)
34-
: null;
20+
const batchedSuperlogDrain = createBatchedSuperlogDrain();
3521

3622
const devFsLogsDir = join(
3723
dirname(fileURLToPath(import.meta.url)),

bun.lock

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/shared/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,15 @@
1717
"./constants/deep-link-apps": "./src/constants/deep-link-apps.ts",
1818
"./analytics-filters": "./src/analytics-filters.ts",
1919
"./agent-credits": "./src/agent-credits.ts",
20-
"./tracking-blocks": "./src/tracking-blocks.ts"
20+
"./tracking-blocks": "./src/tracking-blocks.ts",
21+
"./evlog-superlog": "./src/evlog-superlog.ts"
2122
},
2223
"scripts": {
2324
"check-types": "tsc --noEmit",
2425
"test": "bun test src"
2526
},
2627
"dependencies": {
28+
"evlog": "catalog:",
2729
"ua-parser-js": "catalog:",
2830
"undici": "^8.2.0",
2931
"zod": "catalog:"

0 commit comments

Comments
 (0)