Skip to content

Commit 9931364

Browse files
heyamstrask
andauthored
Statsbeat + smoke test (#1660)
* Statsbeat * Fix merge conflicts * Add Statsbeat Helper * Track instrumentation list and encode it as long * Track instrumentation names * Add java.vendor to the feature list * Add Attach statsbeat * Add azure metadata service api call * Put common custom dimensions in the base statsbeat * Test azure metadata sevice call on both linux and windows * Add statbest metric names * Add scheduler to send statsbeat * Reset after each interval * Refactor update frequency interval * Remove unused imports * Increment network statsbeat counters * Add tests * Add tests for encode/decode feature * Add StatsbeatModule * Use non-static methods * Add unit tests for NetworkStatsbeat * Add tests for AttachStatsbeat * Add tests for FeatureStatsbeat * Refactor tests * Update instrumentations map * Sort instrumentation list alphabetically * Fix failed tests * Send statsbeat to a testing ikey * Refactor * Refactor * Fix nullpointerexception in instrumentation list * Send network statsbeats when its count is not zero * Refactor * Move private instance variables to the top of the class * Reinit resourceProviderId after reset * Track request durations * Remove unnecessary changes * Fix merge conflicts * Set feature statsbeat to have daily interval * Clean up logs * Reset after send is done * Add smoke test * Use the default ingestion endpoint if statsbeat's endpoint is unknown * Get statsbeat ikey from the config * Delete StatsbeatTelemetry * Make smoke test work * Make smoke test stable * Revert a line * Fix unit tests compilation errors * Make feature interval configurable * Fix not seeing network and attach statsbeat in smoketest * Update smoke test config * Validate more statsbeats in the smoke test * Enable more smoke test app servers for Statsbeat * Address comments * Address comments * Fix lgtm * Make MetricTelemetry class final since there is no need for StatsbeatTelemetry * Update statsbeat iKey to be a workspace based mode * Fix spotbug, make constants package protected * No need for lazy init * User more efficient entrySet instead of keySet * Fix spotbugs * Init azure metadata service in AttachStatsbeat * No need for lazy init * Fix nullpointerexception in ErrorHandlerTest and ThrottlingHandlerTest * Fix exception messages * Fix a nullpointerexception * Handle race condition between send and reset * Add unit test for race condition * Make sense to make metadata service have the same interval as AttachStatsbeat * Decide precisely when to shutdown AzureMetadataService * Clean up debug logs * Avoid reading resources from signed jar - logging configuration * Avoid reading resources from signed jar - sdk version * Add ignore matcher for ai agent classes * Add signed jar access debug option * Convert appsvc logging configuration also * CLEAN UP SHADOW JAR * Update submodule * Cleanup * Igore a test for now * Fix Illegal reflective access by org.junit.contrib.java.lang.system.EnvironmentVariables * Fix unit tests * Remove unused instance var * Fix test failures * Delete a console output log * Fix spotbug DC_DOUBLECHECK * Fix statsbeat smoke test not sending 'Request Duration' * Fix a compiling error in unit test * Address feedback * Make statsbeat config internal and skip parsing the connection string * Address feedback * Track total and count for request durations * Address more feedback * Address feedback * Use BitSet and move decode to a test utils class * Remove transient properties since it's not using failOnUnknown on json adapter * Refactor AttachStatsbeat * Refactor AzureMetadataService * Reduce visibility * Refactor StatsbeatModule * Introduce resetForTest * Make a couple things volatile * A bit more * Simplify * Update singleton naming a bit * Reduce reliance on singletons for testing * Reduce reliance on singletons for testing * Synchronization * Use long instead of double * Fix a compiling error * Remove unused methods * Remove unused imports * Fix statsbeat module is not properly initialized * Use AtomicLong for consistency * Fix array index out of bound * Fix ThrottlingTest * Fix ErrorHandlerTest * Reuse requestSuccessCount for requestDurationCount * Throw an exception when init gets called more than once * Switch expected and actual parameters in tests * Atomicity * Can not be null * Inline * Warning message * Remove null check * Remove null check * Add name to TODO * Remove getInterval method * Extract ResourceProvider into enum * Extract OperatingSystem into enum * Extract Feature enum * Fix ErrorHandlerTest and ThrottleingHandlerTest * Convert CustomDimensions to use explicit fields * Move more constants out of Constants * Move more constants out of Constants * Move more constants out of Constants * Move more constants out of Constants * Remove unused field * Add todo * Small renaming * Remove StatsbeatHelper * Comments * Send base 64 encoded string instead of utf-8 string for instrumentation and feature * Add missing copyrights * Add a comment * Spotbugs * Remove todos * Fix instrumentation is empty in the payload * WithoutPadding on Base64 encoder Co-authored-by: Trask Stalnaker <[email protected]>
1 parent 3abc5f2 commit 9931364

File tree

46 files changed

+1813
-17
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

46 files changed

+1813
-17
lines changed

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

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,11 @@
5353
import com.microsoft.applicationinsights.internal.config.ParamXmlElement;
5454
import com.microsoft.applicationinsights.internal.config.TelemetryConfigurationFactory;
5555
import com.microsoft.applicationinsights.internal.config.TelemetryModulesXmlElement;
56+
import com.microsoft.applicationinsights.internal.config.connection.ConnectionString;
57+
import com.microsoft.applicationinsights.internal.config.connection.InvalidConnectionStringException;
5658
import com.microsoft.applicationinsights.internal.profiler.GcEventMonitor;
5759
import com.microsoft.applicationinsights.internal.profiler.ProfilerServiceInitializer;
60+
import com.microsoft.applicationinsights.internal.statsbeat.StatsbeatModule;
5861
import com.microsoft.applicationinsights.internal.system.SystemInformation;
5962
import com.microsoft.applicationinsights.internal.util.PropertyHelper;
6063
import com.microsoft.applicationinsights.profiler.config.ServiceProfilerServiceConfig;
@@ -157,6 +160,12 @@ private static void start(Instrumentation instrumentation) {
157160
configuration.getContextInitializers().add(new SdkVersionContextInitializer());
158161
configuration.getContextInitializers().add(new ResourceAttributesContextInitializer(config.customDimensions));
159162

163+
try {
164+
ConnectionString.updateStatsbeatConnectionString(config.internal.statsbeat.instrumentationKey, config.internal.statsbeat.endpoint, configuration);
165+
} catch (InvalidConnectionStringException ex) {
166+
startupLogger.warn("Statsbeat endpoint is invalid. {}", ex.getMessage());
167+
}
168+
160169
Global.setSamplingPercentage(config.sampling.percentage);
161170
final TelemetryClient telemetryClient = new TelemetryClient();
162171
Global.setTelemetryClient(telemetryClient);
@@ -200,6 +209,9 @@ public void run() {
200209
if (rpConfiguration != null) {
201210
RpConfigurationPolling.startPolling(rpConfiguration, config);
202211
}
212+
213+
// initialize StatsbeatModule
214+
StatsbeatModule.initialize(telemetryClient, config.internal.statsbeat.intervalSeconds, config.internal.statsbeat.featureIntervalSeconds);
203215
}
204216

205217
private static GcEventMonitor.GcEventMonitorConfiguration formGcEventMonitorConfiguration(Configuration.GcEventConfiguration gcEvents) {

agent/agent-tooling/src/main/java/com/microsoft/applicationinsights/agent/internal/wasbootstrap/configuration/Configuration.java

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import com.microsoft.applicationinsights.agent.bootstrap.diagnostics.DiagnosticsHelper;
2525
import com.microsoft.applicationinsights.agent.bootstrap.diagnostics.status.StatusFile;
2626
import com.microsoft.applicationinsights.customExceptions.FriendlyException;
27+
import com.microsoft.applicationinsights.internal.config.connection.ConnectionString;
2728
import com.microsoft.applicationinsights.internal.profiler.GcReportingLevel;
2829

2930
import java.util.ArrayList;
@@ -34,6 +35,7 @@
3435
import java.util.regex.Pattern;
3536
import java.util.regex.PatternSyntaxException;
3637

38+
import static java.util.concurrent.TimeUnit.DAYS;
3739
import static java.util.concurrent.TimeUnit.MINUTES;
3840

3941
// an assumption is made throughout this file that user will not explicitly use `null` value in json file
@@ -50,6 +52,7 @@ public class Configuration {
5052
public Proxy proxy = new Proxy();
5153
public SelfDiagnostics selfDiagnostics = new SelfDiagnostics();
5254
public PreviewConfiguration preview = new PreviewConfiguration();
55+
public InternalConfiguration internal = new InternalConfiguration();
5356

5457
// this is just here to detect if using old format in order to give a helpful error message
5558
public Map<String, Object> instrumentationSettings;
@@ -153,10 +156,16 @@ public static class SpringSchedulingInstrumentation {
153156
}
154157

155158
public static class Heartbeat {
156-
157159
public long intervalSeconds = MINUTES.toSeconds(15);
158160
}
159161

162+
public static class Statsbeat {
163+
public String instrumentationKey = "c4a29126-a7cb-47e5-b348-11414998b11e"; //workspace-aistatsbeat
164+
public String endpoint = ConnectionString.Defaults.INGESTION_ENDPOINT; // this supports the government cloud
165+
public long intervalSeconds = MINUTES.toSeconds(15); // default to 15 minutes
166+
public long featureIntervalSeconds = DAYS.toSeconds(1); // default to daily
167+
}
168+
160169
public static class Proxy {
161170

162171
public String host;
@@ -186,6 +195,11 @@ public static class PreviewConfiguration {
186195
public GcEventConfiguration gcEvents = new GcEventConfiguration();
187196
}
188197

198+
public static class InternalConfiguration {
199+
// This is used for collecting internal stats
200+
public Statsbeat statsbeat = new Statsbeat();
201+
}
202+
189203
public static class PreviewInstrumentation {
190204
public DisabledByDefaultInstrumentation azureSdk = new DisabledByDefaultInstrumentation();
191205
public DisabledByDefaultInstrumentation javaHttpClient = new DisabledByDefaultInstrumentation();

agent/exporter/src/main/java/com/microsoft/applicationinsights/agent/Exporter.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
import com.google.common.cache.CacheBuilder;
3535
import com.microsoft.applicationinsights.TelemetryClient;
3636
import com.microsoft.applicationinsights.TelemetryConfiguration;
37+
import com.microsoft.applicationinsights.internal.statsbeat.StatsbeatModule;
3738
import com.microsoft.applicationinsights.telemetry.Duration;
3839
import com.microsoft.applicationinsights.telemetry.EventTelemetry;
3940
import com.microsoft.applicationinsights.telemetry.ExceptionTelemetry;
@@ -173,6 +174,7 @@ public CompletableResultCode shutdown() {
173174
private void export(SpanData span) {
174175
SpanKind kind = span.getKind();
175176
String instrumentationName = span.getInstrumentationLibraryInfo().getName();
177+
StatsbeatModule.get().getNetworkStatsbeat().addInstrumentation(instrumentationName);
176178
Matcher matcher = COMPONENT_PATTERN.matcher(instrumentationName);
177179
String stdComponent = matcher.matches() ? matcher.group(1) : null;
178180
if (kind == SpanKind.INTERNAL) {

agent/instrumentation/build.gradle

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,8 +73,8 @@ dependencies {
7373
implementation group: 'io.opentelemetry.javaagent.instrumentation', name: 'opentelemetry-javaagent-servlet-3.0', version: versions.opentelemetryInstrumentationAlpha
7474
implementation group: 'io.opentelemetry.javaagent.instrumentation', name: 'opentelemetry-javaagent-servlet-common', version: versions.opentelemetryInstrumentationAlpha
7575
implementation group: 'io.opentelemetry.javaagent.instrumentation', name: 'opentelemetry-javaagent-spring-scheduling-3.1', version: versions.opentelemetryInstrumentationAlpha
76-
implementation group: 'io.opentelemetry.javaagent.instrumentation', name: 'opentelemetry-javaagent-spring-webmvc-3.1', version: versions.opentelemetryInstrumentationAlpha
7776
implementation group: 'io.opentelemetry.javaagent.instrumentation', name: 'opentelemetry-javaagent-spring-webflux-5.0', version: versions.opentelemetryInstrumentationAlpha
77+
implementation group: 'io.opentelemetry.javaagent.instrumentation', name: 'opentelemetry-javaagent-spring-webmvc-3.1', version: versions.opentelemetryInstrumentationAlpha
7878
implementation group: 'io.opentelemetry.javaagent.instrumentation', name: 'opentelemetry-javaagent-tomcat-7.0', version: versions.opentelemetryInstrumentationAlpha
7979

8080
// also: jaxrs, spring, struts

core/src/main/java/com/microsoft/applicationinsights/TelemetryConfiguration.java

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,7 @@ public final class TelemetryConfiguration {
5050
private String connectionString;
5151
private String roleName;
5252
private String roleInstance;
53+
private String statsbeatInstrumentationKey;
5354

5455
private final EndpointProvider endpointProvider = new EndpointProvider();
5556

@@ -166,7 +167,6 @@ public String getInstrumentationKey() {
166167
* @throws IllegalArgumentException when the new value is null or empty
167168
*/
168169
public void setInstrumentationKey(String key) {
169-
170170
// A non null, non empty instrumentation key is a must
171171
if (Strings.isNullOrEmpty(key)) {
172172
throw new IllegalArgumentException("key");
@@ -175,6 +175,14 @@ public void setInstrumentationKey(String key) {
175175
instrumentationKey = key;
176176
}
177177

178+
public String getStatsbeatInstrumentationKey() {
179+
return statsbeatInstrumentationKey;
180+
}
181+
182+
public void setStatsbeatInstrumentationKey(String key) {
183+
statsbeatInstrumentationKey = key;
184+
}
185+
178186
public String getRoleName() {
179187
return roleName;
180188
}

core/src/main/java/com/microsoft/applicationinsights/channel/concrete/TelemetryChannelBase.java

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,9 @@ public abstract class TelemetryChannelBase<T> implements TelemetryChannel {
7272

7373
protected TelemetriesTransmitter<T> telemetriesTransmitter;
7474
protected TelemetryBuffer<T> telemetryBuffer;
75+
protected TelemetriesTransmitter<T> statsbeatTransmitter;
76+
protected TelemetryBuffer<T> statsbeatBuffer;
77+
7578

7679
private boolean developerMode = false;
7780

@@ -135,8 +138,10 @@ protected synchronized void initialize(TelemetryConfiguration configuration, Str
135138
makeSureEndpointAddressIsValid(endpointAddress);
136139

137140
final ConfiguredTransmitterFactory<T> transmitterFactory = getTransmitterFactory();
138-
telemetriesTransmitter = transmitterFactory.create(configuration, maxTransmissionStorageCapacity, throttling, maxInstantRetry);
141+
telemetriesTransmitter = transmitterFactory.create(configuration, maxTransmissionStorageCapacity, throttling, maxInstantRetry, false);
139142
telemetryBuffer = new TelemetryBuffer<>(telemetriesTransmitter, maxTelemetryBufferCapacityEnforcer, sendIntervalInSeconds);
143+
statsbeatTransmitter = transmitterFactory.create(configuration, maxTransmissionStorageCapacity, throttling, maxInstantRetry, true);
144+
statsbeatBuffer = new TelemetryBuffer<>(statsbeatTransmitter, maxTelemetryBufferCapacityEnforcer, sendIntervalInSeconds);
140145

141146
setDeveloperMode(developerMode);
142147
isInitailized = true;

core/src/main/java/com/microsoft/applicationinsights/channel/concrete/inprocess/InProcessTelemetryChannel.java

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,13 @@
2424
import com.microsoft.applicationinsights.TelemetryConfiguration;
2525
import com.microsoft.applicationinsights.channel.concrete.TelemetryChannelBase;
2626
import com.microsoft.applicationinsights.internal.channel.ConfiguredTransmitterFactory;
27+
import com.microsoft.applicationinsights.telemetry.MetricTelemetry;
2728
import com.microsoft.applicationinsights.telemetry.Telemetry;
2829

2930
import java.util.Map;
3031

32+
import static com.microsoft.applicationinsights.internal.statsbeat.Constants.STATSBEAT_TELEMETRY_NAME;
33+
3134
/**
3235
* An implementation of {@link com.microsoft.applicationinsights.channel.TelemetryChannel}
3336
*
@@ -64,13 +67,23 @@ protected boolean doSend(Telemetry telemetry) {
6467
if (telemetry.previouslyUsed()) {
6568
throw new IllegalStateException("Telemetry was previously used: " + telemetry);
6669
}
67-
telemetryBuffer.add(telemetry);
70+
71+
// TODO Prepare for AAD support for Statsbeat iKey
72+
if (telemetry instanceof MetricTelemetry) {
73+
MetricTelemetry mt = (MetricTelemetry) telemetry;
74+
if (STATSBEAT_TELEMETRY_NAME.equalsIgnoreCase(mt.getTelemetryName())) {
75+
statsbeatBuffer.add(telemetry);
76+
} else {
77+
telemetryBuffer.add(telemetry);
78+
}
79+
} else {
80+
telemetryBuffer.add(telemetry);
81+
}
6882
return true;
6983
}
7084

7185
@Override
7286
protected ConfiguredTransmitterFactory<Telemetry> createTransmitterFactory() {
7387
return new InProcessTelemetryTransmitterFactory();
7488
}
75-
7689
}

core/src/main/java/com/microsoft/applicationinsights/channel/concrete/inprocess/InProcessTelemetryTransmitterFactory.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,14 @@
4747
final class InProcessTelemetryTransmitterFactory implements ConfiguredTransmitterFactory {
4848

4949
@Override
50-
public TelemetriesTransmitter create(TelemetryConfiguration configuration, String maxTransmissionStorageCapacity, boolean throttlingIsEnabled, int maxInstantRetries) {
50+
public TelemetriesTransmitter create(TelemetryConfiguration configuration, String maxTransmissionStorageCapacity, boolean throttlingIsEnabled, int maxInstantRetries, boolean isStatsbeat) {
5151
final TransmissionPolicyManager transmissionPolicyManager = new TransmissionPolicyManager(throttlingIsEnabled);
5252
transmissionPolicyManager.addTransmissionHandler(new ErrorHandler(transmissionPolicyManager));
5353
transmissionPolicyManager.addTransmissionHandler(new PartialSuccessHandler());
5454
transmissionPolicyManager.addTransmissionHandler(new ThrottlingHandler(transmissionPolicyManager));
5555
transmissionPolicyManager.setMaxInstantRetries(maxInstantRetries);
5656
// An active object with the network sender
57-
TransmissionNetworkOutput actualNetworkSender = TransmissionNetworkOutput.create(configuration, transmissionPolicyManager);
57+
TransmissionNetworkOutput actualNetworkSender = TransmissionNetworkOutput.create(configuration, transmissionPolicyManager, isStatsbeat);
5858

5959
return finishTransmitterConstruction(maxTransmissionStorageCapacity, transmissionPolicyManager, actualNetworkSender);
6060
}

core/src/main/java/com/microsoft/applicationinsights/internal/channel/ConfiguredTransmitterFactory.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,5 +13,5 @@ public interface ConfiguredTransmitterFactory<T> {
1313
* @param maxInstantRetries
1414
* @return
1515
*/
16-
TelemetriesTransmitter<T> create(@Nullable TelemetryConfiguration configuration, String maxTransmissionStorageCapacity, boolean throttlingIsEnabled, int maxInstantRetries);
16+
TelemetriesTransmitter<T> create(@Nullable TelemetryConfiguration configuration, String maxTransmissionStorageCapacity, boolean throttlingIsEnabled, int maxInstantRetries, boolean isStatsbeat);
1717
}

core/src/main/java/com/microsoft/applicationinsights/internal/channel/common/ErrorHandler.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import com.microsoft.applicationinsights.internal.channel.TransmissionHandler;
44
import com.microsoft.applicationinsights.internal.channel.TransmissionHandlerArgs;
5+
import com.microsoft.applicationinsights.internal.statsbeat.StatsbeatModule;
56
import org.slf4j.Logger;
67
import org.slf4j.LoggerFactory;
78

@@ -48,6 +49,10 @@ boolean validateTransmissionAndSend(TransmissionHandlerArgs args) {
4849
case TransmissionSendResult.SERVICE_UNAVAILABLE:
4950
case TransmissionSendResult.CLIENT_SIDE_EXCEPTION:
5051
backoffAndSendTransmission(args);
52+
// TODO (heya) remove this null check later
53+
if (StatsbeatModule.get() != null) {
54+
StatsbeatModule.get().getNetworkStatsbeat().incrementRetryCount();
55+
}
5156
return true;
5257
default:
5358
logger.trace("Http response code {} not handled by {}", args.getResponseCode(),

0 commit comments

Comments
 (0)