diff --git a/eng/.docsettings.yml b/eng/.docsettings.yml index 988b2e5f435c..21f346f7f4ce 100644 --- a/eng/.docsettings.yml +++ b/eng/.docsettings.yml @@ -172,7 +172,6 @@ known_content_issues: - ['sdk/core/Microsoft.Azure.Core.Spatial/README.md', '#15423'] - ['sdk/core/Microsoft.Azure.Core.Spatial.NewtonsoftJson/README.md', '#15423'] - ['sdk/servicebus/Microsoft.Azure.WebJobs.Extensions.ServiceBus/README.md', '#15423'] - - ['sdk/monitor/Azure.Monitor.Query/README.md', '#15423'] # .net climbs upwards. placing these to prevent assigning readmes to the wrong project package_indexing_exclusion_list: diff --git a/sdk/monitor/Azure.Monitor.Query/README.md b/sdk/monitor/Azure.Monitor.Query/README.md index 765c381f4dfa..539756fccc58 100644 --- a/sdk/monitor/Azure.Monitor.Query/README.md +++ b/sdk/monitor/Azure.Monitor.Query/README.md @@ -1,9 +1,62 @@ # Azure Monitor Query client library for .NET -TODO +The `Azure.Monitor.Query` package provides an ability to query [Azure Monitor Logs](https://docs.microsoft.com/azure/azure-monitor/logs/data-platform-logs) and [Azure Monitor Metrics](https://docs.microsoft.com/azure/azure-monitor/essentials/data-platform-metrics) data. +[Azure Monitor Logs](https://docs.microsoft.com/azure/azure-monitor/logs/data-platform-logs) is a feature of Azure Monitor that collects and organizes log and performance data from monitored resources. Data from different sources such as platform logs from Azure services, log and performance data from virtual machines agents, and usage and performance data from applications can be consolidated into a single workspace so they can be analyzed together using a sophisticated query language that's capable of quickly analyzing millions of records. -## Samples +[Azure Monitor Metrics](https://docs.microsoft.com/azure/azure-monitor/essentials/data-platform-metrics) is a feature of Azure Monitor that collects numeric data from monitored resources into a time series database. Metrics are numerical values that are collected at regular intervals and describe some aspect of a system at a particular time. Metrics in Azure Monitor are lightweight and capable of supporting near real-time scenarios making them particularly useful for alerting and fast detection of issues. + +[Source code][query_client_src] | [Package (NuGet)][query_client_nuget_package] + +## Getting started + +### Install the package +Install the Azure Monitor Query client library for .NET with [NuGet][query_client_nuget_package]: + +``` +dotnet add package Azure.Monitor.Query --prerelease +``` + +### Prerequisites +* An [Azure subscription][azure_sub]. +* To be able to query logs you would need an existing Log Analytics workspace. You can create it in one of the following approaches: + * [Azure Portal](https://docs.microsoft.com/azure/azure-monitor/logs/quick-create-workspace) + * [Azure CLI](https://docs.microsoft.com/azure/azure-monitor/logs/quick-create-workspace-cli) + * [PowerShell](https://docs.microsoft.com/azure/azure-monitor/logs/powershell-workspace-configuration) + +* To be able to query metrics all you need is an Azure resource of any kind (Storage Account, KeyVault, CosmosDB etc.) + +### Authenticate the Client + +In order to interact with the Azure Monitor service, you will need to create an instance of a [TokenCredential](https://docs.microsoft.com/dotnet/api/azure.core.tokencredential?view=azure-dotnet) class and pass it to the constructor of your `LogsClient` or `MetricsClient` class. + +## Key concepts + +- `LogsClient` - Client that provides methods to query logs from Azure Monitor Logs. +- `MetricsClient` - Client that provides methods to query metrics from Azure Monitor Metrics. + +### Thread safety +We guarantee that all client instance methods are thread-safe and independent of each other ([guideline](https://azure.github.io/azure-sdk/dotnet_introduction.html#dotnet-service-methods-thread-safety)). This ensures that the recommendation of reusing client instances is always safe, even across threads. + +### Additional concepts + +[Client options](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/core/Azure.Core/README.md#configuring-service-clients-using-clientoptions) | +[Accessing the response](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/core/Azure.Core/README.md#accessing-http-response-details-using-responset) | +[Long-running operations](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/core/Azure.Core/README.md#consuming-long-running-operations-using-operationt) | +[Handling failures](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/core/Azure.Core/README.md#reporting-errors-requestfailedexception) | +[Diagnostics](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/core/Azure.Core/samples/Diagnostics.md) | +[Mocking](https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/core/Azure.Core/README.md#mocking) | +[Client lifetime](https://devblogs.microsoft.com/azure-sdk/lifetime-management-and-thread-safety-guarantees-of-azure-sdk-net-clients/) + + +## Examples + +- [Query logs](#query-logs) +- [Query logs as model](#query-logs-as-model) +- [Query logs as primitive](#query-logs-as-primitive) +- [Batch query](#batch-query) +- [Query dynamic table](#query-dynamic-table) +- [Increase query timeout](#increase-query-timeout) ### Query logs @@ -92,7 +145,7 @@ foreach (var logEntryModel in topEntries) } ``` -### Query dynamic table +### Query dynamic table You can also dynamically inspect the list of columns. The following example prints the result of the query as a table: @@ -120,4 +173,104 @@ foreach (var row in table.Rows) Console.WriteLine(); } -``` \ No newline at end of file +``` + +### Increase query timeout + +Some queries take longer to execute than the default service timeout allows. You can use the `LogsQueryOptions` parameter to specify the service timeout. + +```C# Snippet:QueryLogsPrintTable +LogsClient client = new LogsClient(new DefaultAzureCredential()); +string workspaceId = ""; +Response response = await client.QueryAsync(workspaceId, "AzureActivity | top 10 by TimeGenerated"); + +LogsQueryResultTable table = response.Value.PrimaryTable; + +foreach (var column in table.Columns) +{ + Console.Write(column.Name + ";"); +} + +Console.WriteLine(); + +var columnCount = table.Columns.Count; +foreach (var row in table.Rows) +{ + for (int i = 0; i < columnCount; i++) + { + Console.Write(row[i] + ";"); + } + + Console.WriteLine(); +} +``` + +## Troubleshooting + +### General + +When you interact with the Azure Monitor Query client library using the .NET SDK, errors returned by the service correspond to the same HTTP status codes returned for [REST API][monitor_rest_api] requests. + +For example, if you submit an invalid query a `400` error is returned, indicating "Bad Request". + +```C# Snippet:BadRequest +string workspaceId = ""; +LogsClient client = new LogsClient(new DefaultAzureCredential()); +try +{ + await client.QueryAsync(workspaceId, "My Not So Valid Query"); +} +catch (Exception e) +{ + Console.WriteLine(e); + throw; +} +``` + +The exception also contains some additional information like the full error content. + +``` +Azure.RequestFailedException : The request had some invalid properties +Status: 400 (Bad Request) +ErrorCode: BadArgumentError + +Content: +{"error":{"message":"The request had some invalid properties","code":"BadArgumentError","correlationId":"34f5f93a-6007-48a4-904f-487ca4e62a82","innererror":{"code":"SyntaxError","message":"A recognition error occurred in the query.","innererror":{"code":"SYN0002","message":"Query could not be parsed at 'Not' on line [1,3]","line":1,"pos":3,"token":"Not"}}}} +``` + +### Setting up console logging + +The simplest way to see the logs is to enable the console logging. +To create an Azure SDK log listener that outputs messages to console use AzureEventSourceListener.CreateConsoleLogger method. + +```C# +// Setup a listener to monitor logged events. +using AzureEventSourceListener listener = AzureEventSourceListener.CreateConsoleLogger(); +``` + +To learn more about other logging mechanisms see [here][logging]. + +## Next steps + +## Contributing + +This project welcomes contributions and suggestions. Most contributions require +you to agree to a Contributor License Agreement (CLA) declaring that you have +the right to, and actually do, grant us the rights to use your contribution. For +details, visit [cla.microsoft.com][cla]. + +This project has adopted the [Microsoft Open Source Code of Conduct][coc]. +For more information see the [Code of Conduct FAQ][coc_faq] or contact +[opencode@microsoft.com][coc_contact] with any additional questions or comments. + +[query_client_src]: https://github.com/Azure/azure-sdk-for-net/blob/master/sdk/monitor/Azure.Monitor.Query/src +[query_client_nuget_package]: https://www.nuget.org/packages?q=Azure.Monitor.Query +[monitor_rest_api]: https://docs.microsoft.com/rest/api/monitor/ +[azure_cli]: https://docs.microsoft.com/cli/azure +[azure_sub]: https://azure.microsoft.com/free/ +[cla]: https://cla.microsoft.com +[coc]: https://opensource.microsoft.com/codeofconduct/ +[coc_faq]: https://opensource.microsoft.com/codeofconduct/faq/ +[coc_contact]: mailto:opencode@microsoft.com + +![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-net%2Fsdk%2Fmonitor%2FAzure.Monitor.Query%2FREADME.png) diff --git a/sdk/monitor/Azure.Monitor.Query/src/Azure.Monitor.Query.csproj b/sdk/monitor/Azure.Monitor.Query/src/Azure.Monitor.Query.csproj index a5874cc2c050..19eeaf4d87a0 100644 --- a/sdk/monitor/Azure.Monitor.Query/src/Azure.Monitor.Query.csproj +++ b/sdk/monitor/Azure.Monitor.Query/src/Azure.Monitor.Query.csproj @@ -5,7 +5,7 @@ 1.0.0-beta.1 Azure Monitor Query $(RequiredTargetFrameworks) - $(NoWarn);AZC0001;CS1591 + $(NoWarn);AZC0001 diff --git a/sdk/monitor/Azure.Monitor.Query/src/LogsClient.cs b/sdk/monitor/Azure.Monitor.Query/src/LogsClient.cs index e545f2d72771..d4249b232e0e 100644 --- a/sdk/monitor/Azure.Monitor.Query/src/LogsClient.cs +++ b/sdk/monitor/Azure.Monitor.Query/src/LogsClient.cs @@ -3,7 +3,6 @@ using System; using System.Collections.Generic; -using System.Text; using System.Text.Json; using System.Threading; using System.Threading.Tasks; @@ -13,6 +12,9 @@ namespace Azure.Monitor.Query { + /// + /// The allows to query the Azure Monitor Logs service. + /// public class LogsClient { private readonly QueryRestClient _queryClient; @@ -20,10 +22,19 @@ public class LogsClient private readonly HttpPipeline _pipeline; private readonly RowBinder _rowBinder; + /// + /// Initializes a new instance of . + /// + /// The instance to use for authentication. public LogsClient(TokenCredential credential) : this(credential, null) { } + /// + /// Initializes a new instance of . + /// + /// The instance to use for authentication. + /// The instance to use as client configuration. public LogsClient(TokenCredential credential, LogsClientOptions options) { Argument.AssertNotNull(credential, nameof(credential)); @@ -36,10 +47,22 @@ public LogsClient(TokenCredential credential, LogsClientOptions options) _rowBinder = new RowBinder(); } + /// + /// Initializes a new instance of for mocking. + /// protected LogsClient() { } + /// + /// Executes the logs query. + /// + /// The workspace to include in the query. + /// The query text to execute. + /// The timespan over which to query data. Logs would be filtered to include entries produced starting at Now - timeSpan. + /// The to configure the query. + /// The to use. + /// Query results mapped to a type . public virtual Response> Query(string workspaceId, string query, TimeSpan? timeSpan = null, LogsQueryOptions options = null, CancellationToken cancellationToken = default) { Response response = Query(workspaceId, query, timeSpan, options, cancellationToken); @@ -47,6 +70,15 @@ public virtual Response> Query(string workspaceId, string qu return Response.FromValue(_rowBinder.BindResults(response), response.GetRawResponse()); } + /// + /// Executes the logs query. + /// + /// The workspace to include in the query. + /// The query text to execute. + /// The timespan over which to query data. Logs would be filtered to include entries produced starting at Now - timeSpan. + /// The to configure the query. + /// The to use. + /// Query results mapped to a type . public virtual async Task>> QueryAsync(string workspaceId, string query, TimeSpan? timeSpan = null, LogsQueryOptions options = null, CancellationToken cancellationToken = default) { Response response = await QueryAsync(workspaceId, query, timeSpan, options, cancellationToken).ConfigureAwait(false); @@ -54,6 +86,15 @@ public virtual async Task>> QueryAsync(string works return Response.FromValue(_rowBinder.BindResults(response), response.GetRawResponse()); } + /// + /// Executes the logs query. + /// + /// The workspace to include in the query. + /// The query text to execute. + /// The timespan over which to query data. Logs would be filtered to include entries produced starting at Now - timeSpan. + /// The to configure the query. + /// The to use. + /// The containing the query results. public virtual Response Query(string workspaceId, string query, TimeSpan? timeSpan = null, LogsQueryOptions options = null, CancellationToken cancellationToken = default) { using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(LogsClient)}.{nameof(Query)}"); @@ -69,6 +110,15 @@ public virtual Response Query(string workspaceId, string query, } } + /// + /// Executes the logs query. + /// + /// The workspace to include in the query. + /// The query text to execute. + /// The timespan over which to query data. Logs would be filtered to include entries produced starting at Now - timeSpan. + /// The to configure the query. + /// The to use. + /// The with the query results. public virtual async Task> QueryAsync(string workspaceId, string query, TimeSpan? timeSpan = null, LogsQueryOptions options = null, CancellationToken cancellationToken = default) { using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(LogsClient)}.{nameof(Query)}"); @@ -84,6 +134,10 @@ public virtual async Task> QueryAsync(string workspace } } + /// + /// Creates an instance of that allows executing multiple queries at once. + /// + /// The instance that allows building a list of queries and submitting them. public virtual LogsBatchQuery CreateBatchQuery() { return new LogsBatchQuery(_clientDiagnostics, _queryClient, _rowBinder); @@ -161,4 +215,4 @@ await JsonDocument.ParseAsync(message.Response.ContentStream, default, cancellat } } } -} \ No newline at end of file +} diff --git a/sdk/monitor/Azure.Monitor.Query/src/LogsClientOptions.cs b/sdk/monitor/Azure.Monitor.Query/src/LogsClientOptions.cs index 756bc0d67221..c1f40f46012b 100644 --- a/sdk/monitor/Azure.Monitor.Query/src/LogsClientOptions.cs +++ b/sdk/monitor/Azure.Monitor.Query/src/LogsClientOptions.cs @@ -5,6 +5,9 @@ namespace Azure.Monitor.Query { + /// + /// Provides the client configuration options for connecting to Azure Monitor Logs service. + /// public class LogsClientOptions: ClientOptions { private readonly ServiceVersion _version; @@ -27,11 +30,14 @@ public LogsClientOptions(ServiceVersion version = LatestVersion) } /// - /// The versions of Azure Monitor Query service supported by this client + /// The versions of Azure Monitor Logs service supported by this client /// library. /// public enum ServiceVersion { + /// + /// The V1 version of the service + /// V1 } } diff --git a/sdk/monitor/Azure.Monitor.Query/src/LogsQueryOptions.cs b/sdk/monitor/Azure.Monitor.Query/src/LogsQueryOptions.cs index 87ec794c254e..78d957de68e2 100644 --- a/sdk/monitor/Azure.Monitor.Query/src/LogsQueryOptions.cs +++ b/sdk/monitor/Azure.Monitor.Query/src/LogsQueryOptions.cs @@ -2,12 +2,25 @@ // Licensed under the MIT License. using System; +using Azure.Monitor.Query.Models; namespace Azure.Monitor.Query { + /// + /// Options for and methods. + /// public class LogsQueryOptions { + /// + /// Gets or sets the value indicating the service timeout for the query. Defaults to null. + /// public TimeSpan? Timeout { get; set; } + + /// + /// Gets or sets the value indicating whether to include query execution statistics as part of the response. + /// Statistics can be retrieved via the property. + /// Defaults to false. + /// public bool IncludeStatistics { get; set; } } } \ No newline at end of file diff --git a/sdk/monitor/Azure.Monitor.Query/src/MetricsClient.cs b/sdk/monitor/Azure.Monitor.Query/src/MetricsClient.cs index 4493a992bbce..7f07cfc16066 100644 --- a/sdk/monitor/Azure.Monitor.Query/src/MetricsClient.cs +++ b/sdk/monitor/Azure.Monitor.Query/src/MetricsClient.cs @@ -11,18 +11,29 @@ namespace Azure.Monitor.Query { + /// + /// The allows to query the Azure Monitor Metrics service. + /// public class MetricsClient { private readonly MetricDefinitionsRestClient _metricDefinitionsClient; private readonly MetricsRestClient _metricsRestClient; private readonly MetricNamespacesRestClient _namespacesRestClient; private readonly ClientDiagnostics _clientDiagnostics; - private HttpPipeline _pipeline; + /// + /// Initializes a new instance of . + /// + /// The instance to use for authentication. public MetricsClient(TokenCredential credential) : this(credential, null) { } + /// + /// Initializes a new instance of . + /// + /// The instance to use for authentication. + /// The instance to as client configuration. public MetricsClient(TokenCredential credential, MetricsClientOptions options) { Argument.AssertNotNull(credential, nameof(credential)); @@ -30,16 +41,30 @@ public MetricsClient(TokenCredential credential, MetricsClientOptions options) options ??= new MetricsClientOptions(); _clientDiagnostics = new ClientDiagnostics(options); - _pipeline = HttpPipelineBuilder.Build(options, new BearerTokenAuthenticationPolicy(credential, "https://management.azure.com//.default")); - _metricDefinitionsClient = new MetricDefinitionsRestClient(_clientDiagnostics, _pipeline); - _metricsRestClient = new MetricsRestClient(_clientDiagnostics, _pipeline); - _namespacesRestClient = new MetricNamespacesRestClient(_clientDiagnostics, _pipeline); + + var pipeline = HttpPipelineBuilder.Build(options, new BearerTokenAuthenticationPolicy(credential, "https://management.azure.com//.default")); + _metricDefinitionsClient = new MetricDefinitionsRestClient(_clientDiagnostics, pipeline); + _metricsRestClient = new MetricsRestClient(_clientDiagnostics, pipeline); + _namespacesRestClient = new MetricNamespacesRestClient(_clientDiagnostics, pipeline); } + /// + /// Initializes a new instance of for mocking. + /// protected MetricsClient() { } + /// + /// Queries metrics for a resource. + /// + /// The resource name. + /// For example: /subscriptions/[subscription_id]/resourceGroups/[resource_group_name]/providers/Microsoft.OperationalInsights/workspaces/[workspace_name]. + /// The start time for the metric query. + /// The end time for the metric query. + /// The interval at which to sample the metrics. + /// The to use. + /// The instance containing the query results. public virtual Response Query(string resource, DateTimeOffset startTime, DateTimeOffset endTime, TimeSpan interval, CancellationToken cancellationToken = default) { using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(MetricsClient)}.{nameof(Query)}"); @@ -55,6 +80,16 @@ public virtual Response Query(string resource, DateTimeOffset } } + /// + /// Queries metrics for a resource. + /// + /// The resource name. + /// For example: /subscriptions/[subscription_id]/resourceGroups/[resource_group_name]/providers/Microsoft.OperationalInsights/workspaces/[workspace_name]. + /// The start time for the metric query. + /// The end time for the metric query. + /// The interval at which to sample the metrics. + /// The to use. + /// The instance with query results. public virtual async Task> QueryAsync(string resource, DateTimeOffset startTime, DateTimeOffset endTime, TimeSpan interval, CancellationToken cancellationToken = default) { using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(MetricsClient)}.{nameof(Query)}"); @@ -70,6 +105,15 @@ public virtual async Task> QueryAsync(string resourc } } + /// + /// Gets metric definitions for a particular resource and metric namespace. + /// + /// The resource name. + /// For example: /subscriptions/[subscription_id]/resourceGroups/[resource_group_name]/providers/Microsoft.OperationalInsights/workspaces/[workspace_name]. + /// The metric namespace. + /// For example: Microsoft.OperationalInsights/workspaces. + /// The to use. + /// A list of metric definitions. public virtual Response> GetMetrics(string resource, string metricsNamespace, CancellationToken cancellationToken = default) { using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(MetricsClient)}.{nameof(GetMetrics)}"); @@ -87,6 +131,15 @@ public virtual Response> GetMetrics(string resou } } + /// + /// Gets metric definitions for a particular resource and metric namespace. + /// + /// The resource name. + /// For example: /subscriptions/[subscription_id]/resourceGroups/[resource_group_name]/providers/Microsoft.OperationalInsights/workspaces/[workspace_name]. + /// The metric namespace. + /// For example: Microsoft.OperationalInsights/workspaces. + /// The to use. + /// A list of metric definitions. public virtual async Task>> GetMetricsAsync(string resource, string metricsNamespace, CancellationToken cancellationToken = default) { using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(MetricsClient)}.{nameof(GetMetrics)}"); @@ -104,6 +157,13 @@ public virtual async Task>> GetMetricsA } } + /// + /// Gets metric namespaces for a particular resource. + /// + /// The resource name. + /// For example: /subscriptions/[subscription_id]/resourceGroups/[resource_group_name]/providers/Microsoft.OperationalInsights/workspaces/[workspace_name]. + /// The to use. + /// A list of metric namespaces. public virtual Response> GetMetricNamespaces(string resource, CancellationToken cancellationToken = default) { using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(MetricsClient)}.{nameof(GetMetricNamespaces)}"); @@ -121,6 +181,12 @@ public virtual Response> GetMetricNamespaces(stri } } + /// + /// Gets metric namespaces for a particular resource. + /// + /// The resource name. + /// The to use. + /// A list of metric namespaces. public virtual async Task>> GetMetricNamespacesAsync(string resource, CancellationToken cancellationToken = default) { using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(MetricsClient)}.{nameof(GetMetricNamespaces)}"); @@ -143,4 +209,4 @@ private static string GetTimespan(DateTimeOffset startTime, DateTimeOffset endTi return $"{TypeFormatters.ToString(startTime, "o")}/{TypeFormatters.ToString(endTime, "o")}"; } } -} \ No newline at end of file +} diff --git a/sdk/monitor/Azure.Monitor.Query/src/MetricsClientOptions.cs b/sdk/monitor/Azure.Monitor.Query/src/MetricsClientOptions.cs index 8a73819e2546..a555c6c23752 100644 --- a/sdk/monitor/Azure.Monitor.Query/src/MetricsClientOptions.cs +++ b/sdk/monitor/Azure.Monitor.Query/src/MetricsClientOptions.cs @@ -5,6 +5,9 @@ namespace Azure.Monitor.Query { + /// + /// Provides the client configuration options for connecting to Azure Monitor Metrics service. + /// public class MetricsClientOptions: ClientOptions { private readonly ServiceVersion _version; @@ -33,6 +36,9 @@ public MetricsClientOptions(ServiceVersion version = LatestVersion) public enum ServiceVersion { #pragma warning disable CA1707 // Identifiers should not contain underscores + /// + /// Version 2018-01-01 of the service. + /// V2018_01_01 #pragma warning restore CA1707 // Identifiers should not contain underscores } diff --git a/sdk/monitor/Azure.Monitor.Query/src/Models/LogsBatchQuery.cs b/sdk/monitor/Azure.Monitor.Query/src/Models/LogsBatchQuery.cs index f70e8b66a136..0150373c97c7 100644 --- a/sdk/monitor/Azure.Monitor.Query/src/Models/LogsBatchQuery.cs +++ b/sdk/monitor/Azure.Monitor.Query/src/Models/LogsBatchQuery.cs @@ -10,6 +10,9 @@ namespace Azure.Monitor.Query { + /// + /// Represents a batch that consists out of multiple log queries. + /// public class LogsBatchQuery { private readonly ClientDiagnostics _clientDiagnostics; @@ -26,10 +29,21 @@ internal LogsBatchQuery(ClientDiagnostics clientDiagnostics, QueryRestClient res _batch = new BatchRequest(); } + /// + /// Initializes a new instance of for mocking. + /// protected LogsBatchQuery() { } + /// + /// Adds the specified query to the batch. Results can be retrieved after the query is submitted via the call. + /// + /// The workspace to include in the query. + /// The query text to execute. + /// The timespan over which to query data. + /// The to configure the query. + /// The query identifier that has to be passed into to get the result. public virtual string AddQuery(string workspaceId, string query, TimeSpan? timeSpan = null, LogsQueryOptions options = null) { var id = _counter.ToString("G", CultureInfo.InvariantCulture); @@ -48,6 +62,11 @@ public virtual string AddQuery(string workspaceId, string query, TimeSpan? timeS return id; } + /// + /// Submits the batch. + /// + /// The to use. + /// The containing the query identifier that has to be passed into to get the result. public virtual Response Submit(CancellationToken cancellationToken = default) { using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(LogsBatchQuery)}.{nameof(Submit)}"); @@ -65,6 +84,11 @@ public virtual Response Submit(CancellationToken cancellat } } + /// + /// Submits the batch. + /// + /// The to use. + /// The that allows retrieving query results. public virtual async Task> SubmitAsync(CancellationToken cancellationToken = default) { using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(LogsBatchQuery)}.{nameof(Submit)}"); @@ -82,4 +106,4 @@ public virtual async Task> SubmitAsync(Cancellati } } } -} \ No newline at end of file +} diff --git a/sdk/monitor/Azure.Monitor.Query/src/Models/LogsBatchQueryResult.cs b/sdk/monitor/Azure.Monitor.Query/src/Models/LogsBatchQueryResult.cs index 79584fcbdb26..85a21c68b775 100644 --- a/sdk/monitor/Azure.Monitor.Query/src/Models/LogsBatchQueryResult.cs +++ b/sdk/monitor/Azure.Monitor.Query/src/Models/LogsBatchQueryResult.cs @@ -15,6 +15,13 @@ public partial class LogsBatchQueryResult private BatchResponseError Error { get; } internal RowBinder RowBinder { get; set; } + /// + /// Gets the result for the query that was a part of the batch. + /// + /// The query identifier returned from the . + /// The with the query results. + /// When the query with was not part of the batch. + /// When the query failed. public LogsQueryResult GetResult(string queryId) { LogQueryResponse result = Responses.SingleOrDefault(r => r.Id == queryId); @@ -33,9 +40,16 @@ public LogsQueryResult GetResult(string queryId) return result.Body; } + /// + /// Gets the result for the query that was a part of the batch. + /// + /// The query identifier returned from the . + /// Query results mapped to a type . + /// When the query with was not part of the batch. + /// When the query failed. public IReadOnlyList GetResult(string queryId) { return RowBinder.BindResults(GetResult(queryId)); } } -} \ No newline at end of file +} diff --git a/sdk/monitor/Azure.Monitor.Query/src/Models/LogsQueryResult.cs b/sdk/monitor/Azure.Monitor.Query/src/Models/LogsQueryResult.cs index d8324fee76b9..a605bd1450cd 100644 --- a/sdk/monitor/Azure.Monitor.Query/src/Models/LogsQueryResult.cs +++ b/sdk/monitor/Azure.Monitor.Query/src/Models/LogsQueryResult.cs @@ -11,9 +11,15 @@ namespace Azure.Monitor.Query.Models public partial class LogsQueryResult { // TODO: Handle not found + /// + /// Returns the primary result of the query. + /// public LogsQueryResultTable PrimaryTable => Tables.Single(t => t.Name == "PrimaryResult"); #pragma warning disable AZC0014 + /// + /// Returns the query statistics if the is set to true. + /// public JsonElement Statistics { get; } #pragma warning restore AZC0014 diff --git a/sdk/monitor/Azure.Monitor.Query/src/Models/LogsQueryResultRow.cs b/sdk/monitor/Azure.Monitor.Query/src/Models/LogsQueryResultRow.cs index 4ada3d22d264..9dc8be02208d 100644 --- a/sdk/monitor/Azure.Monitor.Query/src/Models/LogsQueryResultRow.cs +++ b/sdk/monitor/Azure.Monitor.Query/src/Models/LogsQueryResultRow.cs @@ -9,6 +9,9 @@ namespace Azure.Monitor.Query.Models { + /// + /// Represents a row in the table of results returned from the logs query. + /// public class LogsQueryResultRow { private readonly Dictionary _columns; @@ -20,31 +23,156 @@ internal LogsQueryResultRow(Dictionary columns, JsonElement row) _row = row; } + /// + /// Returns the cell count. + /// public int Count => _row.GetArrayLength(); + /// + /// Gets the value of the column at the specified index as . + /// + /// The column index. + /// The value of the column. public int GetInt32(int index) => _row[index].GetInt32(); + + /// + /// Gets the value of the column at the specified index as . + /// + /// The column index. + /// The value of the column. public long GetInt64(int index) => _row[index].GetInt64(); + + /// + /// Gets the value of the column at the specified index as . + /// + /// The column index. + /// The value of the column. public bool GetBoolean(int index) => _row[index].GetBoolean(); + + /// + /// Gets the value of the column at the specified index as . + /// + /// The column index. + /// The value of the column. public decimal GetDecimal(int index) => decimal.Parse(_row[index].GetString(), CultureInfo.InvariantCulture); + + /// + /// Gets the value of the column at the specified index as . + /// + /// The column index. + /// The value of the column. public double GetDouble(int index) => _row[index].GetDouble(); + + /// + /// Gets the value of the column at the specified index as . + /// + /// The column index. + /// The value of the column. public string GetString(int index) => _row[index].GetString(); + + /// + /// Gets the value of the column at the specified index as . + /// + /// The column index. + /// The value of the column. public DateTimeOffset GetDateTimeOffset(int index) => _row[index].GetDateTimeOffset(); + + /// + /// Gets the value of the column at the specified index as . + /// + /// The column index. + /// The value of the column. public TimeSpan GetTimeSpan(int index) => _row[index].GetTimeSpan("c"); + + /// + /// Gets the value of the column at the specified index as . + /// + /// The column index. + /// The value of the column. public Guid GetGuid(int index) => _row[index].GetGuid(); + /// + /// Returns true if the value of the column at the specified index is null, otherwise false. + /// + /// The column index. + /// True if the value is null, otherwise false. public bool IsNull(int index) => _row[index].ValueKind == JsonValueKind.Null; + /// + /// Gets the value of the column with the specified name as . + /// + /// The column name. + /// The value of the column. public int GetInt32(string name) => GetInt32(_columns[name]); + + /// + /// Gets the value of the column with the specified name as . + /// + /// The column name. + /// The value of the column. public long GetInt64(string name) => GetInt64(_columns[name]); + + /// + /// Gets the value of the column with the specified name as . + /// + /// The column name. + /// The value of the column. public bool GetBoolean(string name) => GetBoolean(_columns[name]); + + /// + /// Gets the value of the column with the specified name as . + /// + /// The column name. + /// The value of the column. public decimal GetDecimal(string name) => GetDecimal(_columns[name]); + + /// + /// Gets the value of the column with the specified name as . + /// + /// The column name. + /// The value of the column. public double GetDouble(string name) => GetDouble(_columns[name]); + + /// + /// Gets the value of the column with the specified name as . + /// + /// The column name. + /// The value of the column. public string GetString(string name) => GetString(_columns[name]); + + /// + /// Gets the value of the column with the specified name as . + /// + /// The column name. + /// The value of the column. public DateTimeOffset GetDateTimeOffset(string name) => GetDateTimeOffset(_columns[name]); + + /// + /// Gets the value of the column with the specified name as . + /// + /// The column name. + /// The value of the column. public TimeSpan GetTimeSpan(string name) => GetTimeSpan(_columns[name]); + + /// + /// Gets the value of the column with the specified name as . + /// + /// The column name. + /// The value of the column. public Guid GetGuid(string name) => GetGuid(_columns[name]); + + /// + /// Returns true if the value of the column with the specified name is null, otherwise false. + /// + /// The column name. + /// True if the value is null, otherwise false. public bool IsNull(string name) => IsNull(_columns[name]); + /// + /// Gets the value of the column at the specified index as . + /// + /// The column index. + /// The value of the column. public object GetObject(int index) { var element = _row[index]; @@ -76,9 +204,25 @@ public object GetObject(int index) } } + /// + /// Gets the value of the column with the specified name as . + /// + /// The column name. + /// The value of the column. public object GetObject(string name) => GetObject(_columns[name]); + /// + /// Gets the value of the column at the specified index as . + /// + /// The column index. + /// The value of the column. public object this[int index] => GetObject(index); + + /// + /// Gets the value of the column with the specified name as . + /// + /// The column name. + /// The value of the column. public object this[string name] => GetObject(name); internal bool TryGetColumn(string name, out int column) => _columns.TryGetValue(name, out column); diff --git a/sdk/monitor/Azure.Monitor.Query/src/Models/LogsQueryResultTable.cs b/sdk/monitor/Azure.Monitor.Query/src/Models/LogsQueryResultTable.cs index abb9d07ebe97..f3283bb07396 100644 --- a/sdk/monitor/Azure.Monitor.Query/src/Models/LogsQueryResultTable.cs +++ b/sdk/monitor/Azure.Monitor.Query/src/Models/LogsQueryResultTable.cs @@ -14,8 +14,11 @@ public partial class LogsQueryResultTable private IReadOnlyList _rows; [CodeGenMember("Rows")] - internal JsonElement InternalRows { get; } + private JsonElement InternalRows { get; } + /// + /// Gets the rows of the result table. + /// public IReadOnlyList Rows => _rows ??= CreateRows(); private IReadOnlyList CreateRows() diff --git a/sdk/monitor/Azure.Monitor.Query/src/Models/MetadataValue.cs b/sdk/monitor/Azure.Monitor.Query/src/Models/MetadataValue.cs index 0e6fad135c9e..aaf66a0b1a06 100644 --- a/sdk/monitor/Azure.Monitor.Query/src/Models/MetadataValue.cs +++ b/sdk/monitor/Azure.Monitor.Query/src/Models/MetadataValue.cs @@ -7,10 +7,12 @@ namespace Azure.Monitor.Query.Models { public partial class MetadataValue { - /// the name of the metadata. [CodeGenMember("Name")] - internal LocalizableString LocalizedName { get; } + private LocalizableString LocalizedName { get; } + /// + /// Gets the name of the metadata value. + /// public string Name => LocalizedName.Value; } } \ No newline at end of file diff --git a/sdk/monitor/Azure.Monitor.Query/src/Models/MetricNamespace.cs b/sdk/monitor/Azure.Monitor.Query/src/Models/MetricNamespace.cs index fa6e15a4caa1..c1221afb3fbf 100644 --- a/sdk/monitor/Azure.Monitor.Query/src/Models/MetricNamespace.cs +++ b/sdk/monitor/Azure.Monitor.Query/src/Models/MetricNamespace.cs @@ -6,7 +6,7 @@ namespace Azure.Monitor.Query.Models public partial class MetricNamespace { /// Properties which include the fully qualified namespace name. - internal MetricNamespaceName Properties { get; } + private MetricNamespaceName Properties { get; } /// The fully qualified namespace name. public string FullyQualifiedName => Properties.MetricNamespaceNameValue; diff --git a/sdk/monitor/Azure.Monitor.Query/src/autorest.md b/sdk/monitor/Azure.Monitor.Query/src/autorest.md index 97ba73ccc594..b7879f0af35f 100644 --- a/sdk/monitor/Azure.Monitor.Query/src/autorest.md +++ b/sdk/monitor/Azure.Monitor.Query/src/autorest.md @@ -6,9 +6,9 @@ Run `dotnet build /t:GenerateCode` to generate code. title: Query input-file: - https://github.com/Azure/azure-sdk-for-java/blob/1d14101ba93c6e616899c2ded93fbecb54699f84/sdk/monitor/azure-monitor-query/swagger/log_query_swagger.json - - https://raw.githubusercontent.com/Azure/azure-rest-api-specs/ca0869b49176e7bc3866debbab9d32999661c4cb/specification/monitor/resource-manager/Microsoft.Insights/stable/2018-01-01/metrics_API.json - - https://raw.githubusercontent.com/Azure/azure-rest-api-specs/cf33826d06605cde127d2241e54dd6df55e9145f/specification/monitor/resource-manager/Microsoft.Insights/stable/2018-01-01/metricDefinitions_API.json - - https://raw.githubusercontent.com/Azure/azure-rest-api-specs/cf33826d06605cde127d2241e54dd6df55e9145f/specification/monitor/resource-manager/Microsoft.Insights/preview/2017-12-01-preview/metricNamespaces_API.json + - https://github.com/Azure/azure-sdk-for-java/blob/1d14101ba93c6e616899c2ded93fbecb54699f84/sdk/monitor/azure-monitor-query/swagger/metrics_definitions.json + - https://github.com/Azure/azure-sdk-for-java/blob/1d14101ba93c6e616899c2ded93fbecb54699f84/sdk/monitor/azure-monitor-query/swagger/metrics_namespaces.json + - https://github.com/Azure/azure-sdk-for-java/blob/1d14101ba93c6e616899c2ded93fbecb54699f84/sdk/monitor/azure-monitor-query/swagger/metrics_swagger.json modelerfour: lenient-model-deduplication: true ``` diff --git a/sdk/monitor/Azure.Monitor.Query/tests/LogsClientSamples.cs b/sdk/monitor/Azure.Monitor.Query/tests/LogsClientSamples.cs index 814f4952bfba..99e007b70c0b 100644 --- a/sdk/monitor/Azure.Monitor.Query/tests/LogsClientSamples.cs +++ b/sdk/monitor/Azure.Monitor.Query/tests/LogsClientSamples.cs @@ -151,6 +151,56 @@ public async Task BatchQuery() #endregion } + [Test] + public async Task QueryLogsWithTimeout() + { + #region Snippet:QueryLogsWithTimeout + + LogsClient client = new LogsClient(new DefaultAzureCredential()); +#if SNIPPET + string workspaceId = ""; +#else + string workspaceId = TestEnvironment.WorkspaceId; +#endif + + // Query TOP 10 resource groups by event count + Response> response = await client.QueryAsync(workspaceId, + "AzureActivity | summarize count()", + options: new LogsQueryOptions() + { + Timeout = TimeSpan.FromMinutes(10) + }); + + foreach (var resourceGroup in response.Value) + { + Console.WriteLine(resourceGroup); + } + + #endregion + } + + [Test] + public async Task BadRequest() + { + #region Snippet:BadRequest +#if SNIPPET + string workspaceId = ""; +#else + string workspaceId = TestEnvironment.WorkspaceId; +#endif + LogsClient client = new LogsClient(new DefaultAzureCredential()); + try + { + await client.QueryAsync(workspaceId, "My Not So Valid Query"); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + #endregion + } + #region Snippet:QueryLogsAsModelsModel public class MyLogEntryModel diff --git a/sdk/monitor/Azure.Monitor.Query/tests/MetricsQueryClientLiveTests.cs b/sdk/monitor/Azure.Monitor.Query/tests/MetricsQueryClientLiveTests.cs index 08d72636dd61..d010973decc4 100644 --- a/sdk/monitor/Azure.Monitor.Query/tests/MetricsQueryClientLiveTests.cs +++ b/sdk/monitor/Azure.Monitor.Query/tests/MetricsQueryClientLiveTests.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. using System; +using System.Linq; using System.Threading.Tasks; using Azure.Core.TestFramework; using Azure.Monitor.Query; @@ -55,7 +56,11 @@ public async Task CanListNamespacesMetrics() var results = await client.GetMetricNamespacesAsync( TestEnvironment.MetricsResource); - CollectionAssert.IsNotEmpty(results.Value); + var ns = results.Value.Single(); + + Assert.AreEqual(ns.Name, "Microsoft.OperationalInsights-workspaces"); + Assert.AreEqual(ns.Type, "Microsoft.Insights/metricNamespaces"); + Assert.AreEqual(ns.FullyQualifiedName, "Microsoft.OperationalInsights/workspaces"); } } } \ No newline at end of file