Skip to content

Commit

Permalink
feat: generate request telemetry id (#252)
Browse files Browse the repository at this point in the history
* feat: generate request telemetry id

* pr-fix: add unit tests for generate request id

* pr-fix: missing ')'

* pr-sug: add xml exception docs

* Update docs/preview/02-Features/sinks/azure-application-insights.md

Co-authored-by: Frederik Gheysels <[email protected]>

* Update docs/preview/02-Features/sinks/azure-application-insights.md

Co-authored-by: Frederik Gheysels <[email protected]>

Co-authored-by: Frederik Gheysels <[email protected]>
  • Loading branch information
stijnmoreels and fgheysels authored Jan 6, 2022
1 parent af6f4bd commit f364a3a
Show file tree
Hide file tree
Showing 9 changed files with 498 additions and 59 deletions.
19 changes: 19 additions & 0 deletions docs/preview/02-Features/sinks/azure-application-insights.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,25 @@ ILogger logger = new LoggerConfiguration()

The Azure Application Insights sink has some additional configuration which can be changed to influence the tracking.

### Requests

### Request ID

When tracking requests, the ID for the request telemetry is by default a generated GUID. The generation of this ID can be configured via the options.
This is useful (for example) in a service-to-service correlation system where you want the ID of the incoming request to be based on the sending system, or you want to incorporate the operation ID in the request ID.

```csharp
using Serilog;
using Serilog.Configuration;

ILogger logger = new LoggerConfiguration()
.WriteTo.AzureApplicationInsights("<key>", options =>
{
// Configurable generation function for the telemetry request ID.
options.Request.GenerateId = () => $"my-custom-ID-{Guid.NewGuid()}";
})
```

### Exceptions

#### Properties
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,11 @@
/// </summary>
public class ApplicationInsightsSinkOptions
{
/// <summary>
/// Gets the Application Insights options related to tracking requests.
/// </summary>
public ApplicationInsightsSinkRequestOptions Request { get; } = new ApplicationInsightsSinkRequestOptions();

/// <summary>
/// Gets the Application Insights options related to tracking exceptions.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
using System;
using GuardNet;

namespace Arcus.Observability.Telemetry.Serilog.Sinks.ApplicationInsights.Configuration
{
/// <summary>
/// User-defined configuration options to influence the behavior of the Azure Application Insights Serilog sink while tracking requests.
/// </summary>
public class ApplicationInsightsSinkRequestOptions
{
private Func<string> _generatedId = Guid.NewGuid().ToString;

/// <summary>
/// <para>Gets or sets the function to generate the request ID of the telemetry model while tracking requests.</para>
/// <para>Default: GUID generation.</para>
/// </summary>
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="value"/> is <c>null</c>.</exception>
public Func<string> GenerateId
{
get => _generatedId;
set
{
Guard.NotNull(value, nameof(value), "Requires a function to generate the request ID of the telemetry model while tracking requests");
_generatedId = value;
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,17 @@ namespace Arcus.Observability.Telemetry.Serilog.Sinks.ApplicationInsights.Conver
/// </summary>
public class ApplicationInsightsTelemetryConverter : TelemetryConverterBase
{
private readonly RequestTelemetryConverter _requestTelemetryConverter;
private readonly ExceptionTelemetryConverter _exceptionTelemetryConverter;
private readonly TraceTelemetryConverter _traceTelemetryConverter = new TraceTelemetryConverter();
private readonly EventTelemetryConverter _eventTelemetryConverter = new EventTelemetryConverter();
private readonly MetricTelemetryConverter _metricTelemetryConverter = new MetricTelemetryConverter();
private readonly RequestTelemetryConverter _requestTelemetryConverter = new RequestTelemetryConverter();
private readonly DependencyTelemetryConverter _dependencyTelemetryConverter = new DependencyTelemetryConverter();

private ApplicationInsightsTelemetryConverter(ApplicationInsightsSinkOptions options)
{
Guard.NotNull(options, nameof(options), "Requires a set of options to influence how to track to Application Insights");
_requestTelemetryConverter = new RequestTelemetryConverter(options.Request);
_exceptionTelemetryConverter = new ExceptionTelemetryConverter(options.Exception);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
using System.Collections.Generic;
using Arcus.Observability.Telemetry.Core;
using Arcus.Observability.Telemetry.Core.Logging;
using Arcus.Observability.Telemetry.Serilog.Sinks.ApplicationInsights.Configuration;
using GuardNet;
using Microsoft.ApplicationInsights.DataContracts;
using Serilog.Events;
Expand All @@ -13,6 +14,27 @@ namespace Arcus.Observability.Telemetry.Serilog.Sinks.ApplicationInsights.Conver
/// </summary>
public class RequestTelemetryConverter : CustomTelemetryConverter<RequestTelemetry>
{
private ApplicationInsightsSinkRequestOptions _options;

/// <summary>
/// Initializes a new instance of the <see cref="RequestTelemetryConverter" /> class.
/// </summary>
public RequestTelemetryConverter()
: this(new ApplicationInsightsSinkRequestOptions())
{
}

/// <summary>
/// Initializes a new instance of the <see cref="RequestTelemetryConverter" /> class.
/// </summary>
/// <param name="options">The user-defined configuration options to tracking requests.</param>
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="options" /> is <c>null</c>.</exception>
public RequestTelemetryConverter(ApplicationInsightsSinkRequestOptions options)
{
Guard.NotNull(options, nameof(options), "Requires a set of user-configurable options to influence the behavior of how requests are tracked");
_options = options;
}

/// <summary>
/// Creates a telemetry entry for a given log event
/// </summary>
Expand All @@ -34,19 +56,18 @@ protected override RequestTelemetry CreateTelemetryEntry(LogEvent logEvent, IFor
DateTimeOffset requestTime = logEntry.Properties.GetAsDateTimeOffset(nameof(RequestLogEntry.RequestTime));
IDictionary<string, string> context = logEntry.Properties.GetAsDictionary(nameof(RequestLogEntry.Context));

string operationId = logEvent.Properties.GetAsRawString(ContextProperties.Correlation.OperationId);
string id = _options.GenerateId();

var requestName = $"{requestMethod} {requestUri}";
bool isSuccessfulRequest = DetermineRequestOutcome(responseStatusCode);
var url = new Uri($"{requestHost}{requestUri}");

var requestTelemetry = new RequestTelemetry(requestName, requestTime, requestDuration, responseStatusCode, isSuccessfulRequest)
{
Id = operationId,
Id = id,
Url = url
};

// Add requestMethod to the operationName if it is missing
if (!operationName.StartsWith(requestMethod))
{
requestTelemetry.Context.Operation.Name = $"{requestMethod} {operationName}";
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ protected async Task RetryAssertUntilTelemetryShouldBeAvailableAsync(Func<Task>
await Policy.TimeoutAsync(timeout)
.WrapAsync(Policy.Handle<Exception>(exception =>
{
_outputWriter.WriteLine($"Failed to contact Azure Application Insights. Reason: {exception.Message}");
_outputWriter.WriteLine($"Failed to find correct telemetry at Azure Application Insights. Reason: {exception.Message}");
return true;
})
.WaitAndRetryForeverAsync(index => TimeSpan.FromSeconds(3)))
Expand Down
Loading

0 comments on commit f364a3a

Please sign in to comment.