1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
|
import { count, sum } from "drizzle-orm";
import { Counter, Gauge, Histogram, register } from "prom-client";
import { db } from "@karakeep/db";
import { assets, bookmarks, users } from "@karakeep/db/schema";
import {
AssetPreprocessingQueue,
FeedQueue,
LinkCrawlerQueue,
OpenAIQueue,
RuleEngineQueue,
SearchIndexingQueue,
TidyAssetsQueue,
VideoWorkerQueue,
WebhookQueue,
} from "@karakeep/shared/queues";
// Queue metrics
const queuePendingJobsGauge = new Gauge({
name: "karakeep_queue_jobs",
help: "Number of jobs in each background queue",
labelNames: ["queue_name", "status"],
async collect() {
const queues = [
{ name: "link_crawler", queue: LinkCrawlerQueue },
{ name: "openai", queue: OpenAIQueue },
{ name: "search_indexing", queue: SearchIndexingQueue },
{ name: "tidy_assets", queue: TidyAssetsQueue },
{ name: "video_worker", queue: VideoWorkerQueue },
{ name: "feed", queue: FeedQueue },
{ name: "asset_preprocessing", queue: AssetPreprocessingQueue },
{ name: "webhook", queue: WebhookQueue },
{ name: "rule_engine", queue: RuleEngineQueue },
];
const stats = await Promise.all(
queues.map(async ({ name, queue }) => {
try {
return {
...(await queue.stats()),
name,
};
} catch (error) {
console.error(`Failed to get stats for queue ${name}:`, error);
return { name, pending: 0, pending_retry: 0, failed: 0, running: 0 };
}
}),
);
stats.forEach(({ name, pending, pending_retry, failed, running }) => {
this.set({ queue_name: name, status: "pending" }, pending);
this.set({ queue_name: name, status: "pending_retry" }, pending_retry);
this.set({ queue_name: name, status: "failed" }, failed);
this.set({ queue_name: name, status: "running" }, running);
});
},
});
// User metrics
const totalUsersGauge = new Gauge({
name: "karakeep_total_users",
help: "Total number of users in the system",
async collect() {
try {
const result = await db.select({ count: count() }).from(users);
this.set(result[0]?.count ?? 0);
} catch (error) {
console.error("Failed to get user count:", error);
this.set(0);
}
},
});
// Asset metrics
const totalAssetSizeGauge = new Gauge({
name: "karakeep_total_asset_size_bytes",
help: "Total size of all assets in bytes",
async collect() {
try {
const result = await db
.select({ totalSize: sum(assets.size) })
.from(assets);
this.set(Number(result[0]?.totalSize ?? 0));
} catch (error) {
console.error("Failed to get total asset size:", error);
this.set(0);
}
},
});
// Bookmark metrics
const totalBookmarksGauge = new Gauge({
name: "karakeep_total_bookmarks",
help: "Total number of bookmarks in the system",
async collect() {
try {
const result = await db.select({ count: count() }).from(bookmarks);
this.set(result[0]?.count ?? 0);
} catch (error) {
console.error("Failed to get bookmark count:", error);
this.set(0);
}
},
});
// Api metrics
const apiRequestsTotalCounter = new Counter({
name: "karakeep_trpc_requests_total",
help: "Total number of API requests",
labelNames: ["type", "path", "is_error"],
});
const apiErrorsTotalCounter = new Counter({
name: "karakeep_trpc_errors_total",
help: "Total number of API requests",
labelNames: ["type", "path", "code"],
});
const apiRequestDurationSummary = new Histogram({
name: "karakeep_trpc_request_duration_seconds",
help: "Duration of tRPC requests in seconds",
labelNames: ["type", "path"],
buckets: [
5e-3, 0.01, 0.025, 0.05, 0.075, 0.1, 0.25, 0.5, 0.75, 1, 2.5, 5, 7.5, 10,
],
});
// Register all metrics
register.registerMetric(queuePendingJobsGauge);
register.registerMetric(totalUsersGauge);
register.registerMetric(totalAssetSizeGauge);
register.registerMetric(totalBookmarksGauge);
register.registerMetric(apiRequestsTotalCounter);
register.registerMetric(apiErrorsTotalCounter);
register.registerMetric(apiRequestDurationSummary);
export {
queuePendingJobsGauge,
totalUsersGauge,
totalAssetSizeGauge,
totalBookmarksGauge,
apiRequestsTotalCounter,
apiErrorsTotalCounter,
apiRequestDurationSummary,
};
|