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);
        }
Esempio n. 2
0
        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));
        }
Esempio n. 4
0
        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);
        }