Skip to content

Commit 52163bc

Browse files
committed
feat: dont report responses served from browser cache
1 parent 5d61f9d commit 52163bc

File tree

1 file changed

+96
-40
lines changed

1 file changed

+96
-40
lines changed

src/index.js

Lines changed: 96 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,17 @@ class Saturn {
1717
this.opts = Object.assign({}, {
1818
clientId: randomUUID(),
1919
cdnURL: 'saturn.ms',
20+
logURL: 'https://twb3qukm2i654i3tnvx36char40aymqq.lambda-url.us-west-2.on.aws/',
2021
connectTimeout: 5_000,
2122
downloadTimeout: 0
2223
}, opts)
2324

24-
this.reportingLogs = process?.env?.NODE_ENV !== 'development'
2525
this.logs = []
26+
this.reportingLogs = process?.env?.NODE_ENV !== 'development'
27+
this.hasPerformanceAPI = typeof window !== 'undefined' && window?.performance
28+
if (this.reportingLogs && this.hasPerformanceAPI) {
29+
this._monitorPerformanceBuffer()
30+
}
2631
}
2732

2833
/**
@@ -43,9 +48,7 @@ class Saturn {
4348

4449
const log = {
4550
url,
46-
range: null,
47-
startTime: new Date(),
48-
numBytesSent: 0
51+
startTime: new Date()
4952
}
5053

5154
const controller = new AbortController()
@@ -80,8 +83,6 @@ class Saturn {
8083
this._finalizeLog(log)
8184

8285
throw err
83-
} finally {
84-
this._addPerformanceAPIMetrics(log)
8586
}
8687

8788
return { res, log }
@@ -100,6 +101,8 @@ class Saturn {
100101
const { res, log } = await this.fetchCID(cidPath, opts)
101102

102103
async function * metricsIterable (itr) {
104+
log.numBytesSent = 0
105+
103106
for await (const chunk of itr) {
104107
log.numBytesSent += chunk.length
105108
yield chunk
@@ -177,48 +180,101 @@ class Saturn {
177180
}
178181

179182
async _reportLogs () {
180-
if (this.logs.length) {
181-
await fetch(
182-
'https://twb3qukm2i654i3tnvx36char40aymqq.lambda-url.us-west-2.on.aws/',
183-
{
184-
method: 'POST',
185-
body: JSON.stringify({
186-
bandwidthLogs: this.logs
187-
})
188-
}
189-
)
190-
this.logs = []
183+
if (!this.logs.length) {
184+
return
185+
}
186+
187+
let bandwidthLogs = this.logs
188+
if (this.hasPerformanceAPI) {
189+
bandwidthLogs = this._matchLogsWithPerformanceMetrics(bandwidthLogs)
191190
}
191+
192+
await fetch(
193+
this.opts.logURL,
194+
{
195+
method: 'POST',
196+
body: JSON.stringify({ bandwidthLogs, logSender: this.opts.logSender })
197+
}
198+
)
199+
200+
this.logs = []
201+
this._clearPerformanceBuffer()
202+
}
203+
204+
/**
205+
*
206+
* @param {Array<object>} logs
207+
*/
208+
_matchLogsWithPerformanceMetrics (logs) {
209+
const logsToReport = []
210+
211+
for (const log of logs) {
212+
const metrics = this._getPerformanceMetricsForLog(log)
213+
214+
if (!metrics.isFromBrowserCache) {
215+
delete metrics.isFromBrowserCache
216+
Object.assign(log, metrics)
217+
logsToReport.push(log)
218+
}
219+
}
220+
221+
return logsToReport
192222
}
193223

194224
/**
195225
*
196226
* @param {object} log
227+
* @returns {object}
197228
*/
198-
_addPerformanceAPIMetrics (log) {
199-
if (typeof window !== 'undefined' && window?.performance) {
200-
const entry = performance
201-
.getEntriesByType('resource')
202-
.find((r) => r.name === log.url.href)
203-
if (entry) {
204-
const dnsStart = entry.domainLookupStart
205-
const dnsEnd = entry.domainLookupEnd
206-
const hasData = dnsEnd > 0 && dnsStart > 0
207-
if (hasData) {
208-
log.dnsTimeMs = Math.round(dnsEnd - dnsStart)
209-
log.ttfbAfterDnsMs = Math.round(
210-
entry.responseStart - entry.requestStart
211-
)
212-
}
213-
214-
if (log.httpProtocol === null && entry.nextHopProtocol) {
215-
log.httpProtocol = entry.nextHopProtocol
216-
}
217-
// else response didn't have Timing-Allow-Origin: *
218-
//
219-
// if both dnsStart and dnsEnd are > 0 but have the same value,
220-
// its a dns cache hit.
229+
_getPerformanceMetricsForLog (log) {
230+
const metrics = {}
231+
232+
const entry = performance
233+
.getEntriesByType('resource')
234+
.find((r) => r.name === log.url.href)
235+
236+
if (entry) {
237+
const dnsStart = entry.domainLookupStart
238+
const dnsEnd = entry.domainLookupEnd
239+
const hasDnsMetrics = dnsEnd > 0 && dnsStart > 0
240+
241+
if (hasDnsMetrics) {
242+
metrics.dnsTimeMs = Math.round(dnsEnd - dnsStart)
243+
metrics.ttfbAfterDnsMs = Math.round(
244+
entry.responseStart - entry.requestStart
245+
)
246+
}
247+
248+
if (entry.nextHopProtocol) {
249+
metrics.httpProtocol = entry.nextHopProtocol
221250
}
251+
252+
metrics.isFromBrowserCache = (
253+
entry.deliveryType === 'cache' ||
254+
(log.httpStatusCode && entry.transferSize === 0)
255+
)
256+
}
257+
258+
return metrics
259+
}
260+
261+
_monitorPerformanceBuffer () {
262+
// Using static method prevents multiple unnecessary listeners.
263+
performance.addEventListener('resourcetimingbufferfull', Saturn._setResourceBufferSize)
264+
}
265+
266+
static _setResourceBufferSize () {
267+
const increment = 250
268+
const maxSize = 1000
269+
const size = performance.getEntriesByType('resource').length
270+
const newSize = Math.min(size + increment, maxSize)
271+
272+
performance.setResourceTimingBufferSize(newSize)
273+
}
274+
275+
_clearPerformanceBuffer () {
276+
if (this.hasPerformanceAPI) {
277+
performance.clearResourceTimings()
222278
}
223279
}
224280
}

0 commit comments

Comments
 (0)