Skip to content

Commit 1ded29a

Browse files
heyamstrask
andauthored
Track disk IO via Non-essential statsbeat (#1925)
* Just track read and write failures * Increment read/write failure counters * Simplify a bit * Fix tests * Fix spotlessApply * Remove a comment * Update smoke test * Fix smoke test * Add unit tests * Fix smoke test * Add comments * Don't increment read failure count where there is no file to be loaded * Fix smoke test * Increment read failure count at the exception level, not at the higher level * Increment write failure count at the exception level for easier understanding * Fix unit tests * Be consistent and getter used by tests only * Add a todo * Add comment Co-authored-by: Trask Stalnaker <[email protected]> * Delete Co-authored-by: Trask Stalnaker <[email protected]> * Add a comment Co-authored-by: Trask Stalnaker <[email protected]> * Remove this * Fix spotlessApply Co-authored-by: Trask Stalnaker <[email protected]>
1 parent 1813c36 commit 1ded29a

File tree

14 files changed

+242
-81
lines changed

14 files changed

+242
-81
lines changed

agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/localstorage/LocalFileLoader.java

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
package com.microsoft.applicationinsights.agent.internal.localstorage;
2323

2424
import com.microsoft.applicationinsights.agent.internal.common.OperationLogger;
25+
import com.microsoft.applicationinsights.agent.internal.statsbeat.NonessentialStatsbeat;
2526
import java.io.File;
2627
import java.io.IOException;
2728
import java.nio.ByteBuffer;
@@ -37,13 +38,19 @@ public class LocalFileLoader {
3738

3839
private final LocalFileCache localFileCache;
3940
private final File telemetryFolder;
41+
// this is null for Statsbeat telemetry
42+
@Nullable private final NonessentialStatsbeat nonessentialStatsbeat;
4043

4144
private static final OperationLogger operationLogger =
4245
new OperationLogger(LocalFileLoader.class, "Loading telemetry from disk");
4346

44-
public LocalFileLoader(LocalFileCache localFileCache, File telemetryFolder) {
47+
public LocalFileLoader(
48+
LocalFileCache localFileCache,
49+
File telemetryFolder,
50+
@Nullable NonessentialStatsbeat nonessentialStatsbeat) {
4551
this.localFileCache = localFileCache;
4652
this.telemetryFolder = telemetryFolder;
53+
this.nonessentialStatsbeat = nonessentialStatsbeat;
4754
}
4855

4956
// Load ByteBuffer from persisted files on disk in FIFO order.
@@ -81,7 +88,7 @@ PersistedFile loadTelemetriesFromDisk() {
8188
+ TEMPORARY_FILE_EXTENSION
8289
+ " extension: ",
8390
e);
84-
// TODO (heya) track number of failures to create a temp file via Statsbeat
91+
incrementReadFailureCount();
8592
return null;
8693
}
8794

@@ -90,8 +97,8 @@ PersistedFile loadTelemetriesFromDisk() {
9097
// TODO (trask) optimization: read this directly into ByteBuffer(s)
9198
result = Files.readAllBytes(tempFile.toPath());
9299
} catch (IOException ex) {
93-
// TODO (heya) track deserialization failure via Statsbeat
94100
operationLogger.recordFailure("Fail to read telemetry from " + tempFile.getName(), ex);
101+
incrementReadFailureCount();
95102
return null;
96103
}
97104

@@ -139,6 +146,12 @@ private static void deleteFile(File file) {
139146
}
140147
}
141148

149+
private void incrementReadFailureCount() {
150+
if (nonessentialStatsbeat != null) {
151+
nonessentialStatsbeat.incrementReadFailureCount();
152+
}
153+
}
154+
142155
static class PersistedFile {
143156
final File file;
144157
final ByteBuffer rawBytes;

agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/localstorage/LocalFileSender.java

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,6 @@ public void run() {
6767
} catch (RuntimeException ex) {
6868
logger.error(
6969
"Unexpected error occurred while sending telemetries from the local storage.", ex);
70-
// TODO (heya) track sending persisted telemetries failure via Statsbeat.
7170
}
7271
}
7372
}

agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/localstorage/LocalFileWriter.java

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,13 +22,15 @@
2222
package com.microsoft.applicationinsights.agent.internal.localstorage;
2323

2424
import com.microsoft.applicationinsights.agent.internal.common.OperationLogger;
25+
import com.microsoft.applicationinsights.agent.internal.statsbeat.NonessentialStatsbeat;
2526
import java.io.File;
2627
import java.io.FileOutputStream;
2728
import java.io.IOException;
2829
import java.nio.ByteBuffer;
2930
import java.nio.channels.FileChannel;
3031
import java.util.Collection;
3132
import java.util.List;
33+
import javax.annotation.Nullable;
3234
import org.apache.commons.io.FileUtils;
3335
import org.apache.commons.io.FilenameUtils;
3436

@@ -41,41 +43,48 @@ public final class LocalFileWriter {
4143

4244
private final LocalFileCache localFileCache;
4345
private final File telemetryFolder;
46+
// this is null for Statsbeat telemetry
47+
@Nullable private final NonessentialStatsbeat nonessentialStatsbeat;
4448

4549
private static final OperationLogger operationLogger =
4650
new OperationLogger(
4751
LocalFileWriter.class, "Writing telemetry to disk (telemetry is discarded on failure)");
4852

49-
public LocalFileWriter(LocalFileCache localFileCache, File telemetryFolder) {
53+
public LocalFileWriter(
54+
LocalFileCache localFileCache,
55+
File telemetryFolder,
56+
@Nullable NonessentialStatsbeat nonessentialStatsbeat) {
5057
this.telemetryFolder = telemetryFolder;
5158
this.localFileCache = localFileCache;
59+
this.nonessentialStatsbeat = nonessentialStatsbeat;
5260
}
5361

54-
public boolean writeToDisk(List<ByteBuffer> buffers) {
62+
public void writeToDisk(List<ByteBuffer> buffers) {
5563
long size = getTotalSizeOfPersistedFiles(telemetryFolder);
5664
if (size >= MAX_FILE_SIZE_IN_BYTES) {
5765
operationLogger.recordFailure(
5866
"Local persistent storage capacity has been reached. It's currently at ("
5967
+ (size / 1024)
6068
+ "KB). Telemetry will be lost");
61-
return false;
69+
incrementWriteFailureCount();
70+
return;
6271
}
6372

6473
File tempFile;
6574
try {
6675
tempFile = createTempFile(telemetryFolder);
6776
} catch (IOException e) {
6877
operationLogger.recordFailure("unable to create temporary file: " + e, e);
69-
// TODO (heya) track number of failures to create a temp file via Statsbeat
70-
return false;
78+
incrementWriteFailureCount();
79+
return;
7180
}
7281

7382
try {
7483
write(tempFile, buffers);
7584
} catch (IOException e) {
7685
operationLogger.recordFailure(String.format("unable to write to file: %s", e), e);
77-
// TODO (heya) track IO write failure via Statsbeat
78-
return false;
86+
incrementWriteFailureCount();
87+
return;
7988
}
8089

8190
File permanentFile;
@@ -93,15 +102,19 @@ public boolean writeToDisk(List<ByteBuffer> buffers) {
93102
+ PERMANENT_FILE_EXTENSION
94103
+ " extension: ",
95104
e);
96-
// TODO (heya) track number of failures to rename a file via Statsbeat
97-
return false;
105+
incrementWriteFailureCount();
106+
return;
98107
}
99108

100109
localFileCache.addPersistedFilenameToMap(permanentFile.getName());
101110

102-
// TODO (heya) track data persistence success via Statsbeat
103111
operationLogger.recordSuccess();
104-
return true;
112+
}
113+
114+
private void incrementWriteFailureCount() {
115+
if (nonessentialStatsbeat != null) {
116+
nonessentialStatsbeat.incrementWriteFailureCount();
117+
}
105118
}
106119

107120
private static void write(File file, List<ByteBuffer> buffers) throws IOException {
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
/*
2+
* ApplicationInsights-Java
3+
* Copyright (c) Microsoft Corporation
4+
* All rights reserved.
5+
*
6+
* MIT License
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy of this
8+
* software and associated documentation files (the ""Software""), to deal in the Software
9+
* without restriction, including without limitation the rights to use, copy, modify, merge,
10+
* publish, distribute, sublicense, and/or sell copies of the Software, and to permit
11+
* persons to whom the Software is furnished to do so, subject to the following conditions:
12+
* The above copyright notice and this permission notice shall be included in all copies or
13+
* substantial portions of the Software.
14+
* THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED,
15+
* INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR
16+
* PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE
17+
* FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
18+
* OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
19+
* DEALINGS IN THE SOFTWARE.
20+
*/
21+
22+
package com.microsoft.applicationinsights.agent.internal.statsbeat;
23+
24+
import com.microsoft.applicationinsights.agent.internal.exporter.models.TelemetryItem;
25+
import com.microsoft.applicationinsights.agent.internal.telemetry.TelemetryClient;
26+
import java.util.concurrent.atomic.AtomicLong;
27+
28+
public class NonessentialStatsbeat extends BaseStatsbeat {
29+
30+
// TODO when there are more non-essential statsbeat(s) in the future, try to separate different
31+
// kinds of non-essential statsbeat into different classes.
32+
33+
// Track local storage IO success and failure
34+
private static final String READ_FAILURE_COUNT = "Read Failure Count";
35+
private static final String WRITE_FAILURE_COUNT = "Write Failure Count";
36+
private final AtomicLong readFailureCount = new AtomicLong();
37+
private final AtomicLong writeFailureCount = new AtomicLong();
38+
39+
// only used by tests
40+
public NonessentialStatsbeat() {
41+
super(new CustomDimensions());
42+
}
43+
44+
protected NonessentialStatsbeat(CustomDimensions customDimensions) {
45+
super(customDimensions);
46+
}
47+
48+
@Override
49+
protected void send(TelemetryClient telemetryClient) {
50+
if (readFailureCount.get() != 0) {
51+
TelemetryItem telemetryItem =
52+
createStatsbeatTelemetry(telemetryClient, READ_FAILURE_COUNT, readFailureCount.get());
53+
telemetryClient.trackStatsbeatAsync(telemetryItem);
54+
}
55+
56+
if (writeFailureCount.get() != 0) {
57+
TelemetryItem telemetryItem =
58+
createStatsbeatTelemetry(telemetryClient, WRITE_FAILURE_COUNT, writeFailureCount.get());
59+
telemetryClient.trackStatsbeatAsync(telemetryItem);
60+
}
61+
62+
readFailureCount.set(0L);
63+
writeFailureCount.set(0L);
64+
}
65+
66+
public void incrementReadFailureCount() {
67+
readFailureCount.incrementAndGet();
68+
}
69+
70+
// used by tests only
71+
long getReadFailureCount() {
72+
return readFailureCount.get();
73+
}
74+
75+
public void incrementWriteFailureCount() {
76+
writeFailureCount.incrementAndGet();
77+
}
78+
79+
// used by tests only
80+
public long getWriteFailureCount() {
81+
return writeFailureCount.get();
82+
}
83+
}

agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/statsbeat/StatsbeatModule.java

Lines changed: 14 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public class StatsbeatModule {
4646
private final AttachStatsbeat attachStatsbeat;
4747
private final FeatureStatsbeat featureStatsbeat;
4848
private final FeatureStatsbeat instrumentationStatsbeat;
49+
private final NonessentialStatsbeat nonessentialStatsbeat;
4950

5051
private final AtomicBoolean started = new AtomicBoolean();
5152

@@ -55,6 +56,7 @@ public StatsbeatModule(Cache<String, String> ikeyEndpointMap) {
5556
attachStatsbeat = new AttachStatsbeat(customDimensions);
5657
featureStatsbeat = new FeatureStatsbeat(customDimensions, FeatureType.FEATURE);
5758
instrumentationStatsbeat = new FeatureStatsbeat(customDimensions, FeatureType.INSTRUMENTATION);
59+
nonessentialStatsbeat = new NonessentialStatsbeat(customDimensions);
5860
}
5961

6062
public void start(TelemetryClient telemetryClient, Configuration config) {
@@ -104,11 +106,14 @@ public void start(TelemetryClient telemetryClient, Configuration config) {
104106

105107
featureStatsbeat.trackConfigurationOptions(config);
106108

107-
if (config.preview.statsbeat.disabled) {
108-
// disabled will disable non-essentials Statsbeat, such as tracking failure or success of disk
109-
// persistence operations, optional network statsbeat, live metric,
110-
// azure metadata service failure, profile endpoint, etc.
111-
// TODO stop sending non-essential Statsbeat when applicable
109+
if (!config.preview.statsbeat.disabled) {
110+
scheduledExecutor.scheduleWithFixedDelay(
111+
new StatsbeatSender(nonessentialStatsbeat, telemetryClient),
112+
longIntervalSeconds,
113+
longIntervalSeconds,
114+
TimeUnit.SECONDS);
115+
} else {
116+
logger.debug("Non-essential Statsbeat is disabled.");
112117
}
113118
}
114119

@@ -120,6 +125,10 @@ public FeatureStatsbeat getInstrumentationStatsbeat() {
120125
return instrumentationStatsbeat;
121126
}
122127

128+
public NonessentialStatsbeat getNonessentialStatsbeat() {
129+
return nonessentialStatsbeat;
130+
}
131+
123132
/** Runnable which is responsible for calling the send method to transmit Statsbeat telemetry. */
124133
private static class StatsbeatSender implements Runnable {
125134

agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/telemetry/TelemetryChannel.java

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -246,10 +246,7 @@ private CompletableResultCode internalSend(
246246
}
247247

248248
private void writeToDiskOnFailure(List<ByteBuffer> byteBuffers) {
249-
if (!localFileWriter.writeToDisk(byteBuffers)) {
250-
// TODO (heya) track # of write failure via Statsbeat
251-
}
252-
249+
localFileWriter.writeToDisk(byteBuffers);
253250
byteBufferPool.offer(byteBuffers);
254251
}
255252

agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/telemetry/TelemetryClient.java

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -220,8 +220,12 @@ public BatchSpanProcessor getChannelBatcher() {
220220
if (channelBatcher == null) {
221221
LocalFileCache localFileCache = new LocalFileCache();
222222
File telemetryFolder = LocalStorageUtils.getOfflineTelemetryFolder();
223-
LocalFileLoader localFileLoader = new LocalFileLoader(localFileCache, telemetryFolder);
224-
LocalFileWriter localFileWriter = new LocalFileWriter(localFileCache, telemetryFolder);
223+
LocalFileLoader localFileLoader =
224+
new LocalFileLoader(
225+
localFileCache, telemetryFolder, statsbeatModule.getNonessentialStatsbeat());
226+
LocalFileWriter localFileWriter =
227+
new LocalFileWriter(
228+
localFileCache, telemetryFolder, statsbeatModule.getNonessentialStatsbeat());
225229
TelemetryChannel channel =
226230
TelemetryChannel.create(
227231
endpointProvider.getIngestionEndpointUrl(),
@@ -243,8 +247,10 @@ public BatchSpanProcessor getStatsbeatChannelBatcher() {
243247
if (statsbeatChannelBatcher == null) {
244248
LocalFileCache localFileCache = new LocalFileCache();
245249
File statsbeatFolder = LocalStorageUtils.getOfflineStatsbeatFolder();
246-
LocalFileLoader localFileLoader = new LocalFileLoader(localFileCache, statsbeatFolder);
247-
LocalFileWriter localFileWriter = new LocalFileWriter(localFileCache, statsbeatFolder);
250+
LocalFileLoader localFileLoader =
251+
new LocalFileLoader(localFileCache, statsbeatFolder, null);
252+
LocalFileWriter localFileWriter =
253+
new LocalFileWriter(localFileCache, statsbeatFolder, null);
248254
TelemetryChannel channel =
249255
TelemetryChannel.create(
250256
endpointProvider.getStatsbeatEndpointUrl(),

agent/agent-tooling/src/test/java/com/microsoft/applicationinsights/agent/internal/localstorage/IntegrationTests.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,13 @@ public void setup() throws MalformedURLException {
6464
when(mockedClient.send(mockedRequest)).thenReturn(Mono.just(mockedResponse));
6565
HttpPipelineBuilder pipelineBuilder = new HttpPipelineBuilder().httpClient(mockedClient);
6666
localFileCache = new LocalFileCache();
67-
localFileLoader = new LocalFileLoader(localFileCache, tempFolder);
67+
localFileLoader = new LocalFileLoader(localFileCache, tempFolder, null);
6868

6969
telemetryChannel =
7070
new TelemetryChannel(
7171
pipelineBuilder.build(),
7272
new URL("http://foo.bar"),
73-
new LocalFileWriter(localFileCache, tempFolder),
73+
new LocalFileWriter(localFileCache, tempFolder, null),
7474
null);
7575
}
7676

0 commit comments

Comments
 (0)