Skip to content

Commit

Permalink
Closes #660 - Reporting resource response times in the EUM server (#662)
Browse files Browse the repository at this point in the history
  • Loading branch information
ivansenic authored Apr 7, 2020
1 parent b6fbc9b commit b70f1f9
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 10 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,8 +59,14 @@ public class ResourceTimingBeaconRecorder implements BeaconRecorder {
tags.put("crossOrigin", true);

RESOURCE_TIME = MetricDefinitionSettings.builder()
.type(MetricDefinitionSettings.MeasureType.LONG)
.type(MetricDefinitionSettings.MeasureType.DOUBLE)
.description("Response end time of the resource loading")
.unit("ms")
.view(RESOURCE_TIME_METRIC_NAME + "/SUM", ViewDefinitionSettings.builder()
.tags(tags)
.aggregation(ViewDefinitionSettings.Aggregation.SUM)
.build()
)
.view(RESOURCE_TIME_METRIC_NAME + "/COUNT", ViewDefinitionSettings.builder()
.tags(tags)
.aggregation(ViewDefinitionSettings.Aggregation.COUNT)
Expand Down Expand Up @@ -117,16 +123,13 @@ private void record(ResourceTimingEntry resourceTimingEntry, String url) {
boolean sameOrigin = isSameOrigin(url, resourceTimingEntry.url);
extra.put("crossOrigin", String.valueOf(!sameOrigin));
extra.put("initiatorType", resourceTimingEntry.getInitiatorType().toString());
// TODO is this OK, only setting cached if it's same origin, otherwise we can not know
if (sameOrigin) {
extra.put("cached", String.valueOf(resourceTimingEntry.isCached(true)));
}

try (Scope scope = measuresAndViewsManager.getTagContext(extra).buildScoped()) {
// TODO I think we should already collect there the load time, thus we would have a COUNT and a SUM of time
// we would have most of the stuff needed then
// and it would make tests more reliable then now
measuresAndViewsManager.recordMeasure("resource_time", RESOURCE_TIME, 1L);
Optional<Integer> responseEnd = resourceTimingEntry.getResponseEnd();
measuresAndViewsManager.recordMeasure("resource_time", RESOURCE_TIME, responseEnd.orElse(0));
}
}

Expand Down Expand Up @@ -248,6 +251,19 @@ public static class ResourceTimingEntry {
*/
Integer[] timings;

/**
* @return Returns the complete response time in milliseconds if this timing data is available.
*/
public Optional<Integer> getResponseEnd() {
return getTiming(1);
}

private Optional<Integer> getTiming(int index) {
return Optional.ofNullable(timings)
.filter(t -> t.length - 1 >= index)
.map(t -> t[index]);
}

/**
* Cached resources only have 2 timing values if considered as same-origin requests.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,23 @@ public void notAJson() {
verifyNoMoreInteractions(measuresAndViewsManager);
}

@Test
public void badCompression() {
// intentionally change the initiator
String json = "" +
"{\n" +
" \"https://www.google.de/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png\": \"!!\"\n" +
"}";
Map<String, String> fields = new HashMap<>();
fields.put("u", "http://myhost/somepage.html");
fields.put("restiming", json);
Beacon beacon = Beacon.of(fields);

recorder.record(beacon);

verifyNoMoreInteractions(measuresAndViewsManager);
}

@Test
public void nestedResources() {
String json = "" +
Expand All @@ -89,7 +106,10 @@ public void nestedResources() {
recorder.record(beacon);

verify(measuresAndViewsManager, atLeastOnce()).getTagContext(tagsCaptor.capture());
verify(measuresAndViewsManager, times(4)).recordMeasure(eq("resource_time"), any(), eq(1L));
verify(measuresAndViewsManager).recordMeasure(eq("resource_time"), any(), eq(2));
verify(measuresAndViewsManager).recordMeasure(eq("resource_time"), any(), eq(102));
verify(measuresAndViewsManager).recordMeasure(eq("resource_time"), any(), eq(104));
verify(measuresAndViewsManager).recordMeasure(eq("resource_time"), any(), eq(129));
verifyNoMoreInteractions(measuresAndViewsManager);

assertThat(tagsCaptor.getAllValues()).hasSize(4)
Expand Down Expand Up @@ -124,16 +144,18 @@ public void pipedData() {
// intentionally change the initiator
String json = "" +
"{\n" +
" \"https://www.google.de/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png\": \"*02k,7k,1y,a2|180,3l|280,3l\"\n" +
" \"https://www.google.de/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png\": \"*02k,7k,1y,a2|180,3l|280,4l\"\n" +
"}";
Map<String, String> fields = new HashMap<>();
fields.put("u", "http://myhost/somepage.html");
fields.put("restiming", json);
Beacon beacon = Beacon.of(fields);

recorder.record(beacon);

verify(measuresAndViewsManager, atLeastOnce()).getTagContext(tagsCaptor.capture());
verify(measuresAndViewsManager, times(2)).recordMeasure(eq("resource_time"), any(), eq(1L));
verify(measuresAndViewsManager).recordMeasure(eq("resource_time"), any(), eq(129));
verify(measuresAndViewsManager).recordMeasure(eq("resource_time"), any(), eq(165));
verifyNoMoreInteractions(measuresAndViewsManager);

assertThat(tagsCaptor.getAllValues()).hasSize(2)
Expand All @@ -160,6 +182,7 @@ public void onlyAdditionalData() {
fields.put("u", "http://myhost/somepage.html");
fields.put("restiming", json);
Beacon beacon = Beacon.of(fields);

recorder.record(beacon);

verifyNoMoreInteractions(measuresAndViewsManager);
Expand All @@ -176,10 +199,11 @@ public void wrongInitiator() {
fields.put("u", "http://myhost/somepage.html");
fields.put("restiming", json);
Beacon beacon = Beacon.of(fields);

recorder.record(beacon);

verify(measuresAndViewsManager, atLeastOnce()).getTagContext(tagsCaptor.capture());
verify(measuresAndViewsManager, times(1)).recordMeasure(eq("resource_time"), any(), eq(1L));
verify(measuresAndViewsManager).recordMeasure(eq("resource_time"), any(), eq(129));
verifyNoMoreInteractions(measuresAndViewsManager);

assertThat(tagsCaptor.getAllValues()).hasSize(1)
Expand All @@ -190,6 +214,32 @@ public void wrongInitiator() {
);
}

@Test
public void noResponseEnd() {
// intentionally change the initiator
String json = "" +
"{\n" +
" \"https://www.google.de/images/branding/googlelogo/1x/googlelogo_color_272x92dp.png\": \"180\"\n" +
"}";
Map<String, String> fields = new HashMap<>();
fields.put("u", "http://myhost/somepage.html");
fields.put("restiming", json);
Beacon beacon = Beacon.of(fields);

recorder.record(beacon);

verify(measuresAndViewsManager, atLeastOnce()).getTagContext(tagsCaptor.capture());
verify(measuresAndViewsManager).recordMeasure(eq("resource_time"), any(), eq(0));
verifyNoMoreInteractions(measuresAndViewsManager);

assertThat(tagsCaptor.getAllValues()).hasSize(1)
// first
.anySatisfy(map -> assertThat(map).hasSize(2)
.containsEntry("initiatorType", "IMG")
.containsEntry("crossOrigin", "true")
);
}

}

}

0 comments on commit b70f1f9

Please sign in to comment.