Skip to content

Commit 532a18d

Browse files
jackfranklinDevtools-frontend LUCI CQ
authored andcommitted
AI: add basic info for documentRequestLatency insight
Bug: 401232558 Change-Id: I5c24f2367d246b8ec4a771e6923d0409c5db2ca0 Reviewed-on: https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/6348559 Auto-Submit: Jack Franklin <[email protected]> Reviewed-by: Alina Varkki <[email protected]> Commit-Queue: Alina Varkki <[email protected]>
1 parent 0456e4b commit 532a18d

File tree

5 files changed

+100
-3
lines changed

5 files changed

+100
-3
lines changed

front_end/models/trace/insights/DocumentLatency.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,13 +76,17 @@ const TARGET_MS = 100;
7676
// Threshold for compression savings.
7777
const IGNORE_THRESHOLD_IN_BYTES = 1400;
7878

79+
export function isDocumentLatency(x: InsightModel): x is DocumentLatencyInsightModel {
80+
return x.insightKey === 'DocumentLatency';
81+
}
82+
7983
export type DocumentLatencyInsightModel = InsightModel<typeof UIStrings, {
8084
data?: {
8185
serverResponseTime: Types.Timing.Milli,
8286
redirectDuration: Types.Timing.Milli,
8387
uncompressedResponseBytes: number,
88+
checklist: Checklist<'noRedirects'|'serverResponseIsFast'|'usesCompression'>,
8489
documentRequest?: Types.Events.SyntheticNetworkRequest,
85-
checklist: Checklist<'noRedirects'|'serverResponseIsFast'|'usesCompression'>,
8690
},
8791
}>;
8892

front_end/panels/ai_assistance/data_formatters/PerformanceInsightFormatter.test.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -157,7 +157,61 @@ The result of the checks for this insight are:
157157
});
158158
});
159159

160+
describe('Document request latency', () => {
161+
it('serializes the correct details', async function() {
162+
const {parsedTrace, insights} = await TraceLoader.traceEngine(this, 'bad-document-request-latency.json.gz');
163+
assert.isOk(insights);
164+
const firstNav = getFirstOrError(parsedTrace.Meta.navigationsByNavigationId.values());
165+
const insight = getInsightOrError('DocumentLatency', insights, firstNav);
166+
167+
const formatter = new PerformanceInsightFormatter(new ActiveInsight(insight, parsedTrace));
168+
const output = formatter.formatInsight();
169+
170+
const expected = `*IMPORTANT*: all time units given to you are in milliseconds.
171+
## Insight title: Document request latency
172+
173+
## Insight Description:
174+
This insight checks that the first request is responded to promptly. We use the following criteria to check this:
175+
1. Was the initial request redirected?
176+
2. Did the server respond in 600ms or less? We want developers to aim for as close to 100ms as possible, but our threshold for this insight is 600ms.
177+
3. Was there compression applied to the response to minimize the transfer size?
178+
179+
## External resources:
180+
- https://web.dev/articles/optimize-ttfb
181+
182+
## Insight details:
183+
The Largest Contentful Paint (LCP) time for this navigation was 3,604.15 ms.
184+
The LCP is text based and was not fetched from the network.
185+
186+
The result of the checks for this insight are:
187+
- The request was not redirected: FAILED
188+
- Server responded quickly: FAILED
189+
- Compression was applied: FAILED`;
190+
191+
assert.strictEqual(output, expected);
192+
});
193+
});
194+
160195
describe('Formatting TraceEvents', () => {
196+
it('formats network requests that have redirects', async function() {
197+
const {parsedTrace} = await TraceLoader.traceEngine(this, 'bad-document-request-latency.json.gz');
198+
const requestUrl = 'http://localhost:3000/redirect3';
199+
const request = parsedTrace.NetworkRequests.byTime.find(r => r.args.data.url === requestUrl);
200+
assert.isOk(request);
201+
const output = TraceEventFormatter.networkRequest(request, parsedTrace, {verbose: true});
202+
assert.include(output, `Redirects:
203+
#### Redirect 1: http://localhost:3000/
204+
- Start time: 3.04 ms
205+
- Duration: 512.02 ms
206+
#### Redirect 2: http://localhost:3000/redirect1
207+
- Start time: 515.06 ms
208+
- Duration: 505.67 ms
209+
#### Redirect 3: http://localhost:3000/redirect2
210+
- Start time: 1,020.73 ms
211+
- Duration: 507.09 ms
212+
`);
213+
});
214+
161215
it('formats network requests in verbose mode', async function() {
162216
const {parsedTrace} = await TraceLoader.traceEngine(this, 'lcp-images.json.gz');
163217
const requestUrl = 'https://fonts.googleapis.com/css2?family=Poppins:ital,wght@1,800';
@@ -175,6 +229,7 @@ Durations:
175229
- Main thread processing duration: 3.51 ms
176230
- Total duration: 13.93 ms
177231
Initiator: https://chromedevtools.github.io/performance-stories/lcp-large-image/index.html
232+
Redirects: no redirects
178233
Status code: 200
179234
MIME Type: text/css
180235
Priority: VeryHigh

front_end/panels/ai_assistance/data_formatters/PerformanceInsightFormatter.ts

Lines changed: 39 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
// Copyright 2025 The Chromium Authors. All rights reserved.
22
// Use of this source code is governed by a BSD-style license that can be
33
// found in the LICENSE file.
4+
45
import * as i18n from '../../../core/i18n/i18n.js';
56
import * as Trace from '../../../models/trace/trace.js';
67
import type * as TimelineUtils from '../../timeline/utils/utils.js';
@@ -172,6 +173,31 @@ ${checklistBulletPoints.map(point => `- ${point.name}: ${point.passed ? 'PASSED'
172173
173174
${requestSummary.join('\n\n')}`;
174175
}
176+
177+
if (Trace.Insights.Models.DocumentLatency.isDocumentLatency(this.#insight)) {
178+
if (!this.#insight.data) {
179+
return '';
180+
}
181+
const {checklist} = this.#insight.data;
182+
const checklistBulletPoints: Array<{name: string, passed: boolean}> = [];
183+
checklistBulletPoints.push({
184+
name: 'The request was not redirected',
185+
passed: checklist.noRedirects.value,
186+
});
187+
checklistBulletPoints.push({
188+
name: 'Server responded quickly',
189+
passed: checklist.serverResponseIsFast.value,
190+
});
191+
checklistBulletPoints.push({
192+
name: 'Compression was applied',
193+
passed: checklist.usesCompression.value,
194+
});
195+
return `${this.#lcpMetricSharedContext()}
196+
197+
The result of the checks for this insight are:
198+
${checklistBulletPoints.map(point => `- ${point.name}: ${point.passed ? 'PASSED' : 'FAILED'}`).join('\n')}`;
199+
}
200+
175201
return '';
176202
}
177203

@@ -180,7 +206,7 @@ ${requestSummary.join('\n\n')}`;
180206
case 'CLSCulprits':
181207
return '';
182208
case 'DocumentLatency':
183-
return '';
209+
return '- https://web.dev/articles/optimize-ttfb';
184210
case 'DOMSize':
185211
return '';
186212
case 'DuplicateJavaScript':
@@ -218,7 +244,10 @@ ${requestSummary.join('\n\n')}`;
218244
case 'CLSCulprits':
219245
return '';
220246
case 'DocumentLatency':
221-
return '';
247+
return `This insight checks that the first request is responded to promptly. We use the following criteria to check this:
248+
1. Was the initial request redirected?
249+
2. Did the server respond in 600ms or less? We want developers to aim for as close to 100ms as possible, but our threshold for this insight is 600ms.
250+
3. Was there compression applied to the response to minimize the transfer size?`;
222251
case 'DOMSize':
223252
return '';
224253
case 'DuplicateJavaScript':
@@ -308,6 +337,13 @@ export class TraceEventFormatter {
308337
priorityLines.push(`Final priority: ${priority}`);
309338
}
310339

340+
const redirects = request.args.data.redirects.map((redirect, index) => {
341+
const startTime = redirect.ts - baseTime;
342+
return `#### Redirect ${index + 1}: ${redirect.url}
343+
- Start time: ${formatMicro(startTime)}
344+
- Duration: ${formatMicro(redirect.dur)}`;
345+
});
346+
311347
if (!options.verbose) {
312348
return `## Network request: ${url}
313349
- Start time: ${formatMicro(startTimesForLifecycle.start)}
@@ -325,6 +361,7 @@ Timings:
325361
Durations:
326362
- Main thread processing duration: ${formatMicro(mainThreadProcessingDuration)}
327363
- Total duration: ${formatMicro(request.dur)}${initiator ? `\nInitiator: ${initiator.args.data.url}` : ''}
364+
Redirects:${redirects.length ? '\n' + redirects.join('\n') : ' no redirects'}
328365
Status code: ${statusCode}
329366
MIME Type: ${mimeType}
330367
${priorityLines.join('\n')}

front_end/panels/timeline/fixtures/traces/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ copy_to_gen("traces") {
99
"about-blank-first.json.gz",
1010
"animation.json.gz",
1111
"async-js-calls.json.gz",
12+
"bad-document-request-latency.json.gz",
1213
"basic-stack.json.gz",
1314
"basic.cpuprofile.gz",
1415
"basic.json.gz",
Binary file not shown.

0 commit comments

Comments
 (0)