190 build - улучшение работы всего сервиса. часть 1
This commit is contained in:
70
hockey_data/static/client-performance.js
Normal file
70
hockey_data/static/client-performance.js
Normal file
@@ -0,0 +1,70 @@
|
||||
(() => {
|
||||
"use strict";
|
||||
if (window.HockeyPerformance) return;
|
||||
const metrics = { script_started_ms: performance.now() };
|
||||
let errors = 0;
|
||||
let readySent = false;
|
||||
|
||||
function post(event, message = "", values = {}) {
|
||||
const payload = {
|
||||
event,
|
||||
build: String(window.UI_BUILDER_BOOT?.buildVersion || ""),
|
||||
message: String(message).slice(0, 2000),
|
||||
path: location.pathname,
|
||||
metrics: values,
|
||||
};
|
||||
// A diagnostic must not slow first paint or produce a diagnostic loop.
|
||||
fetch("/api/hockey/client-events", {
|
||||
method: "POST", credentials: "same-origin", cache: "no-store", keepalive: true,
|
||||
headers: { "Content-Type": "application/json" }, body: JSON.stringify(payload),
|
||||
}).catch(() => {});
|
||||
}
|
||||
|
||||
window.HockeyPerformance = {
|
||||
mark(name, value) { if (Number.isFinite(value)) metrics[name] = value; },
|
||||
async measure(name, work) {
|
||||
const start = performance.now();
|
||||
try { return await work(); }
|
||||
finally { metrics[name] = performance.now() - start; }
|
||||
},
|
||||
getMetrics() { return { ...metrics }; },
|
||||
reportError(message) { if (errors++ < 10) post("error", message); },
|
||||
};
|
||||
|
||||
window.addEventListener("hockey:runtime-ready", () => {
|
||||
if (readySent) return;
|
||||
readySent = true;
|
||||
metrics.runtime_ready_ms = performance.now();
|
||||
const navigation = performance.getEntriesByType("navigation")[0];
|
||||
if (navigation) {
|
||||
metrics.server_response_ms = Math.max(0, navigation.responseStart - navigation.requestStart);
|
||||
metrics.document_download_ms = Math.max(0, navigation.responseEnd - navigation.responseStart);
|
||||
metrics.connection_ms = Math.max(0, navigation.connectEnd - navigation.connectStart);
|
||||
metrics.dom_interactive_ms = Math.max(0, navigation.domInteractive);
|
||||
}
|
||||
const resources = performance.getEntriesByType("resource");
|
||||
metrics.resource_count = resources.length;
|
||||
metrics.transfer_bytes = resources.reduce((sum, item) => sum + (item.transferSize || 0), 0);
|
||||
metrics.decoded_bytes = resources.reduce((sum, item) => sum + (item.decodedBodySize || 0), 0);
|
||||
metrics.cached_resources = resources.filter(item => item.transferSize === 0 && item.decodedBodySize > 0).length;
|
||||
const send = () => {
|
||||
metrics.first_ready_paint_ms = performance.now();
|
||||
post("ready", "Интерфейс готов к работе", { ...metrics });
|
||||
};
|
||||
// Two frames include a paint, with a fallback for an inactive browser tab.
|
||||
let sent = false;
|
||||
const once = () => { if (!sent) { sent = true; send(); } };
|
||||
requestAnimationFrame(() => requestAnimationFrame(once));
|
||||
setTimeout(once, 1000);
|
||||
});
|
||||
|
||||
window.addEventListener("error", event => {
|
||||
if (errors++ >= 10) return;
|
||||
const file = String(event.filename || "").split("?")[0];
|
||||
post("error", `${event.message || "Ошибка браузера"} · ${file}:${event.lineno || 0}:${event.colno || 0}`);
|
||||
});
|
||||
window.addEventListener("unhandledrejection", event => {
|
||||
if (errors++ >= 10) return;
|
||||
post("unhandledrejection", String(event.reason?.message || event.reason || "Необработанная ошибка"));
|
||||
});
|
||||
})();
|
||||
Reference in New Issue
Block a user