public MailMessage GetEventNotificationMailMessage(EventNotification model) {
            string messageOrSource = !String.IsNullOrEmpty(model.Event.Message) ? model.Event.Message : model.Event.Source;
            if (String.IsNullOrEmpty(messageOrSource))
                return null;

            string notificationType = "Occurrence event";
            if (model.IsNew)
                notificationType = "New event";
            else if (model.IsRegression)
                notificationType = "Regression event";

            if (model.IsCritical)
                notificationType = String.Concat("Critical ", notificationType.ToLower());

            var requestInfo = model.Event.GetRequestInfo();
            var mailerModel = new EventNotificationModel(model) {
                BaseUrl = Settings.Current.BaseURL,
                Subject = String.Concat(notificationType, ": ", messageOrSource.Truncate(120)),
                Message = model.Event.Message,
                Source = model.Event.Source,
                Url = requestInfo != null ? requestInfo.GetFullPath(true, true, true) : null
            };

            return _emailGenerator.GenerateMessage(mailerModel, "Notice").ToMailMessage();
        }
        public MailMessage GetEventNotificationMailMessage(EventNotification model) {
            if (!ShouldHandle(model.Event))
                return null;

            var error = model.Event.GetError();
            var stackingTarget = error.GetStackingTarget();
            var requestInfo = model.Event.GetRequestInfo();

            string notificationType = String.Concat(error.Type, " Occurrence");
            if (model.IsNew)
                notificationType = String.Concat("New ", error.Type);
            else if (model.IsRegression)
                notificationType = String.Concat(error.Type, " Regression");

            if (model.IsCritical)
                notificationType = String.Concat("Critical ", notificationType);

            var mailerModel = new EventNotificationModel(model) {
                BaseUrl = Settings.Current.BaseURL,
                NotificationType = notificationType,
                Url = requestInfo != null ? requestInfo.GetFullPath(true, true) : null,
                Error = stackingTarget.Error,
                Method = stackingTarget.Method,
            };

            return _emailGenerator.GenerateMessage(mailerModel, "Notice");
        }
        public override MailMessage GetEventNotificationMailMessage(EventNotification model) {
            if (!ShouldHandle(model.Event))
                return null;

            var error = model.Event.GetSimpleError();
            if (error == null)
                return null;

            var requestInfo = model.Event.GetRequestInfo();
            string errorType = !String.IsNullOrEmpty(error.Type) ? error.Type : "Error";

            string notificationType = String.Concat(errorType, " Occurrence");
            if (model.IsNew)
                notificationType = String.Concat(!model.IsCritical ? "New " : "new ", errorType);
            else if (model.IsRegression)
                notificationType = String.Concat(errorType, " Regression");

            if (model.IsCritical)
                notificationType = String.Concat("Critical ", notificationType);

            var mailerModel = new EventNotificationModel(model) {
                BaseUrl = Settings.Current.BaseURL,
                Subject = String.Concat(notificationType, ": ", error.Message.Truncate(120)),
                Message = error.Message,
                Url = requestInfo != null ? requestInfo.GetFullPath(true, true, true) : null
            };

            return _emailGenerator.GenerateMessage(mailerModel, "NoticeError").ToMailMessage();
        }
        public override MailMessage GetEventNotificationMailMessage(EventNotification model)
        {
            if (!ShouldHandle(model.Event))
                return null;

            var error = model.Event.GetError();
            var stackingTarget = error?.GetStackingTarget();
            if (stackingTarget?.Error == null)
                return null;

            var requestInfo = model.Event.GetRequestInfo();
            string errorType = !String.IsNullOrEmpty(stackingTarget.Error.Type) ? stackingTarget.Error.Type : "Error";

            string notificationType = String.Concat(errorType, " occurrence");
            if (model.IsNew)
                notificationType = String.Concat(!model.IsCritical ? "New " : "new ", error.Type);
            else if (model.IsRegression)
                notificationType = String.Concat(errorType, " regression");

            if (model.IsCritical)
                notificationType = String.Concat("Critical ", notificationType);

            var mailerModel = new EventNotificationModel(model) {
                BaseUrl = Settings.Current.BaseURL,
                Subject = String.Concat(notificationType, ": ", stackingTarget.Error.Message.Truncate(120)),
                Url = requestInfo?.GetFullPath(true, true, true),
                Message = stackingTarget.Error.Message,
                TypeFullName = errorType,
                MethodFullName = stackingTarget.Method?.GetFullName()
            };

            return _emailGenerator.GenerateMessage(mailerModel, "NoticeError").ToMailMessage();
        }
 public EventNotificationModel(EventNotification notification) {
     Event = notification.Event;
     ProjectName = notification.ProjectName;
     IsNew = notification.IsNew;
     IsCritical = notification.IsCritical;
     IsRegression = notification.IsRegression;
     TotalOccurrences = notification.TotalOccurrences;
 }
        public override MailMessage GetEventNotificationMailMessage(EventNotification model) {
            if (!ShouldHandle(model.Event))
                return null;

            var mailerModel = new EventNotificationModel(model) {
                BaseUrl = Settings.Current.BaseURL,
                Subject = String.Concat("Feature: ", model.Event.Source.Truncate(120)),
            };

            return _emailGenerator.GenerateMessage(mailerModel, "Notice").ToMailMessage();
        }
        public override MailMessage GetEventNotificationMailMessage(EventNotification model) {
            if (!ShouldHandle(model.Event))
                return null;

            string notificationType = "Log Message";
            if (model.IsNew)
                notificationType = "New Log Source";
            else if (model.IsRegression)
                notificationType = "Log Regression";

            if (model.IsCritical)
                notificationType = String.Concat("Critical ", notificationType.ToLower());

           var requestInfo = model.Event.GetRequestInfo();
            var mailerModel = new EventNotificationModel(model) {
                BaseUrl = Settings.Current.BaseURL,
                Subject = String.Concat(notificationType, ": ", model.Event.Source.Truncate(120)),
                Url = requestInfo != null ? requestInfo.GetFullPath(true, true, true) : model.Event.Source
            };

            return _emailGenerator.GenerateMessage(mailerModel, "Notice").ToMailMessage();
        }
        protected async override Task<JobResult> RunInternalAsync(CancellationToken token) {
            QueueEntry<EventNotificationWorkItem> queueEntry = null;
            try {
                queueEntry = _queue.Dequeue();
            } catch (Exception ex) {
                if (!(ex is TimeoutException))
                    return JobResult.FromException(ex, "An error occurred while trying to dequeue the next EventNotification: {0}", ex.Message);
            }
            if (queueEntry == null)
                return JobResult.Success;

            var eventModel = _eventRepository.GetById(queueEntry.Value.EventId);
            if (eventModel == null) {
                queueEntry.Abandon();
                return JobResult.FailedWithMessage("Could not load event {0}.", queueEntry.Value.EventId);
            }

            var eventNotification = new EventNotification(queueEntry.Value, eventModel);
            bool shouldLog = eventNotification.Event.ProjectId != Settings.Current.InternalProjectId;
            int emailsSent = 0;
            Log.Trace().Message("Process notification: project={0} event={1} stack={2}", eventNotification.Event.ProjectId, eventNotification.Event.Id, eventNotification.Event.StackId).WriteIf(shouldLog);

            var project = _projectRepository.GetById(eventNotification.Event.ProjectId, true);
            if (project == null) {
                queueEntry.Abandon();
                return JobResult.FailedWithMessage("Could not load project {0}.", eventNotification.Event.ProjectId);
            }
            Log.Trace().Message("Loaded project: name={0}", project.Name).WriteIf(shouldLog);

            var organization = _organizationRepository.GetById(project.OrganizationId, true);
            if (organization == null) {
                queueEntry.Abandon();
                return JobResult.FailedWithMessage("Could not load organization {0}.", project.OrganizationId);
            }
            Log.Trace().Message("Loaded organization: name={0}", organization.Name).WriteIf(shouldLog);

            var stack = _stackRepository.GetById(eventNotification.Event.StackId);
            if (stack == null) {
                queueEntry.Abandon();
                return JobResult.FailedWithMessage("Could not load stack {0}.", eventNotification.Event.StackId);
            }

            if (!organization.HasPremiumFeatures) {
                queueEntry.Complete();
                Log.Info().Message("Skipping \"{0}\" because organization \"{1}\" does not have premium features.", eventNotification.Event.Id, eventNotification.Event.OrganizationId).WriteIf(shouldLog);
                return JobResult.Success;
            }

            if (stack.DisableNotifications || stack.IsHidden) {
                queueEntry.Complete();
                Log.Info().Message("Skipping \"{0}\" because stack \"{1}\" notifications are disabled or stack is hidden.", eventNotification.Event.Id, eventNotification.Event.StackId).WriteIf(shouldLog);
                return JobResult.Success;
            }

            if (token.IsCancellationRequested) {
                queueEntry.Abandon();
                return JobResult.Cancelled;
            }

            Log.Trace().Message("Loaded stack: title={0}", stack.Title).WriteIf(shouldLog);
            int totalOccurrences = stack.TotalOccurrences;

            // after the first 2 occurrences, don't send a notification for the same stack more then once every 30 minutes
            var lastTimeSent = _cacheClient.Get<DateTime>(String.Concat("notify:stack-throttle:", eventNotification.Event.StackId));
            if (totalOccurrences > 2
                && !eventNotification.IsRegression
                && lastTimeSent != DateTime.MinValue
                && lastTimeSent > DateTime.Now.AddMinutes(-30)) {
                queueEntry.Complete();
                Log.Info().Message("Skipping message because of stack throttling: last sent={0} occurrences={1}", lastTimeSent, totalOccurrences).WriteIf(shouldLog);
                return JobResult.Success;
            }

            // don't send more than 10 notifications for a given project every 30 minutes
            var projectTimeWindow = TimeSpan.FromMinutes(30);
            string cacheKey = String.Concat("notify:project-throttle:", eventNotification.Event.ProjectId, "-", DateTime.UtcNow.Floor(projectTimeWindow).Ticks);
            long notificationCount = _cacheClient.Increment(cacheKey, 1, projectTimeWindow);
            if (notificationCount > 10 && !eventNotification.IsRegression) {
                queueEntry.Complete();
                Log.Info().Project(eventNotification.Event.ProjectId).Message("Skipping message because of project throttling: count={0}", notificationCount).WriteIf(shouldLog);
                return JobResult.Success;
            }

            if (token.IsCancellationRequested) {
                queueEntry.Abandon();
                return JobResult.Cancelled;
            }

            foreach (var kv in project.NotificationSettings) {
                var settings = kv.Value;
                Log.Trace().Message("Processing notification: user={0}", kv.Key).WriteIf(shouldLog);

                var user = _userRepository.GetById(kv.Key);
                if (user == null || String.IsNullOrEmpty(user.EmailAddress)) {
                    Log.Error().Message("Could not load user {0} or blank email address {1}.", kv.Key, user != null ? user.EmailAddress : "").Write();
                    continue;
                }

                if (!user.IsEmailAddressVerified) {
                    Log.Info().Message("User {0} with email address {1} has not been verified.", kv.Key, user != null ? user.EmailAddress : "").WriteIf(shouldLog);
                    continue;
                }

                if (!user.EmailNotificationsEnabled) {
                    Log.Info().Message("User {0} with email address {1} has email notifications disabled.", kv.Key, user != null ? user.EmailAddress : "").WriteIf(shouldLog);
                    continue;
                }

                if (!user.OrganizationIds.Contains(project.OrganizationId)) {
                    Log.Error().Message("Unauthorized user: project={0} user={1} organization={2} event={3}", project.Id, kv.Key, project.OrganizationId, eventNotification.Event.Id).Write();
                    continue;
                }

                Log.Trace().Message("Loaded user: email={0}", user.EmailAddress).WriteIf(shouldLog);

                bool shouldReportNewError = settings.ReportNewErrors && eventNotification.IsNew && eventNotification.Event.IsError();
                bool shouldReportCriticalError = settings.ReportCriticalErrors && eventNotification.IsCritical && eventNotification.Event.IsError();
                bool shouldReportRegression = settings.ReportEventRegressions && eventNotification.IsRegression;
                bool shouldReportNewEvent = settings.ReportNewEvents && eventNotification.IsNew;
                bool shouldReportCriticalEvent = settings.ReportCriticalEvents && eventNotification.IsCritical;
                bool shouldReport = shouldReportNewError || shouldReportCriticalError || shouldReportRegression || shouldReportNewEvent || shouldReportCriticalEvent;

                Log.Trace().Message("Settings: newerror={0} criticalerror={1} regression={2} new={3} critical={4}",
                    settings.ReportNewErrors, settings.ReportCriticalErrors,
                    settings.ReportEventRegressions, settings.ReportNewEvents, settings.ReportCriticalEvents).WriteIf(shouldLog);
                Log.Trace().Message("Should process: newerror={0} criticalerror={1} regression={2} new={3} critical={4}",
                    shouldReportNewError, shouldReportCriticalError,
                    shouldReportRegression, shouldReportNewEvent, shouldReportCriticalEvent).WriteIf(shouldLog);

                var requestInfo = eventNotification.Event.GetRequestInfo();
                // check for known bots if the user has elected to not report them
                if (shouldReport && requestInfo != null && !String.IsNullOrEmpty(requestInfo.UserAgent)) {
                    ClientInfo info = null;
                    try {
                        info = Parser.GetDefault().Parse(requestInfo.UserAgent);
                    } catch (Exception ex) {
                        Log.Warn().Project(eventNotification.Event.ProjectId).Message("Unable to parse user agent {0}. Exception: {1}",
                            requestInfo.UserAgent, ex.Message).Write();
                    }

                    var botPatterns = project.Configuration.Settings.ContainsKey(SettingsDictionary.KnownKeys.UserAgentBotPatterns)
                        ? project.Configuration.Settings.GetStringCollection(SettingsDictionary.KnownKeys.UserAgentBotPatterns).ToList()
                        : new List<string>();

                    if (info != null && info.Device.IsSpider || requestInfo.UserAgent.AnyWildcardMatches(botPatterns)) {
                        shouldReport = false;
                        Log.Info().Message("Skipping because event is from a bot \"{0}\".", requestInfo.UserAgent).WriteIf(shouldLog);
                    }
                }

                if (!shouldReport)
                    continue;

                var model = new EventNotificationModel(eventNotification) {
                    ProjectName = project.Name,
                    TotalOccurrences = totalOccurrences
                };

                // don't send notifications in non-production mode to email addresses that are not on the outbound email list.
                if (Settings.Current.WebsiteMode != WebsiteMode.Production
                    && !Settings.Current.AllowedOutboundAddresses.Contains(v => user.EmailAddress.ToLowerInvariant().Contains(v))) {
                        Log.Info().Message("Skipping because email is not on the outbound list and not in production mode.").WriteIf(shouldLog);
                    continue;
                }
                
                Log.Trace().Message("Sending email to {0}...", user.EmailAddress).Write();
                _mailer.SendNotice(user.EmailAddress, model);
                emailsSent++;
                Log.Trace().Message("Done sending email.").WriteIf(shouldLog);
            }

            // if we sent any emails, mark the last time a notification for this stack was sent.
            if (emailsSent > 0) {
                _cacheClient.Set(String.Concat("notify:stack-throttle:", eventNotification.Event.StackId), DateTime.Now, DateTime.Now.AddMinutes(15));
                Log.Info().Message("Notifications sent: event={0} stack={1} count={2}", eventNotification.Event.Id, eventNotification.Event.StackId, emailsSent).WriteIf(shouldLog);
            }

            queueEntry.Complete();
            return JobResult.Success;
        }
Example #9
0
        public void SendNotice(string emailAddress, EventNotification model) {
            var message = _pluginManager.GetEventNotificationMailMessage(model);
            if (message == null) {
                Log.Warn().Message("Unable to create event notification mail message for event \"{0}\". User: \"{1}\"", model.EventId, emailAddress).Write();
                return;
            }

            message.To = emailAddress;
            QueueMessage(message.ToMailMessage());
        }
Example #10
0
 public Task SendNoticeAsync(string emailAddress, EventNotification notification) {
     return Task.Run(() => SendNotice(emailAddress, notification));
 }
Example #11
0
 public void SendNotice(string emailAddress, EventNotification model) {}
Example #12
0
        public async Task SendNoticeAsync(string emailAddress, EventNotification model) {
            var message = _pluginManager.GetEventNotificationMailMessage(model);
            if (message == null)
                return;

            message.To = emailAddress;
            await QueueMessage(message.ToMailMessage());
        }
Example #13
0
        public Task SendNoticeAsync(string emailAddress, EventNotification model) {
            var message = _pluginManager.GetEventNotificationMailMessage(model);
            if (message == null) {
                Logger.Warn().Message("Unable to create event notification mail message for event \"{0}\". User: \"{1}\"", model.EventId, emailAddress).Write();
                return TaskHelper.Completed();
            }

            message.To = emailAddress;
            return QueueMessageAsync(message.ToMailMessage());
        }
Example #14
0
 public Task SendNoticeAsync(string emailAddress, EventNotification model) {
     return Task.Delay(0);
 }
Example #15
0
 public Task SendNoticeAsync(string emailAddress, EventNotification notification)
 {
     return Task.FromResult(0);
 }
Example #16
0
 public void SendNotice(string emailAddress, EventNotification model) {
     var msg = _pluginManager.GetEventNotificationMailMessage(model);
     msg.To.Add(emailAddress);
     msg.Headers.Add("X-Mailer-Machine", Environment.MachineName);
     msg.Headers.Add("X-Mailer-Date", DateTime.Now.ToString());
     SendMessage(msg);
 }
 public virtual MailMessage GetEventNotificationMailMessage(EventNotification model)
 {
     return null;
 }
Example #18
0
 public Task SendNoticeAsync(string emailAddress, EventNotification model) {
     return Task.CompletedTask;
 }
Example #19
0
        public Task SendEventNoticeAsync(string emailAddress, EventNotification model) {
            var msg = _pluginManager.GetEventNotificationMailMessage(model);
            if (msg == null) {
                _logger.Warn("Unable to create event notification mail message for event \"{0}\". User: \"{1}\"", model.EventId, emailAddress);
                return Task.CompletedTask;
            }

            msg.To = emailAddress;
            return QueueMessageAsync(msg.ToMailMessage(), "eventnotice");
        }
Example #20
0
 public async Task SendNoticeAsync(string emailAddress, EventNotification model) {
     var msg = _pluginManager.GetEventNotificationMailMessage(model);
     msg.To.Add(emailAddress);
     await QueueMessage(msg);
 }