PageSourceSearch

https://eld.matrackinc.com/adminNew/assets/js/perfLogger.js?v=20260807a

js matrackinc.com collected 2026-09-24 10:51:52 UTC 7,476 bytes, 150 lines download raw bytes

1/**
2 * perfLogger.js
3 * Best-effort page-load performance logger (Navigation Timing + Core Web Vitals).
4 *
5 * Hard rules (identical to apiLogger.js):
6 *  - Never throw. Never block the main thread. Zero impact on the end user.
7 *  - Passive collection only (PerformanceObserver via web-vitals, no polling).
8 *  - One fire-and-forget beacon per page, flushed when the page is hidden.
9 *  - Silently no-ops if logUserActivity / web-vitals are unavailable.
10 *
11 * Depends on:
12 *  - window.logUserActivity  (userActivityLog.js)  -> sendBeacon transport
13 *  - window.webVitals        (web-vitals.attribution.iife.js, loaded before this)
14 *
15 * Logged shape: ('PERF_METRICS', { ...navTimings, vitals:{...}, ...deviceNet })
16 */
17(function (global) {
18    'use strict';
19    try {
20        if (!global || typeof global !== 'object' || !global.performance) return;
21
22        // --- Sampling: fraction of page loads to collect. 1.0 = capture all. ---
23        var SAMPLE_RATE = 1.0;
24        if (Math.random() > SAMPLE_RATE) return;
25
26        // Accumulate vitals here as web-vitals finalizes each one.
27        var vitals = {};
28
29        function round(n) { return (typeof n === 'number' && isFinite(n)) ? Math.round(n) : null; }
30
31        // --- (A) Navigation Timing — full fidelity, ready at page load ---
32        function collectNav() {
33            var m = {};
34            try {
35                var nav = performance.getEntriesByType('navigation')[0];
36                if (!nav) return m;
37                m.ttfb            = round(nav.responseStart - nav.requestStart);
38                m.dns             = round(nav.domainLookupEnd - nav.domainLookupStart);
39                m.tcp             = round(nav.connectEnd - nav.connectStart);
40                m.tls             = nav.secureConnectionStart ? round(nav.connectEnd - nav.secureConnectionStart) : 0;
41                m.req             = round(nav.responseEnd - nav.requestStart);
42                m.dom_interactive = round(nav.domInteractive - nav.startTime);
43                m.dcl             = round(nav.domContentLoadedEventEnd - nav.startTime);
44                m.load            = round(nav.loadEventEnd - nav.startTime);
45                m.redirect_count  = nav.redirectCount;
46                m.redirect_ms     = round(nav.redirectEnd - nav.redirectStart);
47                m.transfer_bytes  = nav.transferSize;
48                m.encoded_bytes   = nav.encodedBodySize;
49                m.decoded_bytes   = nav.decodedBodySize;
50                m.nav_type        = nav.type;            // navigate | reload | back_forward | prerender
51                m.protocol        = nav.nextHopProtocol; // h2, http/1.1 ...
52            } catch (e) { /* swallow */ }
53            return m;
54        }
55
56        // --- Route + full device / network context ---
57        function context() {
58            var c = {};
59            try {
60                if (global.location) {
61                    c.path  = global.location.pathname;
62                    c.query = global.location.search;
63                    c.hash  = global.location.hash;
64                    c.href  = global.location.href;
65                }
66                c.ref = document.referrer || '';
67
68                var conn = navigator.connection || {};
69                c.net_effective = conn.effectiveType || null; // 4g / 3g / 2g / slow-2g
70                c.downlink      = (typeof conn.downlink === 'number') ? conn.downlink : null; // Mbps
71                c.rtt           = (typeof conn.rtt === 'number') ? conn.rtt : null;           // ms
72                c.save_data     = !!conn.saveData;
73
74                c.mem_gb   = (typeof navigator.deviceMemory === 'number') ? navigator.deviceMemory : null;
75                c.cores    = navigator.hardwareConcurrency || null;
76                c.dpr      = global.devicePixelRatio || 1;
77                c.vw       = global.innerWidth  || null;
78                c.vh       = global.innerHeight || null;
79                c.sw       = (screen && screen.width)  || null;
80                c.sh       = (screen && screen.height) || null;
81                c.lang     = navigator.language || null;
82                c.tz       = (global.Intl && Intl.DateTimeFormat) ? Intl.DateTimeFormat().resolvedOptions().timeZone : null;
83
84                // Chrome-only heap snapshot — useful for leak hunting.
85                if (performance.memory) {
86                    c.js_heap_used  = performance.memory.usedJSHeapSize;
87                    c.js_heap_limit = performance.memory.jsHeapSizeLimit;
88                }
89
90                // Top 5 slowest resources — pinpoints the culprit asset.
91                c.slow_resources = performance.getEntriesByType('resource')
92                    .map(function (r) {
93                        return { name: r.name, dur: round(r.duration), size: r.transferSize || 0, type: r.initiatorType };
94                    })
95                    .sort(function (a, b) { return (b.dur || 0) - (a.dur || 0); })
96                    .slice(0, 5);
97            } catch (e) { /* swallow */ }
98            return c;
99        }
100
101        // --- (B) Core Web Vitals via web-vitals (attribution build) ---
102        function collectVitals() {
103            try {
104                var wv = global.webVitals;
105                if (!wv) return;
106                var save = function (metric) {
107                    try {
108                        vitals[metric.name] = {
109                            value: (metric.name === 'CLS') ? +metric.value.toFixed(3) : round(metric.value),
110                            rating: metric.rating,           // good | needs-improvement | poor
111                            attribution: metric.attribution || null
112                        };
113                    } catch (e) { /* swallow */ }
114                };
115                if (wv.onLCP)  wv.onLCP(save);
116                if (wv.onINP)  wv.onINP(save);
117                if (wv.onCLS)  wv.onCLS(save);
118                if (wv.onTTFB) wv.onTTFB(save);
119                if (wv.onFCP)  wv.onFCP(save);
120            } catch (e) { /* swallow */ }
121        }
122
123        // --- Flush ONCE when the page is going away (vitals are final only then) ---
124        var sent = false;
125        function flush() {
126            if (sent) return;
127            sent = true;
128            try {
129                var payload = context();
130                var nav = collectNav();
131                for (var k in nav) { if (nav.hasOwnProperty(k)) payload[k] = nav[k]; }
132                payload.vitals = vitals;
133                if (typeof global.logUserActivity === 'function') {
134                    global.logUserActivity('PERF_METRICS', payload);
135                }
136            } catch (e) { /* swallow */ }
137        }
138
139        // web-vitals registers its own hidden/pagehide listeners inside these calls,
140        // so they run before ours and populate `vitals` prior to flush().
141        collectVitals();
142
143        // 'visibilitychange -> hidden' is the reliable end-of-page signal on all
144        // platforms (tab close, navigation, mobile app switch). 'unload' is not.
145        global.addEventListener('visibilitychange', function () {
146            if (document.visibilityState === 'hidden') flush();
147        });
148        global.addEventListener('pagehide', flush); // bfcache belt-and-suspenders
149    } catch (e) { /* never affect the user */ }
150})(typeof window !== 'undefined' ? window : this);

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.