Skip to content

Commit

Permalink
feat: add dependency id to event hubs dep tracking (#378)
Browse files Browse the repository at this point in the history
* feat: add dependency id to event hubs dep tracking

* pr-sug: add better description for duration measurement

* pr-fix: use 'duration' io 'latency' in duration measurement description
  • Loading branch information
stijnmoreels committed Jun 1, 2022
1 parent 2afa996 commit 89b94aa
Show file tree
Hide file tree
Showing 4 changed files with 338 additions and 56 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ public static partial class ILoggerExtensions
/// <param name="namespaceName">The namespace of the resource.</param>
/// <param name="eventHubName">The name of the Event Hub resource.</param>
/// <param name="isSuccessful">The indication whether or not the operation was successful.</param>
/// <param name="measurement">The measuring the latency to call the dependency.</param>
/// <param name="measurement">The measuring of the duration to call the dependency.</param>
/// <param name="context">The context that provides more insights on the dependency that was measured.</param>
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="logger"/> or <paramref name="measurement"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">Thrown when the <paramref name="namespaceName"/> or <paramref name="eventHubName"/> is blank.</exception>
Expand All @@ -72,12 +72,74 @@ public static partial class ILoggerExtensions
/// Logs an Azure Event Hub Dependency.
/// </summary>
/// <param name="logger">The logger to track the telemetry.</param>
/// <param name="namespaceName">Namespace of the resource</param>
/// <param name="eventHubName">Name of the Event Hub resource</param>
/// <param name="isSuccessful">Indication whether or not the operation was successful</param>
/// <param name="startTime">Point in time when the interaction with the dependency was started</param>
/// <param name="duration">Duration of the operation</param>
/// <param name="context">Context that provides more insights on the dependency that was measured</param>
/// <param name="namespaceName">The namespace of the resource.</param>
/// <param name="eventHubName">The name of the Event Hub resource.</param>
/// <param name="isSuccessful">The indication whether or not the operation was successful.</param>
/// <param name="measurement">The measuring of the duration to call the dependency.</param>
/// <param name="dependencyId">The ID of the dependency to link as parent ID.</param>
/// <param name="context">The context that provides more insights on the dependency that was measured.</param>
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="logger"/> or <paramref name="measurement"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">Thrown when the <paramref name="namespaceName"/> or <paramref name="eventHubName"/> is blank.</exception>
public static void LogEventHubsDependency(
this ILogger logger,
string namespaceName,
string eventHubName,
bool isSuccessful,
DurationMeasurement measurement,
string dependencyId,
Dictionary<string, object> context = null)
{
Guard.NotNull(logger, nameof(logger), "Requires a logger instance to track telemetry");
Guard.NotNullOrWhitespace(namespaceName, nameof(namespaceName), "Requires a non-blank resource namespace of the Azure Event Hub to track an Azure Event Hub dependency");
Guard.NotNullOrWhitespace(eventHubName, nameof(eventHubName), "Requires a non-blank Azure Event Hub name to track an Azure Event Hub dependency");
Guard.NotNull(measurement, nameof(measurement), "Requires a dependency measurement instance to track the latency of the Azure Event Hub resource when tracking an Azure Event Hub dependency");

LogEventHubsDependency(logger, namespaceName, eventHubName, isSuccessful, measurement.StartTime, measurement.Elapsed, dependencyId, context);
}

/// <summary>
/// Logs an Azure Event Hub Dependency.
/// </summary>
/// <param name="logger">The logger to track the telemetry.</param>
/// <param name="namespaceName">The namespace of the resource.</param>
/// <param name="eventHubName">The name of the Event Hub resource.</param>
/// <param name="isSuccessful">The indication whether or not the operation was successful.</param>
/// <param name="startTime">The point in time when the interaction with the dependency was started.</param>
/// <param name="duration">The duration of the operation.</param>
/// <param name="context">The context that provides more insights on the dependency that was measured.</param>
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="logger"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">Thrown when the <paramref name="namespaceName"/> or <paramref name="eventHubName"/> is blank.</exception>
/// <exception cref="ArgumentOutOfRangeException">Thrown when the <paramref name="duration"/> is a negative time range.</exception>
public static void LogEventHubsDependency(
this ILogger logger,
string namespaceName,
string eventHubName,
bool isSuccessful,
DateTimeOffset startTime,
TimeSpan duration,
Dictionary<string, object> context = null)
{
Guard.NotNull(logger, nameof(logger), "Requires a logger instance to track telemetry");
Guard.NotNullOrWhitespace(namespaceName, nameof(namespaceName), "Requires a non-blank resource namespace of the Azure Event Hub to track an Azure Event Hub dependency");
Guard.NotNullOrWhitespace(eventHubName, nameof(eventHubName), "Requires a non-blank Azure Event Hub name to track an Azure Event Hub dependency");
Guard.NotLessThan(duration, TimeSpan.Zero, nameof(duration), "Requires a positive time duration of the Azure Events Hubs operation");

context = context ?? new Dictionary<string, object>();

LogEventHubsDependency(logger, namespaceName, eventHubName, isSuccessful, startTime, duration, dependencyId: null, context);
}

/// <summary>
/// Logs an Azure Event Hub Dependency.
/// </summary>
/// <param name="logger">The logger to track the telemetry.</param>
/// <param name="namespaceName">The namespace of the resource.</param>
/// <param name="eventHubName">The name of the Event Hub resource.</param>
/// <param name="isSuccessful">The indication whether or not the operation was successful.</param>
/// <param name="startTime">The point in time when the interaction with the dependency was started.</param>
/// <param name="duration">The duration of the operation.</param>
/// <param name="dependencyId">The ID of the dependency to link as parent ID.</param>
/// <param name="context">The context that provides more insights on the dependency that was measured.</param>
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="logger"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">Thrown when the <paramref name="namespaceName"/> or <paramref name="eventHubName"/> is blank.</exception>
/// <exception cref="ArgumentOutOfRangeException">Thrown when the <paramref name="duration"/> is a negative time range.</exception>
Expand All @@ -88,6 +150,7 @@ public static partial class ILoggerExtensions
bool isSuccessful,
DateTimeOffset startTime,
TimeSpan duration,
string dependencyId,
Dictionary<string, object> context = null)
{
Guard.NotNull(logger, nameof(logger), "Requires a logger instance to track telemetry");
Expand All @@ -104,6 +167,7 @@ public static partial class ILoggerExtensions
targetName: eventHubName,
duration: duration,
startTime: startTime,
dependencyId: dependencyId,
resultCode: null,
isSuccessful: isSuccessful,
context: context));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,10 @@ public static partial class ILoggerExtensions
/// Logs an HTTP dependency
/// </summary>
/// <param name="logger">The logger to track the telemetry.</param>
/// <param name="request">Request that started the HTTP communication</param>
/// <param name="statusCode">Status code that was returned by the service for this HTTP communication</param>
/// <param name="measurement">Measuring the latency of the HTTP dependency</param>
/// <param name="context">Context that provides more insights on the dependency that was measured</param>
/// <param name="request">The request that started the HTTP communication.</param>
/// <param name="statusCode">The status code that was returned by the service for this HTTP communication.</param>
/// <param name="measurement">The measuring the latency of the HTTP dependency.</param>
/// <param name="context">The context that provides more insights on the dependency that was measured.</param>
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="logger"/>, <paramref name="request"/>, or <paramref name="measurement"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">
/// Thrown when the <paramref name="request"/> doesn't have a request URI or HTTP method, the <paramref name="statusCode"/> is outside the bounds of the enumeration.
Expand Down Expand Up @@ -106,10 +106,10 @@ public static partial class ILoggerExtensions
/// Logs an HTTP dependency
/// </summary>
/// <param name="logger">The logger to track the telemetry.</param>
/// <param name="request">Request that started the HTTP communication</param>
/// <param name="statusCode">Status code that was returned by the service for this HTTP communication</param>
/// <param name="startTime">Point in time when the interaction with the HTTP dependency was started</param>
/// <param name="duration">Duration of the operation</param>
/// <param name="request">The request that started the HTTP communication.</param>
/// <param name="statusCode">The status code that was returned by the service for this HTTP communication.</param>
/// <param name="startTime">The point in time when the interaction with the HTTP dependency was started.</param>
/// <param name="duration">The duration of the operation.</param>
/// <param name="context">Context that provides more insights on the dependency that was measured</param>
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="logger"/> or <paramref name="request"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentOutOfRangeException">Thrown when the <paramref name="duration"/> is a negative time range.</exception>
Expand Down Expand Up @@ -141,12 +141,12 @@ public static partial class ILoggerExtensions
/// Logs an HTTP dependency
/// </summary>
/// <param name="logger">The logger to track the telemetry.</param>
/// <param name="request">Request that started the HTTP communication</param>
/// <param name="statusCode">Status code that was returned by the service for this HTTP communication</param>
/// <param name="startTime">Point in time when the interaction with the HTTP dependency was started</param>
/// <param name="duration">Duration of the operation</param>
/// <param name="request">The request that started the HTTP communication.</param>
/// <param name="statusCode">The status code that was returned by the service for this HTTP communication.</param>
/// <param name="startTime">The point in time when the interaction with the HTTP dependency was started.</param>
/// <param name="duration">The duration of the operation.</param>
/// <param name="dependencyId">The ID of the dependency to link as parent ID.</param>
/// <param name="context">Context that provides more insights on the dependency that was measured</param>
/// <param name="context">The context that provides more insights on the dependency that was measured.</param>
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="logger"/> or <paramref name="request"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentOutOfRangeException">Thrown when the <paramref name="duration"/> is a negative time range.</exception>
/// <exception cref="ArgumentException">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ public async Task LogEventHubsDependency_SinksToApplicationInsights_ResultsIEven
string eventHubName = BogusGenerator.Commerce.ProductName();
string namespaceName = BogusGenerator.Finance.AccountName();
string dependencyName = eventHubName;
string dependencyId = BogusGenerator.Random.Guid().ToString();

using (ILoggerFactory loggerFactory = CreateLoggerFactory(config => config.Enrich.WithComponentName(componentName)))
{
Expand All @@ -41,41 +42,53 @@ public async Task LogEventHubsDependency_SinksToApplicationInsights_ResultsIEven
Dictionary<string, object> telemetryContext = CreateTestTelemetryContext();

// Act
logger.LogEventHubsDependency(namespaceName, eventHubName, isSuccessful, startTime, duration, telemetryContext);
logger.LogEventHubsDependency(namespaceName, eventHubName, isSuccessful, startTime, duration, dependencyId, telemetryContext);
}

// Assert
using (ApplicationInsightsDataClient client = CreateApplicationInsightsClient())
{
await RetryAssertUntilTelemetryShouldBeAvailableAsync(async () =>
{
EventsResults<EventsDependencyResult> results = await client.Events.GetDependencyEventsAsync(ApplicationId, timespan: "PT30M");
EventsResults<EventsDependencyResult> results = await client.Events.GetDependencyEventsAsync(ApplicationId, PastHalfHourTimeSpan);
Assert.NotEmpty(results.Value);
Assert.Contains(results.Value, result =>
AssertX.Any(results.Value, result =>
{
return result.Dependency.Type == dependencyType
&& result.Dependency.Target == eventHubName
&& result.Dependency.Data == namespaceName
&& result.Cloud.RoleName == componentName
&& result.Dependency.Name == dependencyName;
Assert.Equal(dependencyType, result.Dependency.Type);
Assert.Equal(eventHubName, result.Dependency.Target);
Assert.Equal(namespaceName, result.Dependency.Data);
Assert.Equal(componentName, result.Cloud.RoleName);
Assert.Equal(dependencyName, result.Dependency.Name);
Assert.Equal(dependencyId, result.Dependency.Id);
});
});
}

AssertX.Any(GetLogEventsFromMemory(), logEvent => {
AssertSerilogLogProperties(dependencyType, namespaceName, eventHubName, dependencyName);
}

private void AssertSerilogLogProperties(
string dependencyType,
string namespaceName,
string eventHubName,
string dependencyName)
{
IEnumerable<LogEvent> logEvents = GetLogEventsFromMemory();
AssertX.Any(logEvents, logEvent =>
{
StructureValue logEntry = logEvent.Properties.GetAsStructureValue(ContextProperties.DependencyTracking.DependencyLogEntry);
Assert.NotNull(logEntry);
var actualDependencyType = Assert.Single(logEntry.Properties, prop => prop.Name == nameof(DependencyLogEntry.DependencyType));
LogEventProperty actualDependencyType = Assert.Single(logEntry.Properties, prop => prop.Name == nameof(DependencyLogEntry.DependencyType));
Assert.Equal(dependencyType, actualDependencyType.Value.ToDecentString());
var actualDependencyData = Assert.Single(logEntry.Properties, prop => prop.Name == nameof(DependencyLogEntry.DependencyData));
LogEventProperty actualDependencyData = Assert.Single(logEntry.Properties, prop => prop.Name == nameof(DependencyLogEntry.DependencyData));
Assert.Equal(namespaceName, actualDependencyData.Value.ToDecentString());
var actualTargetName = Assert.Single(logEntry.Properties, prop => prop.Name == nameof(DependencyLogEntry.TargetName));
LogEventProperty actualTargetName = Assert.Single(logEntry.Properties, prop => prop.Name == nameof(DependencyLogEntry.TargetName));
Assert.Equal(eventHubName, actualTargetName.Value.ToDecentString());
var actualDependencyName = Assert.Single(logEntry.Properties, prop => prop.Name == nameof(DependencyLogEntry.DependencyName));
LogEventProperty actualDependencyName = Assert.Single(logEntry.Properties, prop => prop.Name == nameof(DependencyLogEntry.DependencyName));
Assert.Equal(dependencyName, actualDependencyName.Value.ToDecentString());
Assert.Single(logEntry.Properties, prop => prop.Name == nameof(DependencyLogEntry.Context));
Expand Down
Loading

0 comments on commit 89b94aa

Please sign in to comment.