Skip to content

Commit 1cf96aa

Browse files
authored
Merge pull request #964 from microsoft/fix_quickpulse_counters
counter variables should not be static
2 parents 6c2d6e3 + 8f3ce1e commit 1cf96aa

File tree

4 files changed

+201
-13
lines changed

4 files changed

+201
-13
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
- Added retries to CDSProfileFetcher [#901](https://github.com/microsoft/ApplicationInsights-Java/pull/901)
1010
- Fix [#919](https://github.com/microsoft/ApplicationInsights-Java/issues/919) - Fixed issue when adding duplicate Windows performance counter.
1111
- Added caching of sdk version id, reducing number of file IO operations [#896](https://github.com/microsoft/ApplicationInsights-Java/pull/896)
12+
- Fixed bug with live metrics (QuickPulse) where request/dependency durations were being truncated to the millisecond.
1213
- Misc stability improvements
1314
[#932](https://github.com/microsoft/ApplicationInsights-Java/pull/932)
1415
[#941](https://github.com/microsoft/ApplicationInsights-Java/pull/941)

core/src/main/java/com/microsoft/applicationinsights/internal/quickpulse/QuickPulseDataCollector.java

Lines changed: 20 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -72,19 +72,19 @@ public FinalCounters(Counters currentCounters, MemoryMXBean memory, CpuPerforman
7272
}
7373
exceptions = currentCounters.exceptions.get();
7474

75-
CountAndDuration countAndDuration = currentCounters.decodeCountAndDuration(currentCounters.requestsAndDurations.get());
75+
CountAndDuration countAndDuration = Counters.decodeCountAndDuration(currentCounters.requestsAndDurations.get());
7676
requests = countAndDuration.count;
7777
this.requestsDuration = countAndDuration.duration;
7878
this.unsuccessfulRequests = currentCounters.unsuccessfulRequests.get();
7979

80-
countAndDuration = currentCounters.decodeCountAndDuration(currentCounters.rddsAndDuations.get());
80+
countAndDuration = Counters.decodeCountAndDuration(currentCounters.rddsAndDuations.get());
8181
this.rdds = countAndDuration.count;
8282
this.rddsDuration = countAndDuration.duration;
8383
this.unsuccessfulRdds = currentCounters.unsuccessfulRdds.get();
8484
}
8585
}
8686

87-
private static class CountAndDuration {
87+
static class CountAndDuration {
8888
public final long count;
8989
public final long duration;
9090

@@ -94,17 +94,17 @@ private CountAndDuration(long count, long duration) {
9494
}
9595
}
9696

97-
private static class Counters {
97+
static class Counters {
9898
private static final long MAX_COUNT = 524287L;
9999
private static final long MAX_DURATION = 17592186044415L;
100100

101-
public static final AtomicInteger exceptions = new AtomicInteger(0);
101+
public final AtomicInteger exceptions = new AtomicInteger(0);
102102

103-
static final AtomicLong requestsAndDurations = new AtomicLong(0);
104-
static final AtomicInteger unsuccessfulRequests = new AtomicInteger(0);
103+
final AtomicLong requestsAndDurations = new AtomicLong(0);
104+
final AtomicInteger unsuccessfulRequests = new AtomicInteger(0);
105105

106-
static final AtomicLong rddsAndDuations = new AtomicLong(0);
107-
static final AtomicInteger unsuccessfulRdds = new AtomicInteger(0);
106+
final AtomicLong rddsAndDuations = new AtomicLong(0);
107+
final AtomicInteger unsuccessfulRdds = new AtomicInteger(0);
108108

109109
static long encodeCountAndDuration(long count, long duration) {
110110
if (count > MAX_COUNT || duration > MAX_DURATION) {
@@ -161,6 +161,15 @@ public synchronized FinalCounters getAndRestart() {
161161
return null;
162162
}
163163

164+
/*@VisibleForTesting*/
165+
synchronized FinalCounters peek() {
166+
final Counters currentCounters = this.counters.get(); // this should be the only differece
167+
if (currentCounters != null) {
168+
return new FinalCounters(currentCounters, memory, cpuPerformanceCounterCalculator);
169+
}
170+
return null;
171+
}
172+
164173
public void add(Telemetry telemetry) {
165174
if (!telemetry.getContext().getInstrumentationKey().equals(ikey)) {
166175
return;
@@ -182,7 +191,7 @@ private void addDependency(RemoteDependencyTelemetry telemetry) {
182191
return;
183192
}
184193
counters.rddsAndDuations.addAndGet(
185-
Counters.encodeCountAndDuration(1, telemetry.getDuration().getMilliseconds()));
194+
Counters.encodeCountAndDuration(1, telemetry.getDuration().getTotalMilliseconds()));
186195
if (!telemetry.getSuccess()) {
187196
counters.unsuccessfulRdds.incrementAndGet();
188197
}
@@ -203,7 +212,7 @@ private void addRequest(RequestTelemetry requestTelemetry) {
203212
return;
204213
}
205214

206-
counters.requestsAndDurations.addAndGet(Counters.encodeCountAndDuration(1, requestTelemetry.getDuration().getMilliseconds()));
215+
counters.requestsAndDurations.addAndGet(Counters.encodeCountAndDuration(1, requestTelemetry.getDuration().getTotalMilliseconds()));
207216
if (!requestTelemetry.isSuccess()) {
208217
counters.unsuccessfulRequests.incrementAndGet();
209218
}
Lines changed: 170 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,170 @@
1+
package com.microsoft.applicationinsights.internal.quickpulse;
2+
3+
import com.microsoft.applicationinsights.internal.quickpulse.QuickPulseDataCollector.CountAndDuration;
4+
import com.microsoft.applicationinsights.internal.quickpulse.QuickPulseDataCollector.Counters;
5+
import com.microsoft.applicationinsights.internal.quickpulse.QuickPulseDataCollector.FinalCounters;
6+
import com.microsoft.applicationinsights.telemetry.Duration;
7+
import com.microsoft.applicationinsights.telemetry.ExceptionTelemetry;
8+
import com.microsoft.applicationinsights.telemetry.RemoteDependencyTelemetry;
9+
import com.microsoft.applicationinsights.telemetry.RequestTelemetry;
10+
import org.junit.*;
11+
12+
import java.util.Date;
13+
14+
import static org.junit.Assert.*;
15+
16+
public class QuickPulseDataCollectorTests {
17+
18+
private static final String FAKE_INSTRUMENTATION_KEY = "fake-instrumentation-key";
19+
20+
@Before
21+
public void setup() {
22+
QuickPulseDataCollector.INSTANCE.disable();
23+
}
24+
25+
@After
26+
public void tearDown() {
27+
QuickPulseDataCollector.INSTANCE.disable();
28+
}
29+
30+
@Test
31+
public void initialStateIsDisabled() {
32+
assertNull(QuickPulseDataCollector.INSTANCE.peek());
33+
}
34+
35+
@Test
36+
public void emptyCountsAndDurationsAfterEnable() {
37+
QuickPulseDataCollector.INSTANCE.enable(FAKE_INSTRUMENTATION_KEY);
38+
final FinalCounters counters = QuickPulseDataCollector.INSTANCE.peek();
39+
assertCountersReset(counters);
40+
}
41+
42+
@Test
43+
public void nullCountersAfterDisable() {
44+
QuickPulseDataCollector.INSTANCE.enable(FAKE_INSTRUMENTATION_KEY);
45+
QuickPulseDataCollector.INSTANCE.disable();
46+
assertNull(QuickPulseDataCollector.INSTANCE.peek());
47+
}
48+
49+
@Test
50+
public void requestTelemetryIsCounted_DurationIsSum() {
51+
QuickPulseDataCollector.INSTANCE.enable(FAKE_INSTRUMENTATION_KEY);
52+
53+
// add a success and peek
54+
final long duration = 112233L;
55+
RequestTelemetry rt = new RequestTelemetry("request-test", new Date(), duration, "200", true);
56+
rt.getContext().setInstrumentationKey(FAKE_INSTRUMENTATION_KEY);
57+
QuickPulseDataCollector.INSTANCE.add(rt);
58+
FinalCounters counters = QuickPulseDataCollector.INSTANCE.peek();
59+
assertEquals(1, counters.requests);
60+
assertEquals(0, counters.unsuccessfulRequests);
61+
assertEquals((double)duration, counters.requestsDuration, Math.ulp((double)duration));
62+
63+
// add another success and peek
64+
final long duration2 = 65421L;
65+
rt = new RequestTelemetry("request-test-2", new Date(), duration2, "200", true);
66+
rt.getContext().setInstrumentationKey(FAKE_INSTRUMENTATION_KEY);
67+
QuickPulseDataCollector.INSTANCE.add(rt);
68+
counters = QuickPulseDataCollector.INSTANCE.peek();
69+
double total = duration + duration2;
70+
assertEquals(2, counters.requests);
71+
assertEquals(0, counters.unsuccessfulRequests);
72+
assertEquals(total, counters.requestsDuration, Math.ulp(total));
73+
74+
// add a failure and get/reset
75+
final long duration3 = 9988L;
76+
rt = new RequestTelemetry("request-test-3", new Date(), duration3, "400", false);
77+
rt.getContext().setInstrumentationKey(FAKE_INSTRUMENTATION_KEY);
78+
QuickPulseDataCollector.INSTANCE.add(rt);
79+
counters = QuickPulseDataCollector.INSTANCE.getAndRestart();
80+
total += duration3;
81+
assertEquals(3, counters.requests);
82+
assertEquals(1, counters.unsuccessfulRequests);
83+
assertEquals(total, counters.requestsDuration, Math.ulp(total));
84+
85+
assertCountersReset(QuickPulseDataCollector.INSTANCE.peek());
86+
}
87+
88+
@Test
89+
public void dependencyTelemetryIsCounted_DurationIsSum() {
90+
QuickPulseDataCollector.INSTANCE.enable(FAKE_INSTRUMENTATION_KEY);
91+
92+
// add a success and peek.
93+
final long duration = 112233L;
94+
RemoteDependencyTelemetry rdt = new RemoteDependencyTelemetry("dep-test", "dep-test-cmd", new Duration(duration), true);
95+
rdt.getContext().setInstrumentationKey(FAKE_INSTRUMENTATION_KEY);
96+
QuickPulseDataCollector.INSTANCE.add(rdt);
97+
FinalCounters counters = QuickPulseDataCollector.INSTANCE.peek();
98+
assertEquals(1, counters.rdds);
99+
assertEquals(0, counters.unsuccessfulRdds);
100+
assertEquals((double)duration, counters.rddsDuration, Math.ulp((double)duration));
101+
102+
// add another success and peek.
103+
final long duration2 = 334455L;
104+
rdt = new RemoteDependencyTelemetry("dep-test-2", "dep-test-cmd-2", new Duration(duration2), true);
105+
rdt.getContext().setInstrumentationKey(FAKE_INSTRUMENTATION_KEY);
106+
QuickPulseDataCollector.INSTANCE.add(rdt);
107+
counters = QuickPulseDataCollector.INSTANCE.peek();
108+
assertEquals(2, counters.rdds);
109+
assertEquals(0, counters.unsuccessfulRdds);
110+
double total = duration + duration2;
111+
assertEquals(total, counters.rddsDuration, Math.ulp(total));
112+
113+
// add a failure and get/reset.
114+
final long duration3 = 123456L;
115+
rdt = new RemoteDependencyTelemetry("dep-test-3", "dep-test-cmd-3", new Duration(duration3), false);
116+
rdt.getContext().setInstrumentationKey(FAKE_INSTRUMENTATION_KEY);
117+
QuickPulseDataCollector.INSTANCE.add(rdt);
118+
counters = QuickPulseDataCollector.INSTANCE.getAndRestart();
119+
assertEquals(3, counters.rdds);
120+
assertEquals(1, counters.unsuccessfulRdds);
121+
total += duration3;
122+
assertEquals(total, counters.rddsDuration, Math.ulp(total));
123+
124+
assertCountersReset(QuickPulseDataCollector.INSTANCE.peek());
125+
}
126+
127+
@Test
128+
public void exceptionTelemetryIsCounted() {
129+
QuickPulseDataCollector.INSTANCE.enable(FAKE_INSTRUMENTATION_KEY);
130+
131+
ExceptionTelemetry et = new ExceptionTelemetry(new Exception());
132+
et.getContext().setInstrumentationKey(FAKE_INSTRUMENTATION_KEY);
133+
QuickPulseDataCollector.INSTANCE.add(et);
134+
FinalCounters counters = QuickPulseDataCollector.INSTANCE.peek();
135+
assertEquals(1, counters.exceptions, Math.ulp(1.0));
136+
137+
et = new ExceptionTelemetry(new Exception());
138+
et.getContext().setInstrumentationKey(FAKE_INSTRUMENTATION_KEY);
139+
QuickPulseDataCollector.INSTANCE.add(et);
140+
counters = QuickPulseDataCollector.INSTANCE.getAndRestart();
141+
assertEquals(2, counters.exceptions, Math.ulp(2.0));
142+
143+
assertCountersReset(QuickPulseDataCollector.INSTANCE.peek());
144+
}
145+
146+
@Test
147+
public void encodeDecodeIsIdentity() {
148+
final long count = 456L;
149+
final long duration = 112233L;
150+
final long encoded = Counters.encodeCountAndDuration(count, duration);
151+
final CountAndDuration inputs = Counters.decodeCountAndDuration(encoded);
152+
assertEquals(count, inputs.count);
153+
assertEquals(duration, inputs.duration);
154+
}
155+
156+
private void assertCountersReset(FinalCounters counters) {
157+
assertNotNull(counters);
158+
159+
assertEquals(0, counters.rdds);
160+
assertEquals(0.0, counters.rddsDuration, Math.ulp(0.0));
161+
assertEquals(0, counters.unsuccessfulRdds);
162+
163+
assertEquals(0, counters.requests);
164+
assertEquals(0.0, counters.requestsDuration, Math.ulp(0.0));
165+
assertEquals(0, counters.unsuccessfulRequests);
166+
167+
// FIXME exceptions is stored as a double but counted as an int; is that correct?
168+
assertEquals(0, (int) counters.exceptions);
169+
}
170+
}

test/smoke/testApps/CoreAndFilter/src/smokeTest/java/com/microsoft/applicationinsights/smoketest/CoreAndFilterTests.java

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import static org.hamcrest.Matchers.*;
2929
import static org.junit.Assert.*;
3030

31+
import java.util.Comparator;
3132
import java.util.List;
3233

3334
public class CoreAndFilterTests extends AiSmokeTest {
@@ -65,11 +66,18 @@ public void testTrackEvent() throws Exception {
6566
expectedItems, totalItems);
6667

6768
// TODO get event data envelope and verify value
68-
EventData d = getTelemetryDataForType(0, "EventData");
69+
final List<EventData> events = mockedIngestion.getTelemetryDataByType("EventData");
70+
events.sort(new Comparator<EventData>() {
71+
@Override
72+
public int compare(EventData o1, EventData o2) {
73+
return o1.getName().compareTo(o2.getName());
74+
}
75+
});
76+
EventData d = events.get(1);
6977
final String name = "EventDataTest";
7078
assertEquals(name, d.getName());
7179

72-
EventData d2 = getTelemetryDataForType(1, "EventData");
80+
EventData d2 = events.get(0);
7381

7482
final String expectedname = "EventDataPropertyTest";
7583
final String expectedProperties = "value";

0 commit comments

Comments
 (0)