public void SyncErrorStackTags() { const string Tag1 = "Tag One"; const string Tag2 = "Tag Two"; const string Tag2_Lowercase = "tag two"; Error error = ErrorData.GenerateError(id: TestConstants.ErrorId, projectId: TestConstants.ProjectId, organizationId: TestConstants.OrganizationId, nestingLevel: 5, minimiumNestingLevel: 1); error.Tags = new TagSet { Tag1 }; var pipeline = IoC.GetInstance <ErrorPipeline>(); Assert.DoesNotThrow(() => pipeline.Run(error)); error = Repository.GetById(error.Id); Assert.NotNull(error); Assert.NotNull(error.ErrorStackId); var stack = _errorStackRepository.GetById(error.ErrorStackId); Assert.Equal(new TagSet { Tag1 }, stack.Tags); error = ErrorData.GenerateError(errorStackId: error.ErrorStackId, projectId: TestConstants.ProjectId, organizationId: TestConstants.OrganizationId, nestingLevel: 5, minimiumNestingLevel: 1); error.Tags = new TagSet { Tag2 }; Assert.DoesNotThrow(() => pipeline.Run(error)); stack = _errorStackRepository.GetById(error.ErrorStackId); Assert.Equal(new TagSet { Tag1, Tag2 }, stack.Tags); error = ErrorData.GenerateError(errorStackId: error.ErrorStackId, projectId: TestConstants.ProjectId, organizationId: TestConstants.OrganizationId, nestingLevel: 5, minimiumNestingLevel: 1); error.Tags = new TagSet { Tag2_Lowercase }; Assert.DoesNotThrow(() => pipeline.Run(error)); stack = _errorStackRepository.GetById(error.ErrorStackId); Assert.Equal(new TagSet { Tag1, Tag2 }, stack.Tags); }
public ActionResult Index(string id) { if (String.IsNullOrEmpty(id)) { return(RedirectToAction("Index", "Project")); } ErrorStack errorStack = _repository.GetById(id); if (errorStack == null || !User.CanAccessOrganization(errorStack.OrganizationId)) { return(HttpNotFound("An error stack with this id was not found.")); } RouteData.SetOrganizationId(errorStack.OrganizationId); return(View(errorStack)); }
public StackStatsResult GetByStack(string stackId, DateTime?start = null, DateTime?end = null) { ErrorStack errorStack = _errorStackRepository.GetById(stackId); if (errorStack == null || !User.CanAccessOrganization(errorStack.OrganizationId)) { throw new ArgumentException("Invalid error stack id.", "stackId"); // TODO: These should probably throw http Response exceptions. } Project project = _projectRepository.GetByIdCached(errorStack.ProjectId); DateTime retentionUtcCutoff = _organizationRepository.GetByIdCached(project.OrganizationId).GetRetentionUtcCutoff(); return(_statsHelper.GetErrorStackStats(stackId, _projectRepository.GetDefaultTimeOffset(errorStack.ProjectId), start, end, retentionUtcCutoff)); }
private object ProcessNotification(IMessage <ErrorNotification> message) { int emailsSent = 0; ErrorNotification errorNotification = message.GetBody(); Log.Trace().Message("Process notification: project={0} error={1} stack={2}", errorNotification.ProjectId, errorNotification.ErrorId, errorNotification.ErrorStackId).Write(); var project = _projectRepository.GetByIdCached(errorNotification.ProjectId); if (project == null) { Log.Error().Message("Could not load project {0}.", errorNotification.ProjectId).Write(); return(null); } Log.Trace().Message("Loaded project: name={0}", project.Name).Write(); var organization = _organizationRepository.GetByIdCached(project.OrganizationId); if (organization == null) { Log.Error().Message("Could not load organization {0}.", project.OrganizationId).Write(); return(null); } Log.Trace().Message("Loaded organization: name={0}", organization.Name).Write(); var stack = _stackRepository.GetById(errorNotification.ErrorStackId); if (stack == null) { Log.Error().Message("Could not load stack {0}.", errorNotification.ErrorStackId).Write(); return(null); } if (!organization.HasPremiumFeatures) { Log.Trace().Message("Skipping because organization does not have premium features.").Write(); return(null); } if (stack.DisableNotifications || stack.IsHidden) { Log.Trace().Message("Skipping because stack notifications are disabled or it's hidden.").Write(); return(null); } Log.Trace().Message("Loaded stack: title={0}", stack.Title).Write(); int totalOccurrences = stack.TotalOccurrences; // after the first 5 occurrences, don't send a notification for the same stack more then once every 15 minutes var lastTimeSent = _cacheClient.Get <DateTime>(String.Concat("NOTIFICATION_THROTTLE_", errorNotification.ErrorStackId)); if (totalOccurrences > 5 && !errorNotification.IsRegression && lastTimeSent != DateTime.MinValue && lastTimeSent > DateTime.Now.AddMinutes(-15)) { Log.Info().Message("Skipping message because of throttling: last sent={0} occurrences={1}", lastTimeSent, totalOccurrences).Write(); return(null); } foreach (var kv in project.NotificationSettings) { var settings = kv.Value; Log.Trace().Message("Processing notification: user={0}", kv.Key).Write(); 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 : "").Write(); continue; } if (!user.EmailNotificationsEnabled) { Log.Trace().Message("User {0} with email address {1} has email notifications disabled.", kv.Key, user != null ? user.EmailAddress : "").Write(); continue; } if (!user.OrganizationIds.Contains(project.OrganizationId)) { // TODO: Should this notification setting be deleted? Log.Error().Message("Unauthorized user: project={0} user={1} organization={2} error={3}", project.Id, kv.Key, project.OrganizationId, errorNotification.ErrorId).Write(); continue; } Log.Trace().Message("Loaded user: email={0}", user.EmailAddress).Write(); bool shouldReportOccurrence = settings.Mode != NotificationMode.None; bool shouldReportCriticalError = settings.ReportCriticalErrors && errorNotification.IsCritical; bool shouldReportRegression = settings.ReportRegressions && errorNotification.IsRegression; Log.Trace().Message("Settings: mode={0} critical={1} regression={2} 404={3} bots={4}", settings.Mode, settings.ReportCriticalErrors, settings.ReportRegressions, settings.Report404Errors, settings.ReportKnownBotErrors).Write(); Log.Trace().Message("Should process: occurrence={0} critical={1} regression={2}", shouldReportOccurrence, shouldReportCriticalError, shouldReportRegression).Write(); if (settings.Mode == NotificationMode.New && !errorNotification.IsNew) { shouldReportOccurrence = false; Log.Trace().Message("Skipping because message is not new.").Write(); } // check for 404s if the user has elected to not report them if (shouldReportOccurrence && settings.Report404Errors == false && errorNotification.Code == "404") { shouldReportOccurrence = false; Log.Trace().Message("Skipping because message is 404.").Write(); } // check for known bots if the user has elected to not report them if (shouldReportOccurrence && settings.ReportKnownBotErrors == false && !String.IsNullOrEmpty(errorNotification.UserAgent)) { ClientInfo info = null; try { info = Parser.GetDefault().Parse(errorNotification.UserAgent); } catch (Exception ex) { Log.Warn().Project(errorNotification.ProjectId).Message("Unable to parse user agent {0}. Exception: {1}", errorNotification.UserAgent, ex.Message).Write(); } if (info != null && info.Device.IsSpider) { shouldReportOccurrence = false; Log.Trace().Message("Skipping because message is bot.").Write(); } } // stack being set to send all will override all other settings if (!shouldReportOccurrence && !shouldReportCriticalError && !shouldReportRegression) { continue; } var model = new ErrorNotificationModel(errorNotification) { 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.Trace().Message("Skipping because email is not on the outbound list and not in production mode.").Write(); continue; } Log.Trace().Message("Sending email to {0}...", user.EmailAddress).Write(); _mailer.SendNotice(user.EmailAddress, model); emailsSent++; Log.Trace().Message("Done sending email.").Write(); } // if we sent any emails, mark the last time a notification for this stack was sent. if (emailsSent > 0) { _cacheClient.Set(String.Concat("NOTIFICATION_THROTTLE_", errorNotification.ErrorStackId), DateTime.Now, DateTime.Now.AddMinutes(15)); } return(null); }