Skip to content

Commit

Permalink
Feature - add Azure Event Hubs dependency tracking (#98)
Browse files Browse the repository at this point in the history
* Feature - add Azure Event Hubs dependency tracking

* pr-fix: update with correct signature

* pr-sug: use 'namespaceName' io 'accountName'
  • Loading branch information
stijnmoreels authored Apr 29, 2020
1 parent b09673e commit e45381d
Show file tree
Hide file tree
Showing 4 changed files with 199 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -340,9 +340,50 @@ public static void LogTableStorageDependency(this ILogger logger, string account
Guard.NotNullOrWhitespace(tableName, nameof(tableName));
Guard.NotNullOrWhitespace(accountName, nameof(accountName));

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

logger.LogInformation(DependencyFormat, "Azure table", tableName, accountName, duration, startTime.ToString(CultureInfo.InvariantCulture), isSuccessful, context);
}

/// <summary>
/// Logs an Azure Event Hub Dependency.
/// </summary>
/// <param name="logger">Logger to use</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="measurement">Measuring the latency to call the dependency</param>
/// <param name="context">Context that provides more insights on the dependency that was measured</param>
public static void LogEventHubsDependency(this ILogger logger, string namespaceName, string eventHubName, bool isSuccessful, DependencyMeasurement measurement, Dictionary<string, object> context = null)
{
Guard.NotNull(logger, nameof(logger));
Guard.NotNullOrWhitespace(namespaceName, nameof(namespaceName));
Guard.NotNullOrWhitespace(eventHubName, nameof(eventHubName));

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

/// <summary>
/// Logs an Azure Event Hub Dependency.
/// </summary>
/// <param name="logger">Logger to use</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>
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));
Guard.NotNullOrWhitespace(namespaceName, nameof(namespaceName));
Guard.NotNullOrWhitespace(eventHubName, nameof(eventHubName));

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

logger.LogInformation(DependencyFormat, "Azure Event Hubs", namespaceName, eventHubName, duration, startTime.ToString(CultureInfo.InvariantCulture), isSuccessful, context);
}

/// <summary>
/// Logs an HTTP dependency
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.Azure.ApplicationInsights;
using Microsoft.Azure.ApplicationInsights.Models;
using Microsoft.Extensions.Logging;
using Serilog;
using Xunit;
using Xunit.Abstractions;
using ILogger = Microsoft.Extensions.Logging.ILogger;

namespace Arcus.Observability.Tests.Integration.Serilog.Sinks.ApplicationInsights
{
public class EventHubsTests : ApplicationInsightsSinkTests
{
public EventHubsTests(ITestOutputHelper outputWriter) : base(outputWriter)
{
}

[Fact]
public async Task LogEventHubsDependency_SinksToApplicationInsights_ResultsIEventHubsDependencyTelemetry()
{
// Arrange
string componentName = BogusGenerator.Commerce.ProductName();
string eventHubName = BogusGenerator.Commerce.ProductName();
string namespaceName = BogusGenerator.Finance.AccountName();
using (ILoggerFactory loggerFactory = CreateLoggerFactory(config => config.Enrich.WithComponentName(componentName)))
{
ILogger logger = loggerFactory.CreateLogger<ApplicationInsightsSinkTests>();

bool isSuccessful = BogusGenerator.PickRandom(true, false);
DateTimeOffset startTime = BogusGenerator.Date.RecentOffset(days: 0);
TimeSpan duration = BogusGenerator.Date.Timespan();
Dictionary<string, object> telemetryContext = CreateTestTelemetryContext();

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

// Assert
using (ApplicationInsightsDataClient client = CreateApplicationInsightsClient())
{
await RetryAssertUntilTelemetryShouldBeAvailableAsync(async () =>
{
EventsResults<EventsDependencyResult> results = await client.GetDependencyEventsAsync();
Assert.NotEmpty(results.Value);
Assert.Contains(results.Value, result =>
{
return result.Dependency.Type == "Azure Event Hubs"
&& result.Dependency.Target == eventHubName
&& result.Dependency.Data == namespaceName
&& result.Cloud.RoleName == componentName;
});
});
}
}

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -370,6 +370,54 @@ public void LogTableStorageDependency_WithTableStorageDependency_CreatesDependen
});
}

[Fact]
public void LogEventHubsDependency_WithEventHubsDependency_CreatesDependencyTelemetry()
{
// Arrange
var spySink = new InMemoryLogSink();
string operationId = $"operation-id-{Guid.NewGuid()}";
ILogger logger = CreateLogger(spySink, config => config.Enrich.WithProperty(ContextProperties.Correlation.OperationId, operationId));
string eventHubName = _bogusGenerator.Commerce.ProductName();
string namespaceName = _bogusGenerator.Finance.AccountName();
var startTime = DateTimeOffset.UtcNow;
var duration = TimeSpan.FromSeconds(5);
var telemetryContext = new Dictionary<string, object>
{
["Host"] = "orders.servicebus.windows.net"
};
logger.LogEventHubsDependency(namespaceName, eventHubName, isSuccessful: true, startTime: startTime, duration: duration, context: telemetryContext);
LogEvent logEvent = Assert.Single(spySink.CurrentLogEmits);
Assert.NotNull(logEvent);

var converter = ApplicationInsightsTelemetryConverter.Create();

// Act
IEnumerable<ITelemetry> telemetries = converter.Convert(logEvent, formatProvider: null);

// Assert
AssertDoesNotContainLogProperty(logEvent, DependencyTracking.DependencyType);
AssertDoesNotContainLogProperty(logEvent, DependencyTracking.TargetName);
AssertDoesNotContainLogProperty(logEvent, DependencyTracking.DependencyName);
AssertDoesNotContainLogProperty(logEvent, DependencyTracking.IsSuccessful);
AssertDoesNotContainLogProperty(logEvent, DependencyTracking.DependencyData);
AssertDoesNotContainLogProperty(logEvent, DependencyTracking.ResultCode);
AssertDoesNotContainLogProperty(logEvent, DependencyTracking.StartTime);
AssertDoesNotContainLogProperty(logEvent, DependencyTracking.Duration);
AssertDoesNotContainLogProperty(logEvent, EventTracking.EventContext);
Assert.Collection(telemetries, telemetry =>
{
var dependencyTelemetry = Assert.IsType<DependencyTelemetry>(telemetry);
Assert.Equal("Azure Event Hubs", dependencyTelemetry.Type);
Assert.Equal(eventHubName, dependencyTelemetry.Target);
Assert.Equal(namespaceName, dependencyTelemetry.Data);
Assert.Equal(TruncateToSeconds(startTime), dependencyTelemetry.Timestamp);
Assert.Equal(duration, dependencyTelemetry.Duration);
Assert.True(dependencyTelemetry.Success);

AssertContainsTelemetryProperty(dependencyTelemetry, "Host", "orders.servicebus.windows.net");
});
}

[Fact]
public void LogHttpDependency_WithHttpDependency_CreatesHttpDependencyTelemetry()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -403,6 +403,57 @@ public void LogTableStorageDependencyWithDependencyMeasurement_ValidArguments_Su
Assert.Contains(duration.ToString(), logMessage);
}

[Fact]
public void LogEventHubsDependency_ValidArguments_Succeeds()
{
// Arrange
var logger = new TestLogger();
string eventHubName = _bogusGenerator.Commerce.ProductName();
string namespaceName = _bogusGenerator.Finance.AccountName();
bool isSuccessful = _bogusGenerator.Random.Bool();
DateTimeOffset startTime = _bogusGenerator.Date.PastOffset();
TimeSpan duration = _bogusGenerator.Date.Timespan();

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

// Assert
var logMessage = logger.WrittenMessage;
Assert.StartsWith(MessagePrefixes.Dependency, logMessage);
Assert.Contains(namespaceName, logMessage);
Assert.Contains(eventHubName, logMessage);
Assert.Contains(isSuccessful.ToString(), logMessage);
Assert.Contains(startTime.ToString(CultureInfo.InvariantCulture), logMessage);
Assert.Contains(duration.ToString(), logMessage);
}

[Fact]
public void LogEventHubsDependencyWithDependencyMeasurement_ValidArguments_Succeeds()
{
// Arrange
var logger = new TestLogger();
string eventHubName = _bogusGenerator.Commerce.ProductName();
string namespaceName = _bogusGenerator.Finance.AccountName();
bool isSuccessful = _bogusGenerator.Random.Bool();

var measurement = DependencyMeasurement.Start();
DateTimeOffset startTime = measurement.StartTime;
measurement.Dispose();
TimeSpan duration = measurement.Elapsed;

// Act
logger.LogEventHubsDependency(namespaceName, eventHubName, isSuccessful, measurement);

// Assert
var logMessage = logger.WrittenMessage;
Assert.StartsWith(MessagePrefixes.Dependency, logMessage);
Assert.Contains(namespaceName, logMessage);
Assert.Contains(eventHubName, logMessage);
Assert.Contains(isSuccessful.ToString(), logMessage);
Assert.Contains(startTime.ToString(CultureInfo.InvariantCulture), logMessage);
Assert.Contains(duration.ToString(), logMessage);
}

[Fact]
public void LogSqlDependencyWithDependencyMeasurement_ValidArguments_Succeeds()
{
Expand Down

0 comments on commit e45381d

Please sign in to comment.