Skip to content

Commit

Permalink
feat: Ambiguous method definition on LogMetric (#424)
Browse files Browse the repository at this point in the history
* Update Arcus.Testing.Logging

* Revert "Update Arcus.Testing.Logging"

This reverts commit ec3a69c.

* Rename LogMetric to LogCustomMetric

* Rename LogEvent to LogCustomEvent

* Make CodeFactor happy

* Update src/Arcus.Observability.Telemetry.Core/Extensions/ILoggerEventExtensions.cs

Co-authored-by: Stijn Moreels <[email protected]>

* Update src/Arcus.Observability.Telemetry.Core/Extensions/ILoggerMetricExtensions.cs

Co-authored-by: Stijn Moreels <[email protected]>

* Update src/Arcus.Observability.Telemetry.Core/Extensions/ILoggerMetricExtensions.cs

Co-authored-by: Stijn Moreels <[email protected]>

* Revert changes to obsolete methods
Revert removal unit-tests
Add unit-tests

* Add Unit-tests

* Add unit-test

* Add unit-tests

* Add unit-tests

* infinite loop

* update docs

Co-authored-by: Stijn Moreels <[email protected]>
  • Loading branch information
vincenttermaat and stijnmoreels authored Jul 14, 2022
1 parent c94c6b0 commit 268efd2
Show file tree
Hide file tree
Showing 12 changed files with 496 additions and 8 deletions.
2 changes: 1 addition & 1 deletion docs/preview/02-Guidance/use-with-dotnet-and-aspnetcore.md
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ public class OrderController : ControllerBase
{"Customer", order.Customer.Name}
};

_logger.LogEvent("Order received", contextualInformation);
_logger.LogCustomEvent("Order received", contextualInformation);
}
}
```
Expand Down
2 changes: 1 addition & 1 deletion docs/preview/02-Guidance/use-with-dotnet-and-functions.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ public class DockerHubMetricScraperFunction
{"Image ID", $"{repoName}/{imageName}"}
};

_logger.LogMetric("Image Pulls", pullCount, contextualInformation);
_logger.LogCustomMetric("Image Pulls", pullCount, contextualInformation);
}
}
```
Expand Down
6 changes: 3 additions & 3 deletions docs/preview/03-Features/making-telemetry-more-powerful.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ var telemetryContext = new Dictionary<string, object>
{"OrderId", "ABC"},
};

logger.LogEvent("Order Created", telemetryContext);
logger.LogCustomEvent("Order Created", telemetryContext);
// Output: "Events Order Created (Context: [Customer, Arcus], [OrderId, ABC])"
```

Expand All @@ -34,7 +34,7 @@ Let's use an example - When measuring a metric you get an understanding of the c
```csharp
using Microsoft.Extensions.Logging;

logger.LogMetric("Orders Received", 133);
logger.LogCustomMetric("Orders Received", 133);
// Log output: "Metric Orders Received: 133 (Context: )"
```

Expand All @@ -51,7 +51,7 @@ var telemetryContext = new Dictionary<string, object>
{ "Customer", "Contoso"},
};

logger.LogMetric("Orders Received", 133, telemetryContext);
logger.LogCustomMetric("Orders Received", 133, telemetryContext);
// Log output: "Metric Orders Received: 133 (Context: [Customer, Contoso])"
```

Expand Down
4 changes: 2 additions & 2 deletions docs/preview/03-Features/writing-different-telemetry-types.md
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ Here is how you can report an `Order Created` event:
```csharp
using Microsoft.Extensions.Logging;

logger.LogEvent("Order Created");
logger.LogCustomEvent("Order Created");
// Output: {"EventName": "Order Created", "Context": {}}
```

Expand All @@ -488,7 +488,7 @@ Here is how you can report an `Invoices Received` metric:
```csharp
using Microsoft.Extensions.Logging;

logger.LogMetric("Invoices Received", 133);
logger.LogCustomMetric("Invoices Received", 133);
// Output: {"MetricName": "Invoices Received", "MetricValue": 133, "Timestamp": "03/23/2020 09:32:02 +00:00", "Context: {[TelemetryType, Metric]}}
```

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ public static void LogSecurityEvent(this ILogger logger, string name, Dictionary
context = context ?? new Dictionary<string, object>();
context["EventType"] = "Security";

LogEvent(logger, name, context);
LogCustomEvent(logger, name, context);
}

/// <summary>
Expand All @@ -40,6 +40,25 @@ public static void LogSecurityEvent(this ILogger logger, string name, Dictionary
/// <param name="context">Context that provides more insights on the event that occurred</param>
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="logger"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">Thrown when the <paramref name="name"/> is blank.</exception>
public static void LogCustomEvent(this ILogger logger, string name, Dictionary<string, object> context = null)
{
Guard.NotNull(logger, nameof(logger), "Requires a logger instance to track telemetry");
Guard.NotNullOrWhitespace(name, nameof(name), "Requires a non-blank event name to track an custom event");

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

logger.LogWarning(MessageFormats.EventFormat, new EventLogEntry(name, context));
}

/// <summary>
/// Logs a custom event
/// </summary>
/// <param name="logger">The logger to track the telemetry.</param>
/// <param name="name">Name of the event</param>
/// <param name="context">Context that provides more insights on the event that occurred</param>
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="logger"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">Thrown when the <paramref name="name"/> is blank.</exception>
[Obsolete("Use " + nameof(LogCustomEvent) + " instead")]
public static void LogEvent(this ILogger logger, string name, Dictionary<string, object> context = null)
{
Guard.NotNull(logger, nameof(logger), "Requires a logger instance to track telemetry");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,26 @@ public static partial class ILoggerExtensions
/// <param name="context">Context that provides more insights on the event that occurred</param>
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="logger"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">Thrown when the <paramref name="name"/> is blank.</exception>
public static void LogCustomMetric(this ILogger logger, string name, double value, Dictionary<string, object> context = null)
{
Guard.NotNull(logger, nameof(logger), "Requires a logger instance to track telemetry");
Guard.NotNullOrWhitespace(name, nameof(name), "Requires a non-blank name to track a metric");

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

LogCustomMetric(logger, name, value, DateTimeOffset.UtcNow, context);
}

/// <summary>
/// Logs a custom metric
/// </summary>
/// <param name="logger">The logger to track the metric.</param>
/// <param name="name">Name of the metric</param>
/// <param name="value">Value of the metric</param>
/// <param name="context">Context that provides more insights on the event that occurred</param>
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="logger"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">Thrown when the <paramref name="name"/> is blank.</exception>
[Obsolete("Use " + nameof(LogCustomMetric) + " instead")]
public static void LogMetric(this ILogger logger, string name, double value, Dictionary<string, object> context = null)
{
Guard.NotNull(logger, nameof(logger), "Requires a logger instance to track telemetry");
Expand All @@ -42,6 +62,27 @@ public static void LogMetric(this ILogger logger, string name, double value, Dic
/// <param name="context">Context that provides more insights on the event that occurred</param>
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="logger"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">Thrown when the <paramref name="name"/> is blank.</exception>
public static void LogCustomMetric(this ILogger logger, string name, double value, DateTimeOffset timestamp, Dictionary<string, object> context = null)
{
Guard.NotNull(logger, nameof(logger), "Requires a logger instance to track telemetry");
Guard.NotNullOrWhitespace(name, nameof(name), "Requires a non-blank name to track a metric");

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

logger.LogWarning(MessageFormats.MetricFormat, new MetricLogEntry(name, value, timestamp, context));
}

/// <summary>
/// Logs a custom metric
/// </summary>
/// <param name="logger">The logger to track the metric.</param>
/// <param name="name">Name of the metric</param>
/// <param name="value">Value of the metric</param>
/// <param name="timestamp">Timestamp of the metric</param>
/// <param name="context">Context that provides more insights on the event that occurred</param>
/// <exception cref="ArgumentNullException">Thrown when the <paramref name="logger"/> is <c>null</c>.</exception>
/// <exception cref="ArgumentException">Thrown when the <paramref name="name"/> is blank.</exception>
[Obsolete("Use " + nameof(LogCustomMetric) + " instead")]
public static void LogMetric(this ILogger logger, string name, double value, DateTimeOffset timestamp, Dictionary<string, object> context = null)
{
Guard.NotNull(logger, nameof(logger), "Requires a logger instance to track telemetry");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,24 @@ await RetryAssertUntilTelemetryShouldBeAvailableAsync(async client =>
});
}

[Fact]
public async Task LogCustomEvent_SinksToApplicationInsights_ResultsInEventTelemetry()
{
// Arrange
string eventName = BogusGenerator.Finance.AccountName();
Dictionary<string, object> telemetryContext = CreateTestTelemetryContext();

// Act
Logger.LogCustomEvent(eventName, telemetryContext);

// Assert
await RetryAssertUntilTelemetryShouldBeAvailableAsync(async client =>
{
EventsCustomEventResult[] results = await client.GetEventsAsync();
Assert.Contains(results, result => result.CustomEvent.Name == eventName);
});
}

[Fact]
public async Task LogEventWithComponentName_SinksToApplicationInsights_ResultsInTelemetryWithComponentName()
{
Expand Down Expand Up @@ -84,6 +102,29 @@ await RetryAssertUntilTelemetryShouldBeAvailableAsync(async client=>
});
}

[Fact]
public async Task LogCustomEventWithVersion_SinksToApplicationInsights_ResultsInTelemetryWithVersion()
{
// Arrange
var eventName = "Update version";
LoggerConfiguration.Enrich.WithVersion();

// Act
Logger.LogCustomEvent(eventName);

// Assert
await RetryAssertUntilTelemetryShouldBeAvailableAsync(async client =>
{
EventsCustomEventResult[] events = await client.GetEventsAsync();
Assert.Contains(events, ev =>
{
return ev.CustomEvent.Name == eventName
&& ev.CustomDimensions.TryGetValue(VersionEnricher.DefaultPropertyName, out string actualVersion)
&& !String.IsNullOrWhiteSpace(actualVersion);
});
});
}

[Fact]
public async Task LogEventWithCorrelationInfo_SinksToApplicationInsights_ResultsInTelemetryWithCorrelationInfo()
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,28 @@ await RetryAssertUntilTelemetryShouldBeAvailableAsync(async client =>
Assert.NotEmpty(results);
});
}

[Fact]
public async Task LogCustomMetric_SinksToApplicationInsights_ResultsInMetricTelemetry()
{
// Arrange
string metricName = "threshold";
double metricValue = 0.25;
Dictionary<string, object> telemetryContext = CreateTestTelemetryContext();

// Act
Logger.LogCustomMetric(metricName, metricValue, telemetryContext);

// Assert
await RetryAssertUntilTelemetryShouldBeAvailableAsync(async client =>
{
var bodySchema = new MetricsPostBodySchema(
id: Guid.NewGuid().ToString(),
parameters: new MetricsPostBodySchemaParameters("customMetrics/" + metricName));

MetricsResultsItem[] results = await client.GetMetricsAsync(bodySchema);
Assert.NotEmpty(results);
});
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,33 @@ public void LogEvent_WithTelemetryTypeFilter_IgnoresLogEvent()
}
}

[Fact]
public void LogCustomEvent_WithTelemetryTypeFilter_IgnoresLogEvent()
{
// Arrange
string eventName = _bogusGenerator.Random.Word();
string propertyName = _bogusGenerator.Random.Word();
string propertyValue = _bogusGenerator.Random.Word();
var properties = new Dictionary<string, object> { [propertyName] = propertyValue };

var spySink = new InMemoryLogSink();
Logger serilogLogger = new LoggerConfiguration()
.Filter.With(TelemetryTypeFilter.On(TelemetryType.Events))
.WriteTo.Sink(spySink)
.CreateLogger();

using (var factory = new SerilogLoggerFactory(serilogLogger))
{
ILogger logger = factory.CreateLogger<TelemetryTypeFilterTests>();

// Act
logger.LogCustomEvent(eventName, properties);

// Assert
Assert.Empty(spySink.CurrentLogEmits);
}
}

[Theory]
[MemberData(nameof(TelemetryTypesWithoutEvent))]
public void LogEvent_WithTelemetryTypeFilterOnOtherType_DoesNotFilterOutEntry(TelemetryType telemetryType)
Expand Down Expand Up @@ -90,6 +117,39 @@ public void LogEvent_WithTelemetryTypeFilterOnOtherType_DoesNotFilterOutEntry(Te
}
}

[Theory]
[MemberData(nameof(TelemetryTypesWithoutEvent))]
public void LogCustomEvent_WithTelemetryTypeFilterOnOtherType_DoesNotFilterOutEntry(TelemetryType telemetryType)
{
// Arrange
string eventName = _bogusGenerator.Random.Word();
string propertyName = _bogusGenerator.Random.Word();
string propertyValue = _bogusGenerator.Random.Word();
var properties = new Dictionary<string, object> { [propertyName] = propertyValue };

var spySink = new InMemoryLogSink();
Logger serilogLogger = new LoggerConfiguration()
.Filter.With(TelemetryTypeFilter.On(telemetryType, isTrackingEnabled: false))
.WriteTo.Sink(spySink)
.CreateLogger();

using (var factory = new SerilogLoggerFactory(serilogLogger))
{
ILogger logger = factory.CreateLogger<TelemetryTypeFilterTests>();

// Act
logger.LogCustomEvent(eventName, properties);

// Assert
LogEvent logEvent = Assert.Single(spySink.CurrentLogEmits);
Assert.NotNull(logEvent);
string writtenMessage = logEvent.RenderMessage();
Assert.Contains(propertyName, writtenMessage);
Assert.Contains(propertyValue, writtenMessage);
Assert.Contains(TelemetryType.Events.ToString(), writtenMessage);
}
}

[Fact]
public void LogSecurityEvent_WithTelemetryTypeFilter_IgnoresLogEvent()
{
Expand Down Expand Up @@ -178,6 +238,34 @@ public void LogMetric_WithTelemetryTypeFilter_IgnoresMetric()
}
}

[Fact]
public void LogCustomMetric_WithTelemetryTypeFilter_IgnoresMetric()
{
// Arrange
string metricName = _bogusGenerator.Random.Word();
double metricValue = _bogusGenerator.Random.Double();
string propertyName = _bogusGenerator.Random.Word();
string propertyValue = _bogusGenerator.Random.Word();
var properties = new Dictionary<string, object> { [propertyName] = propertyValue };

var spySink = new InMemoryLogSink();
Logger serilogLogger = new LoggerConfiguration()
.Filter.With(TelemetryTypeFilter.On(TelemetryType.Metrics))
.WriteTo.Sink(spySink)
.CreateLogger();

using (var factory = new SerilogLoggerFactory(serilogLogger))
{
ILogger logger = factory.CreateLogger<TelemetryTypeFilterTests>();

// Act
logger.LogCustomMetric(metricName, metricValue, properties);

// Assert
Assert.Empty(spySink.CurrentLogEmits);
}
}

[Theory]
[MemberData(nameof(TelemetryTypesWithoutMetric))]
public void LogMetric_WithTelemetryTypeFilterOnDifferentTyp_DoesNotFilterOutEntry(TelemetryType telemetryType)
Expand Down Expand Up @@ -214,6 +302,42 @@ public void LogMetric_WithTelemetryTypeFilterOnDifferentTyp_DoesNotFilterOutEntr
}
}

[Theory]
[MemberData(nameof(TelemetryTypesWithoutMetric))]
public void LogCustomMetric_WithTelemetryTypeFilterOnDifferentTyp_DoesNotFilterOutEntry(TelemetryType telemetryType)
{
// Arrange
string metricName = _bogusGenerator.Random.Word();
double metricValue = _bogusGenerator.Random.Double();
string propertyName = _bogusGenerator.Random.Word();
string propertyValue = _bogusGenerator.Random.Word();
var properties = new Dictionary<string, object> { [propertyName] = propertyValue };

var spySink = new InMemoryLogSink();
Logger serilogLogger = new LoggerConfiguration()
.Filter.With(TelemetryTypeFilter.On(telemetryType))
.WriteTo.Sink(spySink)
.CreateLogger();

using (var factory = new SerilogLoggerFactory(serilogLogger))
{
ILogger logger = factory.CreateLogger<TelemetryTypeFilterTests>();

// Act
logger.LogCustomMetric(metricName, metricValue, properties);

// Assert
LogEvent logEvent = Assert.Single(spySink.CurrentLogEmits);
Assert.NotNull(logEvent);
string writtenMessage = logEvent.RenderMessage();
Assert.Contains(metricName, writtenMessage);
Assert.Contains(metricValue.ToString(CultureInfo.InvariantCulture), writtenMessage);
Assert.Contains(propertyName, writtenMessage);
Assert.Contains(propertyValue, writtenMessage);
Assert.Contains(TelemetryType.Metrics.ToString(), writtenMessage);
}
}

[Fact]
public void LogRequest_WithTelemetryTypeFilter_FilersInRequest()
{
Expand Down
Loading

0 comments on commit 268efd2

Please sign in to comment.