diff --git a/docs/preview/02-Features/writing-different-telemetry-types.md b/docs/preview/02-Features/writing-different-telemetry-types.md index 4f4bbac0..f27561a1 100644 --- a/docs/preview/02-Features/writing-different-telemetry-types.md +++ b/docs/preview/02-Features/writing-different-telemetry-types.md @@ -460,6 +460,63 @@ logger.LogMetric("Invoice Received", 133.37, telemetryContext); ## Requests +### Incoming Azure Service Bus requests +Requests allow you to keep track of incoming Azure Service Bus messages on a queue or topic. + +Here is how you can log an Azure Service Bus queue request on a message that's being processed: + +```csharp +using Microsoft.Extensions.Logging; + +bool isSuccessful = false; + +// Start measuring. +using (var measurement = DependencyMeasurement.Start()) +{ + try + { + // Processing message. + + // End processing. + + isSuccessful = true; + } + finally + { + logger.LogServiceBusQueueRequest(".servicebus.windows.net", "", "", isSuccessful, measurement); + // Output: Azure Service Bus from completed in 0.00:12:20.8290760 at 2021-10-26T05:36:03.6067975 +02:00 - (IsSuccessful: True, Context: {[ServiceBus-Endpoint, .servicebus.windows.net]; [ServiceBus-Entity, ]; [ServiceBus-EntityType, Queue]; [TelemetryType, Request]}) + } +} +``` + +We provide support for all Azure Service Bus entity types such as queues, topics and subscriptions. +All these types can be tracked by passing allong the full Azure Service namespace, or with providing the namespace name and the Azure cloud separately. + +```csharp + +DependencyMeasurement measurement = ... + +// Tracking Azure Service Bus topics. +// ---------------------------------- + +// Providing the full Azure Service Bus topic namespace. +logger.LogServiceBusTopicRequest(".servicebus.windows.net", "", "", "", isSuccessful: true, measurement); + +// Providing the Azure Service Bus topic name and Azure cloud separately. +logger.LogServiceBusTopicRequestWithSuffix("", serviceBusNamespaceSuffix: ".servicebus.windows.net", "", "", "", isSuccessful: true, measurement); + + +// Tracking general Azure Service Bus requests. +// -------------------------------------------- + +// Providing the full Azure Service Bus topic namespace. +logger.LogServiceBusRequest(".servicebus.windows.net", "", "", "", isSuccessful: true, measurement, ServiceBusEntityType.Topic); + +// Providing the Azure Service Bus queue namespace name and Azure cloud separately. +logger.LogServiceBusQueueRequestWithSuffix("", serviceBusNamespaceSuffix: ".servicebus.windows.net", "", "", isSuccessful: true, measurement, ServiceBusEntityType.Queue); +``` + +### Incoming HTTP requests Requests allow you to keep track of the HTTP requests that are performed against your API and what the response was that was sent out. **Installation** diff --git a/src/Arcus.Observability.Telemetry.Core/ContextProperties.cs b/src/Arcus.Observability.Telemetry.Core/ContextProperties.cs index cae315a0..aaf29c37 100644 --- a/src/Arcus.Observability.Telemetry.Core/ContextProperties.cs +++ b/src/Arcus.Observability.Telemetry.Core/ContextProperties.cs @@ -1,4 +1,5 @@ using System; +using System.Runtime.InteropServices; #pragma warning disable 1591 namespace Arcus.Observability.Telemetry.Core @@ -65,6 +66,20 @@ public static class RequestTracking public const string ResponseStatusCode = "ResponseStatusCode"; public const string RequestDuration = "RequestDuration"; public const string RequestTime = "RequestTime"; + + public static class ServiceBus + { + public const string Endpoint = "ServiceBus-Endpoint"; + public const string EntityName = "ServiceBus-Entity"; + public const string EntityType = "ServiceBus-EntityType"; + + public static class Topic + { + public const string SubscriptionName = "ServiceBus-TopicSubscription"; + } + + public const string DefaultOperationName = "Process"; + } } public static class MetricTracking diff --git a/src/Arcus.Observability.Telemetry.Core/Extensions/ILoggerExtensions.cs b/src/Arcus.Observability.Telemetry.Core/Extensions/ILoggerExtensions.cs index 79e4d62f..25dc5d18 100644 --- a/src/Arcus.Observability.Telemetry.Core/Extensions/ILoggerExtensions.cs +++ b/src/Arcus.Observability.Telemetry.Core/Extensions/ILoggerExtensions.cs @@ -189,6 +189,444 @@ public static void LogRequest( logger.LogWarning(RequestFormat, new RequestLogEntry(request.Method.ToString(), host, resourcePath, operationName, statusCode, duration, context)); } + /// + /// Logs an Azure Service Bus topic request. + /// + /// The logger instance to track the telemetry. + /// The namespace where the Azure Service Bus topic is registered. (Will be combined with ). + /// The namespace suffix (i.e. '.servicebus.windows.net' or '*.servicebus.cloudapi.de') where the Azure Service Bus topic is registered (Will be combined with ). + /// The name of the Azure Service Bus topic. + /// The name of the subscription on the Azure Service Bus topic. + /// The optional logical name that can be used to identify the operation that consumes the message. + /// The indication whether or not the Azure Service Bus topic request was successfully processed. + /// The instance to measure the latency duration of the Azure Service Bus topic request. + /// The telemetry context that provides more insights on the Azure Service Bus topic request. + /// Thrown when the or the is null. + /// + /// Thrown when the , , , or the is blank. + /// + public static void LogServiceBusTopicRequestWithSuffix( + this ILogger logger, + string serviceBusNamespace, + string serviceBusNamespaceSuffix, + string topicName, + string subscriptionName, + string operationName, + bool isSuccessful, + DependencyMeasurement measurement, + Dictionary context = null) + { + Guard.NotNull(logger, nameof(logger), "Requires an logger instance to track telemetry"); + Guard.NotNullOrWhitespace(serviceBusNamespace, nameof(serviceBusNamespace), "Requires an Azure Service Bus namespace to track the topic request"); + Guard.NotNullOrWhitespace(serviceBusNamespaceSuffix, nameof(serviceBusNamespaceSuffix), "Requires an Azure Service Bus namespace suffix to track the topic request"); + Guard.NotNullOrWhitespace(topicName, nameof(topicName), "Requires an Azure Service Bus topic name to track the topic request"); + Guard.NotNullOrWhitespace(subscriptionName, nameof(subscriptionName), "Requires an Azure Service Bus subscription name on the to track the topic request"); + Guard.NotNull(measurement, nameof(measurement), "Requires an instance to measure the Azure Service Bus topic request process latency duration"); + + // TODO: the dependency date from the measurement instance is not used when tracking the request. + + LogServiceBusTopicRequestWithSuffix(logger, serviceBusNamespace, serviceBusNamespaceSuffix, topicName, subscriptionName, operationName, isSuccessful, measurement.Elapsed, measurement.StartTime, context); + } + + /// + /// Logs an Azure Service Bus topic request. + /// + /// The logger instance to track the telemetry. + /// The namespace where the Azure Service Bus topic is registered (use Azure public cloud). + /// The name of the Azure Service Bus topic. + /// The name of the subscription on the Azure Service Bus topic. + /// The optional logical name that can be used to identify the operation that consumes the message. + /// The indication whether or not the Azure Service Bus topic request was successfully processed. + /// The instance to measure the latency duration of the Azure Service Bus topic request. + /// The telemetry context that provides more insights on the Azure Service Bus topic request. + /// Thrown when the or the is null. + /// + /// Thrown when the , , or the is blank. + /// + public static void LogServiceBusTopicRequest( + this ILogger logger, + string serviceBusNamespace, + string topicName, + string subscriptionName, + string operationName, + bool isSuccessful, + DependencyMeasurement measurement, + Dictionary context = null) + { + Guard.NotNull(logger, nameof(logger), "Requires an logger instance to track telemetry"); + Guard.NotNullOrWhitespace(serviceBusNamespace, nameof(serviceBusNamespace), "Requires an Azure Service Bus namespace to track the topic request"); + Guard.NotNullOrWhitespace(topicName, nameof(topicName), "Requires an Azure Service Bus topic name to track the topic request"); + Guard.NotNullOrWhitespace(subscriptionName, nameof(subscriptionName), "Requires an Azure Service Bus subscription name on the to track the topic request"); + Guard.NotNull(measurement, nameof(measurement), "Requires an instance to measure the Azure Service Bus topic request process latency duration"); + + // TODO: the dependency date from the measurement instance is not used when tracking the request. + + LogServiceBusTopicRequest(logger, serviceBusNamespace, topicName, subscriptionName, operationName, isSuccessful, measurement.Elapsed, measurement.StartTime, context); + } + + /// + /// Logs an Azure Service Bus topic request. + /// + /// The logger instance to track the telemetry. + /// The namespace where the Azure Service Bus topic is registered. (Will be combined with ). + /// The namespace suffix (i.e. '.servicebus.windows.net' or '*.servicebus.cloudapi.de') where the Azure Service Bus topic is registered (Will be combined with ). + /// The name of the Azure Service Bus topic. + /// The name of the subscription on the Azure Service Bus topic. + /// The optional logical name that can be used to identify the operation that consumes the message. + /// The indication whether or not the Azure Service Bus topic request was successfully processed. + /// The duration it took to process the Azure Service Bus topic request. + /// The time when the request was received. + /// The telemetry context that provides more insights on the Azure Service Bus topic request. + /// Thrown when the is null. + /// + /// Thrown when the , , , or the is blank. + /// + /// Thrown when the is a negative time range. + public static void LogServiceBusTopicRequestWithSuffix( + this ILogger logger, + string serviceBusNamespace, + string serviceBusNamespaceSuffix, + string topicName, + string subscriptionName, + string operationName, + bool isSuccessful, + TimeSpan duration, + DateTimeOffset startTime, + Dictionary context = null) + { + Guard.NotNull(logger, nameof(logger), "Requires an logger instance to track telemetry"); + Guard.NotNullOrWhitespace(serviceBusNamespace, nameof(serviceBusNamespace), "Requires an Azure Service Bus namespace to track the topic request"); + Guard.NotNullOrWhitespace(serviceBusNamespaceSuffix, nameof(serviceBusNamespaceSuffix), "Requires an Azure Service Bus namespace suffix to track the topic request"); + Guard.NotNullOrWhitespace(topicName, nameof(topicName), "Requires an Azure Service Bus topic name to track the topic request"); + Guard.NotNullOrWhitespace(subscriptionName, nameof(subscriptionName), "Requires an Azure Service Bus subscription name on the to track the topic request"); + Guard.NotLessThan(duration, TimeSpan.Zero, nameof(duration), "Requires a positive time duration of the Azure Service Bus topic request operation"); + + context = context ?? new Dictionary(); + context[ContextProperties.RequestTracking.ServiceBus.Topic.SubscriptionName] = subscriptionName; + + LogServiceBusRequestWithSuffix(logger, serviceBusNamespace, serviceBusNamespaceSuffix, topicName, operationName, isSuccessful, duration, startTime, ServiceBusEntityType.Topic, context); + } + + /// + /// Logs an Azure Service Bus topic request. + /// + /// The logger instance to track the telemetry. + /// The namespace prefix where the Azure Service Bus topic is registered (use Azure public cloud). + /// The name of the Azure Service Bus topic. + /// The name of the subscription on the Azure Service Bus topic. + /// The optional logical name that can be used to identify the operation that consumes the message. + /// The indication whether or not the Azure Service Bus topic request was successfully processed. + /// The duration it took to process the Azure Service Bus topic request. + /// The time when the request was received. + /// The telemetry context that provides more insights on the Azure Service Bus topic request. + /// Thrown when the is null. + /// Thrown when the , or the is blank. + /// Thrown when the is a negative time range. + public static void LogServiceBusTopicRequest( + this ILogger logger, + string serviceBusNamespace, + string topicName, + string subscriptionName, + string operationName, + bool isSuccessful, + TimeSpan duration, + DateTimeOffset startTime, + Dictionary context = null) + { + Guard.NotNull(logger, nameof(logger), "Requires an logger instance to track telemetry"); + Guard.NotNullOrWhitespace(serviceBusNamespace, nameof(serviceBusNamespace), "Requires an Azure Service Bus namespace to track the topic request"); + Guard.NotNullOrWhitespace(topicName, nameof(topicName), "Requires an Azure Service Bus topic name to track the topic request"); + Guard.NotNullOrWhitespace(subscriptionName, nameof(subscriptionName), "Requires an Azure Service Bus subscription name on the to track the topic request"); + Guard.NotLessThan(duration, TimeSpan.Zero, nameof(duration), "Requires a positive time duration of the Azure Service Bus topic request operation"); + + context = context ?? new Dictionary(); + context[ContextProperties.RequestTracking.ServiceBus.Topic.SubscriptionName] = subscriptionName; + + LogServiceBusRequest(logger, serviceBusNamespace, topicName, operationName, isSuccessful, duration, startTime, ServiceBusEntityType.Topic, context); + } + + /// + /// Logs an Azure Service Bus queue request. + /// + /// The logger instance to track the telemetry. + /// The namespace where the Azure Service Bus queue is registered. (Will be combined with ). + /// The namespace suffix (i.e. '.servicebus.windows.net' or '*.servicebus.cloudapi.de') where the Azure Service Bus queue is registered (Will be combined with ). + /// The name of the Azure Service Bus queue. + /// The optional logical name that can be used to identify the operation that consumes the message. + /// The indication whether or not the Azure Service Bus queue request was successfully processed. + /// The instance to measure the latency duration of the Azure Service Bus queue request. + /// The telemetry context that provides more insights on the Azure Service Bus queue request. + /// Thrown when the or the is null. + /// Thrown when the or is blank. + public static void LogServiceBusQueueRequestWithSuffix( + this ILogger logger, + string serviceBusNamespace, + string serviceBusNamespaceSuffix, + string queueName, + string operationName, + bool isSuccessful, + DependencyMeasurement measurement, + Dictionary context = null) + { + Guard.NotNull(logger, nameof(logger), "Requires an logger instance to track telemetry"); + Guard.NotNullOrWhitespace(serviceBusNamespace, nameof(serviceBusNamespace), "Requires an Azure Service Bus namespace to track the queue request"); + Guard.NotNullOrWhitespace(serviceBusNamespaceSuffix, nameof(serviceBusNamespaceSuffix), "Requires an Azure Service Bus namespace suffix to track the queue request"); + Guard.NotNullOrWhitespace(queueName, nameof(queueName), "Requires an Azure Service Bus queue name to track the queue request"); + Guard.NotNull(measurement, nameof(measurement), "Requires an instance to measure the Azure Service Bus queue request process latency duration"); + + // TODO: the dependency date from the measurement instance is not used when tracking the request. + + LogServiceBusQueueRequestWithSuffix(logger, serviceBusNamespace, serviceBusNamespaceSuffix, queueName, operationName, isSuccessful, measurement.Elapsed, measurement.StartTime, context); + } + + /// + /// Logs an Azure Service Bus queue request. + /// + /// The logger instance to track the telemetry. + /// The namespace prefix where the Azure Service Bus queue is registered (use Azure public cloud). + /// The name of the Azure Service Bus queue. + /// The optional logical name that can be used to identify the operation that consumes the message. + /// The indication whether or not the Azure Service Bus queue request was successfully processed. + /// The instance to measure the latency duration of the Azure Service Bus queue request. + /// The telemetry context that provides more insights on the Azure Service Bus queue request. + /// Thrown when the or the is null. + /// Thrown when the or is blank. + public static void LogServiceBusQueueRequest( + this ILogger logger, + string serviceBusNamespace, + string queueName, + string operationName, + bool isSuccessful, + DependencyMeasurement measurement, + Dictionary context = null) + { + Guard.NotNull(logger, nameof(logger), "Requires an logger instance to track telemetry"); + Guard.NotNullOrWhitespace(serviceBusNamespace, nameof(serviceBusNamespace), "Requires an Azure Service Bus namespace to track the queue request"); + Guard.NotNullOrWhitespace(queueName, nameof(queueName), "Requires an Azure Service Bus queue name to track the queue request"); + Guard.NotNull(measurement, nameof(measurement), "Requires an instance to measure the Azure Service Bus queue request process latency duration"); + + // TODO: the dependency date from the measurement instance is not used when tracking the request. + + LogServiceBusQueueRequest(logger, serviceBusNamespace, queueName, operationName, isSuccessful, measurement.Elapsed, measurement.StartTime, context); + } + + /// + /// Logs an Azure Service Bus queue request. + /// + /// The logger instance to track the telemetry. + /// The namespace where the Azure Service Bus queue is registered. (Will be combined with ). + /// The namespace suffix (i.e. '.servicebus.windows.net' or '*.servicebus.cloudapi.de') where the Azure Service Bus queue is registered (Will be combined with ). + /// The name of the Azure Service Bus queue. + /// The optional logical name that can be used to identify the operation that consumes the message. + /// The indication whether or not the Azure Service Bus queue request was successfully processed. + /// The duration it took to process the Azure Service Bus queue request. + /// The time when the request was received. + /// The telemetry context that provides more insights on the Azure Service Bus queue request. + /// Thrown when the is null. + /// Thrown when the or is blank. + /// Thrown when the is a negative time range. + public static void LogServiceBusQueueRequestWithSuffix( + this ILogger logger, + string serviceBusNamespace, + string serviceBusNamespaceSuffix, + string queueName, + string operationName, + bool isSuccessful, + TimeSpan duration, + DateTimeOffset startTime, + Dictionary context = null) + { + Guard.NotNull(logger, nameof(logger), "Requires an logger instance to track telemetry"); + Guard.NotNullOrWhitespace(serviceBusNamespace, nameof(serviceBusNamespace), "Requires an Azure Service Bus namespace to track the queue request"); + Guard.NotNullOrWhitespace(serviceBusNamespaceSuffix, nameof(serviceBusNamespaceSuffix), "Requires an Azure Service Bus namespace suffix to track the queue request"); + Guard.NotNullOrWhitespace(queueName, nameof(queueName), "Requires an Azure Service Bus queue name to track the queue request"); + Guard.NotLessThan(duration, TimeSpan.Zero, nameof(duration), "Requires a positive time duration of the Azure Service Bus queue request operation"); + + LogServiceBusRequestWithSuffix(logger, serviceBusNamespace, serviceBusNamespaceSuffix, queueName, operationName, isSuccessful, duration, startTime, ServiceBusEntityType.Queue, context); + } + + /// + /// Logs an Azure Service Bus queue request. + /// + /// The logger instance to track the telemetry. + /// The namespace prefix where the Azure Service Bus queue is registered (use Azure public cloud). + /// The name of the Azure Service Bus queue. + /// The optional logical name that can be used to identify the operation that consumes the message. + /// The indication whether or not the Azure Service Bus queue request was successfully processed. + /// The duration it took to process the Azure Service Bus queue request. + /// The time when the request was received. + /// The telemetry context that provides more insights on the Azure Service Bus queue request. + /// Thrown when the is null. + /// Thrown when the or is blank. + /// Thrown when the is a negative time range. + public static void LogServiceBusQueueRequest( + this ILogger logger, + string serviceBusNamespace, + string queueName, + string operationName, + bool isSuccessful, + TimeSpan duration, + DateTimeOffset startTime, + Dictionary context = null) + { + Guard.NotNull(logger, nameof(logger), "Requires an logger instance to track telemetry"); + Guard.NotNullOrWhitespace(serviceBusNamespace, nameof(serviceBusNamespace), "Requires an Azure Service Bus namespace to track the queue request"); + Guard.NotNullOrWhitespace(queueName, nameof(queueName), "Requires an Azure Service Bus queue name to track the queue request"); + Guard.NotLessThan(duration, TimeSpan.Zero, nameof(duration), "Requires a positive time duration of the Azure Service Bus queue request operation"); + + LogServiceBusRequest(logger, serviceBusNamespace, queueName, operationName, isSuccessful, duration, startTime, ServiceBusEntityType.Queue, context); + } + + /// + /// Logs an Azure Service Bus request. + /// + /// The logger instance to track the telemetry. + /// The namespace where the Azure Service Bus queue is registered. (Will be combined with ). + /// The namespace suffix (i.e. '.servicebus.windows.net' or '*.servicebus.cloudapi.de') where the Azure Service Bus queue is registered (Will be combined with ). + /// The name of the Azure Service Bus entity. + /// The optional logical name that can be used to identify the operation that consumes the message. + /// The indication whether or not the Azure Service Bus request was successfully processed. + /// The instance to measure the latency duration of the Azure Service Bus queue request. + /// The type of the Azure Service Bus entity. + /// The telemetry context that provides more insights on the Azure Service Bus request. + /// Thrown when the or the is null. + /// Thrown when the or is blank. + public static void LogServiceBusRequestWithSuffix( + this ILogger logger, + string serviceBusNamespace, + string serviceBusNamespaceSuffix, + string entityName, + string operationName, + bool isSuccessful, + DependencyMeasurement measurement, + ServiceBusEntityType entityType, + Dictionary context = null) + { + Guard.NotNull(logger, nameof(logger), "Requires an logger instance to track telemetry"); + Guard.NotNullOrWhitespace(serviceBusNamespace, nameof(serviceBusNamespace), "Requires an Azure Service Bus namespace to track the queue request"); + Guard.NotNullOrWhitespace(serviceBusNamespaceSuffix, nameof(serviceBusNamespaceSuffix), "Requires an Azure Service Bus namespace suffix to track the queue request"); + Guard.NotNullOrWhitespace(entityName, nameof(entityName), "Requires an Azure Service Bus name to track the request"); + Guard.NotNull(measurement, nameof(measurement), "Requires an instance to measure the Azure Service Bus request process latency duration"); + + // TODO: the dependency date from the measurement instance is not used when tracking the request. + + LogServiceBusRequestWithSuffix(logger, serviceBusNamespace, serviceBusNamespaceSuffix, entityName, operationName, isSuccessful, measurement.Elapsed, measurement.StartTime, entityType, context); + } + + /// + /// Logs an Azure Service Bus request. + /// + /// The logger instance to track the telemetry. + /// The namespace prefix where the Azure Service Bus is registered (use Azure public cloud). + /// The name of the Azure Service Bus entity. + /// The optional logical name that can be used to identify the operation that consumes the message. + /// The indication whether or not the Azure Service Bus request was successfully processed. + /// The instance to measure the latency duration of the Azure Service Bus queue request. + /// The type of the Azure Service Bus entity. + /// The telemetry context that provides more insights on the Azure Service Bus request. + /// Thrown when the or the is null. + /// Thrown when the or is blank. + public static void LogServiceBusRequest( + this ILogger logger, + string serviceBusNamespace, + string entityName, + string operationName, + bool isSuccessful, + DependencyMeasurement measurement, + ServiceBusEntityType entityType, + Dictionary context = null) + { + Guard.NotNull(logger, nameof(logger), "Requires an logger instance to track telemetry"); + Guard.NotNullOrWhitespace(serviceBusNamespace, nameof(serviceBusNamespace), "Requires an Azure Service Bus namespace to track the queue request"); + Guard.NotNullOrWhitespace(entityName, nameof(entityName), "Requires an Azure Service Bus name to track the request"); + Guard.NotNull(measurement, nameof(measurement), "Requires an instance to measure the Azure Service Bus request process latency duration"); + + // TODO: the dependency date from the measurement instance is not used when tracking the request. + + LogServiceBusRequest(logger, serviceBusNamespace, entityName, operationName, isSuccessful, measurement.Elapsed, measurement.StartTime, entityType, context); + } + + /// + /// Logs an Azure Service Bus request. + /// + /// The logger instance to track the telemetry. + /// The namespace where the Azure Service Bus is registered. (Will be combined with ). + /// The namespace suffix (i.e. '.servicebus.windows.net' or '*.servicebus.cloudapi.de') where the Azure Service Bus is registered (Will be combined with ). + /// The name of the Azure Service Bus entity. + /// The optional logical name that can be used to identify the operation that consumes the message. + /// The indication whether or not the Azure Service Bus request was successfully processed. + /// The duration it took to process the Azure Service Bus request. + /// The time when the request was received. + /// The type of the Azure Service Bus entity. + /// The telemetry context that provides more insights on the Azure Service Bus request. + /// Thrown when the is null. + /// Thrown when the or is blank. + /// Thrown when the is a negative time range. + public static void LogServiceBusRequestWithSuffix( + this ILogger logger, + string serviceBusNamespace, + string serviceBusNamespaceSuffix, + string entityName, + string operationName, + bool isSuccessful, + TimeSpan duration, + DateTimeOffset startTime, + ServiceBusEntityType entityType, + Dictionary context = null) + { + Guard.NotNull(logger, nameof(logger), "Requires an logger instance to track telemetry"); + Guard.NotNullOrWhitespace(serviceBusNamespace, nameof(serviceBusNamespace), "Requires an Azure Service Bus namespace to track the request"); + Guard.NotNullOrWhitespace(serviceBusNamespaceSuffix, nameof(serviceBusNamespaceSuffix), "Requires an Azure Service Bus namespace suffix to track the request"); + Guard.NotNullOrWhitespace(entityName, nameof(entityName), "Requires an Azure Service Bus name to track the request"); + Guard.NotLessThan(duration, TimeSpan.Zero, nameof(duration), "Requires a positive time duration of the Azure Service Bus request operation"); + + LogServiceBusRequest(logger, serviceBusNamespace + serviceBusNamespaceSuffix, entityName, operationName, isSuccessful, duration, startTime, entityType, context); + } + + /// + /// Logs an Azure Service Bus request. + /// + /// The logger instance to track the telemetry. + /// The namespace prefix where the Azure Service Bus is registered (use Azure public cloud). + /// The name of the Azure Service Bus entity. + /// The optional logical name that can be used to identify the operation that consumes the message. + /// The indication whether or not the Azure Service Bus request was successfully processed. + /// The duration it took to process the Azure Service Bus request. + /// The time when the request was received. + /// The type of the Azure Service Bus entity. + /// The telemetry context that provides more insights on the Azure Service Bus request. + /// Thrown when the is null. + /// Thrown when the or is blank. + /// Thrown when the is a negative time range. + public static void LogServiceBusRequest( + this ILogger logger, + string serviceBusNamespace, + string entityName, + string operationName, + bool isSuccessful, + TimeSpan duration, + DateTimeOffset startTime, + ServiceBusEntityType entityType, + Dictionary context = null) + { + Guard.NotNull(logger, nameof(logger), "Requires an logger instance to track telemetry"); + Guard.NotNullOrWhitespace(serviceBusNamespace, nameof(serviceBusNamespace), "Requires an Azure Service Bus namespace to track the request"); + Guard.NotNullOrWhitespace(entityName, nameof(entityName), "Requires an Azure Service Bus name to track the request"); + Guard.NotLessThan(duration, TimeSpan.Zero, nameof(duration), "Requires a positive time duration of the Azure Service Bus request operation"); + + if (string.IsNullOrWhiteSpace(operationName)) + { + operationName = ContextProperties.RequestTracking.ServiceBus.DefaultOperationName; + } + + context = context ?? new Dictionary(); + context[ContextProperties.RequestTracking.ServiceBus.Endpoint] = serviceBusNamespace; + context[ContextProperties.RequestTracking.ServiceBus.EntityName] = entityName; + context[ContextProperties.RequestTracking.ServiceBus.EntityType] = entityType; + + logger.LogWarning(RequestFormat, RequestLogEntry.CreateForServiceBus(operationName, isSuccessful, duration, startTime, context)); + } + /// /// Logs a dependency. /// @@ -233,7 +671,7 @@ public static void LogDependency( /// /// Thrown when the is blank. public static void LogDependency( - this ILogger logger, + this ILogger logger, string dependencyType, object dependencyData, bool isSuccessful, @@ -298,7 +736,7 @@ public static void LogDependency( /// Thrown when the is blank. /// Thrown when the is a negative time range. public static void LogDependency( - this ILogger logger, + this ILogger logger, string dependencyType, object dependencyData, bool isSuccessful, diff --git a/src/Arcus.Observability.Telemetry.Core/Logging/RequestLogEntry.cs b/src/Arcus.Observability.Telemetry.Core/Logging/RequestLogEntry.cs index ac2a858f..ea6e5865 100644 --- a/src/Arcus.Observability.Telemetry.Core/Logging/RequestLogEntry.cs +++ b/src/Arcus.Observability.Telemetry.Core/Logging/RequestLogEntry.cs @@ -34,8 +34,14 @@ public RequestLogEntry( string uri, int statusCode, TimeSpan duration, - IDictionary context) : this (method, host, uri, $"{method} {uri}", statusCode, duration, context) - { } + IDictionary context) + : this(method, host, uri, operationName: $"{method} {uri}", statusCode, duration, context) + { + Guard.For(() => host?.Contains(" ") is true, "Requires a HTTP request host name without whitespace"); + Guard.NotLessThan(statusCode, 100, nameof(statusCode), "Requires a HTTP response status code that's within the 100-599 range to track a HTTP request"); + Guard.NotGreaterThan(statusCode, 599, nameof(statusCode), "Requires a HTTP response status code that's within the 100-599 range to track a HTTP request"); + Guard.NotLessThan(duration, TimeSpan.Zero, nameof(duration), "Requires a positive time duration of the request operation"); + } /// /// Initializes a new instance of the class. @@ -64,24 +70,74 @@ public RequestLogEntry( int statusCode, TimeSpan duration, IDictionary context) + : this(method, host, uri, operationName, statusCode, sourceSystem: RequestSourceSystem.Http, duration, requestTime: DateTimeOffset.UtcNow.ToString(FormatSpecifiers.InvariantTimestampFormat), context) { - Guard.For(() => host?.Contains(" ") == true, "Requires a HTTP request host name without whitespace"); + Guard.For(() => host?.Contains(" ") is true, "Requires a HTTP request host name without whitespace"); Guard.NotNullOrWhitespace(operationName, nameof(operationName), "Requires an operation name that is not null or whitespace"); Guard.NotLessThan(statusCode, 100, nameof(statusCode), "Requires a HTTP response status code that's within the 100-599 range to track a HTTP request"); Guard.NotGreaterThan(statusCode, 599, nameof(statusCode), "Requires a HTTP response status code that's within the 100-599 range to track a HTTP request"); Guard.NotLessThan(duration, TimeSpan.Zero, nameof(duration), "Requires a positive time duration of the request operation"); - + } + + private RequestLogEntry( + string method, + string host, + string uri, + string operationName, + int statusCode, + RequestSourceSystem sourceSystem, + TimeSpan duration, + string requestTime, + IDictionary context) + { + Guard.For(() => host?.Contains(" ") is true, "Requires a HTTP request host name without whitespace"); + Guard.NotNullOrWhitespace(operationName, nameof(operationName), "Requires a non-blank operation name"); + Guard.NotLessThan(statusCode, 100, nameof(statusCode), "Requires a HTTP response status code that's within the 100-599 range to track a HTTP request"); + Guard.NotGreaterThan(statusCode, 599, nameof(statusCode), "Requires a HTTP response status code that's within the 100-599 range to track a HTTP request"); + Guard.NotLessThan(duration, TimeSpan.Zero, nameof(duration), "Requires a positive time duration of the request operation"); + RequestMethod = method; RequestHost = host; RequestUri = uri; ResponseStatusCode = statusCode; RequestDuration = duration; OperationName = operationName; - RequestTime = DateTimeOffset.UtcNow.ToString(FormatSpecifiers.InvariantTimestampFormat); + SourceSystem = sourceSystem; + RequestTime = requestTime; Context = context; Context[ContextProperties.General.TelemetryType] = TelemetryType.Request; } + /// + /// Creates an instance for Azure Service Bus requests. + /// + /// The name of the operation of the request. + /// The indication whether or not the Azure Service Bus request was successfully processed. + /// The duration it took to process the Azure Service Bus request. + /// The time when the request was received. + /// The telemetry context that provides more insights on the Azure Service Bus request. + /// Thrown when the is a negative time range. + public static RequestLogEntry CreateForServiceBus( + string operationName, + bool isSuccessful, + TimeSpan duration, + DateTimeOffset startTime, + IDictionary context) + { + Guard.NotLessThan(duration, TimeSpan.Zero, nameof(duration), "Requires a positive time duration of the request operation"); + + return new RequestLogEntry( + method: "", + host: "", + uri: "", + operationName, + statusCode: isSuccessful ? 200 : 500, + sourceSystem: RequestSourceSystem.AzureServiceBus, + duration, + startTime.ToString(FormatSpecifiers.InvariantTimestampFormat), + context); + } + /// /// Gets the HTTP method of the request. /// @@ -113,7 +169,12 @@ public RequestLogEntry( public string RequestTime { get; } /// - /// Gets the name of the operation. + /// Gets the type of source system from where the request came from. + /// + public RequestSourceSystem SourceSystem { get; set; } + + /// + /// Gets the name of the operation of the source system from where the request came from. /// public string OperationName { get; } @@ -128,8 +189,17 @@ public RequestLogEntry( /// A string that represents the current object. public override string ToString() { - string contextFormatted = $"{{{String.Join("; ", Context.Select(item => $"[{item.Key}, {item.Value}]"))}}}"; - return $"{RequestMethod} {RequestHost}/{RequestUri} completed with {ResponseStatusCode} in {RequestDuration} at {RequestTime} - (Context: {contextFormatted})"; + var contextFormatted = $"{{{String.Join("; ", Context.Select(item => $"[{item.Key}, {item.Value}]"))}}}"; + switch (SourceSystem) + { + case RequestSourceSystem.AzureServiceBus: + bool isSuccessful = ResponseStatusCode is 200; + return $"Azure Service Bus from {OperationName} completed in {RequestDuration} at {RequestTime} - (IsSuccessful: {isSuccessful}, Context: {contextFormatted})"; + case RequestSourceSystem.Http: + return $"{RequestMethod} {RequestHost}/{RequestUri} from {OperationName} completed with {ResponseStatusCode} in {RequestDuration} at {RequestTime} - (Context: {contextFormatted})"; + default: + throw new ArgumentOutOfRangeException(nameof(SourceSystem), SourceSystem, "Unknown request source system"); + } } } } diff --git a/src/Arcus.Observability.Telemetry.Core/Logging/RequestSourceSystem.cs b/src/Arcus.Observability.Telemetry.Core/Logging/RequestSourceSystem.cs new file mode 100644 index 00000000..65a13bb6 --- /dev/null +++ b/src/Arcus.Observability.Telemetry.Core/Logging/RequestSourceSystem.cs @@ -0,0 +1,18 @@ +namespace Arcus.Observability.Telemetry.Core.Logging +{ + /// + /// Represents the system from where the request came from. + /// + public enum RequestSourceSystem + { + /// + /// Specifies that the request-source is an Azure Service Bus queue or topic. + /// + AzureServiceBus, + + /// + /// Specifies that the request-source is a HTTP request + /// + Http + } +} \ No newline at end of file diff --git a/src/Arcus.Observability.Telemetry.Serilog.Sinks.ApplicationInsights/Extensions/LogEventPropertyExtensions.cs b/src/Arcus.Observability.Telemetry.Serilog.Sinks.ApplicationInsights/Extensions/LogEventPropertyExtensions.cs index 228b202a..eaf60171 100644 --- a/src/Arcus.Observability.Telemetry.Serilog.Sinks.ApplicationInsights/Extensions/LogEventPropertyExtensions.cs +++ b/src/Arcus.Observability.Telemetry.Serilog.Sinks.ApplicationInsights/Extensions/LogEventPropertyExtensions.cs @@ -144,14 +144,18 @@ internal static double GetAsDouble(this IReadOnlyList properti Guard.NotNullOrWhitespace(propertyKey, nameof(propertyKey), "Requires a non-blank property key to retrieve a Serilog event property as a Double representation"); LogEventProperty logEventPropertyValue = properties.FirstOrDefault(prop => prop.Name == propertyKey); - string rawDouble = logEventPropertyValue?.Value?.ToDecentString(); - - if (rawDouble is null) + if (logEventPropertyValue is null) { return double.NaN; } - return double.Parse(rawDouble, CultureInfo.InvariantCulture); + if (logEventPropertyValue.Value is ScalarValue scalarValue + && scalarValue.Value is double value) + { + return value; + } + + return double.NaN; } /// diff --git a/src/Arcus.Observability.Tests.Integration/Arcus.Observability.Tests.Integration.csproj b/src/Arcus.Observability.Tests.Integration/Arcus.Observability.Tests.Integration.csproj index 70837e3b..78aace53 100644 --- a/src/Arcus.Observability.Tests.Integration/Arcus.Observability.Tests.Integration.csproj +++ b/src/Arcus.Observability.Tests.Integration/Arcus.Observability.Tests.Integration.csproj @@ -1,7 +1,7 @@  - net6.0;netcoreapp3.1 + net6.0 false diff --git a/src/Arcus.Observability.Tests.Integration/Serilog/TelemetryTypeFilterTests.cs b/src/Arcus.Observability.Tests.Integration/Serilog/TelemetryTypeFilterTests.cs index 509e8b88..2e205136 100644 --- a/src/Arcus.Observability.Tests.Integration/Serilog/TelemetryTypeFilterTests.cs +++ b/src/Arcus.Observability.Tests.Integration/Serilog/TelemetryTypeFilterTests.cs @@ -337,8 +337,8 @@ public void LogRequestMessage_WithTelemetryTypeFilter_DoesNotFilterOutEntry(Tele { // Arrange var statusCode = _bogusGenerator.PickRandom(); - var path = $"/{_bogusGenerator.Name.FirstName().ToLower()}"; - string host = _bogusGenerator.Name.FirstName().ToLower(); + var path = $"/{_bogusGenerator.Lorem.Word().ToLower()}"; + string host = _bogusGenerator.Lorem.Word().ToLower(); HttpMethod method = HttpMethod.Head; var request = new HttpRequestMessage(method, new Uri("https://" + host + path)); var response = new HttpResponseMessage(statusCode); diff --git a/src/Arcus.Observability.Tests.Unit/Extensions/TestLoggerExtensions.cs b/src/Arcus.Observability.Tests.Unit/Extensions/TestLoggerExtensions.cs index 45b379a1..e14ab4e4 100644 --- a/src/Arcus.Observability.Tests.Unit/Extensions/TestLoggerExtensions.cs +++ b/src/Arcus.Observability.Tests.Unit/Extensions/TestLoggerExtensions.cs @@ -62,6 +62,36 @@ public static DependencyLogEntry GetMessageAsDependency(this TestLogger logger) context); } + /// + /// Gets the written message to the as a strongly-typed Request. + /// + /// The test logger where a test message is written to. + /// + /// The strongly-typed Request containing the telemetry information. + /// + /// Thrown when the is null. + /// Thrown when no test message was written to the test . + public static RequestLogEntry GetMessageAsRequest(this TestLogger logger) + { + Guard.NotNull(logger, nameof(logger), "Requires a test logger to retrieve the written log message"); + + if (logger.WrittenMessage is null) + { + throw new InvalidOperationException( + "Cannot parse the written message as a telemetry request because no log message was written to this test logger"); + } + const string pattern = @"Azure Service Bus from (?[\w\s]+) completed in (?(\d{1}\.)?\d{2}:\d{2}:\d{2}\.\d{7}) at (?\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}.\d{7} \+\d{2}:\d{2}) - \(IsSuccessful: (?(True|False)), Context: \{(?((\[[\w\-]+, \w+\])(; \[[\w\-]+, [\w\.]+\])*))\}\)$"; + Match match = Regex.Match(logger.WrittenMessage, pattern); + + string operationName = match.GetGroupValue("operationname"); + TimeSpan duration = match.GetGroupValueAsTimeSpan("duration"); + DateTimeOffset startTime = match.GetGroupValueAsDateTimeOffset("timestamp"); + bool isSuccessful = match.GetGroupValueAsBool("issuccessful"); + IDictionary context = match.GetGroupValueAsTelemetryContext("context", TelemetryType.Request); + + return RequestLogEntry.CreateForServiceBus(operationName, isSuccessful, duration, startTime, context); + } + /// /// Gets the written message to the as a strongly-typed Metric. /// diff --git a/src/Arcus.Observability.Tests.Unit/Telemetry/ILoggerExtensionsTests.cs b/src/Arcus.Observability.Tests.Unit/Telemetry/ILoggerExtensionsTests.cs index 3aaf301b..8e5a9bc5 100644 --- a/src/Arcus.Observability.Tests.Unit/Telemetry/ILoggerExtensionsTests.cs +++ b/src/Arcus.Observability.Tests.Unit/Telemetry/ILoggerExtensionsTests.cs @@ -6,6 +6,7 @@ using Arcus.Observability.Telemetry.Core.Logging; using Bogus; using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc.ViewFeatures; using Microsoft.Extensions.Logging; using Moq; using Xunit; @@ -34,7 +35,7 @@ public void LogMetric_ValidArguments_Succeeds() Assert.Equal(metricValue, metric.MetricValue); Assert.Collection(metric.Context, item => { - Assert.Equal(nameof(TelemetryType), item.Key); + Assert.Equal(ContextProperties.General.TelemetryType, item.Key); Assert.Equal(TelemetryType.Metrics, item.Value); }); } @@ -58,7 +59,7 @@ public void LogMetric_ValidArgumentsWithTimestamp_Succeeds() Assert.Equal(timestamp.ToString(FormatSpecifiers.InvariantTimestampFormat), metric.Timestamp); Assert.Collection(metric.Context, item => { - Assert.Equal(nameof(TelemetryType), item.Key); + Assert.Equal(ContextProperties.General.TelemetryType, item.Key); Assert.Equal(TelemetryType.Metrics, item.Value); }); } @@ -92,7 +93,7 @@ public void LogMetric_ValidArgumentsWithCustomContext_Succeeds() }, item => { - Assert.Equal(nameof(TelemetryType), item.Key); + Assert.Equal(ContextProperties.General.TelemetryType, item.Key); Assert.Equal(TelemetryType.Metrics, item.Value); }); } @@ -2246,6 +2247,1758 @@ public void LogRequestMessage_OutsideResponseStatusCodeRange_Fails() Assert.ThrowsAny(() => logger.LogRequest(request, statusCode, duration)); } + [Fact] + public void LogServiceBusTopicRequestWithMeasurement_ValidArguments_Succeeds() + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string topicName = _bogusGenerator.Lorem.Word(); + string subscriptionName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + var measurement = DependencyMeasurement.Start(); + measurement.Dispose(); + + // Act + logger.LogServiceBusTopicRequest(serviceBusNamespace, topicName, subscriptionName, operationName, isSuccessful, measurement, context); + + // Assert + RequestLogEntry entry = logger.GetMessageAsRequest(); + Assert.Equal(operationName, entry.OperationName); + Assert.Equal(isSuccessful, entry.ResponseStatusCode is 200); + Assert.Equal(measurement.Elapsed, entry.RequestDuration); + Assert.Equal(measurement.StartTime.ToString(FormatSpecifiers.InvariantTimestampFormat), entry.RequestTime); + Assert.Contains(new KeyValuePair(key, value), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Endpoint, serviceBusNamespace), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityName, topicName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityType, ServiceBusEntityType.Topic.ToString()), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Topic.SubscriptionName, subscriptionName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.General.TelemetryType, TelemetryType.Request), entry.Context); + } + + [Fact] + public void LogServiceBusTopicRequestWithSuffixWithMeasurement_ValidArguments_Succeeds() + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string topicName = _bogusGenerator.Lorem.Word(); + string subscriptionName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + var measurement = DependencyMeasurement.Start(); + measurement.Dispose(); + + // Act + logger.LogServiceBusTopicRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, topicName, subscriptionName, operationName, isSuccessful, measurement, context); + + // Assert + RequestLogEntry entry = logger.GetMessageAsRequest(); + Assert.Equal(operationName, entry.OperationName); + Assert.Equal(isSuccessful, entry.ResponseStatusCode is 200); + Assert.Equal(measurement.Elapsed, entry.RequestDuration); + Assert.Equal(measurement.StartTime.ToString(FormatSpecifiers.InvariantTimestampFormat), entry.RequestTime); + Assert.Contains(new KeyValuePair(key, value), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Endpoint, serviceBusNamespace + serviceBusNamespaceSuffix), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityName, topicName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityType, ServiceBusEntityType.Topic.ToString()), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Topic.SubscriptionName, subscriptionName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.General.TelemetryType, TelemetryType.Request), entry.Context); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusTopicRequestWithMeasurement_WithoutOperationName_Succeeds(string operationName) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string topicName = _bogusGenerator.Lorem.Word(); + string subscriptionName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + var measurement = DependencyMeasurement.Start(); + measurement.Dispose(); + + // Act + logger.LogServiceBusTopicRequest(serviceBusNamespace, topicName, subscriptionName, operationName, isSuccessful, measurement, context); + + // Assert + RequestLogEntry entry = logger.GetMessageAsRequest(); + Assert.Equal(ContextProperties.RequestTracking.ServiceBus.DefaultOperationName, entry.OperationName); + Assert.Equal(isSuccessful, entry.ResponseStatusCode is 200); + Assert.Equal(measurement.Elapsed, entry.RequestDuration); + Assert.Equal(measurement.StartTime.ToString(FormatSpecifiers.InvariantTimestampFormat), entry.RequestTime); + Assert.Contains(new KeyValuePair(key, value), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Endpoint, serviceBusNamespace), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityName, topicName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityType, ServiceBusEntityType.Topic.ToString()), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Topic.SubscriptionName, subscriptionName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.General.TelemetryType, TelemetryType.Request), entry.Context); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusTopicRequestWithSuffixWithMeasurement_WithoutOperationName_Succeeds(string operationName) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string topicName = _bogusGenerator.Lorem.Word(); + string subscriptionName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + var measurement = DependencyMeasurement.Start(); + measurement.Dispose(); + + // Act + logger.LogServiceBusTopicRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, topicName, subscriptionName, operationName, isSuccessful, measurement, context); + + // Assert + RequestLogEntry entry = logger.GetMessageAsRequest(); + Assert.Equal(ContextProperties.RequestTracking.ServiceBus.DefaultOperationName, entry.OperationName); + Assert.Equal(isSuccessful, entry.ResponseStatusCode is 200); + Assert.Equal(measurement.Elapsed, entry.RequestDuration); + Assert.Equal(measurement.StartTime.ToString(FormatSpecifiers.InvariantTimestampFormat), entry.RequestTime); + Assert.Contains(new KeyValuePair(key, value), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Endpoint, serviceBusNamespace + serviceBusNamespaceSuffix), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityName, topicName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityType, ServiceBusEntityType.Topic.ToString()), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Topic.SubscriptionName, subscriptionName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.General.TelemetryType, TelemetryType.Request), entry.Context); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusTopicRequestWithMeasurement_WithoutServiceBusNamespace_Fails(string serviceBusNamespace) + { + // Arrange + var logger = new TestLogger(); + string topicName = _bogusGenerator.Lorem.Word(); + string subscriptionName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + var measurement = DependencyMeasurement.Start(); + measurement.Dispose(); + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusTopicRequest(serviceBusNamespace, topicName, subscriptionName, operationName, isSuccessful, measurement, context)); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusTopicRequestWithSuffixWithMeasurement_WithoutServiceBusNamespace_Fails(string serviceBusNamespace) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string topicName = _bogusGenerator.Lorem.Word(); + string subcriptionName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + var measurement = DependencyMeasurement.Start(); + measurement.Dispose(); + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusTopicRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, topicName, subcriptionName, operationName, isSuccessful, measurement, context)); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusTopicRequestWithSuffixWithMeasurement_WithoutServiceBusNamespaceSuffix_Fails(string serviceBusNamespaceSuffix) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string topicName = _bogusGenerator.Lorem.Word(); + string subscriptionName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + var measurement = DependencyMeasurement.Start(); + measurement.Dispose(); + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusTopicRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, topicName, subscriptionName, operationName, isSuccessful, measurement, context)); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusTopicRequestWithMeasurement_WithoutTopicName_Fails(string topicName) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string subscriptionName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + var measurement = DependencyMeasurement.Start(); + measurement.Dispose(); + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusTopicRequest(serviceBusNamespace, topicName, subscriptionName, operationName, isSuccessful, measurement, context)); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusTopicRequestWithSuffixWithMeasurement_WithoutTopicName_Fails(string topicName) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string subscriptionName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + var measurement = DependencyMeasurement.Start(); + measurement.Dispose(); + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusTopicRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, topicName, subscriptionName, operationName, isSuccessful, measurement, context)); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusTopicRequestWithMeasurement_WithoutSubscriptionName_Fails(string subscriptionName) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string topicName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + var measurement = DependencyMeasurement.Start(); + measurement.Dispose(); + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusTopicRequest(serviceBusNamespace, topicName, subscriptionName, operationName, isSuccessful, measurement, context)); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusTopicRequestWithSuffixWithMeasurement_WithoutSubscriptionName_Fails(string subscriptionName) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string topicName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + var measurement = DependencyMeasurement.Start(); + measurement.Dispose(); + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusTopicRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, topicName, subscriptionName, operationName, isSuccessful, measurement, context)); + } + + [Fact] + public void LogServiceBusTopicRequestWithMeasurement_WithoutMeasurement_Fails() + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string topicName = _bogusGenerator.Lorem.Word(); + string subscriptionName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act / Assert + Assert.ThrowsAny( + () => logger.LogServiceBusTopicRequest(serviceBusNamespace, topicName, subscriptionName, operationName, isSuccessful, measurement: null, context)); + } + + [Fact] + public void LogServiceBusTopicRequestWithSuffixWithMeasurement_WithoutMeasurement_Fails() + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string topicName = _bogusGenerator.Lorem.Word(); + string subscriptionName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act / Assert + Assert.ThrowsAny( + () => logger.LogServiceBusTopicRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, topicName, subscriptionName, operationName, isSuccessful, measurement: null, context)); + } + + [Fact] + public void LogServiceBusTopicRequest_ValidArguments_Succeeds() + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string topicName = _bogusGenerator.Lorem.Word(); + string subscriptionName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + TimeSpan duration = _bogusGenerator.Date.Timespan(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act + logger.LogServiceBusTopicRequest(serviceBusNamespace, topicName, subscriptionName, operationName, isSuccessful, duration, startTime, context); + + // Assert + RequestLogEntry entry = logger.GetMessageAsRequest(); + Assert.Equal(operationName, entry.OperationName); + Assert.Equal(isSuccessful, entry.ResponseStatusCode is 200); + Assert.Equal(duration, entry.RequestDuration); + Assert.Equal(startTime.ToString(FormatSpecifiers.InvariantTimestampFormat), entry.RequestTime); + Assert.Contains(new KeyValuePair(key, value), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Endpoint, serviceBusNamespace), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityName, topicName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityType, ServiceBusEntityType.Topic.ToString()), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Topic.SubscriptionName, subscriptionName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.General.TelemetryType, TelemetryType.Request), entry.Context); + } + + [Fact] + public void LogServiceBusTopicRequestWithSuffix_ValidArguments_Succeeds() + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string topicName = _bogusGenerator.Lorem.Word(); + string subscriptionName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + TimeSpan duration = _bogusGenerator.Date.Timespan(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act + logger.LogServiceBusTopicRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, topicName, subscriptionName, operationName, isSuccessful, duration, startTime, context); + + // Assert + RequestLogEntry entry = logger.GetMessageAsRequest(); + Assert.Equal(operationName, entry.OperationName); + Assert.Equal(isSuccessful, entry.ResponseStatusCode is 200); + Assert.Equal(duration, entry.RequestDuration); + Assert.Equal(startTime.ToString(FormatSpecifiers.InvariantTimestampFormat), entry.RequestTime); + Assert.Contains(new KeyValuePair(key, value), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Endpoint, serviceBusNamespace + serviceBusNamespaceSuffix), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityName, topicName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityType, ServiceBusEntityType.Topic.ToString()), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Topic.SubscriptionName, subscriptionName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.General.TelemetryType, TelemetryType.Request), entry.Context); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusTopicRequest_WithoutOperationName_Succeeds(string operationName) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string topicName = _bogusGenerator.Lorem.Word(); + string subscriptionName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + TimeSpan duration = _bogusGenerator.Date.Timespan(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act + logger.LogServiceBusTopicRequest(serviceBusNamespace, topicName, subscriptionName, operationName, isSuccessful, duration, startTime, context); + + // Assert + RequestLogEntry entry = logger.GetMessageAsRequest(); + Assert.Equal(ContextProperties.RequestTracking.ServiceBus.DefaultOperationName, entry.OperationName); + Assert.Equal(isSuccessful, entry.ResponseStatusCode is 200); + Assert.Equal(duration, entry.RequestDuration); + Assert.Equal(startTime.ToString(FormatSpecifiers.InvariantTimestampFormat), entry.RequestTime); + Assert.Contains(new KeyValuePair(key, value), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Endpoint, serviceBusNamespace), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityName, topicName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityType, ServiceBusEntityType.Topic.ToString()), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Topic.SubscriptionName, subscriptionName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.General.TelemetryType, TelemetryType.Request), entry.Context); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusTopicRequestWithSuffix_WithoutOperationName_Succeeds(string operationName) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string topicName = _bogusGenerator.Lorem.Word(); + string subscriptionName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + TimeSpan duration = _bogusGenerator.Date.Timespan(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act + logger.LogServiceBusTopicRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, topicName, subscriptionName, operationName, isSuccessful, duration, startTime, context); + + // Assert + RequestLogEntry entry = logger.GetMessageAsRequest(); + Assert.Equal(ContextProperties.RequestTracking.ServiceBus.DefaultOperationName, entry.OperationName); + Assert.Equal(isSuccessful, entry.ResponseStatusCode is 200); + Assert.Equal(duration, entry.RequestDuration); + Assert.Equal(startTime.ToString(FormatSpecifiers.InvariantTimestampFormat), entry.RequestTime); + Assert.Contains(new KeyValuePair(key, value), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Endpoint, serviceBusNamespace + serviceBusNamespaceSuffix), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityName, topicName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityType, ServiceBusEntityType.Topic.ToString()), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Topic.SubscriptionName, subscriptionName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.General.TelemetryType, TelemetryType.Request), entry.Context); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusTopicRequest_WithoutServiceBusNamespace_Fails(string serviceBusNamespace) + { + // Arrange + var logger = new TestLogger(); + string topicName = _bogusGenerator.Lorem.Word(); + string subscriptionName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + TimeSpan duration = _bogusGenerator.Date.Timespan(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusTopicRequest(serviceBusNamespace, topicName, subscriptionName, operationName, isSuccessful, duration, startTime, context)); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusTopicRequestWithSuffix_WithoutServiceBusNamespace_Fails(string serviceBusNamespace) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string topicName = _bogusGenerator.Lorem.Word(); + string subscriptionName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + TimeSpan duration = _bogusGenerator.Date.Timespan(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusTopicRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, topicName, subscriptionName, operationName, isSuccessful, duration, startTime, context)); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusTopicRequestWithSuffix_WithoutServiceBusNamespaceSuffix_Fails(string serviceBusNamespaceSuffix) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string topicName = _bogusGenerator.Lorem.Word(); + string subscriptionName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + TimeSpan duration = _bogusGenerator.Date.Timespan(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusTopicRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, topicName, subscriptionName, operationName, isSuccessful, duration, startTime, context)); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusTopicRequest_WithoutEntityName_Fails(string topicName) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string subscriptionName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + TimeSpan duration = _bogusGenerator.Date.Timespan(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusTopicRequest(serviceBusNamespace, topicName, subscriptionName, operationName, isSuccessful, duration, startTime, context)); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusTopicRequestWithSuffix_WithoutEntityName_Fails(string topicName) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string subscriptionName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + TimeSpan duration = _bogusGenerator.Date.Timespan(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusTopicRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, topicName, subscriptionName, operationName, isSuccessful, duration, startTime, context)); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusTopicRequest_WithoutSubscriptionName_Fails(string subscriptionName) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string topicName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + TimeSpan duration = _bogusGenerator.Date.Timespan(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusTopicRequest(serviceBusNamespace, topicName, subscriptionName, operationName, isSuccessful, duration, startTime, context)); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusTopicRequestWithSuffix_WithoutSubscriptionName_Fails(string subscriptionName) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string topicName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + TimeSpan duration = _bogusGenerator.Date.Timespan(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusTopicRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, topicName, subscriptionName, operationName, isSuccessful, duration, startTime, context)); + } + + [Fact] + public void LogServiceBusTopicRequest_WithNegativeDuration_Fails() + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string topicName = _bogusGenerator.Lorem.Word(); + string subscriptionName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + TimeSpan negativeDuration = _bogusGenerator.Date.Timespan().Negate(); + + // Act / Assert + Assert.ThrowsAny( + () => logger.LogServiceBusTopicRequest(serviceBusNamespace, topicName, subscriptionName, operationName, isSuccessful, negativeDuration, startTime, context)); + } + + [Fact] + public void LogServiceBusTopicRequestWithSuffix_WithNegativeDuration_Fails() + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string topicName = _bogusGenerator.Lorem.Word(); + string subscriptionName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + TimeSpan negativeDuration = _bogusGenerator.Date.Timespan().Negate(); + + // Act / Assert + Assert.ThrowsAny( + () => logger.LogServiceBusTopicRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, topicName, subscriptionName, operationName, isSuccessful, negativeDuration, startTime, context)); + } + + [Fact] + public void LogServiceBusQueueRequestWithMeasurement_ValidArguments_Succeeds() + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string queueName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + var measurement = DependencyMeasurement.Start(); + measurement.Dispose(); + + // Act + logger.LogServiceBusQueueRequest(serviceBusNamespace, queueName, operationName, isSuccessful, measurement, context); + + // Assert + RequestLogEntry entry = logger.GetMessageAsRequest(); + Assert.Equal(operationName, entry.OperationName); + Assert.Equal(isSuccessful, entry.ResponseStatusCode is 200); + Assert.Equal(measurement.Elapsed, entry.RequestDuration); + Assert.Equal(measurement.StartTime.ToString(FormatSpecifiers.InvariantTimestampFormat), entry.RequestTime); + Assert.Contains(new KeyValuePair(key, value), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Endpoint, serviceBusNamespace), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityName, queueName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityType, ServiceBusEntityType.Queue.ToString()), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.General.TelemetryType, TelemetryType.Request), entry.Context); + } + + [Fact] + public void LogServiceBusQueueRequestWithSuffixWithMeasurement_ValidArguments_Succeeds() + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string queueName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + var measurement = DependencyMeasurement.Start(); + measurement.Dispose(); + + // Act + logger.LogServiceBusQueueRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, queueName, operationName, isSuccessful, measurement, context); + + // Assert + RequestLogEntry entry = logger.GetMessageAsRequest(); + Assert.Equal(operationName, entry.OperationName); + Assert.Equal(isSuccessful, entry.ResponseStatusCode is 200); + Assert.Equal(measurement.Elapsed, entry.RequestDuration); + Assert.Equal(measurement.StartTime.ToString(FormatSpecifiers.InvariantTimestampFormat), entry.RequestTime); + Assert.Contains(new KeyValuePair(key, value), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Endpoint, serviceBusNamespace + serviceBusNamespaceSuffix), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityName, queueName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityType, ServiceBusEntityType.Queue.ToString()), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.General.TelemetryType, TelemetryType.Request), entry.Context); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusQueueRequestWithMeasurement_WithoutOperationName_Succeeds(string operationName) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string queueName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + var measurement = DependencyMeasurement.Start(); + measurement.Dispose(); + + // Act + logger.LogServiceBusQueueRequest(serviceBusNamespace, queueName, operationName, isSuccessful, measurement, context); + + // Assert + RequestLogEntry entry = logger.GetMessageAsRequest(); + Assert.Equal(ContextProperties.RequestTracking.ServiceBus.DefaultOperationName, entry.OperationName); + Assert.Equal(isSuccessful, entry.ResponseStatusCode is 200); + Assert.Equal(measurement.Elapsed, entry.RequestDuration); + Assert.Equal(measurement.StartTime.ToString(FormatSpecifiers.InvariantTimestampFormat), entry.RequestTime); + Assert.Contains(new KeyValuePair(key, value), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Endpoint, serviceBusNamespace), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityName, queueName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityType, ServiceBusEntityType.Queue.ToString()), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.General.TelemetryType, TelemetryType.Request), entry.Context); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusQueueRequestWithSuffixWithMeasurement_WithoutOperationName_Succeeds(string operationName) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string queueName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + var measurement = DependencyMeasurement.Start(); + measurement.Dispose(); + + // Act + logger.LogServiceBusQueueRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, queueName, operationName, isSuccessful, measurement, context); + + // Assert + RequestLogEntry entry = logger.GetMessageAsRequest(); + Assert.Equal(ContextProperties.RequestTracking.ServiceBus.DefaultOperationName, entry.OperationName); + Assert.Equal(isSuccessful, entry.ResponseStatusCode is 200); + Assert.Equal(measurement.Elapsed, entry.RequestDuration); + Assert.Equal(measurement.StartTime.ToString(FormatSpecifiers.InvariantTimestampFormat), entry.RequestTime); + Assert.Contains(new KeyValuePair(key, value), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Endpoint, serviceBusNamespace + serviceBusNamespaceSuffix), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityName, queueName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityType, ServiceBusEntityType.Queue.ToString()), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.General.TelemetryType, TelemetryType.Request), entry.Context); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusQueueRequestWithMeasurement_WithoutServiceBusNamespace_Fails(string serviceBusNamespace) + { + // Arrange + var logger = new TestLogger(); + string entityName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + var measurement = DependencyMeasurement.Start(); + measurement.Dispose(); + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusQueueRequest(serviceBusNamespace, entityName, operationName, isSuccessful, measurement, context)); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusQueueRequestWithSuffixWithMeasurement_WithoutServiceBusNamespace_Fails(string serviceBusNamespace) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string entityName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + var measurement = DependencyMeasurement.Start(); + measurement.Dispose(); + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusQueueRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, entityName, operationName, isSuccessful, measurement, context)); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusQueueRequestWithSuffixWithMeasurement_WithoutServiceBusNamespaceSuffix_Fails(string serviceBusNamespaceSuffix) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string entityName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + var measurement = DependencyMeasurement.Start(); + measurement.Dispose(); + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusQueueRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, entityName, operationName, isSuccessful, measurement, context)); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusQueueRequestWithMeasurement_WithoutEntityName_Fails(string entityName) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + var measurement = DependencyMeasurement.Start(); + measurement.Dispose(); + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusQueueRequest(serviceBusNamespace, entityName, operationName, isSuccessful, measurement, context)); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusQueueRequestWithSuffixWithMeasurement_WithoutEntityName_Fails(string entityName) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + var measurement = DependencyMeasurement.Start(); + measurement.Dispose(); + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusQueueRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, entityName, operationName, isSuccessful, measurement, context)); + } + + [Fact] + public void LogServiceBusQueueRequestWithMeasurement_WithoutMeasurement_Fails() + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string entityName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act / Assert + Assert.ThrowsAny( + () => logger.LogServiceBusQueueRequest(serviceBusNamespace, entityName, operationName, isSuccessful, measurement: null, context)); + } + + [Fact] + public void LogServiceBusQueueRequestWithSuffixWithMeasurement_WithoutMeasurement_Fails() + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string entityName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act / Assert + Assert.ThrowsAny( + () => logger.LogServiceBusQueueRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, entityName, operationName, isSuccessful, measurement: null, context)); + } + + [Fact] + public void LogServiceBusQueueRequest_ValidArguments_Succeeds() + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string queueName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + TimeSpan duration = _bogusGenerator.Date.Timespan(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act + logger.LogServiceBusQueueRequest(serviceBusNamespace, queueName, operationName, isSuccessful, duration, startTime, context); + + // Assert + RequestLogEntry entry = logger.GetMessageAsRequest(); + Assert.Equal(operationName, entry.OperationName); + Assert.Equal(isSuccessful, entry.ResponseStatusCode is 200); + Assert.Equal(duration, entry.RequestDuration); + Assert.Equal(startTime.ToString(FormatSpecifiers.InvariantTimestampFormat), entry.RequestTime); + Assert.Contains(new KeyValuePair(key, value), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Endpoint, serviceBusNamespace), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityName, queueName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityType, ServiceBusEntityType.Queue.ToString()), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.General.TelemetryType, TelemetryType.Request), entry.Context); + } + + [Fact] + public void LogServiceBusQueueRequestWithSuffix_ValidArguments_Succeeds() + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string queueName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + TimeSpan duration = _bogusGenerator.Date.Timespan(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act + logger.LogServiceBusQueueRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, queueName, operationName, isSuccessful, duration, startTime, context); + + // Assert + RequestLogEntry entry = logger.GetMessageAsRequest(); + Assert.Equal(operationName, entry.OperationName); + Assert.Equal(isSuccessful, entry.ResponseStatusCode is 200); + Assert.Equal(duration, entry.RequestDuration); + Assert.Equal(startTime.ToString(FormatSpecifiers.InvariantTimestampFormat), entry.RequestTime); + Assert.Contains(new KeyValuePair(key, value), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Endpoint, serviceBusNamespace + serviceBusNamespaceSuffix), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityName, queueName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityType, ServiceBusEntityType.Queue.ToString()), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.General.TelemetryType, TelemetryType.Request), entry.Context); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusQueueRequest_WithoutOperationName_Succeeds(string operationName) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string entityName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + TimeSpan duration = _bogusGenerator.Date.Timespan(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act + logger.LogServiceBusQueueRequest(serviceBusNamespace, entityName, operationName, isSuccessful, duration, startTime, context); + + // Assert + RequestLogEntry entry = logger.GetMessageAsRequest(); + Assert.Equal(ContextProperties.RequestTracking.ServiceBus.DefaultOperationName, entry.OperationName); + Assert.Equal(isSuccessful, entry.ResponseStatusCode is 200); + Assert.Equal(duration, entry.RequestDuration); + Assert.Equal(startTime.ToString(FormatSpecifiers.InvariantTimestampFormat), entry.RequestTime); + Assert.Contains(new KeyValuePair(key, value), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Endpoint, serviceBusNamespace), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityName, entityName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityType, ServiceBusEntityType.Queue.ToString()), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.General.TelemetryType, TelemetryType.Request), entry.Context); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusQueueRequestWithSuffix_WithoutOperationName_Succeeds(string operationName) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string entityName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + TimeSpan duration = _bogusGenerator.Date.Timespan(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act + logger.LogServiceBusQueueRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, entityName, operationName, isSuccessful, duration, startTime, context); + + // Assert + RequestLogEntry entry = logger.GetMessageAsRequest(); + Assert.Equal(ContextProperties.RequestTracking.ServiceBus.DefaultOperationName, entry.OperationName); + Assert.Equal(isSuccessful, entry.ResponseStatusCode is 200); + Assert.Equal(duration, entry.RequestDuration); + Assert.Equal(startTime.ToString(FormatSpecifiers.InvariantTimestampFormat), entry.RequestTime); + Assert.Contains(new KeyValuePair(key, value), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Endpoint, serviceBusNamespace + serviceBusNamespaceSuffix), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityName, entityName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityType, ServiceBusEntityType.Queue.ToString()), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.General.TelemetryType, TelemetryType.Request), entry.Context); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusQueueRequest_WithoutServiceBusNamespace_Fails(string serviceBusNamespace) + { + // Arrange + var logger = new TestLogger(); + string entityName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + TimeSpan duration = _bogusGenerator.Date.Timespan(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusQueueRequest(serviceBusNamespace, entityName, operationName, isSuccessful, duration, startTime, context)); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusQueueRequestWithSuffix_WithoutServiceBusNamespace_Fails(string serviceBusNamespace) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string entityName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + TimeSpan duration = _bogusGenerator.Date.Timespan(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusQueueRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, entityName, operationName, isSuccessful, duration, startTime, context)); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusQueueRequestWithSuffix_WithoutServiceBusNamespaceSuffix_Fails(string serviceBusNamespaceSuffix) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string entityName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + TimeSpan duration = _bogusGenerator.Date.Timespan(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusQueueRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, entityName, operationName, isSuccessful, duration, startTime, context)); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusQueueRequest_WithoutEntityName_Fails(string entityName) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + TimeSpan duration = _bogusGenerator.Date.Timespan(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusQueueRequest(serviceBusNamespace, entityName, operationName, isSuccessful, duration, startTime, context)); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusQueueRequestWithSuffix_WithoutEntityName_Fails(string entityName) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + TimeSpan duration = _bogusGenerator.Date.Timespan(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusQueueRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, entityName, operationName, isSuccessful, duration, startTime, context)); + } + + [Fact] + public void LogServiceBusQueueRequest_WithNegativeDuration_Fails() + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string entityName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + TimeSpan negativeDuration = _bogusGenerator.Date.Timespan().Negate(); + + // Act / Assert + Assert.ThrowsAny( + () => logger.LogServiceBusQueueRequest(serviceBusNamespace, entityName, operationName, isSuccessful, negativeDuration, startTime, context)); + } + + [Fact] + public void LogServiceBusQueueRequestWithSuffix_WithNegativeDuration_Fails() + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string serviceBusNamespaceSufix = _bogusGenerator.Lorem.Word(); + string entityName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + TimeSpan negativeDuration = _bogusGenerator.Date.Timespan().Negate(); + + // Act / Assert + Assert.ThrowsAny( + () => logger.LogServiceBusQueueRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSufix, entityName, operationName, isSuccessful, negativeDuration, startTime, context)); + } + + [Fact] + public void LogServiceBusRequestWithMeasurement_ValidArguments_Succeeds() + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string topicName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + var entityType = _bogusGenerator.Random.Enum(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + var measurement = DependencyMeasurement.Start(); + measurement.Dispose(); + + // Act + logger.LogServiceBusRequest(serviceBusNamespace, topicName, operationName, isSuccessful, measurement, entityType, context); + + // Assert + RequestLogEntry entry = logger.GetMessageAsRequest(); + Assert.Equal(operationName, entry.OperationName); + Assert.Equal(isSuccessful, entry.ResponseStatusCode is 200); + Assert.Equal(measurement.Elapsed, entry.RequestDuration); + Assert.Equal(measurement.StartTime.ToString(FormatSpecifiers.InvariantTimestampFormat), entry.RequestTime); + Assert.Contains(new KeyValuePair(key, value), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Endpoint, serviceBusNamespace), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityName, topicName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityType, entityType.ToString()), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.General.TelemetryType, TelemetryType.Request), entry.Context); + } + + [Fact] + public void LogServiceBusRequestWithSuffixWithMeasurement_ValidArguments_Succeeds() + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string topicName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + var entityType = _bogusGenerator.Random.Enum(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + var measurement = DependencyMeasurement.Start(); + measurement.Dispose(); + + // Act + logger.LogServiceBusRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, topicName, operationName, isSuccessful, measurement, entityType, context); + + // Assert + RequestLogEntry entry = logger.GetMessageAsRequest(); + Assert.Equal(operationName, entry.OperationName); + Assert.Equal(isSuccessful, entry.ResponseStatusCode is 200); + Assert.Equal(measurement.Elapsed, entry.RequestDuration); + Assert.Equal(measurement.StartTime.ToString(FormatSpecifiers.InvariantTimestampFormat), entry.RequestTime); + Assert.Contains(new KeyValuePair(key, value), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Endpoint, serviceBusNamespace + serviceBusNamespaceSuffix), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityName, topicName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityType, entityType.ToString()), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.General.TelemetryType, TelemetryType.Request), entry.Context); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusRequestWithMeasurement_WithoutOperationName_Succeeds(string operationName) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string topicName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + var entityType = _bogusGenerator.Random.Enum(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + var measurement = DependencyMeasurement.Start(); + measurement.Dispose(); + + // Act + logger.LogServiceBusRequest(serviceBusNamespace, topicName, operationName, isSuccessful, measurement, entityType, context); + + // Assert + RequestLogEntry entry = logger.GetMessageAsRequest(); + Assert.Equal(ContextProperties.RequestTracking.ServiceBus.DefaultOperationName, entry.OperationName); + Assert.Equal(isSuccessful, entry.ResponseStatusCode is 200); + Assert.Equal(measurement.Elapsed, entry.RequestDuration); + Assert.Equal(measurement.StartTime.ToString(FormatSpecifiers.InvariantTimestampFormat), entry.RequestTime); + Assert.Contains(new KeyValuePair(key, value), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Endpoint, serviceBusNamespace), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityName, topicName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityType, entityType.ToString()), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.General.TelemetryType, TelemetryType.Request), entry.Context); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusRequestWithSuffixWithMeasurement_WithoutOperationName_Succeeds(string operationName) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string topicName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + var entityType = _bogusGenerator.Random.Enum(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + var measurement = DependencyMeasurement.Start(); + measurement.Dispose(); + + // Act + logger.LogServiceBusRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, topicName, operationName, isSuccessful, measurement, entityType, context); + + // Assert + RequestLogEntry entry = logger.GetMessageAsRequest(); + Assert.Equal(ContextProperties.RequestTracking.ServiceBus.DefaultOperationName, entry.OperationName); + Assert.Equal(isSuccessful, entry.ResponseStatusCode is 200); + Assert.Equal(measurement.Elapsed, entry.RequestDuration); + Assert.Equal(measurement.StartTime.ToString(FormatSpecifiers.InvariantTimestampFormat), entry.RequestTime); + Assert.Contains(new KeyValuePair(key, value), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Endpoint, serviceBusNamespace + serviceBusNamespaceSuffix), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityName, topicName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityType, entityType.ToString()), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.General.TelemetryType, TelemetryType.Request), entry.Context); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusRequestWithMeasurement_WithoutServiceBusNamespace_Fails(string serviceBusNamespace) + { + // Arrange + var logger = new TestLogger(); + string entityName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + var entryType = _bogusGenerator.Random.Enum(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + var measurement = DependencyMeasurement.Start(); + measurement.Dispose(); + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusRequest(serviceBusNamespace, entityName, operationName, isSuccessful, measurement, entryType, context)); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusRequestWithSuffixWithMeasurement_WithoutServiceBusNamespace_Fails(string serviceBusNamespace) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string entityName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + var entryType = _bogusGenerator.Random.Enum(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + var measurement = DependencyMeasurement.Start(); + measurement.Dispose(); + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, entityName, operationName, isSuccessful, measurement, entryType, context)); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusRequestWithSuffixWithMeasurement_WithoutServiceBusNamespaceSuffix_Fails(string serviceBusNamespaceSuffix) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string entityName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + var entryType = _bogusGenerator.Random.Enum(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + var measurement = DependencyMeasurement.Start(); + measurement.Dispose(); + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, entityName, operationName, isSuccessful, measurement, entryType, context)); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusRequestWithMeasurement_WithoutEntityName_Fails(string entityName) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + var entryType = _bogusGenerator.Random.Enum(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + var measurement = DependencyMeasurement.Start(); + measurement.Dispose(); + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusRequest(serviceBusNamespace, entityName, operationName, isSuccessful, measurement, entryType, context)); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusRequestWithSuffixWithMeasurement_WithoutEntityName_Fails(string entityName) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + var entryType = _bogusGenerator.Random.Enum(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + var measurement = DependencyMeasurement.Start(); + measurement.Dispose(); + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, entityName, operationName, isSuccessful, measurement, entryType, context)); + } + + [Fact] + public void LogServiceBusRequestWithMeasurement_WithoutMeasurement_Fails() + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string entityName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + var entityType = _bogusGenerator.Random.Enum(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act / Assert + Assert.ThrowsAny( + () => logger.LogServiceBusRequest(serviceBusNamespace, entityName, operationName, isSuccessful, measurement: null, entityType, context)); + } + + [Fact] + public void LogServiceBusRequestWithSuffixWithMeasurement_WithoutMeasurement_Fails() + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string entityName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + var entityType = _bogusGenerator.Random.Enum(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act / Assert + Assert.ThrowsAny( + () => logger.LogServiceBusRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, entityName, operationName, isSuccessful, measurement: null, entityType, context)); + } + + [Fact] + public void LogServiceBusRequest_ValidArguments_Succeeds() + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string entityName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + TimeSpan duration = _bogusGenerator.Date.Timespan(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + var entityType = _bogusGenerator.Random.Enum(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act + logger.LogServiceBusRequest(serviceBusNamespace, entityName, operationName, isSuccessful, duration, startTime, entityType, context); + + // Assert + RequestLogEntry entry = logger.GetMessageAsRequest(); + Assert.Equal(operationName, entry.OperationName); + Assert.Equal(isSuccessful, entry.ResponseStatusCode is 200); + Assert.Equal(duration, entry.RequestDuration); + Assert.Equal(startTime.ToString(FormatSpecifiers.InvariantTimestampFormat), entry.RequestTime); + Assert.Contains(new KeyValuePair(key, value), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Endpoint, serviceBusNamespace), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityName, entityName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityType, entityType.ToString()), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.General.TelemetryType, TelemetryType.Request), entry.Context); + } + + [Fact] + public void LogServiceBusRequestWithSuffix_ValidArguments_Succeeds() + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string entityName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + TimeSpan duration = _bogusGenerator.Date.Timespan(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + var entityType = _bogusGenerator.Random.Enum(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act + logger.LogServiceBusRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, entityName, operationName, isSuccessful, duration, startTime, entityType, context); + + // Assert + RequestLogEntry entry = logger.GetMessageAsRequest(); + Assert.Equal(operationName, entry.OperationName); + Assert.Equal(isSuccessful, entry.ResponseStatusCode is 200); + Assert.Equal(duration, entry.RequestDuration); + Assert.Equal(startTime.ToString(FormatSpecifiers.InvariantTimestampFormat), entry.RequestTime); + Assert.Contains(new KeyValuePair(key, value), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Endpoint, serviceBusNamespace + serviceBusNamespaceSuffix), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityName, entityName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityType, entityType.ToString()), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.General.TelemetryType, TelemetryType.Request), entry.Context); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusRequest_WithoutOperationName_Succeeds(string operationName) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string entityName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + TimeSpan duration = _bogusGenerator.Date.Timespan(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + var entityType = _bogusGenerator.Random.Enum(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act + logger.LogServiceBusRequest(serviceBusNamespace, entityName, operationName, isSuccessful, duration, startTime, entityType, context); + + // Assert + RequestLogEntry entry = logger.GetMessageAsRequest(); + Assert.Equal(ContextProperties.RequestTracking.ServiceBus.DefaultOperationName, entry.OperationName); + Assert.Equal(isSuccessful, entry.ResponseStatusCode is 200); + Assert.Equal(duration, entry.RequestDuration); + Assert.Equal(startTime.ToString(FormatSpecifiers.InvariantTimestampFormat), entry.RequestTime); + Assert.Contains(new KeyValuePair(key, value), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Endpoint, serviceBusNamespace), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityName, entityName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityType, entityType.ToString()), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.General.TelemetryType, TelemetryType.Request), entry.Context); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusRequestWithSuffix_WithoutOperationName_Succeeds(string operationName) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string entityName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + TimeSpan duration = _bogusGenerator.Date.Timespan(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + var entityType = _bogusGenerator.Random.Enum(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act + logger.LogServiceBusRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, entityName, operationName, isSuccessful, duration, startTime, entityType, context); + + // Assert + RequestLogEntry entry = logger.GetMessageAsRequest(); + Assert.Equal(ContextProperties.RequestTracking.ServiceBus.DefaultOperationName, entry.OperationName); + Assert.Equal(isSuccessful, entry.ResponseStatusCode is 200); + Assert.Equal(duration, entry.RequestDuration); + Assert.Equal(startTime.ToString(FormatSpecifiers.InvariantTimestampFormat), entry.RequestTime); + Assert.Contains(new KeyValuePair(key, value), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.Endpoint, serviceBusNamespace + serviceBusNamespaceSuffix), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityName, entityName), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.RequestTracking.ServiceBus.EntityType, entityType.ToString()), entry.Context); + Assert.Contains(new KeyValuePair(ContextProperties.General.TelemetryType, TelemetryType.Request), entry.Context); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusRequest_WithoutServiceBusNamespace_Fails(string serviceBusNamespace) + { + // Arrange + var logger = new TestLogger(); + string entityName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + TimeSpan duration = _bogusGenerator.Date.Timespan(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + var entryType = _bogusGenerator.Random.Enum(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusRequest(serviceBusNamespace, entityName, operationName, isSuccessful, duration, startTime, entryType, context)); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusRequestWithSuffix_WithoutServiceBusNamespace_Fails(string serviceBusNamespace) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string entityName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + TimeSpan duration = _bogusGenerator.Date.Timespan(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + var entryType = _bogusGenerator.Random.Enum(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, entityName, operationName, isSuccessful, duration, startTime, entryType, context)); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusRequestWithSuffix_WithoutServiceBusNamespaceSuffix_Fails(string serviceBusNamespaceSuffix) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string entityName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + TimeSpan duration = _bogusGenerator.Date.Timespan(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + var entryType = _bogusGenerator.Random.Enum(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, entityName, operationName, isSuccessful, duration, startTime, entryType, context)); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusRequest_WithoutEntityName_Fails(string entityName) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + TimeSpan duration = _bogusGenerator.Date.Timespan(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + var entryType = _bogusGenerator.Random.Enum(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusRequest(serviceBusNamespace, entityName, operationName, isSuccessful, duration, startTime, entryType, context)); + } + + [Theory] + [ClassData(typeof(Blanks))] + public void LogServiceBusRequestWithSuffix_WithoutEntityName_Fails(string entityName) + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + TimeSpan duration = _bogusGenerator.Date.Timespan(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + var entryType = _bogusGenerator.Random.Enum(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + // Act + Assert.ThrowsAny( + () => logger.LogServiceBusRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, entityName, operationName, isSuccessful, duration, startTime, entryType, context)); + } + + [Fact] + public void LogServiceBusRequest_WithNegativeDuration_Fails() + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string entityName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + var entryType = _bogusGenerator.Random.Enum(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + TimeSpan negativeDuration = _bogusGenerator.Date.Timespan().Negate(); + + // Act / Assert + Assert.ThrowsAny( + () => logger.LogServiceBusRequest(serviceBusNamespace, entityName, operationName, isSuccessful, negativeDuration, startTime, entryType, context)); + } + + [Fact] + public void LogServiceBusRequestWithSuffix_WithNegativeDuration_Fails() + { + // Arrange + var logger = new TestLogger(); + string serviceBusNamespace = _bogusGenerator.Lorem.Word(); + string serviceBusNamespaceSuffix = _bogusGenerator.Lorem.Word(); + string entityName = _bogusGenerator.Lorem.Word(); + string operationName = _bogusGenerator.Lorem.Word(); + bool isSuccessful = _bogusGenerator.Random.Bool(); + DateTimeOffset startTime = _bogusGenerator.Date.RecentOffset(); + var entryType = _bogusGenerator.Random.Enum(); + string key = _bogusGenerator.Lorem.Word(); + string value = _bogusGenerator.Lorem.Word(); + var context = new Dictionary { [key] = value }; + + TimeSpan negativeDuration = _bogusGenerator.Date.Timespan().Negate(); + + // Act / Assert + Assert.ThrowsAny( + () => logger.LogServiceBusRequestWithSuffix(serviceBusNamespace, serviceBusNamespaceSuffix, entityName, operationName, isSuccessful, negativeDuration, startTime, entryType, context)); + } + [Fact] public void LogSecurityEvent_ValidArguments_Succeeds() {