public async Task should_be_able_to_identity_tags_when_only_multiple_tags_are_specified_with_spaces() { var repos = Substitute.For <ITagsRepository>(); var provider = Substitute.For <ITagIdentifierProvider>(); provider.GetIdentifiers(Arg.Any <TagIdentifierContext>()).Returns(new ITagIdentifier[0]); var ctx = Substitute.For <IMessageContext>(); var incident = new IncidentSummaryDTO(1, "Ada"); var report = new ReportDTO { ContextCollections = new[] { new ContextCollectionDTO("Data", new Dictionary <string, string> { { "ErrTags[]", "MyTag" }, { "ErrTags[]", "YourTag" } }) } }; var e = new ReportAddedToIncident(incident, report, false); var sut = new IdentifyTagsFromIncident(repos, provider); await sut.HandleAsync(ctx, e); var arguments = repos.ReceivedCalls().First(x => x.GetMethodInfo().Name == "AddAsync") .GetArguments(); var tags = (Tag[])arguments[1]; tags[0].Name.Should().Be("MyTag"); tags[1].Name.Should().Be("YourTag"); }
/// <summary> /// Creates a new instance of <see cref="ReportAddedToIncident" />. /// </summary> /// <param name="incident">incident that the report was added to</param> /// <param name="report">received report</param> /// <param name="isReOpened">Incident was marked as closed, so the received report opened it again.</param> /// <exception cref="ArgumentNullException">incident;report</exception> public ReportAddedToIncident(IncidentSummaryDTO incident, ReportDTO report, bool isReOpened) { if (incident == null) { throw new ArgumentNullException("incident"); } if (report == null) { throw new ArgumentNullException("report"); } Incident = incident; Report = report; IsReOpened = isReOpened; }
public async Task Should_be_able_to_use_position_in_regular_collection() { var repos = Substitute.For <IErrorOriginRepository>(); var incident = new IncidentSummaryDTO(1, "Hello"); var data = new Dictionary <string, string> { { "ReportLongitude", "60.6065" }, { "ReportLatitude", "15.6355" } }; var report = new ReportDTO { ContextCollections = new[] { new ContextCollectionDTO("SomeCollection", data) } }; var e = new ReportAddedToIncident(incident, report, false); var context = Substitute.For <IMessageContext>(); var configWrapper = Substitute.For <IConfiguration <OriginsConfiguration> >(); var sut = new StorePositionFromNewReport(repos, configWrapper); await sut.HandleAsync(context, e); var entity = (ErrorOrigin)repos.ReceivedCalls().First(x => x.GetMethodInfo().Name == "CreateAsync") .GetArguments().First(); entity.Latitude.Should().Be(15.6355); entity.Longitude.Should().Be(60.6065); }
/// <summary> /// Send /// </summary> /// <param name="idOrEmailAddress">Account id or email address</param> /// <param name="incident">Incident that the report belongs to</param> /// <param name="report">Report being processed.</param> /// <returns>task</returns> /// <exception cref="ArgumentNullException">idOrEmailAddress; incident; report</exception> public async Task SendAsync(string idOrEmailAddress, IncidentSummaryDTO incident, ReportDTO report) { if (idOrEmailAddress == null) { throw new ArgumentNullException("idOrEmailAddress"); } if (incident == null) { throw new ArgumentNullException("incident"); } if (report == null) { throw new ArgumentNullException("report"); } var config = ConfigurationStore.Instance.Load <BaseConfiguration>(); var shortName = incident.Name.Length > 40 ? incident.Name.Substring(0, 40) + "..." : incident.Name; // need to be safe for subjects shortName = shortName.Replace("\n", ";"); var baseUrl = string.Format("{0}/#/application/{1}/incident/{2}", config.BaseUrl.ToString().TrimEnd('/'), report.ApplicationId, report.IncidentId); //TODO: Add more information var msg = new EmailMessage(idOrEmailAddress); if (incident.IsReOpened) { msg.Subject = "ReOpened: " + shortName; msg.TextBody = string.Format(@"Incident: {0} Report url: {0}/report/{1} Description: {2} Exception: {3} {4} ", baseUrl, report.Id, incident.Name, report.Exception.FullName, report.Exception.StackTrace); } else if (incident.ReportCount == 1) { msg.Subject = "New: " + shortName; msg.TextBody = string.Format(@"Incident: {0} Description: {1} Exception: {2} {3}", baseUrl, incident.Name, report.Exception.FullName, report.Exception.StackTrace); } else { msg.Subject = "Updated: " + shortName; msg.TextBody = string.Format(@"Incident: {0} Report url: {0}/report/{1} Description: {2} Exception: {3} {4} ", baseUrl, report.Id, incident.Name, report.Exception.FullName, report.Exception.StackTrace); } var emailCmd = new SendEmail(msg); await _commandBus.ExecuteAsync(emailCmd); }
/// <summary> /// Analyze report /// </summary> /// <param name="report">report</param> /// <exception cref="ArgumentNullException">report</exception> public async Task Analyze(IMessageContext context, ErrorReportEntity report) { if (report == null) { throw new ArgumentNullException("report"); } var countThisMonth = _repository.GetMonthReportCount(); if (countThisMonth >= 500) { _repository.AddMissedReport(DateTime.Today); return; } var exists = _repository.ExistsByClientId(report.ClientReportId); if (exists) { _logger.Warn("Report have already been uploaded: " + report.ClientReportId); return; } try { var hashCode = _hashCodeGenerator.GenerateHashCode(report); report.Init(hashCode); } catch (Exception ex) { var reportJson = JsonConvert.SerializeObject(report); if (reportJson.Length > 1000000) { reportJson = reportJson.Substring(0, 100000) + "[....]"; } _logger.Fatal("Failed to init report " + reportJson, ex); return; } var applicationVersion = GetVersionFromReport(report); var isReOpened = false; var firstLine = report.GenerateHashCodeIdentifier(); var incident = _repository.FindIncidentForReport(report.ApplicationId, report.ReportHashCode, firstLine); if (incident == null) { incident = BuildIncident(report); _repository.CreateIncident(incident); var evt = new IncidentCreated(incident.ApplicationId, incident.Id, incident.Description, incident.FullName) { CreatedAtUtc = incident.CreatedAtUtc, ApplicationVersion = applicationVersion, }; await _domainQueue.PublishAsync(context.Principal, evt); await context.SendAsync(evt); } else { await _repository.StoreReportStats(new ReportMapping() { IncidentId = incident.Id, ErrorId = report.ClientReportId, ReceivedAtUtc = report.CreatedAtUtc }); if (incident.IsIgnored) { _logger.Info("Incident is ignored: " + JsonConvert.SerializeObject(report)); incident.WasJustIgnored(); _repository.UpdateIncident(incident); return; } if (incident.IsClosed) { if (applicationVersion != null && incident.IsReportIgnored(applicationVersion)) { _logger.Info("Ignored report since it's for a version less that the solution version: " + JsonConvert.SerializeObject(report)); incident.WasJustIgnored(); _repository.UpdateIncident(incident); return; } isReOpened = true; incident.ReOpen(); var evt = new IncidentReOpened(incident.ApplicationId, incident.Id, incident.CreatedAtUtc) { ApplicationVersion = applicationVersion }; await context.SendAsync(evt); await _domainQueue.PublishAsync(context.Principal, evt); } incident.AddReport(report); _repository.UpdateIncident(incident); if (incident.ReportCount > 25) { _logger.Debug("Report count is more than 25. Storing only report stats for incident " + incident.Id); return; } } if (!string.IsNullOrWhiteSpace(report.EnvironmentName)) { _repository.SaveEnvironmentName(incident.Id, report.EnvironmentName); } report.IncidentId = incident.Id; _repository.CreateReport(report); _logger.Debug("saving report " + report.Id + " for incident " + incident.Id); var appName = _repository.GetAppName(incident.ApplicationId); var summary = new IncidentSummaryDTO(incident.Id, incident.Description) { ApplicationId = incident.ApplicationId, ApplicationName = appName, CreatedAtUtc = incident.CreatedAtUtc, LastUpdateAtUtc = incident.UpdatedAtUtc, IsReOpened = incident.IsReOpened, Name = incident.Description, ReportCount = incident.ReportCount }; var sw = new Stopwatch(); sw.Start(); _logger.Debug("Publishing now: " + report.ClientReportId); var e = new ReportAddedToIncident(summary, ConvertToCoreReport(report, applicationVersion), isReOpened); await context.SendAsync(e); await context.SendAsync(new ProcessInboundContextCollections()); if (sw.ElapsedMilliseconds > 200) { _logger.Debug("PublishAsync took " + sw.ElapsedMilliseconds); } sw.Stop(); }
/// <summary> /// Send /// </summary> /// <param name="accountId">Account to send to</param> /// <param name="incident">Incident that the report belongs to</param> /// <param name="report">report being processed</param> /// <returns>task</returns> /// <exception cref="ArgumentNullException">incident;report</exception> /// <exception cref="ArgumentOutOfRangeException">accountId</exception> public async Task SendAsync(int accountId, IncidentSummaryDTO incident, ReportDTO report) { if (incident == null) { throw new ArgumentNullException("incident"); } if (report == null) { throw new ArgumentNullException("report"); } if (accountId <= 0) { throw new ArgumentOutOfRangeException("accountId"); } var settings = await _userRepository.GetUserAsync(accountId); if (string.IsNullOrEmpty(settings.MobileNumber)) { return; //TODO: LOG } var config = ConfigurationStore.Instance.Load <BaseConfiguration>(); var url = config.BaseUrl; var shortName = incident.Name.Length > 20 ? incident.Name.Substring(0, 20) + "..." : incident.Name; var exMsg = report.Exception.Message.Length > 100 ? report.Exception.Message.Substring(0, 100) : report.Exception.Message; string msg; if (incident.IsReOpened) { msg = string.Format(@"ReOpened: {0} {1}/#/incident/{2} {3}", shortName, url, incident.Id, exMsg); } else if (incident.ReportCount == 1) { msg = string.Format(@"New: {0} {1}/#/incident/{2} Exception: {3}", shortName, url, incident.Id, exMsg); } else { msg = string.Format(@"Updated: {0} ReportCount: {4} {1}/#/incident/{2} {3}", shortName, url, incident.Id, exMsg, incident.ReportCount); } var iso = Encoding.GetEncoding("ISO-8859-1"); var utfBytes = Encoding.UTF8.GetBytes(msg); var isoBytes = Encoding.Convert(Encoding.UTF8, iso, utfBytes); msg = iso.GetString(isoBytes); var request = WebRequest.CreateHttp("https://web.smscom.se/sendsms.aspx?acc=ip1-755&pass=z35llww4&msg=" + Uri.EscapeDataString(msg) + "&to=" + settings.MobileNumber + "&from=OneTrueError&prio=2"); request.ContentType = "application/json"; request.Method = "GET"; await request.GetResponseAsync(); }
/// <summary> /// Send /// </summary> /// <param name="accountId">Account to send to</param> /// <param name="incident">Incident that the report belongs to</param> /// <param name="report">report being processed</param> /// <returns>task</returns> /// <exception cref="ArgumentNullException">incident;report</exception> /// <exception cref="ArgumentOutOfRangeException">accountId</exception> public async Task SendAsync(int accountId, IncidentSummaryDTO incident, ReportDTO report) { if (incident == null) { throw new ArgumentNullException("incident"); } if (report == null) { throw new ArgumentNullException("report"); } if (accountId <= 0) { throw new ArgumentOutOfRangeException("accountId"); } var settings = await _userRepository.GetUserAsync(accountId); if (string.IsNullOrEmpty(settings.MobileNumber)) { return; //TODO: LOG } var url = _baseConfiguration.BaseUrl; var shortName = incident.Name.Length > 20 ? incident.Name.Substring(0, 20) + "..." : incident.Name; var exMsg = report.Exception.Message.Length > 100 ? report.Exception.Message.Substring(0, 100) : report.Exception.Message; var baseUrl = _baseConfiguration.BaseUrl.ToString().TrimEnd('/'); var incidentUrl = $"{baseUrl}/discover/incidents/{report.ApplicationId}/incident/{report.IncidentId}/"; string msg; if (incident.IsReOpened) { msg = $@"ReOpened: {shortName} {incidentUrl} {exMsg}"; } else if (incident.ReportCount == 1) { msg = $@"New: {shortName} {incidentUrl} {exMsg}"; } else { msg = $@"Updated: {shortName} ReportCount: {incident.ReportCount} {incidentUrl} {exMsg}"; } var iso = Encoding.GetEncoding("ISO-8859-1"); var utfBytes = Encoding.UTF8.GetBytes(msg); var isoBytes = Encoding.Convert(Encoding.UTF8, iso, utfBytes); msg = iso.GetString(isoBytes); var request = WebRequest.CreateHttp("https://web.smscom.se/sendsms.aspx?acc=ip1-755&pass=z35llww4&msg=" + Uri.EscapeDataString(msg) + "&to=" + settings.MobileNumber + "&from=Coderr&prio=2"); request.ContentType = "application/json"; request.Method = "GET"; await request.GetResponseAsync(); }
/// <summary> /// Analyze report /// </summary> /// <param name="report">report</param> /// <exception cref="ArgumentNullException">report</exception> public void Analyze(ErrorReportEntity report) { if (report == null) { throw new ArgumentNullException("report"); } var exists = _repository.ExistsByClientId(report.ClientReportId); if (exists) { _logger.Warn("Report have already been uploaded: " + report.ClientReportId); return; } try { var hashCode = _hashCodeGenerator.GenerateHashCode(report); report.Init(hashCode); } catch (Exception ex) { _logger.Fatal("Failed to store report " + JsonConvert.SerializeObject(report), ex); return; } var isReOpened = false; var firstLine = report.GenerateHashCodeIdentifier(); var incident = _repository.FindIncidentForReport(report.ApplicationId, report.ReportHashCode, firstLine); if (incident == null) { incident = BuildIncident(report); _repository.CreateIncident(incident); } else { if (incident.ReportCount > 10000) { _logger.Debug("Reportcount is more than 10000. Ignoring report for incident " + incident.Id); return; } if (incident.IsIgnored) { _logger.Info("Incident is ignored: " + JsonConvert.SerializeObject(report)); incident.WasJustIgnored(); _repository.UpdateIncident(incident); return; } if (incident.IsSolved) { isReOpened = true; incident.ReOpen(); _eventBus.PublishAsync(new IncidentReOpened(incident.ApplicationId, incident.Id, incident.CreatedAtUtc)); } incident.AddReport(report); _repository.UpdateIncident(incident); } report.IncidentId = incident.Id; _repository.CreateReport(report); _logger.Debug("saving report " + report.Id + " for incident " + incident.Id); var appName = _repository.GetAppName(incident.ApplicationId); var summary = new IncidentSummaryDTO(incident.Id, incident.Description) { ApplicationId = incident.ApplicationId, ApplicationName = appName, CreatedAtUtc = incident.CreatedAtUtc, LastUpdateAtUtc = incident.UpdatedAtUtc, IsReOpened = incident.IsReOpened, Name = incident.Description, ReportCount = incident.ReportCount }; var sw = new Stopwatch(); sw.Start(); _logger.Debug("Publishing now: " + report.ClientReportId); var e = new ReportAddedToIncident(summary, ConvertToCoreReport(report), isReOpened); _eventBus.PublishAsync(e); if (sw.ElapsedMilliseconds > 200) { _logger.Debug("Publish took " + sw.ElapsedMilliseconds); } sw.Stop(); }
/// <summary> /// Analyze report /// </summary> /// <param name="report">report</param> /// <exception cref="ArgumentNullException">report</exception> public async Task Analyze(IMessageContext context, ErrorReportEntity report) { if (report == null) { throw new ArgumentNullException(nameof(report)); } var countThisMonth = _repository.GetMonthReportCount(); if (countThisMonth >= 500) { _repository.AddMissedReport(DateTime.Today); return; } _logger.Debug("Running as " + context.Principal.ToFriendlyString()); var exists = _repository.ExistsByClientId(report.ClientReportId); if (exists) { _logger.Warn($"Report have already been uploaded: {report.ClientReportId} for {report.RemoteAddress}."); return; } ErrorHashCode hashcodeResult; try { hashcodeResult = _hashCodeGenerator.GenerateHashCode(report); report.Init(hashcodeResult.HashCode); } catch (Exception ex) { var reportJson = JsonConvert.SerializeObject(report); if (reportJson.Length > 1000000) { reportJson = reportJson.Substring(0, 100000) + "[....]"; } _logger.Fatal($"Failed to init report {reportJson}", ex); return; } var storeReport = true; var applicationVersion = GetVersionFromReport(report); var isReOpened = false; IncidentBeingAnalyzed incident = null; if (hashcodeResult.CompabilityHashSource != null) { incident = _repository.FindIncidentForReport(report.ApplicationId, hashcodeResult.CompabilityHashSource, hashcodeResult.CollisionIdentifier); if (incident != null) { report.Init(hashcodeResult.CompabilityHashSource); } } if (incident == null) { incident = _repository.FindIncidentForReport(report.ApplicationId, report.ReportHashCode, hashcodeResult.CollisionIdentifier); } var isNewIncident = false; if (incident == null) { if (report.Exception == null) { _logger.Debug("Got no exception"); } isNewIncident = true; incident = BuildIncident(report); _repository.CreateIncident(incident); await _repository.StoreReportStats(new ReportMapping() { IncidentId = incident.Id, ErrorId = report.ClientReportId, ReceivedAtUtc = report.CreatedAtUtc }); var evt = new IncidentCreated(incident.ApplicationId, incident.Id, incident.Description, incident.FullName) { CreatedAtUtc = incident.CreatedAtUtc, ApplicationVersion = applicationVersion, }; _logger.Info($"Storing IncidentCreated with {context.Principal.ToFriendlyString()}: {JsonConvert.SerializeObject(evt)}"); await _domainQueue.PublishAsync(context.Principal, evt); await context.SendAsync(evt); } else { if (incident.IsIgnored) { _logger.Info("Incident is ignored: " + JsonConvert.SerializeObject(report)); incident.WasJustIgnored(); _repository.UpdateIncident(incident); return; } // Do this before checking closed // as we want to see if it still gets reports. var stat = new ReportMapping() { IncidentId = incident.Id, ErrorId = report.ClientReportId, ReceivedAtUtc = report.CreatedAtUtc }; await _repository.StoreReportStats(stat); _logger.Debug("Storing stats " + JsonConvert.SerializeObject(stat)); if (incident.IsClosed) { if (applicationVersion != null && incident.IsReportIgnored(applicationVersion)) { _logger.Info("Ignored report since it's for a version less that the solution version: " + JsonConvert.SerializeObject(report)); incident.WasJustIgnored(); _repository.UpdateIncident(incident); return; } isReOpened = true; incident.ReOpen(); var evt = new IncidentReOpened(incident.ApplicationId, incident.Id, incident.CreatedAtUtc) { ApplicationVersion = applicationVersion }; await context.SendAsync(evt); await _domainQueue.PublishAsync(context.Principal, evt); } incident.AddReport(report); // Let's continue to receive reports once a day when // limit is reached (to get more fresh data, and still not load the system unnecessary). var timesSinceLastReport = DateTime.UtcNow.Subtract(incident.LastStoredReportUtc); if (incident.ReportCount > _reportConfig.Value.MaxReportsPerIncident && timesSinceLastReport < TimeSpan.FromMinutes(10)) { _repository.UpdateIncident(incident); _logger.Debug($"Report count is more than {_reportConfig.Value.MaxReportsPerIncident}. Ignoring reports for incident {incident.Id}. Minutes since last report: " + timesSinceLastReport.TotalMinutes); storeReport = false; //don't exit here, since we want to be able to process reports } } if (!string.IsNullOrWhiteSpace(report.EnvironmentName)) { _repository.SaveEnvironmentName(incident.Id, report.EnvironmentName); } report.IncidentId = incident.Id; if (storeReport) { incident.LastStoredReportUtc = DateTime.UtcNow; _repository.UpdateIncident(incident); _repository.CreateReport(report); _logger.Debug($"saving report {report.Id} for incident {incident.Id}"); } var appName = _repository.GetAppName(incident.ApplicationId); var summary = new IncidentSummaryDTO(incident.Id, incident.Description) { ApplicationId = incident.ApplicationId, ApplicationName = appName, CreatedAtUtc = incident.CreatedAtUtc, LastUpdateAtUtc = incident.UpdatedAtUtc, IsReOpened = incident.IsReOpened, Name = incident.Description, ReportCount = incident.ReportCount }; var sw = new Stopwatch(); sw.Start(); var e = new ReportAddedToIncident(summary, ConvertToCoreReport(report, applicationVersion), isReOpened) { IsNewIncident = isNewIncident, IsStored = storeReport, EnvironmentName = report.EnvironmentName }; await context.SendAsync(e); if (storeReport) { await context.SendAsync(new ProcessInboundContextCollections()); } if (sw.ElapsedMilliseconds > 200) { _logger.Debug($"PublishAsync took {sw.ElapsedMilliseconds}"); } sw.Stop(); }
/// <summary> /// Send /// </summary> /// <param name="idOrEmailAddress">Account id or email address</param> /// <param name="incident">Incident that the report belongs to</param> /// <param name="report">Report being processed.</param> /// <returns>task</returns> /// <exception cref="ArgumentNullException">idOrEmailAddress; incident; report</exception> public async Task SendAsync(IMessageContext context, string idOrEmailAddress, IncidentSummaryDTO incident, ReportDTO report) { if (idOrEmailAddress == null) { throw new ArgumentNullException("idOrEmailAddress"); } if (incident == null) { throw new ArgumentNullException("incident"); } if (report == null) { throw new ArgumentNullException("report"); } var shortName = incident.Name.Length > 40 ? incident.Name.Substring(0, 40) + "..." : incident.Name; var pos = shortName.IndexOfAny(new[] { '\r', '\n' }); if (pos != -1) { shortName = shortName.Substring(0, pos) + "[...]"; } var baseUrl = _baseConfiguration.BaseUrl.ToString().TrimEnd('/'); var incidentUrl = $"{baseUrl}/discover/incidents/{report.ApplicationId}/incident/{report.IncidentId}/"; //TODO: Add more information var msg = new EmailMessage(idOrEmailAddress); if (incident.IsReOpened) { msg.Subject = "ReOpened: " + shortName; msg.TextBody = $@"{incident.Name} {report.Exception.FullName} {report.Exception.StackTrace} {incidentUrl}"; } else if (incident.ReportCount == 1) { msg.Subject = "New: " + shortName; msg.TextBody = $@"{incident.Name} {report.Exception.FullName} {report.Exception.StackTrace} {incidentUrl}"; } else { msg.Subject = "Updated: " + shortName; msg.TextBody = $@"{incident.Name} {report.Exception.FullName} {report.Exception.StackTrace} {incidentUrl}"; } var emailCmd = new SendEmail(msg); await context.SendAsync(emailCmd); }