diff --git a/server/latency-service.js b/server/latency-service.js index 10566bb..8a68e1a 100644 --- a/server/latency-service.js +++ b/server/latency-service.js @@ -55,44 +55,53 @@ async function pollLatency() { const targetMetrics = [ 'probe_icmp_duration_seconds', 'probe_http_duration_seconds', - 'probe_dns_lookup_time_seconds', 'probe_duration_seconds' ]; let foundLatency = null; - const encodedTarget = target.toLowerCase(); - for (const metricName of targetMetrics) { + let bestLine = null; + + // First pass: look for phase="rtt" which is the most accurate "ping" for (const line of lines) { - if (!line.startsWith(metricName)) continue; - - // Precise matching for the specific target if multiple targets exist in data - // Handles: metric{instance="target"} value OR metric value - const regex = new RegExp(`^${metricName}(?:\\{[^}]*\\})?\\s+([\\d.eE+-]+)`); - const match = line.match(regex); - - if (match) { - // If there are labels, verify they relate to our target (if target label exists) - if (line.includes('{')) { - const labelsPart = line.substring(line.indexOf('{') + 1, line.lastIndexOf('}')).toLowerCase(); - // Only check if the labels contain our target to be safe, - // though /probe usually only returns one target anyway. - if (labelsPart.includes(encodedTarget) || labelsPart.includes('instance') || labelsPart.includes('target')) { - const val = parseFloat(match[1]); - if (!isNaN(val)) foundLatency = val * 1000; + if (line.startsWith(metricName) && line.includes('phase="rtt"')) { + bestLine = line; + break; + } + } + + // Second pass: if no rtt phase, look for a line without phases (legacy format) or just the first line + if (!bestLine) { + for (const line of lines) { + if (line.startsWith(metricName)) { + // Prefer lines without {} if possible, otherwise take the first one + if (!line.includes('{')) { + bestLine = line; + break; } - } else { - const val = parseFloat(match[1]); - if (!isNaN(val)) foundLatency = val * 1000; + if (!bestLine) bestLine = line; + } + } + } + + if (bestLine) { + // Regex to capture the number, including scientific notation + const regex = new RegExp(`^${metricName}(?:\\{[^}]*\\})?\\s+([\\d.eE+-]+)`); + const match = bestLine.match(regex); + + if (match) { + const val = parseFloat(match[1]); + if (!isNaN(val)) { + foundLatency = val * 1000; // convert to ms + break; } } - if (foundLatency !== null) break; } - if (foundLatency !== null) break; } // 3. Final decision // If it's a success, use found latency. If success=0 or missing, handle carefully. + let latency; if (isProbeSuccess && foundLatency !== null) { latency = foundLatency; } else { diff --git a/server/prometheus-service.js b/server/prometheus-service.js index 3fcb884..c348788 100644 --- a/server/prometheus-service.js +++ b/server/prometheus-service.js @@ -809,12 +809,14 @@ module.exports = { // Construct a single optimized query searching for priority metrics and common labels // Prioritize probe_icmp_duration_seconds OVER probe_duration_seconds const queryExpr = `( + probe_icmp_duration_seconds{phase="rtt", instance="${target}"} or + probe_icmp_duration_seconds{phase="rtt", target="${target}"} or + probe_http_duration_seconds{phase="rtt", instance="${target}"} or + probe_http_duration_seconds{phase="rtt", target="${target}"} or probe_icmp_duration_seconds{instance="${target}"} or probe_icmp_duration_seconds{target="${target}"} or - probe_icmp_duration_seconds{instance_name="${target}"} or probe_duration_seconds{instance="${target}"} or - probe_duration_seconds{target="${target}"} or - probe_duration_seconds{instance_name="${target}"} + probe_duration_seconds{target="${target}"} )`; const params = new URLSearchParams({ query: queryExpr });