107 lines
3.4 KiB
JavaScript
107 lines
3.4 KiB
JavaScript
#!/usr/bin/env node
|
|
|
|
const apiToken = process.env.CLOUDFLARE_API_TOKEN;
|
|
const zoneTag = process.env.CLOUDFLARE_ZONE_TAG;
|
|
const hostname = process.env.CLOUDFLARE_HOSTNAME || "linearvoid.tv";
|
|
const windowMinutes = Number.parseInt(process.env.CLOUDFLARE_CACHE_HIT_RATIO_WINDOW_MINUTES || "60", 10);
|
|
const endpoint = process.env.CLOUDFLARE_GRAPHQL_ENDPOINT || "https://api.cloudflare.com/client/v4/graphql";
|
|
|
|
if (!apiToken || !zoneTag) {
|
|
console.error("Missing required environment variables: CLOUDFLARE_API_TOKEN, CLOUDFLARE_ZONE_TAG");
|
|
process.exit(1);
|
|
}
|
|
|
|
const end = new Date();
|
|
const start = new Date(end.getTime() - windowMinutes * 60 * 1000);
|
|
|
|
const iso = (value) => value.toISOString();
|
|
|
|
const query = `
|
|
query CacheHitRatio($zoneTag: string, $start: Time, $end: Time, $hostname: String) {
|
|
viewer {
|
|
zones(filter: { zoneTag: $zoneTag }) {
|
|
requests: httpRequestsAdaptiveGroups(
|
|
limit: 1000
|
|
filter: {
|
|
datetime_geq: $start
|
|
datetime_lt: $end
|
|
clientRequestHTTPHost: $hostname
|
|
requestSource: "eyeball"
|
|
}
|
|
) {
|
|
count
|
|
dimensions {
|
|
cacheStatus
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
`;
|
|
|
|
async function main() {
|
|
const response = await fetch(endpoint, {
|
|
method: "POST",
|
|
headers: {
|
|
Authorization: `Bearer ${apiToken}`,
|
|
"Content-Type": "application/json",
|
|
Accept: "application/json",
|
|
},
|
|
body: JSON.stringify({
|
|
query,
|
|
variables: {
|
|
zoneTag,
|
|
hostname,
|
|
start: iso(start),
|
|
end: iso(end),
|
|
},
|
|
}),
|
|
});
|
|
|
|
const payload = await response.json();
|
|
if (!response.ok || payload.errors) {
|
|
const message = payload.errors ? JSON.stringify(payload.errors) : `HTTP ${response.status}`;
|
|
throw new Error(message);
|
|
}
|
|
|
|
const groups = payload?.data?.viewer?.zones?.[0]?.requests || [];
|
|
let totalRequests = 0;
|
|
let hitRequests = 0;
|
|
|
|
for (const group of groups) {
|
|
const count = Number(group?.count || 0);
|
|
const status = String(group?.dimensions?.cacheStatus || "").toUpperCase();
|
|
totalRequests += count;
|
|
if (status === "HIT") {
|
|
hitRequests += count;
|
|
}
|
|
}
|
|
|
|
const ratio = totalRequests > 0 ? hitRequests / totalRequests : 0;
|
|
|
|
console.log(
|
|
`# HELP linearvoid_cache_hit_ratio Cloudflare cache hit ratio for the selected hostname and time window.`,
|
|
);
|
|
console.log(`# TYPE linearvoid_cache_hit_ratio gauge`);
|
|
console.log(
|
|
`linearvoid_cache_hit_ratio{zone="${zoneTag}",hostname="${hostname}",window_minutes="${windowMinutes}"} ${ratio.toFixed(6)}`,
|
|
);
|
|
console.log(`# HELP linearvoid_cache_hit_requests_total Cloudflare cache HIT requests counted in the time window.`);
|
|
console.log(`# TYPE linearvoid_cache_hit_requests_total counter`);
|
|
console.log(
|
|
`linearvoid_cache_hit_requests_total{zone="${zoneTag}",hostname="${hostname}",window_minutes="${windowMinutes}"} ${hitRequests}`,
|
|
);
|
|
console.log(
|
|
`# HELP linearvoid_cache_total_requests_total Cloudflare total eyeball requests counted in the time window.`,
|
|
);
|
|
console.log(`# TYPE linearvoid_cache_total_requests_total counter`);
|
|
console.log(
|
|
`linearvoid_cache_total_requests_total{zone="${zoneTag}",hostname="${hostname}",window_minutes="${windowMinutes}"} ${totalRequests}`,
|
|
);
|
|
}
|
|
|
|
main().catch((error) => {
|
|
console.error(error instanceof Error ? error.message : String(error));
|
|
process.exit(1);
|
|
});
|