Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add dependency id to cosmosdb dep tracking #380

Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -78,13 +78,81 @@ public static void LogCosmosSqlDependency(
/// Logs a Cosmos SQL dependency.
/// </summary>
/// <param name="logger">The logger to track the telemetry.</param>
/// <param name="accountName">Name of the storage resource</param>
/// <param name="database">Name of the database</param>
/// <param name="container">Name of the container</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="accountName">The name of the storage resource.</param>
/// <param name="database">The name of the database.</param>
/// <param name="container">The name of the container.</param>
/// <param name="isSuccessful">The indication whether or not the operation was successful</param>
/// <param name="measurement">The measuring the latency of the dependency.</param>
stijnmoreels marked this conversation as resolved.
Show resolved Hide resolved
/// <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="accountName"/>, <paramref name="database"/>, or <paramref name="container"/> is blank.</exception>
public static void LogCosmosSqlDependency(
this ILogger logger,
string accountName,
string database,
string container,
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(accountName, nameof(accountName), "Requires a non-blank account name of the Cosmos SQL storage to track a Cosmos SQL dependency");
Guard.NotNullOrWhitespace(database, nameof(database), "Requires a non-blank database name of the Cosmos SQL storage to track a Cosmos SQL dependency");
Guard.NotNullOrWhitespace(container, nameof(container), "Requires a non-blank container name of the Cosmos SQL storage to track a Cosmos SQL dependency");
Guard.NotNull(measurement, nameof(measurement), "Requires a dependency measurement instance to track the latency of the Cosmos SQL storage when tracking an Cosmos SQL dependency");

LogCosmosSqlDependency(logger, accountName, database, container, isSuccessful, measurement.StartTime, measurement.Elapsed, dependencyId, context);
}

/// <summary>
/// Logs a Cosmos SQL dependency.
/// </summary>
/// <param name="logger">The logger to track the telemetry.</param>
/// <param name="accountName">The name of the storage resource.</param>
/// <param name="database">The name of the database.</param>
/// <param name="container">The name of the container.</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="accountName"/>, <paramref name="database"/>, or <paramref name="container"/> is blank.</exception>
/// <exception cref="ArgumentOutOfRangeException">Thrown when the <paramref name="duration"/> is a negative time range.</exception>
public static void LogCosmosSqlDependency(
this ILogger logger,
string accountName,
string database,
string container,
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(accountName, nameof(accountName), "Requires a non-blank account name of the Cosmos SQL storage to track a Cosmos SQL dependency");
Guard.NotNullOrWhitespace(database, nameof(database), "Requires a non-blank database name of the Cosmos SQL storage to track a Cosmos SQL dependency");
Guard.NotNullOrWhitespace(container, nameof(container), "Requires a non-blank container name of the Cosmos SQL storage to track a Cosmos SQL dependency");
Guard.NotLessThan(duration, TimeSpan.Zero, nameof(duration), "Requires a positive time duration of the Cosmos SQL operation");

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

LogCosmosSqlDependency(logger, accountName, database, container, isSuccessful, startTime, duration, dependencyId: null, context);
}

/// <summary>
/// Logs a Cosmos SQL dependency.
/// </summary>
/// <param name="logger">The logger to track the telemetry.</param>
/// <param name="accountName">The name of the storage resource.</param>
/// <param name="database">The name of the database.</param>
/// <param name="container">The name of the container.</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="accountName"/>, <paramref name="database"/>, or <paramref name="container"/> is blank.</exception>
/// <exception cref="ArgumentOutOfRangeException">Thrown when the <paramref name="duration"/> is a negative time range.</exception>
Expand All @@ -96,6 +164,7 @@ public static void LogCosmosSqlDependency(
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 @@ -114,6 +183,7 @@ public static void LogCosmosSqlDependency(
targetName: accountName,
duration: duration,
startTime: startTime,
dependencyId: dependencyId,
resultCode: null,
isSuccessful:
isSuccessful,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,20 +29,20 @@ public async Task LogCosmosSqlDependency_SinksToApplicationInsights_ResultsInCos
string container = BogusGenerator.Commerce.ProductName();
string database = BogusGenerator.Commerce.ProductName();
string accountName = BogusGenerator.Finance.AccountName();
string dependencyData = $"{database}/{container}";
string dependencyName = $"{database}/{container}";
string dependencyId = BogusGenerator.Random.Guid().ToString();

using (ILoggerFactory loggerFactory = CreateLoggerFactory(config => config.Enrich.WithComponentName(componentName)))
{
ILogger logger = loggerFactory.CreateLogger<ApplicationInsightsSinkTests>();

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

// Act
logger.LogCosmosSqlDependency(accountName, database, container, isSuccessful, startTime, duration, telemetryContext);
logger.LogCosmosSqlDependency(accountName, database, container, isSuccessful, startTime, duration, dependencyId, telemetryContext);
}

// Assert
Expand All @@ -51,34 +51,41 @@ public async Task LogCosmosSqlDependency_SinksToApplicationInsights_ResultsInCos
await RetryAssertUntilTelemetryShouldBeAvailableAsync(async () =>
{
EventsResults<EventsDependencyResult> results =
await client.Events.GetDependencyEventsAsync(ApplicationId,
timespan: "PT15M");
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 == accountName
&& result.Dependency.Data == dependencyData
&& result.Cloud.RoleName == componentName
&& result.Dependency.Name == dependencyName;
Assert.Equal(dependencyType, result.Dependency.Type);
Assert.Equal(accountName, result.Dependency.Target);
Assert.Equal(dependencyName, result.Dependency.Data);
Assert.Equal(componentName, result.Cloud.RoleName);
Assert.Equal(dependencyName, result.Dependency.Name);
});
});
}

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

private void AssertSerilogLogProperties(string dependencyType, string dependencyName, string accountName)
{
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));
Assert.Equal(dependencyData, actualDependencyData.Value.ToDecentString());
LogEventProperty actualDependencyData = Assert.Single(logEntry.Properties, prop => prop.Name == nameof(DependencyLogEntry.DependencyData));
Assert.Equal(dependencyName, 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(accountName, 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