diff --git a/eng/versioning/version_client.txt b/eng/versioning/version_client.txt index 138e39751965..afd95058f9fb 100644 --- a/eng/versioning/version_client.txt +++ b/eng/versioning/version_client.txt @@ -441,8 +441,8 @@ com.azure.tools:azure-sdk-build-tool;1.0.0;1.1.0-beta.1 # note: The unreleased dependencies will not be manipulated with the automatic PR creation code. # In the pom, the version update tag after the version should name the unreleased package and the dependency version: # -unreleased_com.azure:azure-core-test;1.22.0-beta.1 unreleased_com.azure:azure-messaging-eventgrid;4.19.0-beta.1 +unreleased_com.azure:azure-core-test;1.22.0-beta.1 # Released Beta dependencies: Copy the entry from above, prepend "beta_", remove the current # version and set the version to the released beta. Released beta dependencies are only valid diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/assets.json b/sdk/monitor/azure-monitor-opentelemetry-exporter/assets.json index 3e558946941e..5d5f20b9dab7 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/assets.json +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/assets.json @@ -2,5 +2,5 @@ "AssetsRepo": "Azure/azure-sdk-assets", "AssetsRepoPrefixPath": "java", "TagPrefix": "java/monitor/azure-monitor-opentelemetry-exporter", - "Tag": "java/monitor/azure-monitor-opentelemetry-exporter_4051462e7c" + "Tag": "java/monitor/azure-monitor-opentelemetry-exporter_28d6ecbf1a" } diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/pom.xml b/sdk/monitor/azure-monitor-opentelemetry-exporter/pom.xml index 37a345553c54..d3a5fe1e7b71 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/pom.xml +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/pom.xml @@ -151,7 +151,7 @@ com.azure azure-core-test - 1.21.0 + 1.22.0-beta.1 test diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/AzureMonitorExporterBuilder.java b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/AzureMonitorExporterBuilder.java index 0dabb769b6d8..6c778151592b 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/AzureMonitorExporterBuilder.java +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/AzureMonitorExporterBuilder.java @@ -27,6 +27,7 @@ import com.azure.monitor.opentelemetry.exporter.implementation.SpanDataMapper; import com.azure.monitor.opentelemetry.exporter.implementation.builders.AbstractTelemetryBuilder; import com.azure.monitor.opentelemetry.exporter.implementation.configuration.ConnectionString; +import com.azure.monitor.opentelemetry.exporter.implementation.configuration.StatsbeatConnectionString; import com.azure.monitor.opentelemetry.exporter.implementation.heartbeat.HeartbeatExporter; import com.azure.monitor.opentelemetry.exporter.implementation.localstorage.LocalStorageStats; import com.azure.monitor.opentelemetry.exporter.implementation.localstorage.LocalStorageTelemetryPipelineListener; @@ -34,6 +35,9 @@ import com.azure.monitor.opentelemetry.exporter.implementation.pipeline.TelemetryItemExporter; import com.azure.monitor.opentelemetry.exporter.implementation.pipeline.TelemetryPipeline; import com.azure.monitor.opentelemetry.exporter.implementation.pipeline.TelemetryPipelineListener; +import com.azure.monitor.opentelemetry.exporter.implementation.statsbeat.Feature; +import com.azure.monitor.opentelemetry.exporter.implementation.statsbeat.StatsbeatModule; +import com.azure.monitor.opentelemetry.exporter.implementation.utils.PropertyHelper; import com.azure.monitor.opentelemetry.exporter.implementation.utils.ResourceParser; import com.azure.monitor.opentelemetry.exporter.implementation.utils.TempDirs; import com.azure.monitor.opentelemetry.exporter.implementation.utils.VersionGenerator; @@ -55,9 +59,12 @@ import java.util.List; import java.util.Map; import java.util.Objects; +import java.util.Set; import java.util.function.BiConsumer; +import static java.util.concurrent.TimeUnit.DAYS; import static java.util.concurrent.TimeUnit.MINUTES; +import static java.util.concurrent.TimeUnit.SECONDS; /** * This class provides a fluent builder API to configure the OpenTelemetry SDK with Azure Monitor Exporters. @@ -71,6 +78,9 @@ public final class AzureMonitorExporterBuilder { private static final String APPLICATIONINSIGHTS_AUTHENTICATION_SCOPE = "https://monitor.azure.com//.default"; + private static final String STATSBEAT_LONG_INTERVAL_SECONDS = "STATSBEAT_LONG_INTERVAL_SECONDS"; + private static final String STATSBEAT_SHORT_INTERVAL_SECONDS = "STATSBEAT_SHORT_INTERVAL_SECONDS"; + private static final Map PROPERTIES = CoreUtils.getProperties("azure-monitor-opentelemetry-exporter.properties"); @@ -257,7 +267,7 @@ public SpanExporter buildTraceExporter() { public MetricExporter buildMetricExporter() { internalBuildAndFreeze(); // TODO (trask) how to pass along configuration properties? - return buildMetricExporter(DefaultConfigProperties.createForTest(Collections.emptyMap())); + return buildMetricExporter(DefaultConfigProperties.create(Collections.emptyMap())); } /** @@ -271,7 +281,7 @@ public MetricExporter buildMetricExporter() { public LogRecordExporter buildLogRecordExporter() { internalBuildAndFreeze(); // TODO (trask) how to pass along configuration properties? - return buildLogRecordExporter(DefaultConfigProperties.createForTest(Collections.emptyMap())); + return buildLogRecordExporter(DefaultConfigProperties.create(Collections.emptyMap())); } /** @@ -351,10 +361,45 @@ private SpanExporter buildTraceExporter(ConfigProperties configProperties) { private MetricExporter buildMetricExporter(ConfigProperties configProperties) { HeartbeatExporter.start( MINUTES.toSeconds(15), createDefaultsPopulator(configProperties), builtTelemetryItemExporter::send); + if (connectionString != null) { + StatsbeatModule statsbeatModule = new StatsbeatModule(PropertyHelper::lazyUpdateVmRpIntegration); + statsbeatModule.start( + builtTelemetryItemExporter, + this::getStatsbeatConnectionString, + connectionString::getInstrumentationKey, + false, + getStatsbeaLongInterval(configProperties.getInt(STATSBEAT_LONG_INTERVAL_SECONDS)), + getStatsbeatShortInterval(configProperties.getInt(STATSBEAT_SHORT_INTERVAL_SECONDS)), + false, + initStatsbeatFeatures()); + } return new AzureMonitorMetricExporter( new MetricDataMapper(createDefaultsPopulator(configProperties), true), builtTelemetryItemExporter); } + private long getStatsbeaLongInterval(Integer intervalFromConfig) { + if (intervalFromConfig == null) { + return DAYS.toSeconds(1); // default to every 24 hours + } + return SECONDS.toSeconds(intervalFromConfig); + } + + private long getStatsbeatShortInterval(Integer intervalFromConfig) { + if (intervalFromConfig == null) { + return MINUTES.toSeconds(15); // default to every 15 mins + } + return SECONDS.toSeconds(intervalFromConfig); + } + + private Set initStatsbeatFeatures() { + // TODO (jean): start tracking native image usage based on a system property or env var to indicate it's from the native image path + return Collections.emptySet(); + } + + private StatsbeatConnectionString getStatsbeatConnectionString() { + return StatsbeatConnectionString.create(connectionString, null, null); + } + private LogRecordExporter buildLogRecordExporter(ConfigProperties configProperties) { return new AzureMonitorLogRecordExporter( new LogDataMapper(true, false, createDefaultsPopulator(configProperties)), builtTelemetryItemExporter); diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/configuration/StatsbeatConnectionString.java b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/configuration/StatsbeatConnectionString.java index db5c5b56021d..e79289dbfb1e 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/configuration/StatsbeatConnectionString.java +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/configuration/StatsbeatConnectionString.java @@ -91,7 +91,7 @@ public final class StatsbeatConnectionString { public static StatsbeatConnectionString create( ConnectionString connectionString, @Nullable String instrumentationKey, - String ingestionEndpoint) { + @Nullable String ingestionEndpoint) { if (instrumentationKey == null || instrumentationKey.isEmpty()) { InstrumentationKeyEndpointPair pair = diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/pipeline/TelemetryItemExporter.java b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/pipeline/TelemetryItemExporter.java index 558b03553606..8705ca7ffe9c 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/pipeline/TelemetryItemExporter.java +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/pipeline/TelemetryItemExporter.java @@ -137,7 +137,6 @@ public CompletableResultCode shutdown() { CompletableResultCode internalSendByBatch(TelemetryItemBatchKey telemetryItemBatchKey, List telemetryItems) { List byteBuffers; - // Don't send _OTELRESOURCE_ custom metric when OTEL_RESOURCE_ATTRIBUTES env var is empty // Don't send _OTELRESOURCE_ custom metric to Statsbeat yet // insert _OTELRESOURCE_ at the beginning of each batch diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/statsbeat/AzureMetadataService.java b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/statsbeat/AzureMetadataService.java index f2e5dfa9fc1f..1e0077f4131e 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/statsbeat/AzureMetadataService.java +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/statsbeat/AzureMetadataService.java @@ -8,9 +8,6 @@ import com.azure.core.http.HttpPipelineBuilder; import com.azure.core.http.HttpRequest; import com.azure.core.http.HttpResponse; -import com.azure.core.http.policy.CookiePolicy; -import com.azure.core.http.policy.RetryPolicy; -import com.azure.core.http.policy.UserAgentPolicy; import com.azure.monitor.opentelemetry.exporter.implementation.NoopTracer; import com.azure.monitor.opentelemetry.exporter.implementation.utils.ThreadPoolUtils; import com.fasterxml.jackson.databind.DeserializationFeature; @@ -81,7 +78,7 @@ void updateMetadata(String response) throws IOException { private void updateMetadata(MetadataInstanceResponse metadataInstanceResponse) { vmMetadataServiceCallback.accept(metadataInstanceResponse); attachStatsbeat.updateMetadataInstance(metadataInstanceResponse); - customDimensions.setResourceProvider(ResourceProvider.RP_VM); + customDimensions.setResourceProviderVm(); // osType from the Azure Metadata Service has a higher precedence over the running app’s // operating system. diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/statsbeat/CustomDimensions.java b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/statsbeat/CustomDimensions.java index e3c54fc67427..94e0691bd21f 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/statsbeat/CustomDimensions.java +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/statsbeat/CustomDimensions.java @@ -6,6 +6,7 @@ import com.azure.monitor.opentelemetry.exporter.implementation.builders.StatsbeatTelemetryBuilder; import com.azure.monitor.opentelemetry.exporter.implementation.utils.PropertyHelper; import com.azure.monitor.opentelemetry.exporter.implementation.utils.SystemInformation; +import com.azure.monitor.opentelemetry.exporter.implementation.utils.VersionGenerator; public class CustomDimensions { @@ -19,51 +20,28 @@ public class CustomDimensions { // visible for testing CustomDimensions() { - String qualifiedSdkVersion = PropertyHelper.getQualifiedSdkVersionString(); - - if (qualifiedSdkVersion.startsWith("aw")) { - resourceProvider = ResourceProvider.RP_APPSVC; - operatingSystem = OperatingSystem.OS_WINDOWS; - } else if (qualifiedSdkVersion.startsWith("al")) { - resourceProvider = ResourceProvider.RP_APPSVC; - operatingSystem = OperatingSystem.OS_LINUX; - } else if (qualifiedSdkVersion.startsWith("kw")) { - resourceProvider = ResourceProvider.RP_AKS; - operatingSystem = OperatingSystem.OS_WINDOWS; - } else if (qualifiedSdkVersion.startsWith("kl")) { - resourceProvider = ResourceProvider.RP_AKS; - operatingSystem = OperatingSystem.OS_LINUX; - } else if (qualifiedSdkVersion.startsWith("fw")) { - resourceProvider = ResourceProvider.RP_FUNCTIONS; - operatingSystem = OperatingSystem.OS_WINDOWS; - } else if (qualifiedSdkVersion.startsWith("fl")) { - resourceProvider = ResourceProvider.RP_FUNCTIONS; - operatingSystem = OperatingSystem.OS_LINUX; - } else { - resourceProvider = ResourceProvider.UNKNOWN; - operatingSystem = initOperatingSystem(); - } - - sdkVersion = qualifiedSdkVersion.substring(qualifiedSdkVersion.lastIndexOf(':') + 1); + resourceProvider = ResourceProvider.initResourceProvider(); + operatingSystem = initOperatingSystem(); + sdkVersion = initSdkVersion(); runtimeVersion = System.getProperty("java.version"); - attachType = RpAttachType.getRpAttachType(); + attachType = RpAttachType.getRpAttachTypeString(); language = "java"; } - public ResourceProvider getResourceProvider() { + ResourceProvider getResourceProvider() { return resourceProvider; } - public OperatingSystem getOperatingSystem() { + OperatingSystem getOperatingSystem() { return operatingSystem; } - public void setResourceProvider(ResourceProvider resourceProvider) { - this.resourceProvider = resourceProvider; + void setResourceProviderVm() { + this.resourceProvider = ResourceProvider.RP_VM; } - public void setOperatingSystem(OperatingSystem operatingSystem) { + void setOperatingSystem(OperatingSystem operatingSystem) { this.operatingSystem = operatingSystem; } @@ -86,4 +64,12 @@ private static OperatingSystem initOperatingSystem() { return OperatingSystem.OS_UNKNOWN; } } + + private static String initSdkVersion() { + if (RpAttachType.getRpAttachType() == RpAttachType.MANUAL) { + return VersionGenerator.getSdkVersion(); + } + String qualifiedSdkVersionString = PropertyHelper.getQualifiedSdkVersionString(); + return qualifiedSdkVersionString.substring(qualifiedSdkVersionString.lastIndexOf(':')+1); + } } diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/statsbeat/FeatureStatsbeat.java b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/statsbeat/FeatureStatsbeat.java index 5da37359c1da..b29b25e70ab4 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/statsbeat/FeatureStatsbeat.java +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/statsbeat/FeatureStatsbeat.java @@ -69,11 +69,14 @@ protected void send(TelemetryItemExporter telemetryItemExporter) { featureType = "1"; } - StatsbeatTelemetryBuilder telemetryBuilder = createStatsbeatTelemetry(FEATURE_METRIC_NAME, 0); - telemetryBuilder.addProperty("feature", featureValue); - telemetryBuilder.addProperty("type", featureType); + // don't send feature/instrumentation statsbeat when it's empty + if (!featureValue.isEmpty()) { + StatsbeatTelemetryBuilder telemetryBuilder = createStatsbeatTelemetry(FEATURE_METRIC_NAME, 0); + telemetryBuilder.addProperty("feature", featureValue); + telemetryBuilder.addProperty("type", featureType); - telemetryItemExporter.send(Collections.singletonList(telemetryBuilder.build())); + telemetryItemExporter.send(Collections.singletonList(telemetryBuilder.build())); + } } void trackConfigurationOptions(Set featureSet) { diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/statsbeat/ResourceProvider.java b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/statsbeat/ResourceProvider.java index 3a6fee228665..37b7c2cce6e6 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/statsbeat/ResourceProvider.java +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/statsbeat/ResourceProvider.java @@ -3,11 +3,14 @@ package com.azure.monitor.opentelemetry.exporter.implementation.statsbeat; +import com.azure.monitor.opentelemetry.exporter.implementation.utils.Strings; + enum ResourceProvider { RP_FUNCTIONS("functions"), RP_APPSVC("appsvc"), RP_VM("vm"), RP_AKS("aks"), + RP_SPRING_CLOUD("springcloud"), UNKNOWN("unknown"); private final String value; @@ -19,4 +22,18 @@ enum ResourceProvider { String getValue() { return value; } + + static ResourceProvider initResourceProvider() { + if ("java".equals(System.getenv("FUNCTIONS_WORKER_RUNTIME"))) { + return ResourceProvider.RP_FUNCTIONS; + } else if (!Strings.isNullOrEmpty(System.getenv("WEBSITE_SITE_NAME"))) { + return ResourceProvider.RP_APPSVC; + } else if (!Strings.isNullOrEmpty(System.getenv("KUBERNETES_SERVICE_HOST"))) { + return ResourceProvider.RP_AKS; + } else if (!Strings.isNullOrEmpty(System.getenv("APPLICATIONINSIGHTS_SPRINGCLOUD_SERVICE_ID"))) { + return ResourceProvider.RP_SPRING_CLOUD; + } else { + return ResourceProvider.UNKNOWN; + } + } } diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/statsbeat/RpAttachType.java b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/statsbeat/RpAttachType.java index 4e3340dfa9c7..fd1d7498efcb 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/statsbeat/RpAttachType.java +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/statsbeat/RpAttachType.java @@ -9,7 +9,7 @@ public enum RpAttachType { STANDALONE_AUTO("StandaloneAuto"), // RP attach is enabled via a custom JAVA_OPTS or on premise resources INTEGRATED_AUTO("IntegratedAuto"); // RP attach is on by default - private static volatile RpAttachType attachType; + private static volatile RpAttachType attachType = MANUAL; private final String label; private RpAttachType(String label) { @@ -20,7 +20,11 @@ public static void setRpAttachType(RpAttachType type) { attachType = type; } - public static String getRpAttachType() { + public static RpAttachType getRpAttachType() { + return attachType; + } + + public static String getRpAttachTypeString() { return attachType != null ? attachType.label : null; } } diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/statsbeat/StatsbeatModule.java b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/statsbeat/StatsbeatModule.java index a1f12e706c4c..e78c93e7aaf3 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/statsbeat/StatsbeatModule.java +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/statsbeat/StatsbeatModule.java @@ -11,13 +11,11 @@ import org.slf4j.MDC; import java.util.Set; -import java.util.concurrent.Callable; import java.util.concurrent.Executors; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicBoolean; import java.util.function.Consumer; -import java.util.function.Function; import java.util.function.Supplier; import static com.azure.monitor.opentelemetry.exporter.implementation.utils.AzureMonitorMsgId.FAIL_TO_SEND_STATSBEAT_ERROR; @@ -37,19 +35,23 @@ public class StatsbeatModule { private final FeatureStatsbeat instrumentationStatsbeat; private final NonessentialStatsbeat nonessentialStatsbeat; private final AzureMetadataService azureMetadataService; - private final AtomicBoolean started = new AtomicBoolean(); private final AtomicBoolean shutdown = new AtomicBoolean(); public StatsbeatModule(Consumer vmMetadataServiceCallback) { customDimensions = new CustomDimensions(); - networkStatsbeat = new NetworkStatsbeat(customDimensions); attachStatsbeat = new AttachStatsbeat(customDimensions); featureStatsbeat = new FeatureStatsbeat(customDimensions, FeatureType.FEATURE); instrumentationStatsbeat = new FeatureStatsbeat(customDimensions, FeatureType.INSTRUMENTATION); - nonessentialStatsbeat = new NonessentialStatsbeat(customDimensions); azureMetadataService = new AzureMetadataService(attachStatsbeat, customDimensions, vmMetadataServiceCallback); + if (RpAttachType.getRpAttachType() == RpAttachType.MANUAL) { + networkStatsbeat = new NetworkStatsbeat(customDimensions); + nonessentialStatsbeat = new NonessentialStatsbeat(customDimensions); + } else { + networkStatsbeat = null; + nonessentialStatsbeat = null; + } } public void start( @@ -80,11 +82,13 @@ public void start( updateConnectionString(connectionString.get()); updateInstrumentationKey(instrumentationKey.get()); - scheduledExecutor.scheduleWithFixedDelay( - new StatsbeatSender(networkStatsbeat, telemetryItemExporter), - shortIntervalSeconds, - shortIntervalSeconds, - TimeUnit.SECONDS); + if (RpAttachType.getRpAttachType() != RpAttachType.MANUAL) { + scheduledExecutor.scheduleWithFixedDelay( + new StatsbeatSender(networkStatsbeat, telemetryItemExporter), + shortIntervalSeconds, + shortIntervalSeconds, + TimeUnit.SECONDS); + } scheduledExecutor.scheduleWithFixedDelay( new StatsbeatSender(attachStatsbeat, telemetryItemExporter), Math.min(60, longIntervalSeconds), @@ -110,7 +114,7 @@ public void start( featureStatsbeat.trackConfigurationOptions(featureSet); - if (!disabled) { + if (!disabled && RpAttachType.getRpAttachType() != RpAttachType.MANUAL) { nonessentialStatsbeat.setConnectionString(connectionString.get()); nonessentialStatsbeat.setInstrumentationKey(instrumentationKey.get()); scheduledExecutor.scheduleWithFixedDelay( @@ -151,7 +155,9 @@ public NonessentialStatsbeat getNonessentialStatsbeat() { private void updateConnectionString(StatsbeatConnectionString connectionString) { if (connectionString != null) { - networkStatsbeat.setConnectionString(connectionString); + if (RpAttachType.getRpAttachType() != RpAttachType.MANUAL) { + networkStatsbeat.setConnectionString(connectionString); + } attachStatsbeat.setConnectionString(connectionString); featureStatsbeat.setConnectionString(connectionString); instrumentationStatsbeat.setConnectionString(connectionString); @@ -160,7 +166,9 @@ private void updateConnectionString(StatsbeatConnectionString connectionString) private void updateInstrumentationKey(String instrumentationKey) { if (instrumentationKey != null && !instrumentationKey.isEmpty()) { - networkStatsbeat.setInstrumentationKey(instrumentationKey); + if (RpAttachType.getRpAttachType() != RpAttachType.MANUAL) { + networkStatsbeat.setInstrumentationKey(instrumentationKey); + } attachStatsbeat.setInstrumentationKey(instrumentationKey); featureStatsbeat.setInstrumentationKey(instrumentationKey); instrumentationStatsbeat.setInstrumentationKey(instrumentationKey); diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/utils/PropertyHelper.java b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/utils/PropertyHelper.java index 2908b8b4c4a8..74de0ce2f84d 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/utils/PropertyHelper.java +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/main/java/com/azure/monitor/opentelemetry/exporter/implementation/utils/PropertyHelper.java @@ -3,6 +3,8 @@ package com.azure.monitor.opentelemetry.exporter.implementation.utils; +import com.azure.monitor.opentelemetry.exporter.implementation.statsbeat.MetadataInstanceResponse; +import com.azure.monitor.opentelemetry.exporter.implementation.statsbeat.RpAttachType; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -24,6 +26,8 @@ public final class PropertyHelper { public static final String UNKNOWN_VERSION_VALUE = "unknown"; + private static volatile char rpIntegrationChar; + /** * Reads the properties from a properties file. * @@ -86,6 +90,36 @@ public static String getSdkVersionNumber() { return SdkPropertyValues.SDK_VERSION_NUMBER; } + /** Is resource provider (Azure Spring Cloud, AppService, Azure Functions, AKS, VM...). */ + public static boolean isRpIntegration() { + return rpIntegrationChar != 0; + } + + public static void setRpIntegrationChar(char ch) { + rpIntegrationChar = ch; + } + + public static void lazyUpdateVmRpIntegration(MetadataInstanceResponse response) { + rpIntegrationChar = 'v'; + PropertyHelper.setSdkNamePrefix(getRpIntegrationSdkNamePrefix()); + RpAttachType.setRpAttachType(RpAttachType.STANDALONE_AUTO); + } + + public static String getRpIntegrationSdkNamePrefix() { + StringBuilder sdkNamePrefix = new StringBuilder(3); + sdkNamePrefix.append(rpIntegrationChar); + if (SystemInformation.isWindows()) { + sdkNamePrefix.append("w"); + } else if (SystemInformation.isLinux()) { + sdkNamePrefix.append("l"); + } else { + logger.warn("could not detect os: {}", System.getProperty("os.name")); + sdkNamePrefix.append("u"); + } + sdkNamePrefix.append("_"); + return sdkNamePrefix.toString(); + } + private static class SdkPropertyValues { private static final String SDK_VERSION_NUMBER; private static volatile String sdkVersionString; diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/test/java/com/azure/monitor/opentelemetry/exporter/AzureMonitorExportersEndToEndTest.java b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/test/java/com/azure/monitor/opentelemetry/exporter/AzureMonitorExportersEndToEndTest.java index 64d736ee23f5..eca84c55e426 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/test/java/com/azure/monitor/opentelemetry/exporter/AzureMonitorExportersEndToEndTest.java +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/test/java/com/azure/monitor/opentelemetry/exporter/AzureMonitorExportersEndToEndTest.java @@ -34,9 +34,7 @@ import java.io.ByteArrayOutputStream; import java.net.URL; import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.List; -import java.util.Map; +import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.CountDownLatch; import java.util.zip.GZIPInputStream; @@ -51,6 +49,11 @@ public class AzureMonitorExportersEndToEndTest extends MonitorExporterClientTest "InstrumentationKey=00000000-0000-0000-0000-0FEEDDADBEEF;" + "IngestionEndpoint=https://test.in.applicationinsights.azure.com/;" + "LiveEndpoint=https://test.livediagnostics.monitor.azure.com/"; + + private static final String STATSBEAT_CONNECTION_STRING = + "InstrumentationKey=00000000-0000-0000-0000-000000000000;" + + "IngestionEndpoint=https://westus-0.in.applicationinsights.azure.com/;" + + "LiveEndpoint=https://westus.livediagnostics.monitor.azure.com/"; private static final String INSTRUMENTATION_KEY = "00000000-0000-0000-0000-000000000000"; @Test @@ -192,10 +195,65 @@ public void testBuildTraceMetricLogExportersConsecutively() throws Exception { // TODO (trask) also export and validate logs in this test } + @Test + public void testStatsbeat() throws Exception { + // create the OpenTelemetry SDK + CountDownLatch countDownLatch = new CountDownLatch(1); + CustomValidationPolicy customValidationPolicy = new CustomValidationPolicy(countDownLatch); + OpenTelemetrySdk openTelemetry = + TestUtils.createOpenTelemetrySdk( + getHttpPipeline(customValidationPolicy), getStatsbeatConfiguration(), STATSBEAT_CONNECTION_STRING); + + // generate a metric + generateMetric(openTelemetry); + + // close to flush + openTelemetry.close(); + + Thread.sleep(1000); + + // wait for export + countDownLatch.await(10, SECONDS); + assertThat(customValidationPolicy.url) + .isEqualTo(new URL("https://westus-0.in.applicationinsights.azure.com/v2.1/track")); + assertThat(customValidationPolicy.actualTelemetryItems.size()).isEqualTo(4); + + TelemetryItem attachStatsbeat = + customValidationPolicy.actualTelemetryItems.stream() + .filter(item -> item.getName().equals("Statsbeat")) + .filter(item -> { + MetricsData metricsData = (MetricsData) item.getData().getBaseData(); + return metricsData.getMetrics().stream().allMatch(metricDataPoint -> metricDataPoint.getName().equals("Attach")); + }) + .findFirst() + .get(); + validateAttachStatsbeat(attachStatsbeat); + + TelemetryItem featureStatsbeat = + customValidationPolicy.actualTelemetryItems.stream() + .filter(item -> item.getName().equals("Statsbeat")) + .filter(item -> { + MetricsData metricsData = (MetricsData) item.getData().getBaseData(); + return metricsData.getMetrics().stream().allMatch(metricDataPoint -> metricDataPoint.getName().equals("Feature")); + }) + .findFirst() + .get(); + validateFeatureStatsbeat(featureStatsbeat); + } + private static Map getConfiguration() { return Collections.singletonMap("APPLICATIONINSIGHTS_CONNECTION_STRING", CONNECTION_STRING_ENV); } + private static Map getStatsbeatConfiguration() { + Map map = new HashMap<>(3); + map.put("APPLICATIONINSIGHTS_CONNECTION_STRING", CONNECTION_STRING_ENV); + map.put("STATSBEAT_LONG_INTERVAL_SECONDS", "1"); + map.put("STATSBEAT_SHORT_INTERVAL_SECONDS", "1"); + return map; + } + + @SuppressWarnings("try") private static void generateSpan(OpenTelemetry openTelemetry) { Tracer tracer = openTelemetry.getTracer("Sample"); @@ -240,8 +298,23 @@ private static void validateSpan(TelemetryItem telemetryItem) { .containsExactly(entry("color", "red"), entry("name", "apple")); } + private static void validateAttachStatsbeat(TelemetryItem telemetryItem) { + assertThat(telemetryItem.getData().getBaseType()).isEqualTo("MetricData"); + MetricsData actualMetricsData = (MetricsData) telemetryItem.getData().getBaseData(); + assertThat(actualMetricsData.getMetrics().get(0).getName()).isEqualTo("Attach"); + assertThat(actualMetricsData.getProperties()).contains(entry("rp", "unknown"), entry("attach", "Manual"), entry("language", "java")); + assertThat(actualMetricsData.getProperties()).containsKeys("attach", "cikey", "language", "os", "rp", "runtimeVersion", "version"); + } + + private static void validateFeatureStatsbeat(TelemetryItem telemetryItem) { + assertThat(telemetryItem.getData().getBaseType()).isEqualTo("MetricData"); + MetricsData actualMetricsData = (MetricsData) telemetryItem.getData().getBaseData(); + assertThat(actualMetricsData.getMetrics().get(0).getName()).isEqualTo("Feature"); + assertThat(actualMetricsData.getProperties()).contains(entry("type", "0"), entry("language", "java")); + assertThat(actualMetricsData.getProperties()).containsKeys("feature", "cikey", "language", "os", "rp", "runtimeVersion", "version"); + } + private static void validateMetric(TelemetryItem telemetryItem) { - assertThat(telemetryItem.getName()).isEqualTo("Metric"); assertThat(telemetryItem.getInstrumentationKey()).isEqualTo(INSTRUMENTATION_KEY); assertThat(telemetryItem.getTags()).containsEntry("ai.cloud.role", "unknown_service:java"); assertThat(telemetryItem.getTags()) diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/test/java/com/azure/monitor/opentelemetry/exporter/implementation/statsbeat/CustomDimensionsTest.java b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/test/java/com/azure/monitor/opentelemetry/exporter/implementation/statsbeat/CustomDimensionsTest.java index e8a4d5a43871..b35e52f3cfe8 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/test/java/com/azure/monitor/opentelemetry/exporter/implementation/statsbeat/CustomDimensionsTest.java +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/test/java/com/azure/monitor/opentelemetry/exporter/implementation/statsbeat/CustomDimensionsTest.java @@ -50,11 +50,8 @@ public void testVersion() { StatsbeatTelemetryBuilder telemetryBuilder = StatsbeatTelemetryBuilder.create("test", 1); customDimensions.populateProperties(telemetryBuilder, null); - String sdkVersion = PropertyHelper.getQualifiedSdkVersionString(); - String version = sdkVersion.substring(sdkVersion.lastIndexOf(':') + 1); - MetricsData data = (MetricsData) telemetryBuilder.build().getData().getBaseData(); - assertThat(data.getProperties()).containsEntry("version", version); + assertThat(data.getProperties()).containsKeys("version"); } @Test diff --git a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/test/java/com/azure/monitor/opentelemetry/exporter/implementation/utils/TestUtils.java b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/test/java/com/azure/monitor/opentelemetry/exporter/implementation/utils/TestUtils.java index 0456124699d6..411046135bb4 100644 --- a/sdk/monitor/azure-monitor-opentelemetry-exporter/src/test/java/com/azure/monitor/opentelemetry/exporter/implementation/utils/TestUtils.java +++ b/sdk/monitor/azure-monitor-opentelemetry-exporter/src/test/java/com/azure/monitor/opentelemetry/exporter/implementation/utils/TestUtils.java @@ -70,17 +70,22 @@ public static OpenTelemetrySdk createOpenTelemetrySdk(HttpPipeline httpPipeline) public static OpenTelemetrySdk createOpenTelemetrySdk( HttpPipeline httpPipeline, Map configuration) { + return createOpenTelemetrySdk(httpPipeline, configuration, TRACE_CONNECTION_STRING); + } + + public static OpenTelemetrySdk createOpenTelemetrySdk(HttpPipeline httpPipeline, Map configuration, String connectionString) { AutoConfiguredOpenTelemetrySdkBuilder sdkBuilder = AutoConfiguredOpenTelemetrySdk.builder(); new AzureMonitorExporterBuilder() - .connectionString(TRACE_CONNECTION_STRING) + .connectionString(connectionString) .httpPipeline(httpPipeline) .build(sdkBuilder); return sdkBuilder.addPropertiesSupplier(() -> configuration).build().getOpenTelemetrySdk(); } + private TestUtils() { } }