Skip to content

Commit c5b5389

Browse files
committed
Add injection duration telemetry
1 parent 14c338b commit c5b5389

File tree

8 files changed

+90
-3
lines changed

8 files changed

+90
-3
lines changed

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

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public class RumHttpServletResponseWrapper extends HttpServletResponseWrapper {
1818
private PrintWriter printWriter;
1919
private InjectingPipeWriter wrappedPipeWriter;
2020
private boolean shouldInject = true;
21+
private long injectionStartTime = -1;
2122

2223
private static final MethodHandle SET_CONTENT_LENGTH_LONG = getMh("setContentLengthLong");
2324

@@ -48,6 +49,10 @@ public ServletOutputStream getOutputStream() throws IOException {
4849
RumInjector.getTelemetryCollector().onInjectionSkipped();
4950
return super.getOutputStream();
5051
}
52+
// start timing injection
53+
if (injectionStartTime == -1) {
54+
injectionStartTime = System.nanoTime();
55+
}
5156
try {
5257
String encoding = getCharacterEncoding();
5358
if (encoding == null) {
@@ -76,6 +81,10 @@ public PrintWriter getWriter() throws IOException {
7681
RumInjector.getTelemetryCollector().onInjectionSkipped();
7782
return super.getWriter();
7883
}
84+
// start timing injection
85+
if (injectionStartTime == -1) {
86+
injectionStartTime = System.nanoTime();
87+
}
7988
try {
8089
wrappedPipeWriter =
8190
new InjectingPipeWriter(
@@ -149,6 +158,7 @@ public void reset() {
149158
this.wrappedPipeWriter = null;
150159
this.printWriter = null;
151160
this.shouldInject = false;
161+
this.injectionStartTime = -1;
152162
super.reset();
153163
}
154164

@@ -162,6 +172,15 @@ public void resetBuffer() {
162172

163173
public void onInjected() {
164174
RumInjector.getTelemetryCollector().onInjectionSucceed();
175+
176+
// report injection time
177+
if (injectionStartTime != -1) {
178+
long nanoseconds = System.nanoTime() - injectionStartTime;
179+
long milliseconds = nanoseconds / 1_000_000L;
180+
RumInjector.getTelemetryCollector().onInjectionTime(milliseconds);
181+
injectionStartTime = -1;
182+
}
183+
165184
try {
166185
setHeader("x-datadog-rum-injected", "1");
167186
} 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: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ public class RumHttpServletResponseWrapper extends HttpServletResponseWrapper {
1515
private InjectingPipeWriter wrappedPipeWriter;
1616
private PrintWriter printWriter;
1717
private boolean shouldInject = true;
18+
private long injectionStartTime = -1;
1819

1920
public RumHttpServletResponseWrapper(HttpServletResponse response) {
2021
super(response);
@@ -30,6 +31,10 @@ public ServletOutputStream getOutputStream() throws IOException {
3031
RumInjector.getTelemetryCollector().onInjectionSkipped();
3132
return super.getOutputStream();
3233
}
34+
// start timing injection
35+
if (injectionStartTime == -1) {
36+
injectionStartTime = System.nanoTime();
37+
}
3338
try {
3439
String encoding = getCharacterEncoding();
3540
if (encoding == null) {
@@ -57,6 +62,10 @@ public PrintWriter getWriter() throws IOException {
5762
RumInjector.getTelemetryCollector().onInjectionSkipped();
5863
return super.getWriter();
5964
}
65+
// start timing installation
66+
if (injectionStartTime == -1) {
67+
injectionStartTime = System.nanoTime();
68+
}
6069
try {
6170
wrappedPipeWriter =
6271
new InjectingPipeWriter(
@@ -126,6 +135,7 @@ public void reset() {
126135
this.wrappedPipeWriter = null;
127136
this.printWriter = null;
128137
this.shouldInject = false;
138+
this.injectionStartTime = -1;
129139
super.reset();
130140
}
131141

@@ -139,6 +149,15 @@ public void resetBuffer() {
139149

140150
public void onInjected() {
141151
RumInjector.getTelemetryCollector().onInjectionSucceed();
152+
153+
// report injection time
154+
if (injectionStartTime != -1) {
155+
long nanoseconds = System.nanoTime() - injectionStartTime;
156+
long milliseconds = nanoseconds / 1_000_000L;
157+
RumInjector.getTelemetryCollector().onInjectionTime(milliseconds);
158+
injectionStartTime = -1;
159+
}
160+
142161
try {
143162
setHeader("x-datadog-rum-injected", "1");
144163
} 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
@@ -169,6 +169,26 @@ class RumInjectorTest extends DDSpecification {
169169
RumInjector.shutdownTelemetry()
170170
}
171171

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

0 commit comments

Comments
 (0)