/// <summary> /// Receives a <see cref="BrokeredMessage"/> from the event bus. /// </summary> protected virtual void ReceiveEvent(BrokeredMessage message) { DateTimeOffset startedAt = DateTimeOffset.UtcNow; Stopwatch mainStopWatch = Stopwatch.StartNew(); string responseCode = "200"; // Null means it was skipped bool? wasSuccessfull = true; string telemetryName = string.Format("Cqrs/Handle/Event/{0}", message.MessageId); ISingleSignOnToken authenticationToken = null; Guid? guidAuthenticationToken = null; string stringAuthenticationToken = null; int?intAuthenticationToken = null; IDictionary <string, string> telemetryProperties = ExtractTelemetryProperties(message, "Azure/Servicebus"); TelemetryHelper.TrackMetric("Cqrs/Handle/Event", CurrentHandles++, telemetryProperties); var brokeredMessageRenewCancellationTokenSource = new CancellationTokenSource(); try { Logger.LogDebug(string.Format("An event message arrived with the id '{0}'.", message.MessageId)); string messageBody = message.GetBody <string>(); IEvent <TAuthenticationToken> @event = AzureBusHelper.ReceiveEvent(messageBody, ReceiveEvent, string.Format("id '{0}'", message.MessageId), ExtractSignature(message), SigningTokenConfigurationKey, () => { wasSuccessfull = null; telemetryName = string.Format("Cqrs/Handle/Event/Skipped/{0}", message.MessageId); responseCode = "204"; // Remove message from queue try { message.Complete(); } catch (MessageLockLostException exception) { throw new MessageLockLostException(string.Format("The lock supplied for the skipped event message '{0}' is invalid.", message.MessageId), exception); } Logger.LogDebug(string.Format("An event message arrived with the id '{0}' but processing was skipped due to event settings.", message.MessageId)); TelemetryHelper.TrackEvent("Cqrs/Handle/Event/Skipped", telemetryProperties); }, () => { AzureBusHelper.RefreshLock(brokeredMessageRenewCancellationTokenSource, message, "event"); } ); if (wasSuccessfull != null) { if (@event != null) { telemetryName = string.Format("{0}/{1}/{2}", @event.GetType().FullName, @event.GetIdentity(), @event.Id); authenticationToken = @event.AuthenticationToken as ISingleSignOnToken; if (AuthenticationTokenIsGuid) { guidAuthenticationToken = @event.AuthenticationToken as Guid?; } if (AuthenticationTokenIsString) { stringAuthenticationToken = @event.AuthenticationToken as string; } if (AuthenticationTokenIsInt) { intAuthenticationToken = @event.AuthenticationToken as int?; } var telemeteredMessage = @event as ITelemeteredMessage; if (telemeteredMessage != null) { telemetryName = telemeteredMessage.TelemetryName; } telemetryName = string.Format("Cqrs/Handle/Event/{0}", telemetryName); } // Remove message from queue try { message.Complete(); } catch (MessageLockLostException exception) { throw new MessageLockLostException(string.Format("The lock supplied for event '{0}' of type {1} is invalid.", @event.Id, @event.GetType().Name), exception); } } Logger.LogDebug(string.Format("An event message arrived and was processed with the id '{0}'.", message.MessageId)); IList <IEvent <TAuthenticationToken> > events; if (EventWaits.TryGetValue(@event.CorrelationId, out events)) { events.Add(@event); } } catch (MessageLockLostException exception) { IDictionary <string, string> subTelemetryProperties = new Dictionary <string, string>(telemetryProperties); subTelemetryProperties.Add("TimeTaken", mainStopWatch.Elapsed.ToString()); TelemetryHelper.TrackException(exception, null, subTelemetryProperties); if (ThrowExceptionOnReceiverMessageLockLostExceptionDuringComplete) { Logger.LogError(exception.Message, exception: exception); // Indicates a problem, unlock message in queue message.Abandon(); wasSuccessfull = false; } else { Logger.LogWarning(exception.Message, exception: exception); try { message.DeadLetter("LockLostButHandled", "The message was handled but the lock was lost."); } catch (Exception) { // Oh well, move on. message.Abandon(); } } responseCode = "599"; } catch (UnAuthorisedMessageReceivedException exception) { TelemetryHelper.TrackException(exception, null, telemetryProperties); // Indicates a problem, unlock message in queue Logger.LogError(string.Format("An event message arrived with the id '{0}' but was not authorised.", message.MessageId), exception: exception); message.DeadLetter("UnAuthorisedMessageReceivedException", exception.Message); wasSuccessfull = false; responseCode = "401"; telemetryProperties.Add("ExceptionType", exception.GetType().FullName); telemetryProperties.Add("ExceptionMessage", exception.Message); } catch (NoHandlersRegisteredException exception) { TelemetryHelper.TrackException(exception, null, telemetryProperties); // Indicates a problem, unlock message in queue Logger.LogError(string.Format("An event message arrived with the id '{0}' but no handlers were found to process it.", message.MessageId), exception: exception); message.DeadLetter("NoHandlersRegisteredException", exception.Message); wasSuccessfull = false; responseCode = "501"; telemetryProperties.Add("ExceptionType", exception.GetType().FullName); telemetryProperties.Add("ExceptionMessage", exception.Message); } catch (NoHandlerRegisteredException exception) { TelemetryHelper.TrackException(exception, null, telemetryProperties); // Indicates a problem, unlock message in queue Logger.LogError(string.Format("An event message arrived with the id '{0}' but no handler was found to process it.", message.MessageId), exception: exception); message.DeadLetter("NoHandlerRegisteredException", exception.Message); wasSuccessfull = false; responseCode = "501"; telemetryProperties.Add("ExceptionType", exception.GetType().FullName); telemetryProperties.Add("ExceptionMessage", exception.Message); } catch (Exception exception) { TelemetryHelper.TrackException(exception, null, telemetryProperties); // Indicates a problem, unlock message in queue Logger.LogError(string.Format("An event message arrived with the id '{0}' but failed to be process.", message.MessageId), exception: exception); message.Abandon(); wasSuccessfull = false; responseCode = "500"; telemetryProperties.Add("ExceptionType", exception.GetType().FullName); telemetryProperties.Add("ExceptionMessage", exception.Message); } finally { // Cancel the lock of renewing the task brokeredMessageRenewCancellationTokenSource.Cancel(); TelemetryHelper.TrackMetric("Cqrs/Handle/Event", CurrentHandles--, telemetryProperties); mainStopWatch.Stop(); if (guidAuthenticationToken != null) { TelemetryHelper.TrackRequest ( telemetryName, guidAuthenticationToken, startedAt, mainStopWatch.Elapsed, responseCode, wasSuccessfull == null || wasSuccessfull.Value, telemetryProperties ); } else if (intAuthenticationToken != null) { TelemetryHelper.TrackRequest ( telemetryName, intAuthenticationToken, startedAt, mainStopWatch.Elapsed, responseCode, wasSuccessfull == null || wasSuccessfull.Value, telemetryProperties ); } else if (stringAuthenticationToken != null) { TelemetryHelper.TrackRequest ( telemetryName, stringAuthenticationToken, startedAt, mainStopWatch.Elapsed, responseCode, wasSuccessfull == null || wasSuccessfull.Value, telemetryProperties ); } else { TelemetryHelper.TrackRequest ( telemetryName, authenticationToken, startedAt, mainStopWatch.Elapsed, responseCode, wasSuccessfull == null || wasSuccessfull.Value, telemetryProperties ); } TelemetryHelper.Flush(); } }
/// <summary> /// Sends the provided <paramref name="command"></paramref> and waits for an event of <typeparamref name="TEvent"/> or exits if the specified timeout is expired. /// </summary> /// <param name="command">The <typeparamref name="TCommand"/> to send.</param> /// <param name="condition">A delegate to be executed over and over until it returns the <typeparamref name="TEvent"/> that is desired, return null to keep trying.</param> /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see cref="F:System.Threading.Timeout.Infinite"/> (-1) to wait indefinitely.</param> /// <param name="eventReceiver">If provided, is the <see cref="IEventReceiver{TAuthenticationToken}" /> that the event is expected to be returned on.</param> public virtual TEvent PublishAndWait <TCommand, TEvent>(TCommand command, Func <IEnumerable <IEvent <TAuthenticationToken> >, TEvent> condition, int millisecondsTimeout, IEventReceiver <TAuthenticationToken> eventReceiver = null) where TCommand : ICommand <TAuthenticationToken> { DateTimeOffset startedAt = DateTimeOffset.UtcNow; Stopwatch mainStopWatch = Stopwatch.StartNew(); string responseCode = "200"; bool wasSuccessfull = false; IDictionary <string, string> telemetryProperties = new Dictionary <string, string> { { "Type", "Azure/EventHub" } }; string telemetryName = string.Format("{0}/{1}", command.GetType().FullName, command.Id); var telemeteredCommand = command as ITelemeteredMessage; if (telemeteredCommand != null) { telemetryName = telemeteredCommand.TelemetryName; } telemetryName = string.Format("Command/{0}", telemetryName); TEvent result; try { if (eventReceiver != null) { throw new NotSupportedException("Specifying a different event receiver is not yet supported."); } if (!AzureBusHelper.PrepareAndValidateCommand(command, "Azure-EventHub")) { return((TEvent)(object)null); } result = (TEvent)(object)null; EventWaits.Add(command.CorrelationId, new List <IEvent <TAuthenticationToken> >()); try { EventHubPublisher.Send(new EventData(Encoding.UTF8.GetBytes(MessageSerialiser.SerialiseCommand(command)))); } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish a command.", exception: exception, metaData: new Dictionary <string, object> { { "Command", command } }); throw; } Logger.LogInfo(string.Format("A command was sent of type {0}.", command.GetType().FullName)); wasSuccessfull = true; } finally { mainStopWatch.Stop(); TelemetryHelper.TrackDependency("Azure/EventHub/CommandBus", "Command", telemetryName, null, startedAt, mainStopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } SpinWait.SpinUntil(() => { IList <IEvent <TAuthenticationToken> > events = EventWaits[command.CorrelationId]; result = condition(events); return(result != null); }, millisecondsTimeout, 1000); TelemetryHelper.TrackDependency("Azure/EventHub/CommandBus", "Command/AndWait", string.Format("Command/AndWait{0}", telemetryName.Substring(7)), null, startedAt, mainStopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); return(result); }
/// <summary> /// Publishes the provided <paramref name="events"/> on the event bus. /// </summary> public virtual void Publish <TEvent>(IEnumerable <TEvent> events) where TEvent : IEvent <TAuthenticationToken> { IList <TEvent> sourceEvents = events.ToList(); DateTimeOffset startedAt = DateTimeOffset.UtcNow; Stopwatch mainStopWatch = Stopwatch.StartNew(); string responseCode = "200"; bool wasSuccessfull = false; IDictionary <string, string> telemetryProperties = new Dictionary <string, string> { { "Type", "Azure/EventHub" } }; string telemetryName = "Events"; string telemetryNames = string.Empty; foreach (TEvent @event in sourceEvents) { string subTelemetryName = string.Format("{0}/{1}", @event.GetType().FullName, @event.Id); var telemeteredEvent = @event as ITelemeteredMessage; if (telemeteredEvent != null) { subTelemetryName = telemeteredEvent.TelemetryName; } telemetryNames = string.Format("{0}{1},", telemetryNames, subTelemetryName); } if (telemetryNames.Length > 0) { telemetryNames = telemetryNames.Substring(0, telemetryNames.Length - 1); } telemetryProperties.Add("Events", telemetryNames); try { IList <string> sourceEventMessages = new List <string>(); IList <Microsoft.ServiceBus.Messaging.EventData> brokeredMessages = new List <Microsoft.ServiceBus.Messaging.EventData>(sourceEvents.Count); foreach (TEvent @event in sourceEvents) { if (!AzureBusHelper.PrepareAndValidateEvent(@event, "Azure-EventHub")) { continue; } var brokeredMessage = new Microsoft.ServiceBus.Messaging.EventData(Encoding.UTF8.GetBytes(MessageSerialiser.SerialiseEvent(@event))); brokeredMessage.Properties.Add("Type", @event.GetType().FullName); brokeredMessages.Add(brokeredMessage); sourceEventMessages.Add(string.Format("A command was sent of type {0}.", @event.GetType().FullName)); } try { EventHubPublisher.SendBatch(brokeredMessages); wasSuccessfull = true; } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the event being sent was too large.", exception: exception, metaData: new Dictionary <string, object> { { "Events", sourceEvents } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish a event.", exception: exception, metaData: new Dictionary <string, object> { { "Events", sourceEvents } }); throw; } foreach (string message in sourceEventMessages) { Logger.LogInfo(message); } wasSuccessfull = true; } finally { mainStopWatch.Stop(); TelemetryHelper.TrackDependency("Azure/EventHub/EventBus", "Event", telemetryName, null, startedAt, mainStopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } }
private void InitializeTelemetry() { // Fetch the session synchronously on the UI thread; if this doesn't happen before we try using this on // the background thread then the VS process will deadlock. TelemetryHelper.Initialize(); }
protected virtual void ReceiveCommand(BrokeredMessage message) { DateTimeOffset startedAt = DateTimeOffset.UtcNow; Stopwatch mainStopWatch = Stopwatch.StartNew(); string responseCode = "200"; // Null means it was skipped bool? wasSuccessfull = true; string telemetryName = string.Format("Cqrs/Handle/Command/{0}", message.MessageId); ISingleSignOnToken authenticationToken = null; IDictionary <string, string> telemetryProperties = new Dictionary <string, string> { { "Type", "Azure/Servicebus" } }; object value; if (message.Properties.TryGetValue("Type", out value)) { telemetryProperties.Add("MessageType", value.ToString()); } TelemetryHelper.TrackMetric("Cqrs/Handle/Command", CurrentHandles++, telemetryProperties); var brokeredMessageRenewCancellationTokenSource = new CancellationTokenSource(); try { Logger.LogDebug(string.Format("A command message arrived with the id '{0}'.", message.MessageId)); string messageBody = message.GetBody <string>(); ICommand <TAuthenticationToken> command = AzureBusHelper.ReceiveCommand(messageBody, ReceiveCommand, string.Format("id '{0}'", message.MessageId), () => { wasSuccessfull = null; telemetryName = string.Format("Cqrs/Handle/Command/Skipped/{0}", message.MessageId); responseCode = "204"; // Remove message from queue try { message.Complete(); } catch (MessageLockLostException exception) { throw new MessageLockLostException(string.Format("The lock supplied for the skipped command message '{0}' is invalid.", message.MessageId), exception); } Logger.LogDebug(string.Format("A command message arrived with the id '{0}' but processing was skipped due to event settings.", message.MessageId)); TelemetryHelper.TrackEvent("Cqrs/Handle/Command/Skipped", telemetryProperties); }, () => { AzureBusHelper.RefreshLock(brokeredMessageRenewCancellationTokenSource, message, "command"); } ); if (wasSuccessfull != null) { if (command != null) { telemetryName = string.Format("{0}/{1}", command.GetType().FullName, command.Id); authenticationToken = command.AuthenticationToken as ISingleSignOnToken; var telemeteredMessage = command as ITelemeteredMessage; if (telemeteredMessage != null) { telemetryName = telemeteredMessage.TelemetryName; } telemetryName = string.Format("Cqrs/Handle/Command/{0}", telemetryName); } // Remove message from queue try { message.Complete(); } catch (MessageLockLostException exception) { throw new MessageLockLostException(string.Format("The lock supplied for command '{0}' of type {1} is invalid.", command.Id, command.GetType().Name), exception); } } Logger.LogDebug(string.Format("A command message arrived and was processed with the id '{0}'.", message.MessageId)); } catch (MessageLockLostException exception) { IDictionary <string, string> subTelemetryProperties = new Dictionary <string, string>(telemetryProperties); subTelemetryProperties.Add("TimeTaken", mainStopWatch.Elapsed.ToString()); TelemetryHelper.TrackException(exception, null, subTelemetryProperties); if (ThrowExceptionOnReceiverMessageLockLostExceptionDuringComplete) { Logger.LogError(exception.Message, exception: exception); // Indicates a problem, unlock message in queue message.Abandon(); wasSuccessfull = false; } else { Logger.LogWarning(exception.Message, exception: exception); try { message.DeadLetter("LockLostButHandled", "The message was handled but the lock was lost."); } catch (Exception) { // Oh well, move on. message.Abandon(); } } responseCode = "599"; } catch (Exception exception) { TelemetryHelper.TrackException(exception, null, telemetryProperties); // Indicates a problem, unlock message in queue Logger.LogError(string.Format("A command message arrived with the id '{0}' but failed to be process.", message.MessageId), exception: exception); message.Abandon(); wasSuccessfull = false; responseCode = "500"; telemetryProperties.Add("ExceptionType", exception.GetType().FullName); telemetryProperties.Add("ExceptionMessage", exception.Message); } finally { // Cancel the lock of renewing the task brokeredMessageRenewCancellationTokenSource.Cancel(); TelemetryHelper.TrackMetric("Cqrs/Handle/Command", CurrentHandles--, telemetryProperties); mainStopWatch.Stop(); TelemetryHelper.TrackRequest ( telemetryName, authenticationToken, startedAt, mainStopWatch.Elapsed, responseCode, wasSuccessfull == null || wasSuccessfull.Value, telemetryProperties ); TelemetryHelper.Flush(); } }
/// <summary> /// Using a <see cref="Task"/>, clears all dead letters from the topic and subscription of the /// provided <paramref name="topicName"/> and <paramref name="topicSubscriptionName"/>. /// </summary> /// <param name="topicName">The name of the topic.</param> /// <param name="topicSubscriptionName">The name of the subscription.</param> /// <returns></returns> protected virtual CancellationTokenSource CleanUpDeadLetters(string topicName, string topicSubscriptionName) { var brokeredMessageRenewCancellationTokenSource = new CancellationTokenSource(); IDictionary <string, string> telemetryProperties = new Dictionary <string, string> { { "Type", "Azure/Servicebus" } }; int lockIssues = 0; #if NET452 Action <BrokeredMessage, IMessage> leaveDeadlLetterInQueue = (deadLetterBrokeredMessage, deadLetterMessage) => #endif #if NETSTANDARD2_0 Action <IMessageReceiver, BrokeredMessage, IMessage> leaveDeadlLetterInQueue = (client, deadLetterBrokeredMessage, deadLetterMessage) => #endif { // Remove message from queue try { #if NET452 deadLetterBrokeredMessage.Abandon(); #endif #if NETSTANDARD2_0 client.AbandonAsync(deadLetterBrokeredMessage.SystemProperties.LockToken).Wait(1500); #endif lockIssues = 0; } catch (MessageLockLostException) { lockIssues++; Logger.LogWarning(string.Format("The lock supplied for abandon for the skipped dead-letter message '{0}' is invalid.", deadLetterBrokeredMessage.MessageId)); } Logger.LogDebug(string.Format("A dead-letter message of type {0} arrived with the id '{1}' but left in the queue due to settings.", deadLetterMessage.GetType().FullName, deadLetterBrokeredMessage.MessageId)); }; #if NET452 Action <BrokeredMessage> removeDeadlLetterFromQueue = (deadLetterBrokeredMessage) => #endif #if NETSTANDARD2_0 Action <IMessageReceiver, BrokeredMessage> removeDeadlLetterFromQueue = (client, deadLetterBrokeredMessage) => #endif { // Remove message from queue try { #if NET452 deadLetterBrokeredMessage.Complete(); #endif #if NETSTANDARD2_0 client.CompleteAsync(deadLetterBrokeredMessage.SystemProperties.LockToken).Wait(1500); #endif lockIssues = 0; } catch (MessageLockLostException) { lockIssues++; Logger.LogWarning(string.Format("The lock supplied for complete for the skipped dead-letter message '{0}' is invalid.", deadLetterBrokeredMessage.MessageId)); } Logger.LogDebug(string.Format("A dead-letter message arrived with the id '{0}' but was removed as processing was skipped due to settings.", deadLetterBrokeredMessage.MessageId)); }; Task.Factory.StartNewSafely(() => { int loop = 0; while (!brokeredMessageRenewCancellationTokenSource.Token.IsCancellationRequested) { lockIssues = 0; IEnumerable <BrokeredMessage> brokeredMessages; #if NET452 MessagingFactory factory = MessagingFactory.CreateFromConnectionString(ConnectionString); string deadLetterPath = SubscriptionClient.FormatDeadLetterPath(topicName, topicSubscriptionName); MessageReceiver client = factory.CreateMessageReceiver(deadLetterPath, ReceiveMode.PeekLock); brokeredMessages = client.ReceiveBatch(1000); #endif #if NETSTANDARD2_0 string deadLetterPath = EntityNameHelper.FormatDeadLetterPath(EntityNameHelper.FormatSubscriptionPath(topicName, topicSubscriptionName)); MessageReceiver client = new MessageReceiver(ConnectionString, deadLetterPath, ReceiveMode.PeekLock); Task <IList <BrokeredMessage> > receiveTask = client.ReceiveAsync(1000); receiveTask.Wait(10000); if (receiveTask.IsCompleted && receiveTask.Result != null) { brokeredMessages = receiveTask.Result; } else { brokeredMessages = Enumerable.Empty <BrokeredMessage>(); } #endif foreach (BrokeredMessage brokeredMessage in brokeredMessages) { if (lockIssues > 10) { break; } try { Logger.LogDebug(string.Format("A dead-letter message arrived with the id '{0}'.", brokeredMessage.MessageId)); string messageBody = brokeredMessage.GetBodyAsString(); // Closure protection BrokeredMessage message = brokeredMessage; try { AzureBusHelper.ReceiveEvent ( messageBody, @event => { bool isRequired = BusHelper.IsEventRequired(@event.GetType()); if (!isRequired) { #if NET452 removeDeadlLetterFromQueue(message); #endif #if NETSTANDARD2_0 removeDeadlLetterFromQueue(client, message); #endif } else { #if NET452 leaveDeadlLetterInQueue(message, @event); #endif #if NETSTANDARD2_0 leaveDeadlLetterInQueue(client, message, @event); #endif } return(true); }, string.Format("id '{0}'", brokeredMessage.MessageId), ExtractSignature(message), SigningTokenConfigurationKey, () => { #if NET452 removeDeadlLetterFromQueue(message); #endif #if NETSTANDARD2_0 removeDeadlLetterFromQueue(client, message); #endif }, () => { } ); } catch { AzureBusHelper.ReceiveCommand ( messageBody, command => { bool isRequired = BusHelper.IsEventRequired(command.GetType()); if (!isRequired) { #if NET452 removeDeadlLetterFromQueue(message); #endif #if NETSTANDARD2_0 removeDeadlLetterFromQueue(client, message); #endif } else { #if NET452 leaveDeadlLetterInQueue(message, command); #endif #if NETSTANDARD2_0 leaveDeadlLetterInQueue(client, message, command); #endif } return(true); }, string.Format("id '{0}'", brokeredMessage.MessageId), ExtractSignature(message), SigningTokenConfigurationKey, () => { #if NET452 removeDeadlLetterFromQueue(message); #endif #if NETSTANDARD2_0 removeDeadlLetterFromQueue(client, message); #endif }, () => { } ); } } catch (Exception exception) { TelemetryHelper.TrackException(exception, null, telemetryProperties); // Indicates a problem, unlock message in queue Logger.LogError(string.Format("A dead-letter message arrived with the id '{0}' but failed to be process.", brokeredMessage.MessageId), exception: exception); try { #if NET452 brokeredMessage.Abandon(); #endif #if NETSTANDARD2_0 client.AbandonAsync(brokeredMessage.SystemProperties.LockToken).Wait(1500); #endif } catch (MessageLockLostException) { lockIssues++; Logger.LogWarning(string.Format("The lock supplied for abandon for the skipped dead-letter message '{0}' is invalid.", brokeredMessage.MessageId)); } } } #if NET452 client.Close(); #endif #if NETSTANDARD2_0 client.CloseAsync().Wait(1500); #endif if (loop++ % 5 == 0) { loop = 0; Thread.Yield(); } else { Thread.Sleep(500); } } try { brokeredMessageRenewCancellationTokenSource.Dispose(); } catch (ObjectDisposedException) { } }, brokeredMessageRenewCancellationTokenSource.Token); return(brokeredMessageRenewCancellationTokenSource); }
private static void CheckIsSonarCloud(string uri) { TelemetryHelper.IsSonarCloud(new Uri(uri)).Should().BeTrue(); }
public virtual void Publish <TEvent>(TEvent @event) where TEvent : IEvent <TAuthenticationToken> { DateTimeOffset startedAt = DateTimeOffset.UtcNow; Stopwatch mainStopWatch = Stopwatch.StartNew(); string responseCode = null; bool mainWasSuccessfull = false; bool telemeterOverall = false; IDictionary <string, string> telemetryProperties = new Dictionary <string, string> { { "Type", "Azure/Servicebus" } }; string telemetryName = string.Format("{0}/{1}", @event.GetType().FullName, @event.Id); var telemeteredEvent = @event as ITelemeteredMessage; if (telemeteredEvent != null) { telemetryName = telemeteredEvent.TelemetryName; } telemetryName = string.Format("Event/{0}", telemetryName); try { if (!AzureBusHelper.PrepareAndValidateEvent(@event, "Azure-ServiceBus")) { return; } var privateEventAttribute = Attribute.GetCustomAttribute(typeof(TEvent), typeof(PrivateEventAttribute)) as PrivateEventAttribute; var publicEventAttribute = Attribute.GetCustomAttribute(typeof(TEvent), typeof(PrivateEventAttribute)) as PublicEventAttribute; // We only add telemetry for overall operations if two occured telemeterOverall = publicEventAttribute != null && privateEventAttribute != null; // Backwards compatibility and simplicity bool wasSuccessfull; Stopwatch stopWatch = Stopwatch.StartNew(); if (publicEventAttribute == null && privateEventAttribute == null) { stopWatch.Restart(); responseCode = "200"; wasSuccessfull = false; try { var brokeredMessage = new BrokeredMessage(MessageSerialiser.SerialiseEvent(@event)) { CorrelationId = CorrelationIdHelper.GetCorrelationId().ToString("N") }; brokeredMessage.Properties.Add("Type", @event.GetType().FullName); PublicServiceBusPublisher.Send(brokeredMessage); wasSuccessfull = true; } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the event being sent was too large or the topic has reached it's limit.", exception: exception, metaData: new Dictionary <string, object> { { "Event", @event } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish an event.", exception: exception, metaData: new Dictionary <string, object> { { "Event", @event } }); throw; } finally { TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Event", telemetryName, "Default Bus", startedAt, stopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } Logger.LogDebug(string.Format("An event was published on the public bus with the id '{0}' was of type {1}.", @event.Id, @event.GetType().FullName)); } if (publicEventAttribute != null) { stopWatch.Restart(); responseCode = "200"; wasSuccessfull = false; try { var brokeredMessage = new BrokeredMessage(MessageSerialiser.SerialiseEvent(@event)) { CorrelationId = CorrelationIdHelper.GetCorrelationId().ToString("N") }; brokeredMessage.Properties.Add("Type", @event.GetType().FullName); PublicServiceBusPublisher.Send(brokeredMessage); wasSuccessfull = true; } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the event being sent was too large.", exception: exception, metaData: new Dictionary <string, object> { { "Event", @event } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish an event.", exception: exception, metaData: new Dictionary <string, object> { { "Event", @event } }); throw; } finally { TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Event", telemetryName, "Public Bus", startedAt, stopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } Logger.LogDebug(string.Format("An event was published on the public bus with the id '{0}' was of type {1}.", @event.Id, @event.GetType().FullName)); } if (privateEventAttribute != null) { stopWatch.Restart(); responseCode = "200"; wasSuccessfull = false; try { var brokeredMessage = new BrokeredMessage(MessageSerialiser.SerialiseEvent(@event)) { CorrelationId = CorrelationIdHelper.GetCorrelationId().ToString("N") }; brokeredMessage.Properties.Add("Type", @event.GetType().FullName); PrivateServiceBusPublisher.Send(brokeredMessage); wasSuccessfull = true; } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the event being sent was too large.", exception: exception, metaData: new Dictionary <string, object> { { "Event", @event } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish an event.", exception: exception, metaData: new Dictionary <string, object> { { "Event", @event } }); throw; } finally { TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Event", telemetryName, "Private Bus", startedAt, stopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } Logger.LogDebug(string.Format("An event was published on the private bus with the id '{0}' was of type {1}.", @event.Id, @event.GetType().FullName)); } mainWasSuccessfull = true; } finally { mainStopWatch.Stop(); if (telemeterOverall) { TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Event", telemetryName, null, startedAt, mainStopWatch.Elapsed, responseCode, mainWasSuccessfull, telemetryProperties); } } }
/// <summary> /// Receives a <see cref="EventData"/> from the event bus. /// </summary> protected virtual void ReceiveEvent(PartitionContext context, EventData eventData) { DateTimeOffset startedAt = DateTimeOffset.UtcNow; Stopwatch mainStopWatch = Stopwatch.StartNew(); string responseCode = "200"; // Null means it was skipped bool?wasSuccessfull = true; #if NET452 string telemetryName = string.Format("Cqrs/Handle/Event/{0}", eventData.SequenceNumber); #endif #if NETSTANDARD2_0 string telemetryName = string.Format("Cqrs/Handle/Event/{0}", eventData.SystemProperties.SequenceNumber); #endif ISingleSignOnToken authenticationToken = null; Guid? guidAuthenticationToken = null; string stringAuthenticationToken = null; int? intAuthenticationToken = null; IDictionary <string, string> telemetryProperties = new Dictionary <string, string> { { "Type", "Azure/EventHub" } }; object value; if (eventData.Properties.TryGetValue("Type", out value)) { telemetryProperties.Add("MessageType", value.ToString()); } TelemetryHelper.TrackMetric("Cqrs/Handle/Event", CurrentHandles++, telemetryProperties); // Do a manual 10 try attempt with back-off for (int i = 0; i < 10; i++) { try { #if NET452 Logger.LogDebug(string.Format("An event message arrived with the partition key '{0}', sequence number '{1}' and offset '{2}'.", eventData.PartitionKey, eventData.SequenceNumber, eventData.Offset)); #endif #if NETSTANDARD2_0 Logger.LogDebug(string.Format("An event message arrived with the partition key '{0}', sequence number '{1}' and offset '{2}'.", eventData.SystemProperties.PartitionKey, eventData.SystemProperties.SequenceNumber, eventData.SystemProperties.Offset)); #endif #if NET452 string messageBody = Encoding.UTF8.GetString(eventData.GetBytes()); #endif #if NETSTANDARD2_0 string messageBody = Encoding.UTF8.GetString(eventData.Body.Array, eventData.Body.Offset, eventData.Body.Count); #endif IEvent <TAuthenticationToken> @event = AzureBusHelper.ReceiveEvent(null, messageBody, ReceiveEvent, #if NET452 string.Format("partition key '{0}', sequence number '{1}' and offset '{2}'", eventData.PartitionKey, eventData.SequenceNumber, eventData.Offset), #endif #if NETSTANDARD2_0 string.Format("partition key '{0}', sequence number '{1}' and offset '{2}'", eventData.SystemProperties.PartitionKey, eventData.SystemProperties.SequenceNumber, eventData.SystemProperties.Offset), #endif ExtractSignature(eventData), SigningTokenConfigurationKey, () => { wasSuccessfull = null; #if NET452 telemetryName = string.Format("Cqrs/Handle/Event/Skipped/{0}", eventData.SequenceNumber); #endif #if NETSTANDARD2_0 telemetryName = string.Format("Cqrs/Handle/Event/Skipped/{0}", eventData.SystemProperties.SequenceNumber); #endif responseCode = "204"; // Remove message from queue context.CheckpointAsync(eventData); #if NET452 Logger.LogDebug(string.Format("An event message arrived with the partition key '{0}', sequence number '{1}' and offset '{2}' but processing was skipped due to event settings.", eventData.PartitionKey, eventData.SequenceNumber, eventData.Offset)); #endif #if NETSTANDARD2_0 Logger.LogDebug(string.Format("An event message arrived with the partition key '{0}', sequence number '{1}' and offset '{2}' but processing was skipped due to event settings.", eventData.SystemProperties.PartitionKey, eventData.SystemProperties.SequenceNumber, eventData.SystemProperties.Offset)); #endif TelemetryHelper.TrackEvent("Cqrs/Handle/Event/Skipped", telemetryProperties); } ); if (wasSuccessfull != null) { if (@event != null) { telemetryName = string.Format("{0}/{1}/{2}", @event.GetType().FullName, @event.GetIdentity(), @event.Id); authenticationToken = @event.AuthenticationToken as ISingleSignOnToken; if (AuthenticationTokenIsGuid) { guidAuthenticationToken = @event.AuthenticationToken as Guid?; } if (AuthenticationTokenIsString) { stringAuthenticationToken = @event.AuthenticationToken as string; } if (AuthenticationTokenIsInt) { intAuthenticationToken = @event.AuthenticationToken as int?; } var telemeteredMessage = @event as ITelemeteredMessage; if (telemeteredMessage != null) { telemetryName = telemeteredMessage.TelemetryName; } telemetryName = string.Format("Cqrs/Handle/Event/{0}", telemetryName); } // Remove message from queue context.CheckpointAsync(eventData); } #if NET452 Logger.LogDebug(string.Format("An event message arrived and was processed with the partition key '{0}', sequence number '{1}' and offset '{2}'.", eventData.PartitionKey, eventData.SequenceNumber, eventData.Offset)); #endif #if NETSTANDARD2_0 Logger.LogDebug(string.Format("An event message arrived and was processed with the partition key '{0}', sequence number '{1}' and offset '{2}'.", eventData.SystemProperties.PartitionKey, eventData.SystemProperties.SequenceNumber, eventData.SystemProperties.Offset)); #endif IList <IEvent <TAuthenticationToken> > events; if (EventWaits.TryGetValue(@event.CorrelationId, out events)) { events.Add(@event); } wasSuccessfull = true; responseCode = "200"; return; } catch (UnAuthorisedMessageReceivedException exception) { TelemetryHelper.TrackException(exception, null, telemetryProperties); // Indicates a problem, unlock message in queue #if NET452 Logger.LogError(string.Format("An event message arrived with the partition key '{0}', sequence number '{1}' and offset '{2}' but was not authorised.", eventData.PartitionKey, eventData.SequenceNumber, eventData.Offset), exception: exception); #endif #if NETSTANDARD2_0 Logger.LogError(string.Format("An event message arrived with the partition key '{0}', sequence number '{1}' and offset '{2}' but was not authorised.", eventData.SystemProperties.PartitionKey, eventData.SystemProperties.SequenceNumber, eventData.SystemProperties.Offset), exception: exception); #endif wasSuccessfull = false; responseCode = "401"; telemetryProperties.Add("ExceptionType", exception.GetType().FullName); telemetryProperties.Add("ExceptionMessage", exception.Message); } catch (NoHandlersRegisteredException exception) { TelemetryHelper.TrackException(exception, null, telemetryProperties); // Indicates a problem, unlock message in queue #if NET452 Logger.LogError(string.Format("An event message arrived with the partition key '{0}', sequence number '{1}' and offset '{2}' but no handlers were found to process it.", eventData.PartitionKey, eventData.SequenceNumber, eventData.Offset), exception: exception); #endif #if NETSTANDARD2_0 Logger.LogError(string.Format("An event message arrived with the partition key '{0}', sequence number '{1}' and offset '{2}' but no handlers were found to process it.", eventData.SystemProperties.PartitionKey, eventData.SystemProperties.SequenceNumber, eventData.SystemProperties.Offset), exception: exception); #endif wasSuccessfull = false; responseCode = "501"; telemetryProperties.Add("ExceptionType", exception.GetType().FullName); telemetryProperties.Add("ExceptionMessage", exception.Message); } catch (NoHandlerRegisteredException exception) { TelemetryHelper.TrackException(exception, null, telemetryProperties); // Indicates a problem, unlock message in queue #if NET452 Logger.LogError(string.Format("An event message arrived with the partition key '{0}', sequence number '{1}' and offset '{2}'s but no handler was found to process it.", eventData.PartitionKey, eventData.SequenceNumber, eventData.Offset), exception: exception); #endif #if NETSTANDARD2_0 Logger.LogError(string.Format("An event message arrived with the partition key '{0}', sequence number '{1}' and offset '{2}'s but no handler was found to process it.", eventData.SystemProperties.PartitionKey, eventData.SystemProperties.SequenceNumber, eventData.SystemProperties.Offset), exception: exception); #endif wasSuccessfull = false; responseCode = "501"; telemetryProperties.Add("ExceptionType", exception.GetType().FullName); telemetryProperties.Add("ExceptionMessage", exception.Message); } catch (Exception exception) { // Indicates a problem, unlock message in queue #if NET452 Logger.LogError(string.Format("An event message arrived with the partition key '{0}', sequence number '{1}' and offset '{2}' but failed to be process.", eventData.PartitionKey, eventData.SequenceNumber, eventData.Offset), exception: exception); #endif #if NETSTANDARD2_0 Logger.LogError(string.Format("An event message arrived with the partition key '{0}', sequence number '{1}' and offset '{2}' but failed to be process.", eventData.SystemProperties.PartitionKey, eventData.SystemProperties.SequenceNumber, eventData.SystemProperties.Offset), exception: exception); #endif switch (i) { case 0: case 1: // 10 seconds Thread.Sleep(10 * 1000); break; case 2: case 3: // 30 seconds Thread.Sleep(30 * 1000); break; case 4: case 5: case 6: // 1 minute Thread.Sleep(60 * 1000); break; case 7: case 8: // 3 minutes Thread.Sleep(3 * 60 * 1000); break; case 9: telemetryProperties.Add("ExceptionType", exception.GetType().FullName); telemetryProperties.Add("ExceptionMessage", exception.Message); break; } wasSuccessfull = false; responseCode = "500"; } finally { // Eventually just accept it context.CheckpointAsync(eventData); TelemetryHelper.TrackMetric("Cqrs/Handle/Event", CurrentHandles--, telemetryProperties); mainStopWatch.Stop(); if (guidAuthenticationToken != null) { TelemetryHelper.TrackRequest ( telemetryName, guidAuthenticationToken, startedAt, mainStopWatch.Elapsed, responseCode, wasSuccessfull == null || wasSuccessfull.Value, telemetryProperties ); } else if (intAuthenticationToken != null) { TelemetryHelper.TrackRequest ( telemetryName, intAuthenticationToken, startedAt, mainStopWatch.Elapsed, responseCode, wasSuccessfull == null || wasSuccessfull.Value, telemetryProperties ); } else if (stringAuthenticationToken != null) { TelemetryHelper.TrackRequest ( telemetryName, stringAuthenticationToken, startedAt, mainStopWatch.Elapsed, responseCode, wasSuccessfull == null || wasSuccessfull.Value, telemetryProperties ); } else { TelemetryHelper.TrackRequest ( telemetryName, authenticationToken, startedAt, mainStopWatch.Elapsed, responseCode, wasSuccessfull == null || wasSuccessfull.Value, telemetryProperties ); } TelemetryHelper.Flush(); } } }
/// <summary> /// Publishes the provided <paramref name="command"></paramref> and waits for an event of <typeparamref name="TEvent"/> or exits if the specified timeout is expired. /// </summary> /// <param name="command">The <typeparamref name="TCommand"/> to publish.</param> /// <param name="condition">A delegate to be executed over and over until it returns the <typeparamref name="TEvent"/> that is desired, return null to keep trying.</param> /// <param name="millisecondsTimeout">The number of milliseconds to wait, or <see cref="F:System.Threading.Timeout.Infinite"/> (-1) to wait indefinitely.</param> /// <param name="eventReceiver">If provided, is the <see cref="IEventReceiver{TAuthenticationToken}" /> that the event is expected to be returned on.</param> public virtual TEvent PublishAndWait <TCommand, TEvent>(TCommand command, Func <IEnumerable <IEvent <TAuthenticationToken> >, TEvent> condition, int millisecondsTimeout, IEventReceiver <TAuthenticationToken> eventReceiver = null) where TCommand : ICommand <TAuthenticationToken> { if (command == null) { Logger.LogDebug("No command to publish."); return((TEvent)(object)null); } Type commandType = command.GetType(); DateTimeOffset startedAt = DateTimeOffset.UtcNow; Stopwatch mainStopWatch = Stopwatch.StartNew(); string responseCode = "200"; bool wasSuccessfull = false; IDictionary <string, string> telemetryProperties = new Dictionary <string, string> { { "Type", "Azure/Servicebus" } }; string telemetryName = string.Format("{0}/{1}", commandType.FullName, command.Id); var telemeteredCommand = command as ITelemeteredMessage; if (telemeteredCommand != null) { telemetryName = telemeteredCommand.TelemetryName; } telemetryName = string.Format("Command/{0}", telemetryName); TEvent result; try { if (eventReceiver != null) { throw new NotSupportedException("Specifying a different event receiver is not yet supported."); } if (!AzureBusHelper.PrepareAndValidateCommand(command, "Azure-ServiceBus")) { return((TEvent)(object)null); } result = (TEvent)(object)null; EventWaits.Add(command.CorrelationId, new List <IEvent <TAuthenticationToken> >()); try { var brokeredMessage = new BrokeredMessage(MessageSerialiser.SerialiseCommand(command)) { CorrelationId = CorrelationIdHelper.GetCorrelationId().ToString("N") }; brokeredMessage.Properties.Add("Type", commandType.FullName); brokeredMessage.Properties.Add("Source", string.Format("{0}/{1}/{2}/{3}", LoggerSettings.ModuleName, LoggerSettings.Instance, LoggerSettings.Environment, LoggerSettings.EnvironmentInstance)); int count = 1; do { try { PrivateServiceBusPublisher.Send(brokeredMessage); break; } catch (TimeoutException) { if (count >= TimeoutOnSendRetryMaximumCount) { throw; } } count++; } while (true); } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the command being sent was too large.", exception: exception, metaData: new Dictionary <string, object> { { "Command", command } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish a command.", exception: exception, metaData: new Dictionary <string, object> { { "Command", command } }); throw; } Logger.LogInfo(string.Format("A command was sent of type {0}.", commandType.FullName)); wasSuccessfull = true; } finally { mainStopWatch.Stop(); TelemetryHelper.TrackDependency("Azure/Servicebus/CommandBus", "Command", telemetryName, null, startedAt, mainStopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } SpinWait.SpinUntil(() => { IList <IEvent <TAuthenticationToken> > events = EventWaits[command.CorrelationId]; result = condition(events); return(result != null); }, millisecondsTimeout, sleepInMilliseconds: 1000); TelemetryHelper.TrackDependency("Azure/Servicebus/CommandBus", "Command/AndWait", string.Format("Command/AndWait{0}", telemetryName.Substring(7)), null, startedAt, mainStopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); return(result); }
/// <summary> /// Publishes the provided <paramref name="command"/> on the command bus. /// </summary> public virtual void Publish <TCommand>(TCommand command) where TCommand : ICommand <TAuthenticationToken> { if (command == null) { Logger.LogDebug("No command to publish."); return; } Type commandType = command.GetType(); DateTimeOffset startedAt = DateTimeOffset.UtcNow; Stopwatch mainStopWatch = Stopwatch.StartNew(); string responseCode = "200"; bool mainWasSuccessfull = false; bool telemeterOverall = false; IDictionary <string, string> telemetryProperties = new Dictionary <string, string> { { "Type", "Azure/Servicebus" } }; string telemetryName = string.Format("{0}/{1}/{2}", commandType.FullName, command.GetIdentity(), command.Id); var telemeteredCommand = command as ITelemeteredMessage; if (telemeteredCommand != null) { telemetryName = telemeteredCommand.TelemetryName; } else { telemetryName = string.Format("Command/{0}", telemetryName); } try { if (!AzureBusHelper.PrepareAndValidateCommand(command, "Azure-ServiceBus")) { return; } bool?isPublicBusRequired = BusHelper.IsPublicBusRequired(commandType); bool?isPrivateBusRequired = BusHelper.IsPrivateBusRequired(commandType); // We only add telemetry for overall operations if two occured telemeterOverall = isPublicBusRequired != null && isPublicBusRequired.Value && isPrivateBusRequired != null && isPrivateBusRequired.Value; // Backwards compatibility and simplicity bool wasSuccessfull; Stopwatch stopWatch = Stopwatch.StartNew(); if ((isPublicBusRequired == null || !isPublicBusRequired.Value) && (isPrivateBusRequired == null || !isPrivateBusRequired.Value)) { stopWatch.Restart(); responseCode = "200"; wasSuccessfull = false; try { var brokeredMessage = new BrokeredMessage(MessageSerialiser.SerialiseCommand(command)) { CorrelationId = CorrelationIdHelper.GetCorrelationId().ToString("N") }; brokeredMessage.Properties.Add("Type", commandType.FullName); brokeredMessage.Properties.Add("Source", string.Format("{0}/{1}/{2}/{3}", LoggerSettings.ModuleName, LoggerSettings.Instance, LoggerSettings.Environment, LoggerSettings.EnvironmentInstance)); int count = 1; do { try { PublicServiceBusPublisher.Send(brokeredMessage); break; } catch (TimeoutException) { if (count >= TimeoutOnSendRetryMaximumCount) { throw; } } count++; } while (true); wasSuccessfull = true; } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the command being sent was too large or the topic has reached it's limit.", exception: exception, metaData: new Dictionary <string, object> { { "Command", command } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish a command.", exception: exception, metaData: new Dictionary <string, object> { { "Command", command } }); throw; } finally { TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Command", telemetryName, "Default Bus", startedAt, stopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } Logger.LogDebug(string.Format("An command was published on the public bus with the id '{0}' was of type {1}.", command.Id, commandType.FullName)); } if ((isPublicBusRequired != null && isPublicBusRequired.Value)) { stopWatch.Restart(); responseCode = "200"; wasSuccessfull = false; try { var brokeredMessage = new BrokeredMessage(MessageSerialiser.SerialiseCommand(command)) { CorrelationId = CorrelationIdHelper.GetCorrelationId().ToString("N") }; brokeredMessage.Properties.Add("Type", commandType.FullName); brokeredMessage.Properties.Add("Source", string.Format("{0}/{1}/{2}/{3}", LoggerSettings.ModuleName, LoggerSettings.Instance, LoggerSettings.Environment, LoggerSettings.EnvironmentInstance)); int count = 1; do { try { PublicServiceBusPublisher.Send(brokeredMessage); break; } catch (TimeoutException) { if (count >= TimeoutOnSendRetryMaximumCount) { throw; } } count++; } while (true); wasSuccessfull = true; } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the command being sent was too large or the topic has reached it's limit.", exception: exception, metaData: new Dictionary <string, object> { { "Command", command } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish a command.", exception: exception, metaData: new Dictionary <string, object> { { "Command", command } }); throw; } finally { TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Command", telemetryName, "Public Bus", startedAt, stopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } Logger.LogDebug(string.Format("An command was published on the public bus with the id '{0}' was of type {1}.", command.Id, commandType.FullName)); } if (isPrivateBusRequired != null && isPrivateBusRequired.Value) { stopWatch.Restart(); responseCode = "200"; wasSuccessfull = false; try { var brokeredMessage = new BrokeredMessage(MessageSerialiser.SerialiseCommand(command)) { CorrelationId = CorrelationIdHelper.GetCorrelationId().ToString("N") }; brokeredMessage.Properties.Add("Type", commandType.FullName); brokeredMessage.Properties.Add("Source", string.Format("{0}/{1}/{2}/{3}", LoggerSettings.ModuleName, LoggerSettings.Instance, LoggerSettings.Environment, LoggerSettings.EnvironmentInstance)); int count = 1; do { try { PrivateServiceBusPublisher.Send(brokeredMessage); break; } catch (TimeoutException) { if (count >= TimeoutOnSendRetryMaximumCount) { throw; } } count++; } while (true); wasSuccessfull = true; } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the command being sent was too large or the topic has reached it's limit.", exception: exception, metaData: new Dictionary <string, object> { { "Command", command } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish an command.", exception: exception, metaData: new Dictionary <string, object> { { "Command", command } }); throw; } finally { TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Command", telemetryName, "Private Bus", startedAt, stopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } Logger.LogDebug(string.Format("An command was published on the private bus with the id '{0}' was of type {1}.", command.Id, commandType.FullName)); } mainWasSuccessfull = true; } finally { mainStopWatch.Stop(); if (telemeterOverall) { TelemetryHelper.TrackDependency("Azure/Servicebus/CommandBus", "Command", telemetryName, null, startedAt, mainStopWatch.Elapsed, responseCode, mainWasSuccessfull, telemetryProperties); } } }
/// <summary> /// Publishes the provided <paramref name="commands"/> on the command bus. /// </summary> public virtual void Publish <TCommand>(IEnumerable <TCommand> commands) where TCommand : ICommand <TAuthenticationToken> { if (commands == null) { Logger.LogDebug("No commands to publish."); return; } IList <TCommand> sourceCommands = commands.ToList(); if (!sourceCommands.Any()) { Logger.LogDebug("An empty collection of commands to publish."); return; } DateTimeOffset startedAt = DateTimeOffset.UtcNow; Stopwatch mainStopWatch = Stopwatch.StartNew(); string responseCode = "200"; bool wasSuccessfull = false; IDictionary <string, string> telemetryProperties = new Dictionary <string, string> { { "Type", "Azure/Servicebus" } }; string telemetryName = "Commands"; string telemetryNames = string.Empty; foreach (TCommand command in sourceCommands) { Type commandType = command.GetType(); string subTelemetryName = string.Format("{0}/{1}", commandType.FullName, command.Id); var telemeteredCommand = command as ITelemeteredMessage; if (telemeteredCommand != null) { subTelemetryName = telemeteredCommand.TelemetryName; } telemetryNames = string.Format("{0}{1},", telemetryNames, subTelemetryName); } if (telemetryNames.Length > 0) { telemetryNames = telemetryNames.Substring(0, telemetryNames.Length - 1); } telemetryProperties.Add("Commands", telemetryNames); try { IList <string> sourceCommandMessages = new List <string>(); IList <BrokeredMessage> brokeredMessages = new List <BrokeredMessage>(sourceCommands.Count); foreach (TCommand command in sourceCommands) { Type commandType = command.GetType(); if (!AzureBusHelper.PrepareAndValidateCommand(command, "Azure-ServiceBus")) { continue; } var brokeredMessage = new BrokeredMessage(MessageSerialiser.SerialiseCommand(command)) { CorrelationId = CorrelationIdHelper.GetCorrelationId().ToString("N") }; brokeredMessage.Properties.Add("Type", commandType.FullName); brokeredMessage.Properties.Add("Source", string.Format("{0}/{1}/{2}/{3}", LoggerSettings.ModuleName, LoggerSettings.Instance, LoggerSettings.Environment, LoggerSettings.EnvironmentInstance)); brokeredMessages.Add(brokeredMessage); sourceCommandMessages.Add(string.Format("A command was sent of type {0}.", commandType.FullName)); } try { int count = 1; do { try { if (brokeredMessages.Any()) { PrivateServiceBusPublisher.SendBatch(brokeredMessages); } else { Logger.LogDebug("An empty collection of commands to publish post validation."); } break; } catch (TimeoutException) { if (count >= TimeoutOnSendRetryMaximumCount) { throw; } } count++; } while (true); } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the command being sent was too large.", exception: exception, metaData: new Dictionary <string, object> { { "Commands", sourceCommands } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish a command.", exception: exception, metaData: new Dictionary <string, object> { { "Commands", sourceCommands } }); throw; } foreach (string message in sourceCommandMessages) { Logger.LogInfo(message); } wasSuccessfull = true; } finally { mainStopWatch.Stop(); TelemetryHelper.TrackDependency("Azure/Servicebus/CommandBus", "Command", telemetryName, null, startedAt, mainStopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } }
/// <summary> /// Using a <see cref="Task"/>, clears all dead letters from the topic and subscription of the /// provided <paramref name="topicName"/> and <paramref name="topicSubscriptionName"/>. /// </summary> /// <param name="topicName">The name of the topic.</param> /// <param name="topicSubscriptionName">The name of the subscription.</param> /// <returns></returns> protected virtual CancellationTokenSource CleanUpDeadLetters(string topicName, string topicSubscriptionName) { var brokeredMessageRenewCancellationTokenSource = new CancellationTokenSource(); IDictionary <string, string> telemetryProperties = new Dictionary <string, string> { { "Type", "Azure/Servicebus" } }; int lockIssues = 0; Action <BrokeredMessage, IMessage> leaveDeadlLetterInQueue = (deadLetterBrokeredMessage, deadLetterMessage) => { // Remove message from queue try { deadLetterBrokeredMessage.Abandon(); lockIssues = 0; } catch (MessageLockLostException) { lockIssues++; Logger.LogWarning(string.Format("The lock supplied for abandon for the skipped dead-letter message '{0}' is invalid.", deadLetterBrokeredMessage.MessageId)); } Logger.LogDebug(string.Format("A dead-letter message of type {0} arrived with the id '{1}' but left in the queue due to settings.", deadLetterMessage.GetType().FullName, deadLetterBrokeredMessage.MessageId)); }; Action <BrokeredMessage> removeDeadlLetterFromQueue = deadLetterBrokeredMessage => { // Remove message from queue try { deadLetterBrokeredMessage.Complete(); lockIssues = 0; } catch (MessageLockLostException) { lockIssues++; Logger.LogWarning(string.Format("The lock supplied for complete for the skipped dead-letter message '{0}' is invalid.", deadLetterBrokeredMessage.MessageId)); } Logger.LogDebug(string.Format("A dead-letter message arrived with the id '{0}' but was removed as processing was skipped due to settings.", deadLetterBrokeredMessage.MessageId)); }; Task.Factory.StartNewSafely(() => { int loop = 0; while (!brokeredMessageRenewCancellationTokenSource.Token.IsCancellationRequested) { lockIssues = 0; MessagingFactory factory = MessagingFactory.CreateFromConnectionString(ConnectionString); string deadLetterPath = SubscriptionClient.FormatDeadLetterPath(topicName, topicSubscriptionName); MessageReceiver client = factory.CreateMessageReceiver(deadLetterPath, ReceiveMode.PeekLock); IEnumerable <BrokeredMessage> brokeredMessages = client.ReceiveBatch(1000); foreach (BrokeredMessage brokeredMessage in brokeredMessages) { if (lockIssues > 10) { break; } try { Logger.LogDebug(string.Format("A dead-letter message arrived with the id '{0}'.", brokeredMessage.MessageId)); string messageBody = brokeredMessage.GetBody <string>(); // Closure protection BrokeredMessage message = brokeredMessage; try { AzureBusHelper.ReceiveEvent ( messageBody, @event => { bool isRequired = BusHelper.IsEventRequired(@event.GetType()); if (!isRequired) { removeDeadlLetterFromQueue(message); } else { leaveDeadlLetterInQueue(message, @event); } return(true); }, string.Format("id '{0}'", brokeredMessage.MessageId), () => { removeDeadlLetterFromQueue(message); }, () => { } ); } catch { AzureBusHelper.ReceiveCommand ( messageBody, command => { bool isRequired = BusHelper.IsEventRequired(command.GetType()); if (!isRequired) { removeDeadlLetterFromQueue(message); } else { leaveDeadlLetterInQueue(message, command); } return(true); }, string.Format("id '{0}'", brokeredMessage.MessageId), () => { removeDeadlLetterFromQueue(message); }, () => { } ); } } catch (Exception exception) { TelemetryHelper.TrackException(exception, null, telemetryProperties); // Indicates a problem, unlock message in queue Logger.LogError(string.Format("A dead-letter message arrived with the id '{0}' but failed to be process.", brokeredMessage.MessageId), exception: exception); try { brokeredMessage.Abandon(); } catch (MessageLockLostException) { lockIssues++; Logger.LogWarning(string.Format("The lock supplied for abandon for the skipped dead-letter message '{0}' is invalid.", brokeredMessage.MessageId)); } } } client.Close(); if (loop++ % 5 == 0) { loop = 0; Thread.Yield(); } else { Thread.Sleep(500); } } try { brokeredMessageRenewCancellationTokenSource.Dispose(); } catch (ObjectDisposedException) { } }, brokeredMessageRenewCancellationTokenSource.Token); return(brokeredMessageRenewCancellationTokenSource); }
/// <summary> /// Publishes the provided <paramref name="event"/> on the event bus. /// </summary> public virtual void Publish <TEvent>(TEvent @event) where TEvent : IEvent <TAuthenticationToken> { if (@event == null) { Logger.LogDebug("No event to publish."); return; } DateTimeOffset startedAt = DateTimeOffset.UtcNow; Stopwatch mainStopWatch = Stopwatch.StartNew(); string responseCode = "200"; bool wasSuccessfull = false; IDictionary <string, string> telemetryProperties = new Dictionary <string, string> { { "Type", "Azure/EventHub" } }; string telemetryName = string.Format("{0}/{1}/{2}", @event.GetType().FullName, @event.GetIdentity(), @event.Id); var telemeteredEvent = @event as ITelemeteredMessage; if (telemeteredEvent != null) { telemetryName = telemeteredEvent.TelemetryName; } telemetryName = string.Format("Event/{0}", telemetryName); try { if (!AzureBusHelper.PrepareAndValidateEvent(@event, "Azure-EventHub")) { return; } try { var brokeredMessage = new Microsoft.ServiceBus.Messaging.EventData(Encoding.UTF8.GetBytes(MessageSerialiser.SerialiseEvent(@event))); brokeredMessage.Properties.Add("Type", @event.GetType().FullName); EventHubPublisher.Send(brokeredMessage); wasSuccessfull = true; } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the event being sent was too large.", exception: exception, metaData: new Dictionary <string, object> { { "Event", @event } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish an event.", exception: exception, metaData: new Dictionary <string, object> { { "Event", @event } }); throw; } } finally { TelemetryHelper.TrackDependency("Azure/EventHub/EventBus", "Event", telemetryName, null, startedAt, mainStopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } Logger.LogInfo(string.Format("An event was published with the id '{0}' was of type {1}.", @event.Id, @event.GetType().FullName)); }
/// <summary> /// Publishes the provided <paramref name="events"/> on the event bus. /// </summary> public virtual void Publish <TEvent>(IEnumerable <TEvent> events) where TEvent : IEvent <TAuthenticationToken> { if (events == null) { Logger.LogDebug("No events to publish."); return; } IList <TEvent> sourceEvents = events.ToList(); if (!sourceEvents.Any()) { Logger.LogDebug("An empty collection of events to publish."); return; } DateTimeOffset startedAt = DateTimeOffset.UtcNow; Stopwatch mainStopWatch = Stopwatch.StartNew(); string responseCode = null; bool mainWasSuccessfull = false; IDictionary <string, string> telemetryProperties = new Dictionary <string, string> { { "Type", "Azure/Servicebus" } }; string telemetryName = "Events"; string telemetryNames = string.Empty; foreach (TEvent @event in sourceEvents) { Type eventType = @event.GetType(); string subTelemetryName = string.Format("{0}/{1}/{2}", eventType.FullName, @event.GetIdentity(), @event.Id); var telemeteredEvent = @event as ITelemeteredMessage; if (telemeteredEvent != null) { subTelemetryName = telemeteredEvent.TelemetryName; } telemetryNames = string.Format("{0}{1},", telemetryNames, subTelemetryName); } if (telemetryNames.Length > 0) { telemetryNames = telemetryNames.Substring(0, telemetryNames.Length - 1); } telemetryProperties.Add("Events", telemetryNames); try { IList <string> sourceEventMessages = new List <string>(); IList <BrokeredMessage> privateBrokeredMessages = new List <BrokeredMessage>(sourceEvents.Count); IList <BrokeredMessage> publicBrokeredMessages = new List <BrokeredMessage>(sourceEvents.Count); foreach (TEvent @event in sourceEvents) { if (!AzureBusHelper.PrepareAndValidateEvent(@event, "Azure-ServiceBus")) { continue; } Type eventType = @event.GetType(); BrokeredMessage brokeredMessage = CreateBrokeredMessage(MessageSerialiser.SerialiseEvent, eventType, @event); bool?isPublicBusRequired = BusHelper.IsPublicBusRequired(eventType); bool?isPrivateBusRequired = BusHelper.IsPrivateBusRequired(eventType); // Backwards compatibility and simplicity if ((isPublicBusRequired == null || !isPublicBusRequired.Value) && (isPrivateBusRequired == null || !isPrivateBusRequired.Value)) { publicBrokeredMessages.Add(brokeredMessage); sourceEventMessages.Add(string.Format("An event was published on the public bus with the id '{0}' was of type {1}.", @event.Id, eventType.FullName)); } if ((isPublicBusRequired != null && isPublicBusRequired.Value)) { publicBrokeredMessages.Add(brokeredMessage); sourceEventMessages.Add(string.Format("An event was published on the public bus with the id '{0}' was of type {1}.", @event.Id, eventType.FullName)); } if (isPrivateBusRequired != null && isPrivateBusRequired.Value) { privateBrokeredMessages.Add(brokeredMessage); sourceEventMessages.Add(string.Format("An event was published on the private bus with the id '{0}' was of type {1}.", @event.Id, eventType.FullName)); } } bool wasSuccessfull; Stopwatch stopWatch = Stopwatch.StartNew(); // Backwards compatibility and simplicity stopWatch.Restart(); responseCode = "200"; wasSuccessfull = false; try { int count = 1; do { try { if (publicBrokeredMessages.Any()) { #if NET452 PublicServiceBusPublisher.SendBatch(publicBrokeredMessages); #endif #if NETSTANDARD2_0 PublicServiceBusPublisher.SendAsync(publicBrokeredMessages).Wait(); #endif } else { Logger.LogDebug("An empty collection of public events to publish post validation."); } break; } catch (TimeoutException) { if (count >= TimeoutOnSendRetryMaximumCount) { throw; } } count++; } while (true); wasSuccessfull = true; } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the event being sent was too large.", exception: exception, metaData: new Dictionary <string, object> { { "Events", publicBrokeredMessages } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish an event.", exception: exception, metaData: new Dictionary <string, object> { { "Events", publicBrokeredMessages } }); throw; } finally { TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Event", telemetryName, "Public Bus", startedAt, stopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } stopWatch.Restart(); responseCode = "200"; wasSuccessfull = false; try { int count = 1; do { try { if (privateBrokeredMessages.Any()) { #if NET452 PrivateServiceBusPublisher.SendBatch(privateBrokeredMessages); #endif #if NETSTANDARD2_0 PrivateServiceBusPublisher.SendAsync(privateBrokeredMessages).Wait(); #endif } else { Logger.LogDebug("An empty collection of private events to publish post validation."); } break; } catch (TimeoutException) { if (count >= TimeoutOnSendRetryMaximumCount) { throw; } } count++; } while (true); wasSuccessfull = true; } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the event being sent was too large.", exception: exception, metaData: new Dictionary <string, object> { { "Events", privateBrokeredMessages } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish an event.", exception: exception, metaData: new Dictionary <string, object> { { "Events", privateBrokeredMessages } }); throw; } finally { TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Event", telemetryName, "Private Bus", startedAt, stopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } foreach (string message in sourceEventMessages) { Logger.LogInfo(message); } mainWasSuccessfull = true; } finally { mainStopWatch.Stop(); TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Event", telemetryName, null, startedAt, mainStopWatch.Elapsed, responseCode, mainWasSuccessfull, telemetryProperties); } }
public static void Stop() { CancellationTokenSource.Cancel(); TelemetryHelper.WriteEvent(TelemetryEventNames.TaskSchedulerEnd); }
/// <summary> /// Publishes the provided <paramref name="event"/> on the event bus. /// </summary> public virtual void Publish <TEvent>(TEvent @event) where TEvent : IEvent <TAuthenticationToken> { if (@event == null) { Logger.LogDebug("No event to publish."); return; } Type eventType = @event.GetType(); DateTimeOffset startedAt = DateTimeOffset.UtcNow; Stopwatch mainStopWatch = Stopwatch.StartNew(); string responseCode = null; bool mainWasSuccessfull = false; bool telemeterOverall = false; IDictionary <string, string> telemetryProperties = new Dictionary <string, string> { { "Type", "Azure/Servicebus" } }; string telemetryName = string.Format("{0}/{1}/{2}", eventType.FullName, @event.GetIdentity(), @event.Id); var telemeteredEvent = @event as ITelemeteredMessage; if (telemeteredEvent != null) { telemetryName = telemeteredEvent.TelemetryName; } else { telemetryName = string.Format("Event/{0}", telemetryName); } try { if (!AzureBusHelper.PrepareAndValidateEvent(@event, "Azure-ServiceBus")) { return; } bool?isPublicBusRequired = BusHelper.IsPublicBusRequired(eventType); bool?isPrivateBusRequired = BusHelper.IsPrivateBusRequired(eventType); // We only add telemetry for overall operations if two occurred telemeterOverall = isPublicBusRequired != null && isPublicBusRequired.Value && isPrivateBusRequired != null && isPrivateBusRequired.Value; // Backwards compatibility and simplicity bool wasSuccessfull; Stopwatch stopWatch = Stopwatch.StartNew(); if ((isPublicBusRequired == null || !isPublicBusRequired.Value) && (isPrivateBusRequired == null || !isPrivateBusRequired.Value)) { stopWatch.Restart(); responseCode = "200"; wasSuccessfull = false; try { var brokeredMessage = CreateBrokeredMessage(MessageSerialiser.SerialiseEvent, eventType, @event); int count = 1; do { try { #if NET452 PublicServiceBusPublisher.Send(brokeredMessage); #endif #if NETSTANDARD2_0 PublicServiceBusPublisher.SendAsync(brokeredMessage).Wait(); #endif break; } catch (TimeoutException) { if (count >= TimeoutOnSendRetryMaximumCount) { throw; } } count++; } while (true); wasSuccessfull = true; } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the event being sent was too large or the topic has reached it's limit.", exception: exception, metaData: new Dictionary <string, object> { { "Event", @event } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish an event.", exception: exception, metaData: new Dictionary <string, object> { { "Event", @event } }); throw; } finally { TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Event", telemetryName, "Default Bus", startedAt, stopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } Logger.LogDebug(string.Format("An event was published on the public bus with the id '{0}' was of type {1}.", @event.Id, eventType.FullName)); } if ((isPublicBusRequired != null && isPublicBusRequired.Value)) { stopWatch.Restart(); responseCode = "200"; wasSuccessfull = false; try { var brokeredMessage = CreateBrokeredMessage(MessageSerialiser.SerialiseEvent, eventType, @event); int count = 1; do { try { #if NET452 PublicServiceBusPublisher.Send(brokeredMessage); #endif #if NETSTANDARD2_0 PublicServiceBusPublisher.SendAsync(brokeredMessage).Wait(); #endif break; } catch (TimeoutException) { if (count >= TimeoutOnSendRetryMaximumCount) { throw; } } count++; } while (true); wasSuccessfull = true; } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the event being sent was too large.", exception: exception, metaData: new Dictionary <string, object> { { "Event", @event } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish an event.", exception: exception, metaData: new Dictionary <string, object> { { "Event", @event } }); throw; } finally { TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Event", telemetryName, "Public Bus", startedAt, stopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } Logger.LogDebug(string.Format("An event was published on the public bus with the id '{0}' was of type {1}.", @event.Id, eventType.FullName)); } if (isPrivateBusRequired != null && isPrivateBusRequired.Value) { stopWatch.Restart(); responseCode = "200"; wasSuccessfull = false; try { var brokeredMessage = CreateBrokeredMessage(MessageSerialiser.SerialiseEvent, eventType, @event); int count = 1; do { try { #if NET452 PrivateServiceBusPublisher.Send(brokeredMessage); #endif #if NETSTANDARD2_0 PrivateServiceBusPublisher.SendAsync(brokeredMessage).Wait(); #endif break; } catch (TimeoutException) { if (count >= TimeoutOnSendRetryMaximumCount) { throw; } } count++; } while (true); wasSuccessfull = true; } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the event being sent was too large.", exception: exception, metaData: new Dictionary <string, object> { { "Event", @event } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish an event.", exception: exception, metaData: new Dictionary <string, object> { { "Event", @event } }); throw; } finally { TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Event", telemetryName, "Private Bus", startedAt, stopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } Logger.LogDebug(string.Format("An event was published on the private bus with the id '{0}' was of type {1}.", @event.Id, eventType.FullName)); } mainWasSuccessfull = true; } finally { mainStopWatch.Stop(); if (telemeterOverall) { TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Event", telemetryName, null, startedAt, mainStopWatch.Elapsed, responseCode, mainWasSuccessfull, telemetryProperties); } } }
public virtual void Publish <TCommand>(TCommand command) where TCommand : ICommand <TAuthenticationToken> { DateTimeOffset startedAt = DateTimeOffset.UtcNow; Stopwatch mainStopWatch = Stopwatch.StartNew(); string responseCode = "200"; bool wasSuccessfull = false; IDictionary <string, string> telemetryProperties = new Dictionary <string, string> { { "Type", "Azure/Servicebus" } }; string telemetryName = string.Format("{0}/{1}", command.GetType().FullName, command.Id); var telemeteredCommand = command as ITelemeteredMessage; if (telemeteredCommand != null) { telemetryName = telemeteredCommand.TelemetryName; } telemetryName = string.Format("Command/{0}", telemetryName); try { if (!AzureBusHelper.PrepareAndValidateCommand(command, "Azure-ServiceBus")) { return; } try { var brokeredMessage = new BrokeredMessage(MessageSerialiser.SerialiseCommand(command)) { CorrelationId = CorrelationIdHelper.GetCorrelationId().ToString("N") }; brokeredMessage.Properties.Add("Type", command.GetType().FullName); PrivateServiceBusPublisher.Send(brokeredMessage); } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the command being sent was too large.", exception: exception, metaData: new Dictionary <string, object> { { "Command", command } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish a command.", exception: exception, metaData: new Dictionary <string, object> { { "Command", command } }); throw; } Logger.LogInfo(string.Format("A command was sent of type {0}.", command.GetType().FullName)); wasSuccessfull = true; } finally { mainStopWatch.Stop(); TelemetryHelper.TrackDependency("Azure/Servicebus/CommandBus", "Command", telemetryName, null, startedAt, mainStopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } }
public void IsSonarCloud_InvalidUri_Null() { TelemetryHelper.IsSonarCloud(null).Should().BeFalse(); }
public ActionResult Index() { TelemetryHelper.LogVerbose(@"HomeController::Index"); return(this.View()); }
protected virtual void ReceiveEvent(BrokeredMessage message) { DateTimeOffset startedAt = DateTimeOffset.UtcNow; Stopwatch mainStopWatch = Stopwatch.StartNew(); string responseCode = "200"; // Null means it was skipped bool?wasSuccessfull = true; IDictionary <string, string> telemetryProperties = new Dictionary <string, string> { { "Type", "Azure/Servicebus" } }; object value; if (message.Properties.TryGetValue("Type", out value)) { telemetryProperties.Add("MessageType", value.ToString()); } TelemetryHelper.TrackMetric("Cqrs/Handle/Event", CurrentHandles++, telemetryProperties); var brokeredMessageRenewCancellationTokenSource = new CancellationTokenSource(); try { Logger.LogDebug(string.Format("An event message arrived with the id '{0}'.", message.MessageId)); string messageBody = message.GetBody <string>(); IEvent <TAuthenticationToken> @event = AzureBusHelper.ReceiveEvent(messageBody, ReceiveEvent, string.Format("id '{0}'", message.MessageId), () => { wasSuccessfull = null; // Remove message from queue try { message.Complete(); } catch (MessageLockLostException exception) { throw new MessageLockLostException(string.Format("The lock supplied for the skipped event message '{0}' is invalid.", message.MessageId), exception); } Logger.LogDebug(string.Format("An event message arrived with the id '{0}' but processing was skipped due to event settings.", message.MessageId)); TelemetryHelper.TrackEvent("Cqrs/Handle/Event/Skipped", telemetryProperties); }, () => { AzureBusHelper.RefreshLock(brokeredMessageRenewCancellationTokenSource, message, "event"); } ); if (wasSuccessfull != null) { // Remove message from queue try { message.Complete(); } catch (MessageLockLostException exception) { throw new MessageLockLostException(string.Format("The lock supplied for event '{0}' of type {1} is invalid.", @event.Id, @event.GetType().Name), exception); } } Logger.LogDebug(string.Format("An event message arrived and was processed with the id '{0}'.", message.MessageId)); IList <IEvent <TAuthenticationToken> > events; if (EventWaits.TryGetValue(@event.CorrelationId, out events)) { events.Add(@event); } } catch (Exception exception) { TelemetryHelper.TrackException(exception, null, telemetryProperties); // Indicates a problem, unlock message in queue Logger.LogError(string.Format("An event message arrived with the id '{0}' but failed to be process.", message.MessageId), exception: exception); message.Abandon(); wasSuccessfull = false; } finally { // Cancel the lock of renewing the task brokeredMessageRenewCancellationTokenSource.Cancel(); TelemetryHelper.TrackMetric("Cqrs/Handle/Event", CurrentHandles--, telemetryProperties); mainStopWatch.Stop(); if (wasSuccessfull == null || !wasSuccessfull.Value) { TelemetryHelper.TrackRequest ( string.Format("Cqrs/Handle/Event/Skipped/{0}", message.MessageId), startedAt, mainStopWatch.Elapsed, responseCode, wasSuccessfull == null, telemetryProperties ); } TelemetryHelper.Flush(); } }
private AventusTelemetry GetTelemetryFromStorage(IChannel channel) { var telemetryHelper = new TelemetryHelper(AccountConfig, channel); return(telemetryHelper.GetAventusTelemetry()); }
/// <summary> /// Receives <see cref="EventData"/> from the command bus. /// </summary> protected virtual void ReceiveCommand(PartitionContext context, EventData eventData) { DateTimeOffset startedAt = DateTimeOffset.UtcNow; Stopwatch mainStopWatch = Stopwatch.StartNew(); string responseCode = "200"; // Null means it was skipped bool? wasSuccessfull = true; string telemetryName = string.Format("Cqrs/Handle/Command/{0}", eventData.SequenceNumber); ISingleSignOnToken authenticationToken = null; IDictionary <string, string> telemetryProperties = new Dictionary <string, string> { { "Type", "Azure/EventHub" } }; object value; if (eventData.Properties.TryGetValue("Type", out value)) { telemetryProperties.Add("MessageType", value.ToString()); } TelemetryHelper.TrackMetric("Cqrs/Handle/Command", CurrentHandles++, telemetryProperties); // Do a manual 10 try attempt with back-off for (int i = 0; i < 10; i++) { try { Logger.LogDebug(string.Format("A command message arrived with the partition key '{0}', sequence number '{1}' and offset '{2}'.", eventData.PartitionKey, eventData.SequenceNumber, eventData.Offset)); string messageBody = Encoding.UTF8.GetString(eventData.GetBytes()); ICommand <TAuthenticationToken> command = AzureBusHelper.ReceiveCommand(messageBody, ReceiveCommand, string.Format("partition key '{0}', sequence number '{1}' and offset '{2}'", eventData.PartitionKey, eventData.SequenceNumber, eventData.Offset), () => { wasSuccessfull = null; telemetryName = string.Format("Cqrs/Handle/Command/Skipped/{0}", eventData.SequenceNumber); responseCode = "204"; // Remove message from queue context.CheckpointAsync(eventData); Logger.LogDebug(string.Format("A command message arrived with the partition key '{0}', sequence number '{1}' and offset '{2}' but processing was skipped due to command settings.", eventData.PartitionKey, eventData.SequenceNumber, eventData.Offset)); TelemetryHelper.TrackEvent("Cqrs/Handle/Command/Skipped", telemetryProperties); } ); if (wasSuccessfull != null) { if (command != null) { telemetryName = string.Format("{0}/{1}", command.GetType().FullName, command.Id); authenticationToken = command.AuthenticationToken as ISingleSignOnToken; var telemeteredMessage = command as ITelemeteredMessage; if (telemeteredMessage != null) { telemetryName = telemeteredMessage.TelemetryName; } telemetryName = string.Format("Cqrs/Handle/Command/{0}", telemetryName); } // Remove message from queue context.CheckpointAsync(eventData); } Logger.LogDebug(string.Format("A command message arrived and was processed with the partition key '{0}', sequence number '{1}' and offset '{2}'.", eventData.PartitionKey, eventData.SequenceNumber, eventData.Offset)); wasSuccessfull = true; responseCode = "200"; return; } catch (NoHandlersRegisteredException exception) { TelemetryHelper.TrackException(exception, null, telemetryProperties); // Indicates a problem, unlock message in queue Logger.LogError(string.Format("A command message arrived with the partition key '{0}', sequence number '{1}' and offset '{2}' but no handlers were found to process it.", eventData.PartitionKey, eventData.SequenceNumber, eventData.Offset), exception: exception); wasSuccessfull = false; responseCode = "501"; telemetryProperties.Add("ExceptionType", exception.GetType().FullName); telemetryProperties.Add("ExceptionMessage", exception.Message); } catch (NoHandlerRegisteredException exception) { TelemetryHelper.TrackException(exception, null, telemetryProperties); // Indicates a problem, unlock message in queue Logger.LogError(string.Format("A command message arrived with the partition key '{0}', sequence number '{1}' and offset '{2}' but no handler was found to process it.", eventData.PartitionKey, eventData.SequenceNumber, eventData.Offset), exception: exception); wasSuccessfull = false; responseCode = "501"; telemetryProperties.Add("ExceptionType", exception.GetType().FullName); telemetryProperties.Add("ExceptionMessage", exception.Message); } catch (Exception exception) { // Indicates a problem, unlock message in queue Logger.LogError(string.Format("A command message arrived with the partition key '{0}', sequence number '{1}' and offset '{2}' but failed to be process.", eventData.PartitionKey, eventData.SequenceNumber, eventData.Offset), exception: exception); switch (i) { case 0: case 1: // 10 seconds Thread.Sleep(10 * 1000); break; case 2: case 3: // 30 seconds Thread.Sleep(30 * 1000); break; case 4: case 5: case 6: // 1 minute Thread.Sleep(60 * 1000); break; case 7: case 8: // 3 minutes Thread.Sleep(3 * 60 * 1000); break; case 9: telemetryProperties.Add("ExceptionType", exception.GetType().FullName); telemetryProperties.Add("ExceptionMessage", exception.Message); break; } wasSuccessfull = false; responseCode = "500"; } finally { // Eventually just accept it context.CheckpointAsync(eventData); TelemetryHelper.TrackMetric("Cqrs/Handle/Command", CurrentHandles--, telemetryProperties); mainStopWatch.Stop(); TelemetryHelper.TrackRequest ( telemetryName, authenticationToken, startedAt, mainStopWatch.Elapsed, responseCode, wasSuccessfull == null || wasSuccessfull.Value, telemetryProperties ); TelemetryHelper.Flush(); } } }
public async Task <ActionResult> Complete(OrderViewModel order) { try { // TODO: Complete the payment processing via the gateway and update the order... NVPAPICaller gatewayCaller = new NVPAPICaller(); string token = ""; string finalPaymentAmount = ""; NVPCodec decoder = new NVPCodec(); token = Session["token"].ToString(); //PayerID = Session["payerId"].ToString(); //finalPaymentAmount = Session["payment_amt"].ToString(); finalPaymentAmount = order.Total.ToString("C2"); bool ret = gatewayCaller.DoCheckoutPayment(finalPaymentAmount, token, ref decoder); if (ret) { // Retrieve PayPal confirmation value. string PaymentConfirmation = decoder[NVPProperties.Properties.TRANSACTIONID].ToString(); order.PaymentTransactionId = PaymentConfirmation; ProductContext _db = new ProductContext(); // Get the current order id. int currentOrderId = -1; if (Session["currentOrderId"] != null && Session["currentOrderId"].ToString() != string.Empty) { currentOrderId = Convert.ToInt32(Session["currentOrderID"]); } Order myCurrentOrder; if (currentOrderId >= 0) { // Get the order based on order id. myCurrentOrder = _db.Orders.Single(o => o.OrderId == currentOrderId); // Update the order to reflect payment has been completed. myCurrentOrder.PaymentTransactionId = PaymentConfirmation; // Save to DB. _db.SaveChanges(); // Queue up a receipt generation request, asynchronously. await new AzureQueueHelper().QueueReceiptRequest(currentOrderId); // Report successful event to Application Insights. var eventProperties = new Dictionary <string, string>(); eventProperties.Add("CustomerEmail", order.Email); eventProperties.Add("OrderTotal", finalPaymentAmount); eventProperties.Add("PaymentTransactionId", PaymentConfirmation); TelemetryHelper.TrackEvent("OrderCompleted", eventProperties); } // Clear shopping cart. using (ShoppingCartActions usersShoppingCart = new ShoppingCartActions(cartId)) { usersShoppingCart.EmptyCart(); } // Clear order id. Session["currentOrderId"] = string.Empty; } else { var error = gatewayCaller.PopulateGatewayErrorModel(decoder); // Report failed event to Application Insights. Exception ex = new Exception(error.ToString()); ex.Source = "Contoso.Apps.SportsLeague.Web.CheckoutController.cs"; TelemetryHelper.TrackException(ex); // Redirect to the checkout error view: return(RedirectToAction("Error", error)); } } catch (WebException wex) { ExceptionUtility.LogException(wex, "CheckoutController.cs Complete Action"); var error = new CheckoutErrorViewModel { ErrorCode = wex.Message }; if (wex.Response != null && wex.Response.GetType() == typeof(HttpWebResponse)) { // Extract the response body from the WebException's HttpWebResponse: error.LongMessage = ((HttpWebResponse)wex.Response).StatusDescription; } // Redirect to the checkout error view: return(RedirectToAction("Error", error)); } catch (Exception ex) { ExceptionUtility.LogException(ex, "CheckoutController.cs Complete Action"); var error = new CheckoutErrorViewModel { ErrorCode = ex.Message }; // Redirect to the checkout error view: return(RedirectToAction("Error", error)); } return(View(order)); }
protected override void InstantiateReceiving(NamespaceManager namespaceManager, IDictionary <int, SubscriptionClient> serviceBusReceivers, string topicName, string topicSubscriptionName) { base.InstantiateReceiving(namespaceManager, serviceBusReceivers, topicName, topicSubscriptionName); Task.Factory.StartNewSafely (() => { // Because refreshing the rule can take a while, we only want to do this when the value changes string filter; if (!ConfigurationManager.TryGetSetting(FilterKeyConfigurationKey, out filter)) { return; } if (FilterKey == filter) { return; } FilterKey = filter; // https://docs.microsoft.com/en-us/azure/application-insights/app-insights-analytics-reference#summarize-operator // http://www.summa.com/blog/business-blog/everything-you-need-to-know-about-azure-service-bus-brokered-messaging-part-2#rulesfiltersactions // https://github.com/Azure-Samples/azure-servicebus-messaging-samples/tree/master/TopicFilters SubscriptionClient client = serviceBusReceivers[0]; bool reAddRule = false; try { IEnumerable <RuleDescription> rules = namespaceManager.GetRules(client.TopicPath, client.Name).ToList(); RuleDescription ruleDescription = rules.SingleOrDefault(rule => rule.Name == "CqrsConfiguredFilter"); if (ruleDescription != null) { var sqlFilter = ruleDescription.Filter as SqlFilter; if (sqlFilter == null && !string.IsNullOrWhiteSpace(filter)) { reAddRule = true; } else if (sqlFilter != null && sqlFilter.SqlExpression != filter) { reAddRule = true; } if (sqlFilter != null && reAddRule) { client.RemoveRule("CqrsConfiguredFilter"); } } else if (!string.IsNullOrWhiteSpace(filter)) { reAddRule = true; } ruleDescription = rules.SingleOrDefault(rule => rule.Name == "$Default"); // If there is a default rule and we have a rule, it will cause issues if (!string.IsNullOrWhiteSpace(filter) && ruleDescription != null) { client.RemoveRule("$Default"); } // If we don't have a rule and there is no longer a default rule, it will cause issues else if (string.IsNullOrWhiteSpace(filter) && !rules.Any()) { ruleDescription = new RuleDescription ( "$Default", new SqlFilter("1=1") ); client.AddRule(ruleDescription); } } catch (MessagingEntityNotFoundException) { } if (!reAddRule) { return; } int loopCounter = 0; while (loopCounter < 10) { try { RuleDescription ruleDescription = new RuleDescription ( "CqrsConfiguredFilter", new SqlFilter(filter) ); client.AddRule(ruleDescription); break; } catch (MessagingEntityAlreadyExistsException exception) { loopCounter++; // Still waiting for the delete to complete Thread.Sleep(1000); if (loopCounter == 9) { Logger.LogError("Setting the filter failed as it already exists.", exception: exception); TelemetryHelper.TrackException(exception); } } catch (Exception exception) { Logger.LogError("Setting the filter failed.", exception: exception); TelemetryHelper.TrackException(exception); break; } } }); }
public ActionResult Review(CheckoutViewModel data) { if (ModelState.IsValid) { try { NVPAPICaller gatewayCaller = new NVPAPICaller(); string token = ""; NVPCodec decoder = new NVPCodec(); // Call the gateway payment authorization API: bool ret = gatewayCaller.DoCheckoutAuth(data.Order, ref token, ref decoder); // If authorizaton is successful: if (ret) { // Hydrate a new Order model from our OrderViewModel. var myOrder = Mapper.Map <Data.Models.Order>(data.Order); // Timestamp with a UTC date. myOrder.OrderDate = DateTime.UtcNow; // Get DB context. ProductContext _db = new ProductContext(); // Add order to DB. _db.Orders.Add(myOrder); _db.SaveChanges(); // Get the shopping cart items and process them. using (ShoppingCartActions usersShoppingCart = new ShoppingCartActions(cartId)) { List <CartItem> myOrderList = usersShoppingCart.GetCartItems(); // Add OrderDetail information to the DB for each product purchased. for (int i = 0; i < myOrderList.Count; i++) { // Create a new OrderDetail object. var myOrderDetail = new OrderDetail(); myOrderDetail.OrderId = myOrder.OrderId; myOrderDetail.ProductId = myOrderList[i].ProductId; myOrderDetail.Quantity = myOrderList[i].Quantity; myOrderDetail.UnitPrice = myOrderList[i].Product.UnitPrice; // Add OrderDetail to DB. _db.OrderDetails.Add(myOrderDetail); _db.SaveChanges(); } // Set OrderId. Session["currentOrderId"] = myOrder.OrderId; Session["Token"] = token; // Report successful event to Application Insights. var eventProperties = new Dictionary <string, string>(); eventProperties.Add("CustomerEmail", data.Order.Email); eventProperties.Add("NumberOfItems", myOrderList.Count.ToString()); eventProperties.Add("OrderTotal", data.Order.Total.ToString("C2")); eventProperties.Add("OrderId", myOrder.OrderId.ToString()); TelemetryHelper.TrackEvent("SuccessfulPaymentAuth", eventProperties); data.Order.OrderId = myOrder.OrderId; if (data.Order.CreditCardNumber.Length > 4) { // Only show the last 4 digits of the credit card number: data.Order.CreditCardNumber = "xxxxxxxxxxx" + data.Order.CreditCardNumber.Substring(data.Order.CreditCardNumber.Length - 4); } } } else { var error = gatewayCaller.PopulateGatewayErrorModel(decoder); // Report failed event to Application Insights. Exception ex = new Exception(error.ToString()); ex.Source = "Contoso.Apps.SportsLeague.Web.CheckoutController.cs"; TelemetryHelper.TrackException(ex); // Redirect to the checkout error view: return(RedirectToAction("Error", error)); } } catch (WebException wex) { ExceptionUtility.LogException(wex, "CheckoutController.cs Complete Action"); var error = new CheckoutErrorViewModel { ErrorCode = wex.Message }; if (wex.Response != null && wex.Response.GetType() == typeof(HttpWebResponse)) { // Extract the response body from the WebException's HttpWebResponse: error.LongMessage = ((HttpWebResponse)wex.Response).StatusDescription; } // Redirect to the checkout error view: return(RedirectToAction("Error", error)); } catch (Exception ex) { ExceptionUtility.LogException(ex, "CheckoutController.cs Review Action"); var error = new CheckoutErrorViewModel { ErrorCode = ex.Message }; // Redirect to the checkout error view: return(RedirectToAction("Error", error)); } } return(View(data)); }
public virtual void Publish <TCommand>(IEnumerable <TCommand> commands) where TCommand : ICommand <TAuthenticationToken> { IList <TCommand> sourceCommands = commands.ToList(); DateTimeOffset startedAt = DateTimeOffset.UtcNow; Stopwatch mainStopWatch = Stopwatch.StartNew(); string responseCode = "200"; bool wasSuccessfull = false; IDictionary <string, string> telemetryProperties = new Dictionary <string, string> { { "Type", "Azure/EventHub" } }; string telemetryName = "Commands"; string telemetryNames = string.Empty; foreach (TCommand command in sourceCommands) { string subTelemetryName = string.Format("{0}/{1}", command.GetType().FullName, command.Id); var telemeteredCommand = command as ITelemeteredMessage; if (telemeteredCommand != null) { subTelemetryName = telemeteredCommand.TelemetryName; } telemetryNames = string.Format("{0}{1},", telemetryNames, subTelemetryName); } if (telemetryNames.Length > 0) { telemetryNames = telemetryNames.Substring(0, telemetryNames.Length - 1); } telemetryProperties.Add("Commands", telemetryNames); try { IList <string> sourceCommandMessages = new List <string>(); IList <EventData> brokeredMessages = new List <EventData>(sourceCommands.Count); foreach (TCommand command in sourceCommands) { if (!AzureBusHelper.PrepareAndValidateCommand(command, "Azure-EventHub")) { continue; } var brokeredMessage = new EventData(Encoding.UTF8.GetBytes(MessageSerialiser.SerialiseCommand(command))); brokeredMessage.Properties.Add("Type", command.GetType().FullName); brokeredMessages.Add(brokeredMessage); sourceCommandMessages.Add(string.Format("A command was sent of type {0}.", command.GetType().FullName)); } try { EventHubPublisher.SendBatch(brokeredMessages); } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish a command.", exception: exception, metaData: new Dictionary <string, object> { { "Commands", sourceCommands } }); throw; } foreach (string message in sourceCommandMessages) { Logger.LogInfo(message); } wasSuccessfull = true; } finally { mainStopWatch.Stop(); TelemetryHelper.TrackDependency("Azure/EventHub/CommandBus", "Command", telemetryName, null, startedAt, mainStopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } }
/// <summary> /// Send a feedback to telemetry server. /// </summary> /// <param name="type">Feedback type.</param> /// <param name="title">Feedback title.</param> /// <param name="content">Feedback content.</param> /// <param name="contactInfo">Contact information (if available)</param> /// <param name="image">Image (optional)</param> /// <returns>An awaitable task.</returns> public static async Task SendfeedbackAsync(FeedbackType type, string title, string content, string contactInfo, ImageUploadResult image = null) { var currentVersion = $"{Package.Current.Id.Version.Major}." + $"{Package.Current.Id.Version.Minor}." + $"{Package.Current.Id.Version.Build}." + $"{Package.Current.Id.Version.Revision}" + $"_{Package.Current.Id.Architecture}"; var osVersion = ulong.Parse(AnalyticsInfo.VersionInfo.DeviceFamilyVersion); var osMajor = (osVersion & 0xFFFF000000000000L) >> 48; var osMinor = (osVersion & 0x0000FFFF00000000L) >> 32; var osBuild = (osVersion & 0x00000000FFFF0000L) >> 16; var osRev = osVersion & 0x000000000000FFFFL; var feedback = new Models.Feedback { Type = type, Title = title, Content = content, RequestId = Guid.NewGuid(), RequestTimeUtc = DateTime.UtcNow, Runtime = new RuntimeInfo { ApplicationVersion = currentVersion, CurrentLanguage = CultureInfo.CurrentCulture.Name, DeviceFamily = AnalyticsInfo.VersionInfo.DeviceFamily, OsReleaseVersion = $"{osMajor}.{osMinor}.{osBuild}.{osRev}" }, ContactInfo = contactInfo }; if (TelemetryHelper.OptinTelemetry) { // Track an event before getting info await TelemetryHelper.TraceEventAsync("Feedback", new Dictionary <string, string> { { "FeedbackId", feedback.RequestId.ToString() } }); feedback.TelemetryMetadata = new ApplicationInsightInfo { UniqueDeviceId = TelemetryHelper.DeviceId, UniqueInstrumentationId = TelemetryHelper.InstrumentationId, UniqueUserId = TelemetryHelper.UserId }; } if (image != null) { feedback.ImageId = image.ImageId; } // Serialize request var requestContent = JsonConvert.SerializeObject(feedback); // Send request using (var httpClient = new HttpClient()) { var uploadEndpoint = $"{RequestUrlPrefix}/api/Feedback"; var result = await httpClient.PostAsync(new Uri(uploadEndpoint), new HttpStringContent( requestContent, UnicodeEncoding.Utf8, JsonMimeType)); // Check if request is throttled. if (result.StatusCode == HttpStatusCode.TooManyRequests) { throw new RequestThrottledException(); } // For other scenarios, HTTP 200 is excepted else if (result.StatusCode != HttpStatusCode.Ok) { throw new FeedbackServerErrorException(); } } }
/// <summary> /// Publishes the provided <paramref name="events"/> on the event bus. /// </summary> public virtual void Publish <TEvent>(IEnumerable <TEvent> events) where TEvent : IEvent <TAuthenticationToken> { IList <TEvent> sourceEvents = events.ToList(); DateTimeOffset startedAt = DateTimeOffset.UtcNow; Stopwatch mainStopWatch = Stopwatch.StartNew(); string responseCode = null; bool mainWasSuccessfull = false; IDictionary <string, string> telemetryProperties = new Dictionary <string, string> { { "Type", "Azure/Servicebus" } }; string telemetryName = "Events"; string telemetryNames = string.Empty; foreach (TEvent @event in sourceEvents) { string subTelemetryName = string.Format("{0}/{1}", @event.GetType().FullName, @event.Id); var telemeteredEvent = @event as ITelemeteredMessage; if (telemeteredEvent != null) { subTelemetryName = telemeteredEvent.TelemetryName; } telemetryNames = string.Format("{0}{1},", telemetryNames, subTelemetryName); } if (telemetryNames.Length > 0) { telemetryNames = telemetryNames.Substring(0, telemetryNames.Length - 1); } telemetryProperties.Add("Events", telemetryNames); try { IList <string> sourceEventMessages = new List <string>(); IList <BrokeredMessage> privateBrokeredMessages = new List <BrokeredMessage>(sourceEvents.Count); IList <BrokeredMessage> publicBrokeredMessages = new List <BrokeredMessage>(sourceEvents.Count); foreach (TEvent @event in sourceEvents) { if (!AzureBusHelper.PrepareAndValidateEvent(@event, "Azure-ServiceBus")) { continue; } var brokeredMessage = new BrokeredMessage(MessageSerialiser.SerialiseEvent(@event)) { CorrelationId = CorrelationIdHelper.GetCorrelationId().ToString("N") }; brokeredMessage.Properties.Add("Type", @event.GetType().FullName); var privateEventAttribute = Attribute.GetCustomAttribute(typeof(TEvent), typeof(PrivateEventAttribute)) as PrivateEventAttribute; var publicEventAttribute = Attribute.GetCustomAttribute(typeof(TEvent), typeof(PrivateEventAttribute)) as PublicEventAttribute; if ( // Backwards compatibility and simplicity (publicEventAttribute == null && privateEventAttribute == null) || publicEventAttribute != null ) { publicBrokeredMessages.Add(brokeredMessage); sourceEventMessages.Add(string.Format("An event was published on the public bus with the id '{0}' was of type {1}.", @event.Id, @event.GetType().FullName)); } if (privateEventAttribute != null) { privateBrokeredMessages.Add(brokeredMessage); sourceEventMessages.Add(string.Format("An event was published on the private bus with the id '{0}' was of type {1}.", @event.Id, @event.GetType().FullName)); } } bool wasSuccessfull; Stopwatch stopWatch = Stopwatch.StartNew(); // Backwards compatibility and simplicity stopWatch.Restart(); responseCode = "200"; wasSuccessfull = false; try { PublicServiceBusPublisher.SendBatch(publicBrokeredMessages); wasSuccessfull = true; } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the event being sent was too large.", exception: exception, metaData: new Dictionary <string, object> { { "Events", publicBrokeredMessages } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish an event.", exception: exception, metaData: new Dictionary <string, object> { { "Events", publicBrokeredMessages } }); throw; } finally { TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Event", telemetryName, "Public Bus", startedAt, stopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } stopWatch.Restart(); responseCode = "200"; wasSuccessfull = false; try { PrivateServiceBusPublisher.SendBatch(privateBrokeredMessages); wasSuccessfull = true; } catch (QuotaExceededException exception) { responseCode = "429"; Logger.LogError("The size of the event being sent was too large.", exception: exception, metaData: new Dictionary <string, object> { { "Events", privateBrokeredMessages } }); throw; } catch (Exception exception) { responseCode = "500"; Logger.LogError("An issue occurred while trying to publish an event.", exception: exception, metaData: new Dictionary <string, object> { { "Events", privateBrokeredMessages } }); throw; } finally { TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Event", telemetryName, "Private Bus", startedAt, stopWatch.Elapsed, responseCode, wasSuccessfull, telemetryProperties); } foreach (string message in sourceEventMessages) { Logger.LogInfo(message); } mainWasSuccessfull = true; } finally { mainStopWatch.Stop(); TelemetryHelper.TrackDependency("Azure/Servicebus/EventBus", "Event", telemetryName, null, startedAt, mainStopWatch.Elapsed, responseCode, mainWasSuccessfull, telemetryProperties); } }
/// <summary> /// Calls <see cref="AzureServiceBus{TAuthenticationToken}.InstantiateReceiving()"/> /// then uses a <see cref="Task"/> to apply the <see cref="FilterKey"/> as a <see cref="RuleDescription"/> /// to the <see cref="IMessageReceiver"/> instances in <paramref name="serviceBusReceivers"/>. /// </summary> /// <param name="manager">The <see cref="Manager"/>.</param> /// <param name="serviceBusReceivers">The receivers collection to place <see cref="IMessageReceiver"/> instances into.</param> /// <param name="topicName">The topic name.</param> /// <param name="topicSubscriptionName">The topic subscription name.</param> protected override void InstantiateReceiving(Manager manager, IDictionary <int, IMessageReceiver> serviceBusReceivers, string topicName, string topicSubscriptionName) { base.InstantiateReceiving(manager, serviceBusReceivers, topicName, topicSubscriptionName); Task.Factory.StartNewSafely (() => { // Because refreshing the rule can take a while, we only want to do this when the value changes string filter; if (!ConfigurationManager.TryGetSetting(FilterKeyConfigurationKey, out filter)) { return; } if (FilterKey.ContainsKey(topicName) && FilterKey[topicName] == filter) { return; } FilterKey[topicName] = filter; // https://docs.microsoft.com/en-us/azure/application-insights/app-insights-analytics-reference#summarize-operator // http://www.summa.com/blog/business-blog/everything-you-need-to-know-about-azure-service-bus-brokered-messaging-part-2#rulesfiltersactions // https://github.com/Azure-Samples/azure-servicebus-messaging-samples/tree/master/TopicFilters #if NET452 SubscriptionClient client = serviceBusReceivers[0]; #endif #if NETSTANDARD2_0 // Since the IMessageReceiver and it's concrete implementation doesn't allow for the management of rules, we're creating a SubscriptionClient just to do the rules... it gets cleaned up somewhat below. SubscriptionClient client = new SubscriptionClient(ConnectionString, topicName, topicSubscriptionName); #endif bool reAddRule = false; try { #if NET452 IEnumerable <RuleDescription> rules = manager.GetRules(client.TopicPath, client.Name).ToList(); #endif #if NETSTANDARD2_0 Task <IList <RuleDescription> > getRulesTask = manager.GetRulesAsync(client.TopicPath, client.SubscriptionName); getRulesTask.Wait(); IEnumerable <RuleDescription> rules = getRulesTask.Result; #endif RuleDescription ruleDescription = rules.SingleOrDefault(rule => rule.Name == "CqrsConfiguredFilter"); if (ruleDescription != null) { var sqlFilter = ruleDescription.Filter as SqlFilter; if (sqlFilter == null && !string.IsNullOrWhiteSpace(filter)) { reAddRule = true; } else if (sqlFilter != null && sqlFilter.SqlExpression != filter) { reAddRule = true; } if (sqlFilter != null && reAddRule) { #if NET452 client.RemoveRule("CqrsConfiguredFilter"); #endif #if NETSTANDARD2_0 client.RemoveRuleAsync("CqrsConfiguredFilter").Wait(); #endif } } else if (!string.IsNullOrWhiteSpace(filter)) { reAddRule = true; } ruleDescription = rules.SingleOrDefault(rule => rule.Name == "$Default"); // If there is a default rule and we have a rule, it will cause issues if (!string.IsNullOrWhiteSpace(filter) && ruleDescription != null) { #if NET452 client.RemoveRule("$Default"); #endif #if NETSTANDARD2_0 client.RemoveRuleAsync("$Default").Wait(); #endif } // If we don't have a rule and there is no longer a default rule, it will cause issues else if (string.IsNullOrWhiteSpace(filter) && !rules.Any()) { ruleDescription = new RuleDescription ( "$Default", new SqlFilter("1=1") ); #if NET452 client.AddRule(ruleDescription); #endif #if NETSTANDARD2_0 client.AddRuleAsync(ruleDescription).Wait(); #endif } } catch (AggregateException ex) { if (!(ex.InnerException is MessagingEntityNotFoundException)) { throw; } } catch (MessagingEntityNotFoundException) { } if (!reAddRule) { return; } int loopCounter = 0; while (loopCounter < 10) { try { RuleDescription ruleDescription = new RuleDescription ( "CqrsConfiguredFilter", new SqlFilter(filter) ); #if NET452 client.AddRule(ruleDescription); #endif #if NETSTANDARD2_0 client.AddRuleAsync(ruleDescription).Wait(); #endif break; } catch (MessagingEntityAlreadyExistsException exception) { loopCounter++; // Still waiting for the delete to complete Thread.Sleep(1000); if (loopCounter == 9) { Logger.LogError("Setting the filter failed as it already exists.", exception: exception); TelemetryHelper.TrackException(exception); } } catch (Exception exception) { Logger.LogError("Setting the filter failed.", exception: exception); TelemetryHelper.TrackException(exception); break; } } #if NETSTANDARD2_0 client.CloseAsync(); #endif }); }