diff --git a/sdk/spring/azure-spring-data-cosmos/CHANGELOG.md b/sdk/spring/azure-spring-data-cosmos/CHANGELOG.md index 9e3dfc4e0043..1eb7f016bb15 100644 --- a/sdk/spring/azure-spring-data-cosmos/CHANGELOG.md +++ b/sdk/spring/azure-spring-data-cosmos/CHANGELOG.md @@ -3,6 +3,7 @@ ### 3.37.0-beta.1 (Unreleased) #### Features Added +* Added Diagnostic Threshold configuration support. In order to use diagnostics they must be enabled on the supplied `CosmosClientBuilder` as part of configuration. - See [PR 35546](https://github.com/Azure/azure-sdk-for-java/pull/35546) #### Breaking Changes diff --git a/sdk/spring/azure-spring-data-cosmos/README.md b/sdk/spring/azure-spring-data-cosmos/README.md index 402301215b02..12503c8235d1 100644 --- a/sdk/spring/azure-spring-data-cosmos/README.md +++ b/sdk/spring/azure-spring-data-cosmos/README.md @@ -124,6 +124,7 @@ Set `maxDegreeOfParallelism` flag to an integer in application.properties to all Set `maxBufferedItemCount` flag to an integer in application.properties to allow the user to set the max number of items that can be buffered during parallel query execution; if set to less than 0, the system automatically decides the number of items to buffer. NOTE: Setting this to a very high value can result in high memory consumption. Set `responseContinuationTokenLimitInKb` flag to an integer in application.properties to allow the user to limit the length of the continuation token in the query response. The continuation token contains both required and optional fields. The required fields are necessary for resuming the execution from where it was stoped. The optional fields may contain serialized index lookup work that was done but not yet utilized. This avoids redoing the work again in subsequent continuations and hence improve the query performance. Setting the maximum continuation size to 1KB, the Azure Cosmos DB service will only serialize required fields. Starting from 2KB, the Azure Cosmos DB service would serialize as much as it could fit till it reaches the maximum specified size. +Set `pointOperationLatencyThresholdInMS`, `nonPointOperationLatencyThresholdInMS`, `requestChargeThresholdInRU` and `payloadSizeThresholdInBytes` to enable diagnostics at the client level when these thresholds are exceeded. ```java readme-sample-AppConfiguration @Configuration @@ -156,6 +157,18 @@ public class AppConfiguration extends AbstractCosmosConfiguration { @Value("${azure.cosmos.responseContinuationTokenLimitInKb}") private int responseContinuationTokenLimitInKb; + @Value("${azure.cosmos.diagnosticsThresholds.pointOperationLatencyThresholdInMS}") + private int pointOperationLatencyThresholdInMS; + + @Value("${azure.cosmos.diagnosticsThresholds.nonPointOperationLatencyThresholdInMS}") + private int nonPointOperationLatencyThresholdInMS; + + @Value("${azure.cosmos.diagnosticsThresholds.requestChargeThresholdInRU}") + private int requestChargeThresholdInRU; + + @Value("${azure.cosmos.diagnosticsThresholds.payloadSizeThresholdInBytes}") + private int payloadSizeThresholdInBytes; + private AzureKeyCredential azureKeyCredential; @Bean @@ -166,7 +179,17 @@ public class AppConfiguration extends AbstractCosmosConfiguration { return new CosmosClientBuilder() .endpoint(uri) .credential(azureKeyCredential) - .directMode(directConnectionConfig, gatewayConnectionConfig); + .directMode(directConnectionConfig, gatewayConnectionConfig) + .clientTelemetryConfig( + new CosmosClientTelemetryConfig() + .diagnosticsThresholds( + new CosmosDiagnosticsThresholds() + .setNonPointOperationLatencyThreshold(Duration.ofMillis(nonPointOperationLatencyThresholdInMS)) + .setPointOperationLatencyThreshold(Duration.ofMillis(pointOperationLatencyThresholdInMS)) + .setPayloadSizeThreshold(payloadSizeThresholdInBytes) + .setRequestChargeThreshold(requestChargeThresholdInRU) + ) + .diagnosticsHandler(CosmosDiagnosticsHandler.DEFAULT_LOGGING_HANDLER)); } @Override @@ -201,6 +224,7 @@ public class AppConfiguration extends AbstractCosmosConfiguration { ``` ### Customizing Configuration You can customize `DirectConnectionConfig` or `GatewayConnectionConfig` or both and provide them to `CosmosClientBuilder` bean to customize `CosmosAsyncClient` +You can customize `pointOperationLatencyThresholdInMS`, `nonPointOperationLatencyThresholdInMS`, `requestChargeThresholdInRU` and `payloadSizeThresholdInBytes` to customize the thresholds for diagnostic logging when combined with `CosmosDiagnosticsHandler` which enables diagnostic logging with the default thresholds when added to the `CosmosClientBuilder`. ```java readme-sample-AppConfigurationCodeSnippet @Bean @@ -210,7 +234,17 @@ public CosmosClientBuilder getCosmosClientBuilder() { GatewayConnectionConfig gatewayConnectionConfig = new GatewayConnectionConfig(); return new CosmosClientBuilder() .endpoint(uri) - .directMode(directConnectionConfig, gatewayConnectionConfig); + .directMode(directConnectionConfig, gatewayConnectionConfig) + .clientTelemetryConfig( + new CosmosClientTelemetryConfig() + .diagnosticsThresholds( + new CosmosDiagnosticsThresholds() + .setNonPointOperationLatencyThreshold(Duration.ofMillis(nonPointOperationLatencyThresholdInMS)) + .setPointOperationLatencyThreshold(Duration.ofMillis(pointOperationLatencyThresholdInMS)) + .setPayloadSizeThreshold(payloadSizeThresholdInBytes) + .setRequestChargeThreshold(requestChargeThresholdInRU) + ) + .diagnosticsHandler(CosmosDiagnosticsHandler.DEFAULT_LOGGING_HANDLER)); } @Override @@ -228,6 +262,14 @@ public CosmosConfig cosmosConfig() { By default, `@EnableCosmosRepositories` will scan the current package for any interfaces that extend one of Spring Data's repository interfaces. Use it to annotate your Configuration class to scan a different root package by `@EnableCosmosRepositories(basePackageClass=UserRepository.class)` if your project layout has multiple projects. +#### Enabling logging diagnostics to Azure Application Insights with JavaAgent + +Diagnostics can be enabled by passing the JavaAgent with your application like below. This will enable logging with the default thresholds. The '-javaagent' must be passed before the '-jar'. + +```commandline +java -javaagent:"" -jar +``` + #### Using database provisioned throughput Cosmos supports both [container](https://docs.microsoft.com/azure/cosmos-db/sql/how-to-provision-container-throughput) @@ -689,7 +731,7 @@ public class SecondaryDatasourceConfiguration { public CosmosAsyncClient getCosmosAsyncClient(@Qualifier("secondary") CosmosProperties secondaryProperties) { return CosmosFactory.createCosmosAsyncClient(new CosmosClientBuilder() .key(secondaryProperties.getKey()) - .endpoint(secondaryProperties.getUri())); + .endpoint(secondaryProperties.getUri()); } @Bean("secondaryCosmosConfig") @@ -727,7 +769,7 @@ public class SecondaryDatasourceConfiguration { public CosmosAsyncClient getCosmosAsyncClient(@Qualifier("secondary") CosmosProperties secondaryProperties) { return CosmosFactory.createCosmosAsyncClient(new CosmosClientBuilder() .key(secondaryProperties.getKey()) - .endpoint(secondaryProperties.getUri())); + .endpoint(secondaryProperties.getUri()); } @Bean("secondaryCosmosConfig") diff --git a/sdk/spring/azure-spring-data-cosmos/src/samples/java/com/azure/spring/data/cosmos/AppConfiguration.java b/sdk/spring/azure-spring-data-cosmos/src/samples/java/com/azure/spring/data/cosmos/AppConfiguration.java index 1dd89b16e2f4..5cbee7405aa3 100644 --- a/sdk/spring/azure-spring-data-cosmos/src/samples/java/com/azure/spring/data/cosmos/AppConfiguration.java +++ b/sdk/spring/azure-spring-data-cosmos/src/samples/java/com/azure/spring/data/cosmos/AppConfiguration.java @@ -4,8 +4,11 @@ import com.azure.core.credential.AzureKeyCredential; import com.azure.cosmos.CosmosClientBuilder; +import com.azure.cosmos.CosmosDiagnosticsHandler; +import com.azure.cosmos.CosmosDiagnosticsThresholds; import com.azure.cosmos.DirectConnectionConfig; import com.azure.cosmos.GatewayConnectionConfig; +import com.azure.cosmos.models.CosmosClientTelemetryConfig; import com.azure.spring.data.cosmos.config.AbstractCosmosConfiguration; import com.azure.spring.data.cosmos.config.CosmosConfig; import com.azure.spring.data.cosmos.core.ResponseDiagnostics; @@ -18,6 +21,8 @@ import org.springframework.context.annotation.Configuration; import org.springframework.lang.Nullable; +import java.time.Duration; + // BEGIN: readme-sample-AppConfiguration @Configuration @EnableCosmosRepositories @@ -49,6 +54,19 @@ public class AppConfiguration extends AbstractCosmosConfiguration { @Value("${azure.cosmos.responseContinuationTokenLimitInKb}") private int responseContinuationTokenLimitInKb; + @Value("${azure.cosmos.diagnosticsThresholds.pointOperationLatencyThresholdInMS}") + private int pointOperationLatencyThresholdInMS; + + @Value("${azure.cosmos.diagnosticsThresholds.nonPointOperationLatencyThresholdInMS}") + private int nonPointOperationLatencyThresholdInMS; + + @Value("${azure.cosmos.diagnosticsThresholds.requestChargeThresholdInRU}") + private int requestChargeThresholdInRU; + + @Value("${azure.cosmos.diagnosticsThresholds.payloadSizeThresholdInBytes}") + private int payloadSizeThresholdInBytes; + + private AzureKeyCredential azureKeyCredential; @Bean @@ -59,7 +77,17 @@ public CosmosClientBuilder getCosmosClientBuilder() { return new CosmosClientBuilder() .endpoint(uri) .credential(azureKeyCredential) - .directMode(directConnectionConfig, gatewayConnectionConfig); + .directMode(directConnectionConfig, gatewayConnectionConfig) + .clientTelemetryConfig( + new CosmosClientTelemetryConfig() + .diagnosticsThresholds( + new CosmosDiagnosticsThresholds() + .setNonPointOperationLatencyThreshold(Duration.ofMillis(nonPointOperationLatencyThresholdInMS)) + .setPointOperationLatencyThreshold(Duration.ofMillis(pointOperationLatencyThresholdInMS)) + .setPayloadSizeThreshold(payloadSizeThresholdInBytes) + .setRequestChargeThreshold(requestChargeThresholdInRU) + ) + .diagnosticsHandler(CosmosDiagnosticsHandler.DEFAULT_LOGGING_HANDLER)); } @Override diff --git a/sdk/spring/azure-spring-data-cosmos/src/samples/java/com/azure/spring/data/cosmos/AppConfigurationCodeSnippet.java b/sdk/spring/azure-spring-data-cosmos/src/samples/java/com/azure/spring/data/cosmos/AppConfigurationCodeSnippet.java index 5dfb9988bd77..d330f5182a5e 100644 --- a/sdk/spring/azure-spring-data-cosmos/src/samples/java/com/azure/spring/data/cosmos/AppConfigurationCodeSnippet.java +++ b/sdk/spring/azure-spring-data-cosmos/src/samples/java/com/azure/spring/data/cosmos/AppConfigurationCodeSnippet.java @@ -3,8 +3,11 @@ package com.azure.spring.data.cosmos; import com.azure.cosmos.CosmosClientBuilder; +import com.azure.cosmos.CosmosDiagnosticsHandler; +import com.azure.cosmos.CosmosDiagnosticsThresholds; import com.azure.cosmos.DirectConnectionConfig; import com.azure.cosmos.GatewayConnectionConfig; +import com.azure.cosmos.models.CosmosClientTelemetryConfig; import com.azure.spring.data.cosmos.config.AbstractCosmosConfiguration; import com.azure.spring.data.cosmos.config.CosmosConfig; import com.azure.spring.data.cosmos.core.ResponseDiagnostics; @@ -17,6 +20,8 @@ import org.springframework.context.annotation.Configuration; import org.springframework.lang.Nullable; +import java.time.Duration; + @Configuration @EnableCosmosRepositories public class AppConfigurationCodeSnippet extends AbstractCosmosConfiguration { @@ -48,6 +53,18 @@ public class AppConfigurationCodeSnippet extends AbstractCosmosConfiguration { @Value("${azure.cosmos.responseContinuationTokenLimitInKb}") private int responseContinuationTokenLimitInKb; + @Value("${azure.cosmos.diagnosticsThresholds.pointOperationLatencyThresholdInMS}") + private int pointOperationLatencyThresholdInMS; + + @Value("${azure.cosmos.diagnosticsThresholds.nonPointOperationLatencyThresholdInMS}") + private int nonPointOperationLatencyThresholdInMS; + + @Value("${azure.cosmos.diagnosticsThresholds.requestChargeThresholdInRU}") + private int requestChargeThresholdInRU; + + @Value("${azure.cosmos.diagnosticsThresholds.payloadSizeThresholdInBytes}") + private int payloadSizeThresholdInBytes; + // BEGIN: readme-sample-AppConfigurationCodeSnippet @Bean public CosmosClientBuilder getCosmosClientBuilder() { @@ -56,7 +73,17 @@ public CosmosClientBuilder getCosmosClientBuilder() { GatewayConnectionConfig gatewayConnectionConfig = new GatewayConnectionConfig(); return new CosmosClientBuilder() .endpoint(uri) - .directMode(directConnectionConfig, gatewayConnectionConfig); + .directMode(directConnectionConfig, gatewayConnectionConfig) + .clientTelemetryConfig( + new CosmosClientTelemetryConfig() + .diagnosticsThresholds( + new CosmosDiagnosticsThresholds() + .setNonPointOperationLatencyThreshold(Duration.ofMillis(nonPointOperationLatencyThresholdInMS)) + .setPointOperationLatencyThreshold(Duration.ofMillis(pointOperationLatencyThresholdInMS)) + .setPayloadSizeThreshold(payloadSizeThresholdInBytes) + .setRequestChargeThreshold(requestChargeThresholdInRU) + ) + .diagnosticsHandler(CosmosDiagnosticsHandler.DEFAULT_LOGGING_HANDLER)); } @Override diff --git a/sdk/spring/azure-spring-data-cosmos/src/samples/java/com/azure/spring/data/cosmos/CosmosProperties.java b/sdk/spring/azure-spring-data-cosmos/src/samples/java/com/azure/spring/data/cosmos/CosmosProperties.java index cbd76815a334..1ab010bc75da 100644 --- a/sdk/spring/azure-spring-data-cosmos/src/samples/java/com/azure/spring/data/cosmos/CosmosProperties.java +++ b/sdk/spring/azure-spring-data-cosmos/src/samples/java/com/azure/spring/data/cosmos/CosmosProperties.java @@ -28,6 +28,14 @@ public class CosmosProperties { private int responseContinuationTokenLimitInKb; + private int pointOperationLatencyThresholdInMS; + + private int nonPointOperationLatencyThresholdInMS; + + private int requestChargeThresholdInRU; + + private int payloadSizeThresholdInBytes; + public String getUri() { return uri; } @@ -72,23 +80,55 @@ public int getMaxDegreeOfParallelism() { return maxDegreeOfParallelism; } + public void setMaxDegreeOfParallelism(int maxDegreeOfParallelism) { + this.maxDegreeOfParallelism = maxDegreeOfParallelism; + } + public int getMaxBufferedItemCount() { return maxBufferedItemCount; } + public void setMaxBufferedItemCount(int maxBufferedItemCount) { + this.maxBufferedItemCount = maxBufferedItemCount; + } + public int getResponseContinuationTokenLimitInKb() { return responseContinuationTokenLimitInKb; } - public void setMaxDegreeOfParallelism(int maxDegreeOfParallelism) { - this.maxDegreeOfParallelism = maxDegreeOfParallelism; + public void setResponseContinuationTokenLimitInKb(int responseContinuationTokenLimitInKb) { + this.responseContinuationTokenLimitInKb = responseContinuationTokenLimitInKb; } - public void setMaxBufferedItemCount(int maxBufferedItemCount) { - this.maxBufferedItemCount = maxBufferedItemCount; + public int getPointOperationLatencyThresholdInMS() { + return pointOperationLatencyThresholdInMS; } - public void setResponseContinuationTokenLimitInKb(int responseContinuationTokenLimitInKb) { - this.responseContinuationTokenLimitInKb = responseContinuationTokenLimitInKb; + public void setPointOperationLatencyThresholdInMS(int pointOperationLatencyThresholdInMS) { + this.pointOperationLatencyThresholdInMS = pointOperationLatencyThresholdInMS; + } + + public int getNonPointOperationLatencyThresholdInMS() { + return nonPointOperationLatencyThresholdInMS; + } + + public void setNonPointOperationLatencyThresholdInMS(int nonPointOperationLatencyThresholdInMS) { + this.nonPointOperationLatencyThresholdInMS = nonPointOperationLatencyThresholdInMS; + } + + public int getRequestChargeThresholdInRU() { + return requestChargeThresholdInRU; + } + + public void setRequestChargeThresholdInRU(int requestChargeThresholdInRU) { + this.requestChargeThresholdInRU = requestChargeThresholdInRU; + } + + public int getPayloadSizeThresholdInBytes() { + return payloadSizeThresholdInBytes; + } + + public void setPayloadSizeThresholdInBytes(int payloadSizeThresholdInBytes) { + this.payloadSizeThresholdInBytes = payloadSizeThresholdInBytes; } } diff --git a/sdk/spring/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/core/CosmosTemplateIT.java b/sdk/spring/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/core/CosmosTemplateIT.java index 1d21efff6891..2f6974623dcc 100644 --- a/sdk/spring/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/core/CosmosTemplateIT.java +++ b/sdk/spring/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/core/CosmosTemplateIT.java @@ -59,6 +59,7 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.List; @@ -90,6 +91,7 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertTrue; import static org.junit.Assert.fail; @RunWith(SpringJUnit4ClassRunner.class) @@ -138,6 +140,8 @@ public class CosmosTemplateIT { private static CosmosEntityInformation personInfo; private static String containerName; + private MappingCosmosConverter cosmosConverter; + private Person insertedPerson; private BasicItem pointReadItem; @@ -174,7 +178,7 @@ private CosmosTemplate createCosmosTemplate(CosmosConfig config, String dbName) final CosmosFactory cosmosFactory = new CosmosFactory(client, dbName); final CosmosMappingContext mappingContext = new CosmosMappingContext(); mappingContext.setInitialEntitySet(new EntityScanner(this.applicationContext).scan(Persistent.class)); - final MappingCosmosConverter cosmosConverter = new MappingCosmosConverter(mappingContext, null); + cosmosConverter = new MappingCosmosConverter(mappingContext, null); return new CosmosTemplate(cosmosFactory, config, cosmosConverter); } @@ -222,6 +226,17 @@ public void testFindAll() { } @Test + public void testDiagnosticsLogged() { + TestRepositoryConfig.capturingLogger.loggedMessages = new ArrayList<>(); + cosmosTemplate.insert(TEST_PERSON_2, + new PartitionKey(personInfo.getPartitionKeyFieldValue(TEST_PERSON_2))); + cosmosTemplate.insert(TEST_PERSON_3, + new PartitionKey(personInfo.getPartitionKeyFieldValue(TEST_PERSON_3))); + final List result = TestUtils.toList(cosmosTemplate.findAll(Person.class.getSimpleName(), + Person.class)); + assertTrue(TestRepositoryConfig.capturingLogger.getLoggedMessages().size() > 0); + } + public void testFindByIdPointRead() { final BasicItem result = cosmosTemplate.findById(BasicItem.class.getSimpleName(), BASIC_ITEM.getId(), BasicItem.class); diff --git a/sdk/spring/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/repository/MultiTenantTestRepositoryConfig.java b/sdk/spring/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/repository/MultiTenantTestRepositoryConfig.java index e28533a40997..34a6d809ef1d 100644 --- a/sdk/spring/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/repository/MultiTenantTestRepositoryConfig.java +++ b/sdk/spring/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/repository/MultiTenantTestRepositoryConfig.java @@ -4,6 +4,9 @@ import com.azure.cosmos.CosmosAsyncClient; import com.azure.cosmos.CosmosClientBuilder; +import com.azure.cosmos.CosmosDiagnosticsHandler; +import com.azure.cosmos.CosmosDiagnosticsThresholds; +import com.azure.cosmos.models.CosmosClientTelemetryConfig; import com.azure.spring.data.cosmos.common.ResponseDiagnosticsTestUtils; import com.azure.spring.data.cosmos.common.TestConstants; import com.azure.spring.data.cosmos.config.AbstractCosmosConfiguration; @@ -18,6 +21,7 @@ import org.springframework.context.annotation.PropertySource; import org.springframework.util.StringUtils; +import java.time.Duration; import java.util.Arrays; import java.util.Collection; @@ -47,6 +51,19 @@ public class MultiTenantTestRepositoryConfig extends AbstractCosmosConfiguration @Value("${cosmos.responseContinuationTokenLimitInKb}") private int responseContinuationTokenLimitInKb; + @Value("${cosmos.diagnosticsThresholds.pointOperationLatencyThresholdInMS}") + private int pointOperationLatencyThresholdInMS; + + @Value("${cosmos.diagnosticsThresholds.nonPointOperationLatencyThresholdInMS}") + private int nonPointOperationLatencyThresholdInMS; + + @Value("${cosmos.diagnosticsThresholds.requestChargeThresholdInRU}") + private int requestChargeThresholdInRU; + + @Value("${cosmos.diagnosticsThresholds.payloadSizeThresholdInBytes}") + private int payloadSizeThresholdInBytes; + + @Bean public ResponseDiagnosticsTestUtils responseDiagnosticsTestUtils() { return new ResponseDiagnosticsTestUtils(); @@ -57,7 +74,17 @@ public CosmosClientBuilder cosmosClientBuilder() { return new CosmosClientBuilder() .key(cosmosDbKey) .endpoint(cosmosDbUri) - .contentResponseOnWriteEnabled(true); + .contentResponseOnWriteEnabled(true) + .clientTelemetryConfig( + new CosmosClientTelemetryConfig() + .diagnosticsThresholds( + new CosmosDiagnosticsThresholds() + .setNonPointOperationLatencyThreshold(Duration.ofMillis(nonPointOperationLatencyThresholdInMS)) + .setPointOperationLatencyThreshold(Duration.ofMillis(pointOperationLatencyThresholdInMS)) + .setPayloadSizeThreshold(payloadSizeThresholdInBytes) + .setRequestChargeThreshold(requestChargeThresholdInRU) + ) + .diagnosticsHandler(CosmosDiagnosticsHandler.DEFAULT_LOGGING_HANDLER)); } @Bean diff --git a/sdk/spring/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/repository/SecondaryTestRepositoryConfig.java b/sdk/spring/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/repository/SecondaryTestRepositoryConfig.java index 27ef9490c2f9..97e8e7234fa7 100644 --- a/sdk/spring/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/repository/SecondaryTestRepositoryConfig.java +++ b/sdk/spring/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/repository/SecondaryTestRepositoryConfig.java @@ -4,6 +4,9 @@ import com.azure.cosmos.CosmosAsyncClient; import com.azure.cosmos.CosmosClientBuilder; +import com.azure.cosmos.CosmosDiagnosticsHandler; +import com.azure.cosmos.CosmosDiagnosticsThresholds; +import com.azure.cosmos.models.CosmosClientTelemetryConfig; import com.azure.spring.data.cosmos.CosmosFactory; import com.azure.spring.data.cosmos.config.CosmosConfig; import com.azure.spring.data.cosmos.core.ReactiveCosmosTemplate; @@ -16,6 +19,8 @@ import org.springframework.context.annotation.PropertySource; import org.springframework.util.StringUtils; +import java.time.Duration; + /** * Secondary Database Account */ @@ -43,12 +48,34 @@ public class SecondaryTestRepositoryConfig { @Value("${cosmos.secondary.responseContinuationTokenLimitInKb}") private int responseContinuationTokenLimitInKb; + @Value("${cosmos.diagnosticsThresholds.pointOperationLatencyThresholdInMS}") + private int pointOperationLatencyThresholdInMS; + + @Value("${cosmos.diagnosticsThresholds.nonPointOperationLatencyThresholdInMS}") + private int nonPointOperationLatencyThresholdInMS; + + @Value("${cosmos.diagnosticsThresholds.requestChargeThresholdInRU}") + private int requestChargeThresholdInRU; + + @Value("${cosmos.diagnosticsThresholds.payloadSizeThresholdInBytes}") + private int payloadSizeThresholdInBytes; + @Bean public CosmosClientBuilder secondaryCosmosClientBuilder() { return new CosmosClientBuilder() .key(cosmosDbKey) .endpoint(cosmosDbUri) - .contentResponseOnWriteEnabled(true); + .contentResponseOnWriteEnabled(true) + .clientTelemetryConfig( + new CosmosClientTelemetryConfig() + .diagnosticsThresholds( + new CosmosDiagnosticsThresholds() + .setNonPointOperationLatencyThreshold(Duration.ofMillis(nonPointOperationLatencyThresholdInMS)) + .setPointOperationLatencyThreshold(Duration.ofMillis(pointOperationLatencyThresholdInMS)) + .setPayloadSizeThreshold(payloadSizeThresholdInBytes) + .setRequestChargeThreshold(requestChargeThresholdInRU) + ) + .diagnosticsHandler(CosmosDiagnosticsHandler.DEFAULT_LOGGING_HANDLER)); } @Bean("secondaryCosmosAsyncClient") diff --git a/sdk/spring/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/repository/TestRepositoryConfig.java b/sdk/spring/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/repository/TestRepositoryConfig.java index 2668fd2c1499..82123a9a305d 100644 --- a/sdk/spring/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/repository/TestRepositoryConfig.java +++ b/sdk/spring/azure-spring-data-cosmos/src/test/java/com/azure/spring/data/cosmos/repository/TestRepositoryConfig.java @@ -2,7 +2,13 @@ // Licensed under the MIT License. package com.azure.spring.data.cosmos.repository; +import com.azure.core.util.Context; import com.azure.cosmos.CosmosClientBuilder; +import com.azure.cosmos.CosmosDiagnosticsContext; +import com.azure.cosmos.CosmosDiagnosticsHandler; +import com.azure.cosmos.CosmosDiagnosticsThresholds; +import com.azure.cosmos.models.CosmosClientTelemetryConfig; +import com.azure.spring.data.cosmos.AbstractIntegrationTestCollectionManager; import com.azure.spring.data.cosmos.common.ResponseDiagnosticsTestUtils; import com.azure.spring.data.cosmos.common.TestConstants; import com.azure.spring.data.cosmos.config.AbstractCosmosConfiguration; @@ -11,14 +17,19 @@ import com.azure.spring.data.cosmos.core.mapping.event.SimpleCosmosMappingEventListener; import com.azure.spring.data.cosmos.repository.config.EnableCosmosRepositories; import com.azure.spring.data.cosmos.repository.config.EnableReactiveCosmosRepositories; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import org.springframework.util.StringUtils; +import java.time.Duration; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.List; @Configuration @PropertySource(value = { "classpath:application.properties" }) @@ -47,6 +58,22 @@ public class TestRepositoryConfig extends AbstractCosmosConfiguration { @Value("${cosmos.responseContinuationTokenLimitInKb}") private int responseContinuationTokenLimitInKb; + @Value("${cosmos.diagnosticsThresholds.pointOperationLatencyThresholdInMS}") + private int pointOperationLatencyThresholdInMS; + + @Value("${cosmos.diagnosticsThresholds.nonPointOperationLatencyThresholdInMS}") + private int nonPointOperationLatencyThresholdInMS; + + @Value("${cosmos.diagnosticsThresholds.requestChargeThresholdInRU}") + private int requestChargeThresholdInRU; + + @Value("${cosmos.diagnosticsThresholds.payloadSizeThresholdInBytes}") + private int payloadSizeThresholdInBytes; + + private static final Logger logger = LoggerFactory.getLogger(TestRepositoryConfig.class); + + public static final CapturingLogger capturingLogger = new CapturingLogger(); + @Bean public ResponseDiagnosticsTestUtils responseDiagnosticsTestUtils() { return new ResponseDiagnosticsTestUtils(); @@ -57,7 +84,17 @@ public CosmosClientBuilder cosmosClientBuilder() { return new CosmosClientBuilder() .key(cosmosDbKey) .endpoint(cosmosDbUri) - .contentResponseOnWriteEnabled(true); + .contentResponseOnWriteEnabled(true) + .clientTelemetryConfig( + new CosmosClientTelemetryConfig() + .diagnosticsThresholds( + new CosmosDiagnosticsThresholds() + .setNonPointOperationLatencyThreshold(Duration.ofMillis(10)) + .setPointOperationLatencyThreshold(Duration.ofMillis(pointOperationLatencyThresholdInMS)) + .setPayloadSizeThreshold(payloadSizeThresholdInBytes) + .setRequestChargeThreshold(requestChargeThresholdInRU) + ) + .diagnosticsHandler(capturingLogger)); } @Bean @@ -98,4 +135,32 @@ protected Collection getMappingBasePackages() { SimpleCosmosMappingEventListener simpleMappingEventListener() { return new SimpleCosmosMappingEventListener(); } + + public static class CapturingLogger implements CosmosDiagnosticsHandler { + public List loggedMessages = new ArrayList<>(); + public CapturingLogger() { + super(); + } + + @Override + public void handleDiagnostics(CosmosDiagnosticsContext ctx, Context traceContext) { + logger.info("--> log - ctx: {}", ctx); + String msg = String.format( + "Account: %s -> DB: %s, Col:%s, StatusCode: %d:%d Diagnostics: %s", + ctx.getAccountName(), + ctx.getDatabaseName(), + ctx.getContainerName(), + ctx.getStatusCode(), + ctx.getSubStatusCode(), + ctx); + + this.loggedMessages.add(msg); + + logger.info(msg); + } + + public List getLoggedMessages() { + return this.loggedMessages; + } + } } diff --git a/sdk/spring/azure-spring-data-cosmos/src/test/resources/application.properties b/sdk/spring/azure-spring-data-cosmos/src/test/resources/application.properties index a93df8ed8cc1..4289a91340d5 100644 --- a/sdk/spring/azure-spring-data-cosmos/src/test/resources/application.properties +++ b/sdk/spring/azure-spring-data-cosmos/src/test/resources/application.properties @@ -11,6 +11,14 @@ cosmos.maxDegreeOfParallelism=0 cosmos.maxBufferedItemCount=0 # Max size of the response continuation token cosmos.responseContinuationTokenLimitInKb=0 +# The latency threshold to determine whether to include request diagnostics or not for point operations (in milliseconds) +cosmos.diagnosticsThresholds.pointOperationLatencyThresholdInMS=1000 +# The latency threshold to determine whether to include request diagnostics or not for non-point operations (in milliseconds) +cosmos.diagnosticsThresholds.nonPointOperationLatencyThresholdInMS=3000 +# The request charge (RU) threshold to determine whether to include request diagnostics or not +cosmos.diagnosticsThresholds.requestChargeThresholdInRU=1000 +# The payload size (in bytes) threshold to determine whether to include request diagnostics or not (in bytes) +cosmos.diagnosticsThresholds.payloadSizeThresholdInBytes=2147483647 # Secondary DataSource Config cosmos.secondary.uri=${NEW_ACCOUNT_HOST} @@ -25,3 +33,12 @@ cosmos.secondary.maxDegreeOfParallelism=0 cosmos.secondary.maxBufferedItemCount=0 # Max size of the response continuation token cosmos.secondary.responseContinuationTokenLimitInKb=0 +# The latency threshold to determine whether to include request diagnostics or not for point operations (in milliseconds) +cosmos.secondary.diagnosticsThresholds.pointOperationLatencyThresholdInMS=1000 +# The latency threshold to determine whether to include request diagnostics or not for non-point operations (in milliseconds) +cosmos.secondary.diagnosticsThresholds.nonPointOperationLatencyThresholdInMS=3000 +# The request charge (RU) threshold to determine whether to include request diagnostics or not +cosmos.secondary.diagnosticsThresholds.requestChargeThresholdInRU=1000 +# The payload size (in bytes) threshold to determine whether to include request diagnostics or not (in bytes) +cosmos.secondary.diagnosticsThresholds.payloadSizeThresholdInBytes=2147483647 +