Skip to content

Commit

Permalink
feat: deprecate iot hub project in favor of dedicated parsing (#508)
Browse files Browse the repository at this point in the history
  • Loading branch information
stijnmoreels authored Jan 6, 2023
1 parent ae12777 commit 9f2af44
Show file tree
Hide file tree
Showing 7 changed files with 394 additions and 9 deletions.
12 changes: 3 additions & 9 deletions docs/preview/03-Features/writing-different-telemetry-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -111,14 +111,6 @@ logger.LogIotHubDependency(iotHubName: "sensors", isSuccessful: true, startTime:

Or, alternatively you can pass along the IoT connection string itself so the host name will be selected for you.

**Installation**

This feature requires to install our NuGet package

```shell
PM > Install-Package Arcus.Observability.Telemetry.IoT
```

**Example**

Here is how you can report a dependency call:
Expand All @@ -132,10 +124,12 @@ var durationMeasurement = new Stopwatch();
durationMeasurement.Start();
var startTime = DateTimeOffset.UtcNow;

logger.LogIotHubDependency(iotHubConnectionString: "Hostname=sensors;", isSuccessful: true, startTime: startTime, duration: durationMeasurement.Elapsed);
logger.LogIotHubDependencyWithConnectionString(iotHubConnectionString: "HostName=sensors.azure-devices.net;...", isSuccessful: true, startTime: startTime, duration: durationMeasurement.Elapsed);
// Output: {"DependencyType": "Azure IoT Hub", "TargetName": "sensors", "Duration": "00:00:00.2521801", "StartTime": "03/23/2020 09:56:31 +00:00", "IsSuccessful": true, "Context": {}}
```

> ⚠ Previously, an `LogIotHubDependency` IoT Hub connection string overload was available in the `Arcus.Observability.Telemetry.IoT` package but is now deprecated and renamed to `LogIotHubDependencyWithConnectionString` which is located in the `Arcus.Observability.Telemetry.Core` package.
### Measuring Azure Key Vault dependencies

We allow you to measure Azure Key vault dependencies.
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
using System;
using System.Collections.Generic;
using Arcus.Observability.Telemetry.Core;
using Arcus.Observability.Telemetry.Core.Iot;
using Arcus.Observability.Telemetry.Core.Logging;
using GuardNet;

Expand Down Expand Up @@ -117,6 +118,66 @@ public static void LogIotHubDependency(
LogIotHubDependency(logger, iotHubName, isSuccessful, startTime, duration, dependencyId: null, context);
}

/// <summary>
/// Logs an Azure Iot Hub Dependency.
/// </summary>
/// <param name="logger">The logger instance to track the IoT Hub dependency.</param>
/// <param name="iotHubConnectionString">The connection string to interact with an IoT Hub resource.</param>
/// <param name="isSuccessful">The indication whether or not the operation was successful.</param>
/// <param name="measurement">The measurement 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"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">Thrown when the <paramref name="iotHubConnectionString"/> is blank or is invalid.</exception>
/// <exception cref="FormatException">Thrown when the <paramref name="iotHubConnectionString"/> is invalid.</exception>
public static void LogIotHubDependencyWithConnectionString(
this ILogger logger,
string iotHubConnectionString,
bool isSuccessful,
DurationMeasurement measurement,
string dependencyId,
Dictionary<string, object> context = null)
{
Guard.NotNull(logger, nameof(logger), "Requires an logger instance to track the IoT Hub dependency");
Guard.NotNullOrWhitespace(iotHubConnectionString, nameof(iotHubConnectionString), "Requires an IoT Hub connection string to retrieve the IoT host name to track the IoT Hub dependency");
Guard.NotNull(measurement, nameof(measurement), "Requires an measurement instance to measure the duration of interaction with the IoT Hub dependency");

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

LogIotHubDependencyWithConnectionString(logger, iotHubConnectionString, isSuccessful, measurement.StartTime, measurement.Elapsed, dependencyId, context);
}

/// <summary>
/// Logs an Azure Iot Hub Dependency.
/// </summary>
/// <param name="logger">The logger instance to track the IoT Hub dependency.</param>
/// <param name="iotHubConnectionString">The connection string to interact with an IoT 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="iotHubConnectionString"/> is blank or is invalid.</exception>
/// <exception cref="FormatException">Thrown when the <paramref name="iotHubConnectionString"/> is invalid.</exception>
public static void LogIotHubDependencyWithConnectionString(
this ILogger logger,
string iotHubConnectionString,
bool isSuccessful,
DateTimeOffset startTime,
TimeSpan duration,
string dependencyId,
Dictionary<string, object> context = null)
{
Guard.NotNull(logger, nameof(logger), "Requires an logger instance to track the IoT Hub dependency");
Guard.NotNullOrWhitespace(iotHubConnectionString, nameof(iotHubConnectionString), "Requires an IoT Hub connection string to retrieve the IoT host name to track the IoT Hub dependency");

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

var result = IotHubConnectionStringParser.Parse(iotHubConnectionString);
LogIotHubDependency(logger, iotHubName: result.HostName, isSuccessful, startTime, duration, dependencyId, context);
}

/// <summary>
/// Logs an Azure Iot Hub Dependency.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using System.Linq;
using GuardNet;

namespace Arcus.Observability.Telemetry.Core.Iot
{
/// <summary>
/// Represents the instance to parse the IoT Hub connection string to a strongly-typed <see cref="IotHubConnectionStringParserResult"/>.
/// </summary>
internal static class IotHubConnectionStringParser
{
/// <summary>
/// Parses the incoming IoT Hub <paramref name="connectionString"/> to a strongly-typed result of IoT Hub properties.
/// </summary>
/// <param name="connectionString">The connection string based on the hostname of the IoT Hub service.</param>
/// <exception cref="ArgumentException">Thrown when the <paramref name="connectionString"/> is blank.</exception>
internal static IotHubConnectionStringParserResult Parse(string connectionString)
{
Guard.NotNullOrWhitespace(connectionString, nameof(connectionString), "Requires a non-blank connection string based on the hostname of the IoT Hub service");

string hostNameProperty =
connectionString.Split(';', StringSplitOptions.RemoveEmptyEntries)
.FirstOrDefault(part => part.StartsWith("HostName", StringComparison.OrdinalIgnoreCase));

if (hostNameProperty is null)
{
throw new FormatException(
"Cannot parse IoT Hub connection string because cannot find 'HostName' in IoT Hub connection string");
}

string hostName =
string.Join("", hostNameProperty.SkipWhile(ch => ch != '=').Skip(1));

return new IotHubConnectionStringParserResult(hostName);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
using System;
using GuardNet;

namespace Arcus.Observability.Telemetry.Core.Iot
{
/// <summary>
/// Represents the result of the <see cref="IotHubConnectionStringParser"/> when parsing an IoT Hub connection string.
/// </summary>
internal class IotHubConnectionStringParserResult
{
/// <summary>
/// Initializes a new instance of the <see cref="IotHubConnectionStringParserResult" /> class.
/// </summary>
/// <param name="hostName">The fully-qualified DNS hostname of the IoT Hub service.</param>
/// <exception cref="ArgumentException">Thrown when the <paramref name="hostName"/> is blank.</exception>
internal IotHubConnectionStringParserResult(string hostName)
{
Guard.NotNullOrWhitespace(hostName, nameof(hostName), "Requires a non-blank fully-qualified DNS hostname of the IoT Hub service");
HostName = hostName;
}

/// <summary>
/// Gets the value of the fully-qualified DNS hostname of the IoT Hub service.
/// </summary>
internal string HostName { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ public static void LogIotHubDependency(this ILogger logger, string iotHubConnect
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="logger"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">Thrown when the <paramref name="iotHubConnectionString"/> is blank or is invalid.</exception>
/// <exception cref="FormatException">Thrown when the <paramref name="iotHubConnectionString"/> is invalid.</exception>
[Obsolete("Use the 'LogIotHubDependencyWithConnectionString' instead in the 'Arcus.Observability.Telemetry.Core' package and remove the 'Arcus.Observability.Telemetry.IoT' package from your project")]
public static void LogIotHubDependency(
this ILogger logger,
string iotHubConnectionString,
Expand Down Expand Up @@ -69,6 +70,7 @@ public static void LogIotHubDependency(
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="logger"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">Thrown when the <paramref name="iotHubConnectionString"/> is blank or is invalid.</exception>
/// <exception cref="FormatException">Thrown when the <paramref name="iotHubConnectionString"/> is invalid.</exception>
[Obsolete("Use the 'LogIotHubDependencyWithConnectionString' instead in the 'Arcus.Observability.Telemetry.Core' package and remove the 'Arcus.Observability.Telemetry.IoT' package from your project")]
public static void LogIotHubDependency(
this ILogger logger,
string iotHubConnectionString,
Expand Down Expand Up @@ -98,6 +100,7 @@ public static void LogIotHubDependency(
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="logger"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">Thrown when the <paramref name="iotHubConnectionString"/> is blank or is invalid.</exception>
/// <exception cref="FormatException">Thrown when the <paramref name="iotHubConnectionString"/> is invalid.</exception>
[Obsolete("Use the 'LogIotHubDependencyWithConnectionString' instead in the 'Arcus.Observability.Telemetry.Core' package and remove the 'Arcus.Observability.Telemetry.IoT' package from your project")]
public static void LogIotHubDependency(
this ILogger logger,
string iotHubConnectionString,
Expand Down Expand Up @@ -127,6 +130,7 @@ public static void LogIotHubDependency(
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="logger"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">Thrown when the <paramref name="iotHubConnectionString"/> is blank or is invalid.</exception>
/// <exception cref="FormatException">Thrown when the <paramref name="iotHubConnectionString"/> is invalid.</exception>
[Obsolete("Use the 'LogIotHubDependencyWithConnectionString' instead in the 'Arcus.Observability.Telemetry.Core' package and remove the 'Arcus.Observability.Telemetry.IoT' package from your project")]
public static void LogIotHubDependency(
this ILogger logger,
string iotHubConnectionString,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,5 +48,40 @@ await RetryAssertUntilTelemetryShouldBeAvailableAsync(async client =>
});
});
}

[Fact]
public async Task LogIoTHubDependencyWithConnectionString_SinksToApplicationInsights_ResultsIEventHubsDependencyTelemetry()
{
// Arrange
string componentName = BogusGenerator.Commerce.ProductName();
LoggerConfiguration.Enrich.WithComponentName(componentName);

string hostName = $"{BogusGenerator.Commerce.ProductName()}.azure-devices.net";
string dependencyName = hostName;
string connectionString = $"HostName={hostName};DeviceId={Guid.NewGuid()};SharedAccessKey={Guid.NewGuid()}";
string dependencyId = BogusGenerator.Random.Guid().ToString();

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

// Act
Logger.LogIotHubDependencyWithConnectionString(connectionString, isSuccessful, startTime, duration, dependencyId, telemetryContext);

// Assert
await RetryAssertUntilTelemetryShouldBeAvailableAsync(async client =>
{
EventsDependencyResult[] results = await client.GetDependenciesAsync();
AssertX.Any(results, result =>
{
Assert.Equal("Azure IoT Hub", result.Dependency.Type);
Assert.Equal(hostName, result.Dependency.Target);
Assert.Equal(componentName, result.Cloud.RoleName);
Assert.Equal(dependencyName, result.Dependency.Name);
Assert.Equal(dependencyId, result.Dependency.Id);
});
});
}
}
}
Loading

0 comments on commit 9f2af44

Please sign in to comment.