Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion eng/.docsettings.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
161 changes: 157 additions & 4 deletions sdk/monitor/Azure.Monitor.Query/README.md
Original file line number Diff line number Diff line change
@@ -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 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 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` and `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 COMMON BAR -->
[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/)
<!-- CLIENT COMMON BAR -->

## 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

Expand Down Expand Up @@ -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:

Expand Down Expand Up @@ -120,4 +173,104 @@ foreach (var row in table.Rows)

Console.WriteLine();
}
```
```

### 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 = "<workspace_id>";
Response<LogsQueryResult> 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 = "<workspace_id>";
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
[[email protected]][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:[email protected]

![Impressions](https://azure-sdk-impressions.azurewebsites.net/api/impressions/azure-sdk-for-net%2Fsdk%2Fmonitor%2FAzure.Monitor.Query%2FREADME.png)
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
<Version>1.0.0-beta.1</Version>
<PackageTags>Azure Monitor Query</PackageTags>
<TargetFrameworks>$(RequiredTargetFrameworks)</TargetFrameworks>
<NoWarn>$(NoWarn);AZC0001;CS1591</NoWarn>
<NoWarn>$(NoWarn);AZC0001</NoWarn>
</PropertyGroup>

<ItemGroup>
Expand Down
56 changes: 55 additions & 1 deletion sdk/monitor/Azure.Monitor.Query/src/LogsClient.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@

using System;
using System.Collections.Generic;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -13,17 +12,29 @@

namespace Azure.Monitor.Query
{
/// <summary>
/// The <see cref="LogsClient"/> allows to query the Azure Monitor Logs service.
/// </summary>
public class LogsClient
{
private readonly QueryRestClient _queryClient;
private readonly ClientDiagnostics _clientDiagnostics;
private readonly HttpPipeline _pipeline;
private readonly RowBinder _rowBinder;

/// <summary>
/// Initializes a new instance of <see cref="LogsClient"/>.
/// </summary>
/// <param name="credential">The <see cref="TokenCredential"/> instance to use for authentication.</param>
public LogsClient(TokenCredential credential) : this(credential, null)
{
}

/// <summary>
/// Initializes a new instance of <see cref="LogsClient"/>.
/// </summary>
/// <param name="credential">The <see cref="TokenCredential"/> instance to use for authentication.</param>
/// <param name="options">The <see cref="LogsClientOptions"/> instance to as client configuration.</param>
public LogsClient(TokenCredential credential, LogsClientOptions options)
{
Argument.AssertNotNull(credential, nameof(credential));
Expand All @@ -36,24 +47,54 @@ public LogsClient(TokenCredential credential, LogsClientOptions options)
_rowBinder = new RowBinder();
}

/// <summary>
/// Initializes a new instance of <see cref="LogsClient"/> for mocking.
/// </summary>
protected LogsClient()
{
}

/// <summary>
/// Executes the logs query.
/// </summary>
/// <param name="workspaceId">The workspace to include in the query.</param>
/// <param name="query">The query text to execute.</param>
/// <param name="timeSpan">The timespan over which to query data.</param>
/// <param name="options">The <see cref="LogsQueryOptions"/> to configure the query.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to use.</param>
/// <returns>Query results mapped to a type <typeparamref name="T"/>.</returns>
public virtual Response<IReadOnlyList<T>> Query<T>(string workspaceId, string query, TimeSpan? timeSpan = null, LogsQueryOptions options = null, CancellationToken cancellationToken = default)
{
Response<LogsQueryResult> response = Query(workspaceId, query, timeSpan, options, cancellationToken);

return Response.FromValue(_rowBinder.BindResults<T>(response), response.GetRawResponse());
}

/// <summary>
/// Executes the logs query.
/// </summary>
/// <param name="workspaceId">The workspace to include in the query.</param>
/// <param name="query">The query text to execute.</param>
/// <param name="timeSpan">The timespan over which to query data.</param>
/// <param name="options">The <see cref="LogsQueryOptions"/> to configure the query.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to use.</param>
/// <returns>Query results mapped to a type <typeparamref name="T"/>.</returns>
public virtual async Task<Response<IReadOnlyList<T>>> QueryAsync<T>(string workspaceId, string query, TimeSpan? timeSpan = null, LogsQueryOptions options = null, CancellationToken cancellationToken = default)
{
Response<LogsQueryResult> response = await QueryAsync(workspaceId, query, timeSpan, options, cancellationToken).ConfigureAwait(false);

return Response.FromValue(_rowBinder.BindResults<T>(response), response.GetRawResponse());
}

/// <summary>
/// Executes the logs query.
/// </summary>
/// <param name="workspaceId">The workspace to include in the query.</param>
/// <param name="query">The query text to execute.</param>
/// <param name="timeSpan">The timespan over which to query data.</param>
/// <param name="options">The <see cref="LogsQueryOptions"/> to configure the query.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to use.</param>
/// <returns>The <see cref="LogsQueryResult"/> with the query results.</returns>
public virtual Response<LogsQueryResult> Query(string workspaceId, string query, TimeSpan? timeSpan = null, LogsQueryOptions options = null, CancellationToken cancellationToken = default)
{
using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(LogsClient)}.{nameof(Query)}");
Expand All @@ -69,6 +110,15 @@ public virtual Response<LogsQueryResult> Query(string workspaceId, string query,
}
}

/// <summary>
/// Executes the logs query.
/// </summary>
/// <param name="workspaceId">The workspace to include in the query.</param>
/// <param name="query">The query text to execute.</param>
/// <param name="timeSpan">The timespan over which to query data.</param>
/// <param name="options">The <see cref="LogsQueryOptions"/> to configure the query.</param>
/// <param name="cancellationToken">The <see cref="CancellationToken"/> to use.</param>
/// <returns>The <see cref="LogsQueryResult"/> with the query results.</returns>
public virtual async Task<Response<LogsQueryResult>> QueryAsync(string workspaceId, string query, TimeSpan? timeSpan = null, LogsQueryOptions options = null, CancellationToken cancellationToken = default)
{
using DiagnosticScope scope = _clientDiagnostics.CreateScope($"{nameof(LogsClient)}.{nameof(Query)}");
Expand All @@ -84,6 +134,10 @@ public virtual async Task<Response<LogsQueryResult>> QueryAsync(string workspace
}
}

/// <summary>
/// Creates an instance of <see cref="LogsBatchQuery"/> that allows executing multiple queries at once.
/// </summary>
/// <returns>The <see cref="LogsBatchQuery"/> instance that allows building a list of queries and submitting them.</returns>
public virtual LogsBatchQuery CreateBatchQuery()
{
return new LogsBatchQuery(_clientDiagnostics, _queryClient, _rowBinder);
Expand Down
8 changes: 7 additions & 1 deletion sdk/monitor/Azure.Monitor.Query/src/LogsClientOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@

namespace Azure.Monitor.Query
{
/// <summary>
/// Provides the client configuration options for connecting to Azure Monitor Logs service.
/// </summary>
public class LogsClientOptions: ClientOptions
{
private readonly ServiceVersion _version;
Expand All @@ -27,11 +30,14 @@ public LogsClientOptions(ServiceVersion version = LatestVersion)
}

/// <summary>
/// The versions of Azure Monitor Query service supported by this client
/// The versions of Azure Monitor Logs service supported by this client
/// library.
/// </summary>
public enum ServiceVersion
{
/// <summary>
/// The V1 version of the service
/// </summary>
V1
}
}
Expand Down
13 changes: 13 additions & 0 deletions sdk/monitor/Azure.Monitor.Query/src/LogsQueryOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,25 @@
// Licensed under the MIT License.

using System;
using Azure.Monitor.Query.Models;

namespace Azure.Monitor.Query
{
/// <summary>
/// Options for <see cref="LogsClient.QueryAsync"/> that allow specifying the service timeout or whether to include
/// the query execution statistics.
/// </summary>
public class LogsQueryOptions
{
/// <summary>
/// Gets or sets the value indicating the service timeout for the query. Defaults to <c>null</c>.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should we say what the default is rather than the default value of the type? For example, Defaults to the HttpClient default ...

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's an interesting question. I was trying to avoid presuming what service does as it might change with the service version.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How would someone know what to set this to?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Their query would fail. No matter whether we give them a default value or not they need to see their query timing out to start setting this value.

/// </summary>
public TimeSpan? Timeout { get; set; }

/// <summary>
/// Gets or sets the value indicating whether to include query execution statistics as part of the response.
/// Statistics can be retrieved via the <see cref="LogsQueryResult.Statistics"/> property.
/// </summary>
public bool IncludeStatistics { get; set; }
}
}
Loading