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

Feature - IoT/Sql separate connection string tracking in diff libraries #130

Merged
Show file tree
Hide file tree
Changes from all commits
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
27 changes: 27 additions & 0 deletions docs/preview/features/writing-different-telemetry-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ _logger.LogEventHubsDependency(namespaceName: "be.sensors.contoso", eventHubName

We allow you to measure Azure IoT Hub dependencies.

**Example**

Here is how you can report a dependency call:

```csharp
Expand All @@ -116,6 +118,31 @@ _logger.logger.LogIotHubDependency(iotHubName: "sensors", isSuccessful: true, st
// Output: "Dependency Azure IoT Hub named sensors in 00:00:00.2521801 at 03/23/2020 09:56:31 +00:00 (Successful: True - Context: )"
```

Or, alternatively you can pass allong 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:

```csharp
var durationMeasurement = new Stopwatch();

// Start measuring
durationMeasurement.Start();
var startTime = DateTimeOffset.UtcNow;

_logger.logger.LogIotHubDependency(iotHubConnectionString: "Hostname=sensors;", isSuccessful: true, startTime: startTime, duration: durationMeasurement.Elapsed);
// Output: "Dependency Azure IoT Hub named sensors in 00:00:00.2521801 at 03/23/2020 09:56:31 +00:00 (Successful: True - Context: )"
```

### Measuring Azure Service Bus dependencies

We allow you to measure Azure Service Bus dependencies for both queues & topics.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -557,36 +557,38 @@ public static void LogHttpDependency(this ILogger logger, HttpRequestMessage req
logger.LogInformation(HttpDependencyFormat, targetName, dependencyName, (int) statusCode, duration, startTime.ToString(CultureInfo.InvariantCulture), isSuccessful, context);
}

/// <summary>
/// Logs a SQL dependency
/// </summary>
/// <param name="logger">Logger to use</param>
/// <param name="connectionString">SQL connection string</param>
/// <param name="tableName">Name of table</param>
/// <param name="operationName">Name of the operation that was performed</param>
/// <param name="isSuccessful">Indication whether or not the operation was successful</param>
/// <param name="measurement">Measuring the latency to call the SQL dependency</param>
/// <summary>
/// Logs a SQL dependency
/// </summary>
/// <param name="logger">Logger to use</param>
/// <param name="connectionString">SQL connection string</param>
/// <param name="tableName">Name of table</param>
/// <param name="operationName">Name of the operation that was performed</param>
/// <param name="isSuccessful">Indication whether or not the operation was successful</param>
/// <param name="measurement">Measuring the latency to call the SQL dependency</param>
/// <param name="context">Context that provides more insights on the dependency that was measured</param>
public static void LogSqlDependency(this ILogger logger, string connectionString, string tableName, string operationName, DependencyMeasurement measurement, bool isSuccessful, Dictionary<string, object> context = null)
stijnmoreels marked this conversation as resolved.
Show resolved Hide resolved
[Obsolete("Will be moved to separate package 'Arcus.Observability.Telemetry.Sql' in the future")]
public static void LogSqlDependency(this ILogger logger, string connectionString, string tableName, string operationName, DependencyMeasurement measurement, bool isSuccessful, Dictionary<string, object> context = null)
{
Guard.NotNull(logger, nameof(logger));
Guard.NotNull(measurement, nameof(measurement));

LogSqlDependency(logger, connectionString, tableName, operationName, measurement.StartTime, measurement.Elapsed, isSuccessful, context);
}

/// <summary>
/// Logs a SQL dependency
/// </summary>
/// <param name="logger">Logger to use</param>
/// <param name="connectionString">SQL connection string</param>
/// <param name="tableName">Name of table</param>
/// <param name="operationName">Name of the operation that was performed</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="isSuccessful">Indication whether or not the operation was successful</param>
/// <summary>
/// Logs a SQL dependency
/// </summary>
/// <param name="logger">Logger to use</param>
/// <param name="connectionString">SQL connection string</param>
/// <param name="tableName">Name of table</param>
/// <param name="operationName">Name of the operation that was performed</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="isSuccessful">Indication whether or not the operation was successful</param>
/// <param name="context">Context that provides more insights on the dependency that was measured</param>
public static void LogSqlDependency(this ILogger logger, string connectionString, string tableName, string operationName, DateTimeOffset startTime, TimeSpan duration, bool isSuccessful, Dictionary<string, object> context = null)
tomkerkhove marked this conversation as resolved.
Show resolved Hide resolved
[Obsolete("Will be moved to separate package 'Arcus.Observability.Telemetry.Sql' in the future")]
public static void LogSqlDependency(this ILogger logger, string connectionString, string tableName, string operationName, DateTimeOffset startTime, TimeSpan duration, bool isSuccessful, Dictionary<string, object> context = null)
{
Guard.NotNullOrEmpty(connectionString, nameof(connectionString));
var connection = new SqlConnectionStringBuilder(connectionString);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup>
<TargetFrameworks>netstandard2.0</TargetFrameworks>
<PackageId>Arcus.Observability.Telemetry.IoT</PackageId>
<Authors>Arcus</Authors>
<Company>Arcus</Company>
<Description>Provides capability to improve IoT telemetry with Serilog in applications</Description>
<Copyright>Copyright (c) Arcus</Copyright>
<PackageLicenseUrl>https://github.com/arcus-azure/arcus.observability/blob/master/LICENSE</PackageLicenseUrl>
<PackageProjectUrl>https://github.com/arcus-azure/arcus.observability</PackageProjectUrl>
<PackageIconUrl>https://raw.githubusercontent.com/arcus-azure/arcus/master/media/arcus.png</PackageIconUrl>
<RepositoryUrl>https://github.com/arcus-azure/arcus.observability</RepositoryUrl>
<RepositoryType>Git</RepositoryType>
<PackageTags>Azure;Observability;Telemetry;Serilog;IoT</PackageTags>
<GeneratePackageOnBuild>true</GeneratePackageOnBuild>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<AssemblyName>Arcus.Observability.Telemetry.IoT</AssemblyName>
<RootNamespace>Arcus.Observability.Telemetry.IoT</RootNamespace>
</PropertyGroup>

<ItemGroup>
<ProjectReference Include="../Arcus.Observability.Telemetry.Core/Arcus.Observability.Telemetry.Core.csproj" />
</ItemGroup>

<ItemGroup>
<PackageReference Include="Guard.Net" Version="1.2.0" />
<PackageReference Include="Microsoft.Azure.Devices.Client" Version="1.26.0" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
using System;
using System.Collections.Generic;
using Arcus.Observability.Telemetry.Core;
using GuardNet;
using Microsoft.Azure.Devices.Client;

// ReSharper disable once CheckNamespace
namespace Microsoft.Extensions.Logging
{
/// <summary>
/// Extensions on the <see cref="ILogger"/> related to tracking IoT dependencies.
/// </summary>
// ReSharper disable once InconsistentNaming
public static class ILoggerExtensions
{
/// <summary>
/// Logs an Azure Iot Hub Dependency.
/// </summary>
/// <param name="logger">Logger to use</param>
/// <param name="iotHubConnectionString">Name of the IoT 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 LogIotHubDependency(this ILogger logger, string iotHubConnectionString, bool isSuccessful, DependencyMeasurement measurement, Dictionary<string, object> context = null)
{
Guard.NotNull(logger, nameof(logger));
Guard.NotNullOrWhitespace(iotHubConnectionString, nameof(iotHubConnectionString));

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

/// <summary>
/// Logs an Azure Iot Hub Dependency.
/// </summary>
/// <param name="logger">Logger to use</param>
/// <param name="iotHubConnectionString">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 LogIotHubDependency(this ILogger logger, string iotHubConnectionString, bool isSuccessful, DateTimeOffset startTime, TimeSpan duration, Dictionary<string, object> context = null)
{
Guard.NotNull(logger, nameof(logger));
Guard.NotNullOrWhitespace(iotHubConnectionString, nameof(iotHubConnectionString));

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

var iotHubConnection = IotHubConnectionStringBuilder.Create(iotHubConnectionString);
logger.LogIotHubDependency(iotHubName: iotHubConnection.HostName, isSuccessful: isSuccessful, startTime: startTime, duration: duration, context: context);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

<ItemGroup>
<ProjectReference Include="..\Arcus.Observability.Correlation\Arcus.Observability.Correlation.csproj" />
<ProjectReference Include="..\Arcus.Observability.Telemetry.IoT\Arcus.Observability.Telemetry.IoT.csproj" />
<ProjectReference Include="..\Arcus.Observability.Telemetry.Serilog.Filters\Arcus.Observability.Telemetry.Serilog.Filters.csproj" />
<ProjectReference Include="..\Arcus.Observability.Telemetry.Serilog.Sinks.ApplicationInsights\Arcus.Observability.Telemetry.Serilog.Sinks.ApplicationInsights.csproj" />
<ProjectReference Include="..\Arcus.Observability.Telemetry.Serilog.Enrichers\Arcus.Observability.Telemetry.Serilog.Enrichers.csproj" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -481,7 +481,7 @@ public void LogIoTHubDependency_WithTableStorageDependency_CreatesDependencyTele
{
["DeviceName"] = "Sensor #102"
};
logger.LogIotHubDependency(iotHubName, isSuccessful: true, startTime: startTime, duration: duration, context: telemetryContext);
logger.LogIotHubDependency(iotHubName: iotHubName, isSuccessful: true, startTime: startTime, duration: duration, context: telemetryContext);
LogEvent logEvent = Assert.Single(spySink.CurrentLogEmits);
Assert.NotNull(logEvent);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -536,7 +536,7 @@ public void LogIotHubDependency_ValidArguments_Succeeds()
TimeSpan duration = _bogusGenerator.Date.Timespan();

// Act
logger.LogIotHubDependency(iotHubName, isSuccessful, startTime, duration);
logger.LogIotHubDependency(iotHubName: iotHubName, isSuccessful: isSuccessful, startTime: startTime, duration: duration);

// Assert
var logMessage = logger.WrittenMessage;
Expand All @@ -562,7 +562,62 @@ public void LogIotHubDependencyWithDependencyMeasurement_ValidArguments_Succeeds
TimeSpan duration = measurement.Elapsed;

// Act
logger.LogIotHubDependency(iotHubName, isSuccessful, measurement);
logger.LogIotHubDependency(iotHubName: iotHubName, isSuccessful: isSuccessful, measurement: measurement);

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

[Fact]
public void LogIotHubConnectionStringDependency_ValidArguments_Succeeds()
{
// Arrange
var logger = new TestLogger();
string iotHubName = _bogusGenerator.Commerce.ProductName().Replace(" ", String.Empty);
string deviceId = _bogusGenerator.Internet.Ip();
string sharedAccessKey = _bogusGenerator.Random.Hash();
string iotHubConnectionString = $"HostName={iotHubName}.;DeviceId={deviceId};SharedAccessKey={sharedAccessKey}";
bool isSuccessful = _bogusGenerator.Random.Bool();
DateTimeOffset startTime = _bogusGenerator.Date.PastOffset();
TimeSpan duration = _bogusGenerator.Date.Timespan();

// Act
logger.LogIotHubDependency(iotHubConnectionString: iotHubConnectionString, isSuccessful: isSuccessful, startTime: startTime, duration: duration);

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

[Fact]
public void LogIotHubDependencyConnectionStringWithDependencyMeasurement_ValidArguments_Succeeds()
{
// Arrange
var logger = new TestLogger();
string iotHubName = _bogusGenerator.Commerce.ProductName().Replace(" ", String.Empty);
string deviceId = _bogusGenerator.Internet.Ip();
string sharedAccessKey = _bogusGenerator.Random.Hash();
string iotHubConnectionString = $"HostName={iotHubName}.;DeviceId={deviceId};SharedAccessKey={sharedAccessKey}";
bool isSuccessful = _bogusGenerator.Random.Bool();

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

// Act
logger.LogIotHubDependency(iotHubConnectionString: iotHubConnectionString, isSuccessful: isSuccessful, measurement: measurement);

// Assert
var logMessage = logger.WrittenMessage;
Expand Down
8 changes: 7 additions & 1 deletion src/Arcus.Observability.sln
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcus.Observability.Telemet
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcus.Observability.Telemetry.Serilog.Filters", "Arcus.Observability.Telemetry.Serilog.Filters\Arcus.Observability.Telemetry.Serilog.Filters.csproj", "{ED747983-87B8-47B5-AAF8-DDFCB69E98EF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Arcus.Observability.Tests.Core", "Arcus.Observability.Tests.Core\Arcus.Observability.Tests.Core.csproj", "{DC18426F-4D03-4DBD-8D92-224C026BFADC}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Arcus.Observability.Tests.Core", "Arcus.Observability.Tests.Core\Arcus.Observability.Tests.Core.csproj", "{DC18426F-4D03-4DBD-8D92-224C026BFADC}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Arcus.Observability.Telemetry.IoT", "Arcus.Observability.Telemetry.IoT\Arcus.Observability.Telemetry.IoT.csproj", "{E4A6624B-C1A4-4956-9FC2-E77D6C634717}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Expand Down Expand Up @@ -59,6 +61,10 @@ Global
{DC18426F-4D03-4DBD-8D92-224C026BFADC}.Debug|Any CPU.Build.0 = Debug|Any CPU
{DC18426F-4D03-4DBD-8D92-224C026BFADC}.Release|Any CPU.ActiveCfg = Release|Any CPU
{DC18426F-4D03-4DBD-8D92-224C026BFADC}.Release|Any CPU.Build.0 = Release|Any CPU
{E4A6624B-C1A4-4956-9FC2-E77D6C634717}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{E4A6624B-C1A4-4956-9FC2-E77D6C634717}.Debug|Any CPU.Build.0 = Debug|Any CPU
{E4A6624B-C1A4-4956-9FC2-E77D6C634717}.Release|Any CPU.ActiveCfg = Release|Any CPU
{E4A6624B-C1A4-4956-9FC2-E77D6C634717}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand Down