Skip to content

Commit dbf1bee

Browse files
committed
Add injection duration telemetry
1 parent 534f05f commit dbf1bee

File tree

8 files changed

+92
-3
lines changed

8 files changed

+92
-3
lines changed

dd-java-agent/instrumentation/servlet/request-3/src/main/java/datadog/trace/instrumentation/servlet3/RumHttpServletResponseWrapper.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public class RumHttpServletResponseWrapper extends HttpServletResponseWrapper {
1414
private ServletOutputStream outputStream;
1515
private PrintWriter printWriter;
1616
private boolean shouldInject = false;
17+
private long injectionStartTime = -1;
1718

1819
public RumHttpServletResponseWrapper(HttpServletResponse response) {
1920
super(response);
@@ -27,6 +28,10 @@ public ServletOutputStream getOutputStream() throws IOException {
2728
return super.getOutputStream();
2829
}
2930
if (outputStream == null) {
31+
// start timing injection
32+
if (injectionStartTime == -1) {
33+
injectionStartTime = System.nanoTime();
34+
}
3035
try {
3136
String encoding = getCharacterEncoding();
3237
if (encoding == null) {
@@ -53,6 +58,10 @@ public PrintWriter getWriter() throws IOException {
5358
return super.getWriter();
5459
}
5560
if (printWriter == null) {
61+
// start timing injection
62+
if (injectionStartTime == -1) {
63+
injectionStartTime = System.nanoTime();
64+
}
5665
try {
5766
printWriter =
5867
new PrintWriter(
@@ -111,6 +120,7 @@ public void reset() {
111120
this.outputStream = null;
112121
this.printWriter = null;
113122
this.shouldInject = false;
123+
this.injectionStartTime = -1;
114124
super.reset();
115125
}
116126

@@ -119,11 +129,21 @@ public void resetBuffer() {
119129
this.outputStream = null;
120130
this.printWriter = null;
121131
this.shouldInject = false;
132+
this.injectionStartTime = -1;
122133
super.resetBuffer();
123134
}
124135

125136
public void onInjected() {
126137
RumInjector.getTelemetryCollector().onInjectionSucceed();
138+
139+
// report injection time
140+
if (injectionStartTime != -1) {
141+
long durationNanos = System.nanoTime() - injectionStartTime;
142+
long milliseconds = durationNanos / 1_000_000L;
143+
RumInjector.getTelemetryCollector().onInjectionTime(milliseconds);
144+
injectionStartTime = -1;
145+
}
146+
127147
try {
128148
setHeader("x-datadog-rum-injected", "1");
129149
} catch (Throwable ignored) {

dd-java-agent/instrumentation/servlet/request-3/src/test/groovy/RumHttpServletResponseWrapperTest.groovy

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,4 +135,6 @@ class RumHttpServletResponseWrapperTest extends AgentTestRunner {
135135
then:
136136
1 * mockTelemetryCollector.onInjectionResponseSize(1024)
137137
}
138+
139+
// test injection timing?
138140
}

dd-java-agent/instrumentation/servlet/request-5/src/main/java/datadog/trace/instrumentation/servlet5/RumHttpServletResponseWrapper.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ public class RumHttpServletResponseWrapper extends HttpServletResponseWrapper {
1414
private ServletOutputStream outputStream;
1515
private PrintWriter printWriter;
1616
private boolean shouldInject = false;
17+
private long injectionStartTime = -1;
1718

1819
public RumHttpServletResponseWrapper(HttpServletResponse response) {
1920
super(response);
@@ -27,6 +28,10 @@ public ServletOutputStream getOutputStream() throws IOException {
2728
return super.getOutputStream();
2829
}
2930
if (outputStream == null) {
31+
// start timing injection
32+
if (injectionStartTime == -1) {
33+
injectionStartTime = System.nanoTime();
34+
}
3035
try {
3136
String encoding = getCharacterEncoding();
3237
if (encoding == null) {
@@ -53,6 +58,10 @@ public PrintWriter getWriter() throws IOException {
5358
return super.getWriter();
5459
}
5560
if (printWriter == null) {
61+
// start timing installation
62+
if (injectionStartTime == -1) {
63+
injectionStartTime = System.nanoTime();
64+
}
5665
try {
5766
printWriter =
5867
new PrintWriter(
@@ -111,6 +120,7 @@ public void reset() {
111120
this.outputStream = null;
112121
this.printWriter = null;
113122
this.shouldInject = false;
123+
this.injectionStartTime = -1;
114124
super.reset();
115125
}
116126

@@ -119,11 +129,21 @@ public void resetBuffer() {
119129
this.outputStream = null;
120130
this.printWriter = null;
121131
this.shouldInject = false;
132+
this.injectionStartTime = -1;
122133
super.resetBuffer();
123134
}
124135

125136
public void onInjected() {
126137
RumInjector.getTelemetryCollector().onInjectionSucceed();
138+
139+
// report injection time
140+
if (injectionStartTime != -1) {
141+
long durationNanos = System.nanoTime() - injectionStartTime;
142+
long milliseconds = durationNanos / 1_000_000L;
143+
RumInjector.getTelemetryCollector().onInjectionTime(millise);
144+
injectionStartTime = -1;
145+
}
146+
127147
try {
128148
setHeader("x-datadog-rum-injected", "1");
129149
} catch (Throwable ignored) {

dd-java-agent/instrumentation/servlet/request-5/src/test/groovy/RumHttpServletResponseWrapperTest.groovy

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -135,4 +135,6 @@ class RumHttpServletResponseWrapperTest extends AgentTestRunner {
135135
then:
136136
1 * mockTelemetryCollector.onInjectionResponseSize(1024)
137137
}
138+
139+
// test injection timing?
138140
}

internal-api/src/main/java/datadog/trace/api/rum/RumInjectorMetrics.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,12 @@ public void onInjectionResponseSize(long bytes) {
7373
statsd.distribution("rum.injection.response.bytes", bytes, NO_TAGS);
7474
}
7575

76+
@Override
77+
public void onInjectionTime(long milliseconds) {
78+
// report distribution metric immediately
79+
statsd.distribution("rum.injection.ms", milliseconds, NO_TAGS);
80+
}
81+
7682
public void close() {
7783
if (null != cancellation) {
7884
cancellation.cancel();

internal-api/src/main/java/datadog/trace/api/rum/RumTelemetryCollector.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ public void onContentSecurityPolicyDetected() {}
2424
@Override
2525
public void onInjectionResponseSize(long bytes) {}
2626

27+
@Override
28+
public void onInjectionTime(long milliseconds) {}
29+
2730
@Override
2831
public void close() {}
2932

@@ -47,9 +50,12 @@ default void start() {}
4750
// call when a Content Security Policy header is detected
4851
void onContentSecurityPolicyDetected();
4952

50-
// call to get the response size (in bytes) before RUM injection
53+
// call to get the response size before RUM injection
5154
void onInjectionResponseSize(long bytes);
5255

56+
// call to report the time it takes to inject the RUM SDK
57+
void onInjectionTime(long milliseconds);
58+
5359
default void close() {}
5460

5561
// human-readable summary of the current health metrics

internal-api/src/test/groovy/datadog/trace/api/rum/RumInjectorMetricsTest.groovy

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,14 +87,27 @@ class RumInjectorMetricsTest extends Specification {
8787

8888
def "test onInjectionResponseSize with multiple sizes"() {
8989
when:
90+
metrics.onInjectionResponseSize(256)
9091
metrics.onInjectionResponseSize(512)
9192
metrics.onInjectionResponseSize(2048)
92-
metrics.onInjectionResponseSize(256)
9393

9494
then:
95+
1 * statsD.distribution('rum.injection.response.bytes', 256, _)
9596
1 * statsD.distribution('rum.injection.response.bytes', 512, _)
9697
1 * statsD.distribution('rum.injection.response.bytes', 2048, _)
97-
1 * statsD.distribution('rum.injection.response.bytes', 256, _)
98+
0 * _
99+
}
100+
101+
def "test onInjectionTime with multiple durations"() {
102+
when:
103+
metrics.onInjectionTime(5L)
104+
metrics.onInjectionTime(10L)
105+
metrics.onInjectionTime(25L)
106+
107+
then:
108+
1 * statsD.distribution('rum.injection.ms', 5L, _)
109+
1 * statsD.distribution('rum.injection.ms', 10L, _)
110+
1 * statsD.distribution('rum.injection.ms', 25L, _)
98111
0 * _
99112
}
100113

internal-api/src/test/groovy/datadog/trace/api/rum/RumInjectorTest.groovy

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,26 @@ class RumInjectorTest extends DDSpecification {
165165
RumInjector.shutdownTelemetry()
166166
}
167167

168+
void 'injection time telemetry does not throw an exception'() {
169+
setup:
170+
def mockStatsDClient = mock(datadog.trace.api.StatsDClient)
171+
172+
when:
173+
RumInjector.enableTelemetry(mockStatsDClient)
174+
175+
def telemetryCollector = RumInjector.getTelemetryCollector()
176+
telemetryCollector.onInjectionTime(5L)
177+
telemetryCollector.onInjectionTime(10L)
178+
telemetryCollector.onInjectionTime(20L)
179+
180+
then:
181+
// injection times are reported immediately as distribution metrics
182+
noExceptionThrown()
183+
184+
cleanup:
185+
RumInjector.shutdownTelemetry()
186+
}
187+
168188
void 'concurrent telemetry calls are thread-safe'() {
169189
setup:
170190
RumInjector.enableTelemetry(mock(datadog.trace.api.StatsDClient))

0 commit comments

Comments
 (0)