/// <summary> /// Creates an incident and a report. /// </summary> public void CreateReportAndIncident(out int reportId, out int incidentId) { ErrorReportEntity report; using (var uow = CreateUnitOfWork()) { report = new ErrorReportEntity(ApplicationId, Guid.NewGuid().ToString("N"), DateTime.UtcNow, new ErrorReportException(new Exception("mofo")), new List <ErrorReportContextCollection> { new ErrorReportContextCollection("Maps", new Dictionary <string, string>()) }) { Title = "Missing here" }; report.Init(report.GenerateHashCodeIdentifier()); uow.SaveChanges(); } using (var dbContext = CreateUnitOfWork()) { var incident = new IncidentBeingAnalyzed(report); var incRepos = new AnalyticsRepository(dbContext); incRepos.CreateIncident(incident); incidentId = incident.Id; report.IncidentId = incident.Id; incRepos.CreateReport(report); reportId = report.Id; dbContext.SaveChanges(); } }
/// <summary> /// Execute on a set of report mastery. /// </summary> /// <returns><c>false</c> if there are no more reports to analyze.</returns> public bool Execute() { using (var transaction = _queue.BeginTransaction()) { var dto = _queue.TryReceive <ReceivedReportDTO>(transaction, TimeSpan.FromMilliseconds(500)); if (dto == null) { return(false); } try { ErrorReportException ex = null; if (dto.Exception != null) { ex = ConvertException(dto.Exception); } var contexts = dto.ContextCollections.Select(ConvertContext).ToArray(); var entity = new ErrorReportEntity(dto.ApplicationId, dto.ReportId, dto.CreatedAtUtc, ex, contexts) { RemoteAddress = dto.RemoteAddress }; _analyzer.Analyze(entity); } catch (Exception ex) { _logger.Error("Failed to analyze report ", ex); } transaction.Commit(); } return(true); }
/// <summary> /// Generate a new hash code /// </summary> /// <param name="entity">entity</param> /// <returns>hashcode</returns> /// <exception cref="ArgumentNullException">entity</exception> public string GenerateHashCode(ErrorReportEntity entity) { string requestUrl = null; var httpCode = 0; foreach (var collection in entity.ContextCollections) { if (collection.Properties.TryGetValue("HttpCode", out var value)) { int.TryParse(value, out httpCode); } if (!collection.Properties.TryGetValue("RequestUrl", out requestUrl)) { collection.Properties.TryGetValue("Url", out requestUrl); } } if (httpCode == 0 || string.IsNullOrWhiteSpace(requestUrl)) { return(null); } var pos = requestUrl.IndexOf("?"); if (pos != -1) { requestUrl = requestUrl.Remove(pos); } return(HashCodeUtility.GetPersistentHashCode(httpCode + "-" + requestUrl).ToString("X")); }
/// <summary> /// Creates a new instance of <see cref="IncidentBeingAnalyzed" />. /// </summary> /// <param name="entity">entity</param> /// <param name="exception">exception to analyze</param> /// <exception cref="ArgumentNullException">entity; exception</exception> /// <exception cref="ArgumentException">entity.hashcode is null</exception> public IncidentBeingAnalyzed(ErrorReportEntity entity, ErrorReportException exception) { if (entity == null) { throw new ArgumentNullException("entity"); } if (exception == null) { throw new ArgumentNullException("exception"); } if (string.IsNullOrEmpty(entity.ReportHashCode)) { throw new ArgumentException("ReportHashCode must be specified to be able to identify duplicates."); } Description = exception.Message; FullName = exception.FullName; StackTrace = HashCodeGenerator.CleanStackTrace(exception.StackTrace); AddReport(entity); ReportHashCode = entity.ReportHashCode; HashCodeIdentifier = entity.GenerateHashCodeIdentifier(); ApplicationId = entity.ApplicationId; UpdatedAtUtc = entity.CreatedAtUtc; CreatedAtUtc = entity.CreatedAtUtc; }
/// <summary> /// Creates an incident and a report. /// </summary> public void CreateReportAndIncident(out int reportId, out int incidentId) { using (var uow = CreateUnitOfWork()) { CreateUserAndApplication(uow, out var accountId, out var applicationId); var report = new ErrorReportEntity(applicationId, Guid.NewGuid().ToString("N"), DateTime.UtcNow, new ErrorReportException(new Exception("mofo")), new List <ErrorReportContext> { new ErrorReportContext("Maps", new Dictionary <string, string>()) }) { Title = "Missing here" }; report.Init(report.GenerateHashCodeIdentifier()); var incident = new IncidentBeingAnalyzed(report); var incRepos = new AnalyticsRepository(new AnalysisDbContext(uow), ConfigStore); incRepos.CreateIncident(incident); incidentId = incident.Id; report.IncidentId = incident.Id; incRepos.CreateReport(report); reportId = report.Id; uow.SaveChanges(); } }
public void Should_remove_AT_from_stacktrace_to_avoid_multi_language_issues() { var exceptionWithAt = new ErrorReportException() { StackTrace = @"at System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction) at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose) at System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady) at System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString) at System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, SqlDataReader ds) at System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite, String methodName) at System.Data.SqlClient.SqlCommand.ExecuteNonQuery() at Coderr.Server.Infrastructure.Configuration.Database.DatabaseStore.Store(IConfigurationSection section) at Coderr.Server.Premise.App.LicenseWrapper.IncreaseReportCount() at Coderr.Server.Web.Controllers.ReportReceiverController.PremiseLicenseCheck() at Coderr.Server.Web.Controllers.ReportReceiverController.Post(String appKey, String sig) at Microsoft.AspNetCore.Mvc.Internal.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments) at System.Threading.Tasks.ValueTask`1.get_Result() at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeActionMethodAsync():55 at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeNextActionFilterAsync() at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) at Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync() at Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextExceptionFilterAsync():33" }; var entityWithAt = new ErrorReportEntity(1, "flldfd", DateTime.UtcNow, exceptionWithAt, new ErrorReportContextCollection[0]); var exceptionWithoutAt = new ErrorReportException() { StackTrace = @"System.Data.SqlClient.SqlConnection.OnError(SqlException exception, Boolean breakConnection, Action`1 wrapCloseInAction) System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj, Boolean callerHasConnectionLock, Boolean asyncClose) System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady) System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds, RunBehavior runBehavior, String resetOptionsString) System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, Boolean async, Int32 timeout, Task& task, Boolean asyncWrite, SqlDataReader ds) System.Data.SqlClient.SqlCommand.InternalExecuteNonQuery(TaskCompletionSource`1 completion, Boolean sendToPipe, Int32 timeout, Boolean asyncWrite, String methodName) System.Data.SqlClient.SqlCommand.ExecuteNonQuery() Coderr.Server.Infrastructure.Configuration.Database.DatabaseStore.Store(IConfigurationSection section) Coderr.Server.Premise.App.LicenseWrapper.IncreaseReportCount() Coderr.Server.Web.Controllers.ReportReceiverController.PremiseLicenseCheck() Coderr.Server.Web.Controllers.ReportReceiverController.Post(String appKey, String sig) Microsoft.AspNetCore.Mvc.Internal.ActionMethodExecutor.TaskOfIActionResultExecutor.Execute(IActionResultTypeMapper mapper, ObjectMethodExecutor executor, Object controller, Object[] arguments) System.Threading.Tasks.ValueTask`1.get_Result() Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeActionMethodAsync():55 Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeNextActionFilterAsync() Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Rethrow(ActionExecutedContext context) Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(State& next, Scope& scope, Object& state, Boolean& isCompleted) Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync() Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeNextExceptionFilterAsync():33" }; var entityWithoutAt = new ErrorReportEntity(1, "flldfd", DateTime.UtcNow, exceptionWithoutAt, new ErrorReportContextCollection[0]); var sut = new HashCodeGenerator(new IHashCodeSubGenerator[0]); var result1 = sut.GenerateHashCode(entityWithAt); var result2 = sut.GenerateHashCode(entityWithAt); result1.HashCode.Should().Be(result2.HashCode); }
/// <summary> /// Method that will be invoked if no implementations of <see cref="IHashCodeSubGenerator" /> generates an hash code. /// </summary> /// <param name="report">received report</param> /// <returns>hash code</returns> /// <exception cref="ArgumentNullException">report</exception> protected virtual ErrorHashCode DefaultCreateHashCode(ErrorReportEntity report) { if (report == null) { throw new ArgumentNullException("report"); } var hashSource = $"{report.Exception.FullName ?? report.Exception.Name}\r\n"; var foundHashSource = false; // the client libraries can by themselves specify how we should identify // unique incidents. We then use that identifier in combination with the exception name. var collection = report.ContextCollections.FirstOrDefault(x => x.Name == "CoderrData"); if (collection != null) { if (collection.Properties.TryGetValue("HashSource", out var reportHashSource)) { foundHashSource = true; hashSource += reportHashSource; } } if (!foundHashSource) { // This identifier is determined by the developer when the error is generated. foreach (var contextCollection in report.ContextCollections) { if (!contextCollection.Properties.TryGetValue("ErrorHashSource", out var ourHashSource)) { continue; } hashSource = ourHashSource; foundHashSource = true; break; } } var hashSourceForCompability = ""; if (!foundHashSource) { hashSourceForCompability = hashSource + CleanStackTrace(report.Exception.StackTrace ?? ""); hashSource += CleanStackTrace(report.Exception.StackTrace ?? ""); } var hash = HashTheSource(hashSource); return(new ErrorHashCode { CollisionIdentifier = report.GenerateHashCodeIdentifier(), HashCode = hash.ToString("X"), CompabilityHashSource = hashSourceForCompability == "" ? null : HashTheSource(hashSourceForCompability).ToString("X") }); }
/// <summary> /// Generate a new hash code /// </summary> /// <param name="entity">entity</param> /// <returns>hashcode</returns> /// <exception cref="ArgumentNullException">entity</exception> public ErrorHashCode GenerateHashCode(ErrorReportEntity entity) { string requestUrl = null; var httpCode = 0; foreach (var collection in entity.ContextCollections) { if (collection.Properties.TryGetValue("HttpCode", out var value)) { int.TryParse(value, out httpCode); // Server side errors are typically handled by other handlers. if (httpCode == 500) { return(null); } } if (!collection.Properties.TryGetValue("RequestUrl", out requestUrl)) { collection.Properties.TryGetValue("Url", out requestUrl); } } if (httpCode == 0 || string.IsNullOrWhiteSpace(requestUrl)) { return(null); } // Since this is for a specific application, remove host to make sure that errors // for different environments/servers are treated as the same error. // Remove host or correct uris if (requestUrl.Contains("://")) { var pos2 = requestUrl.IndexOf("//"); pos2 = requestUrl.IndexOf("/", pos2 + 2); requestUrl = requestUrl.Remove(0, pos2); } // and query string var pos = requestUrl.IndexOf("?"); if (pos != -1) { requestUrl = requestUrl.Remove(pos); } return(new ErrorHashCode { HashCode = HashCodeUtility.GetPersistentHashCode($"{httpCode};{requestUrl}").ToString("X"), CollisionIdentifier = $"{httpCode};{requestUrl}" }); }
private string GetVersionFromReport(ErrorReportEntity report) { foreach (var contextCollection in report.ContextCollections) { if (contextCollection.Properties.TryGetValue(AppAssemblyVersion, out var version)) { return(version); } } return(null); }
public void CreateReport(ErrorReportEntity report) { if (report == null) { throw new ArgumentNullException(nameof(report)); } if (string.IsNullOrEmpty(report.Title) && report.Exception != null) { report.Title = report.Exception.Message; if (report.Title == null) { report.Title = "[Exception message was not specified]"; } else if (report.Title.Length > 100) { report.Title = report.Title.Substring(0, 100); } } var collections = new List <string>(); foreach (var context in report.ContextCollections) { var data = EntitySerializer.Serialize(context); if (data.Length > MaxCollectionSize) { var tooLargeCtx = new ErrorReportContextCollection(context.Name, new Dictionary <string, string>() { { "Error", $"This collection was larger ({data.Length}bytes) than the threshold of {MaxCollectionSize}bytes" } }); data = EntitySerializer.Serialize(tooLargeCtx); } collections.Add(data); } _unitOfWork.Insert(report); var cols = string.Join(", ", collections); var inboound = new InboundCollection { JsonData = $"[{cols}]", ReportId = report.Id }; _unitOfWork.Insert(inboound); }
/// <summary> /// Method that will be invoked if no implementations of <see cref="IHashCodeSubGenerator" /> generates an hash code. /// </summary> /// <param name="report">received report</param> /// <returns>hash code</returns> /// <exception cref="ArgumentNullException">report</exception> protected virtual string DefaultCreateHashCode(ErrorReportEntity report) { if (report == null) { throw new ArgumentNullException("report"); } var hashSource = $"{report.Exception.FullName ?? report.Exception.Name}\r\n"; var foundHashSource = false; // the client libraries can by themselves specify how we should identify // unqiue incidents. We then use that identifier in combination with the exception name. var collection = report.ContextCollections.FirstOrDefault(x => x.Name == "CoderrData"); if (collection != null) { if (collection.Properties.TryGetValue("HashSource", out var reportHashSource)) { foundHashSource = true; hashSource += reportHashSource; } else { hashSource += StripLineNumbers(report.Exception.StackTrace ?? ""); } } if (!foundHashSource) { // This identifier is determined by the developer when the error is generated. foreach (var contextCollection in report.ContextCollections) { if (!contextCollection.Properties.TryGetValue("ErrorHashSource", out var ourHashSource)) { continue; } hashSource = ourHashSource; break; } } var hash = 23; foreach (var c in hashSource) { hash = hash * 31 + c; } return(hash.ToString("X")); }
private IncidentBeingAnalyzed BuildIncident(ErrorReportEntity entity) { if (entity.Exception == null) { return(new IncidentBeingAnalyzed(entity)); } if (entity.Exception.Name == "AggregateException") { try { var exception = entity.Exception; //TODO: Check if there are more than one InnerExceptions and then abort this specialization. while (exception != null && exception.Name == "AggregateException") { exception = exception.InnerException; } var incident = new IncidentBeingAnalyzed(entity, exception); return(incident); } catch (Exception) { } } if (entity.Exception.Name == "ReflectionTypeLoadException") { try { var item = JObject.Parse(entity.Exception.Everything); var i = new IncidentBeingAnalyzed(entity); var items = (JObject)item["LoaderExceptions"]; var exception = items.First; //var incident = new Incident(entity, exception); //incident.AddIncidentTags(new[] { "ReflectionTypeLoadException" }); //return incident; //TODO: load LoaderExceptions which is an Exception[] array } catch (Exception) { } } return(new IncidentBeingAnalyzed(entity)); }
/// <summary> /// Convert received report to our internal format /// </summary> /// <param name="report">client report</param> /// <param name="applicationId">application that we identified that the report belongs to</param> /// <returns>internal format</returns> public ErrorReportEntity ConvertReport(ReceivedReportDTO report, int applicationId) { ErrorReportException ex = null; if (report.Exception != null) { ex = ConvertException(report.Exception); } //var id = _idGeneratorClient.GetNextId(ErrorReportEntity.SEQUENCE); var contexts = report.ContextCollections.Select(x => new ErrorReportContext(x.Name, x.Properties)).ToArray(); var dto = new ErrorReportEntity(applicationId, report.ReportId, report.CreatedAtUtc, ex, contexts); return(dto); }
public static string FindCollectionProperty(this ErrorReportEntity instance, string collectionName, string propertyName) { var collection = instance.FindCollection(collectionName); if (collection == null) { return(null); } if (collection.Properties.TryGetValue(propertyName, out var value)) { return(value); } else { return(null); } }
public void CreateReport(ErrorReportEntity report) { if (report == null) { throw new ArgumentNullException("report"); } if (string.IsNullOrEmpty(report.Title) && report.Exception != null) { report.Title = report.Exception.Message; if (report.Title.Length > 100) { report.Title = report.Title.Substring(0, 100); } } _unitOfWork.Insert(report); }
public void Should_generate_from_relative_uri() { var report = new ErrorReportEntity(1, "kffk", DateTime.UtcNow, new ErrorReportException(), new[] { new ErrorReportContextCollection("Yadayada", new Dictionary <string, string> { { "Url", "/some/path?hada=yada" }, { "HttpCode", "404" } }) }); var sut = new HttpErrorGenerator(); var code = sut.GenerateHashCode(report); var source = $"404;/some/path"; code.HashCode.Should().Be(HashCodeUtility.GetPersistentHashCode(source).ToString("X")); }
/// <summary> /// Generate a new hash code /// </summary> /// <param name="entity">entity</param> /// <returns>hashcode</returns> /// <exception cref="ArgumentNullException">entity</exception> public ErrorHashCode GenerateHashCode(ErrorReportEntity entity) { string requestUrl = null; var httpCode = 0; foreach (var collection in entity.ContextCollections) { if (collection.Properties.TryGetValue("HttpCode", out var value)) { int.TryParse(value, out httpCode); // Server side errors are typically handled by other handlers. if (httpCode == 500) { return(null); } } if (!collection.Properties.TryGetValue("RequestUrl", out requestUrl)) { collection.Properties.TryGetValue("Url", out requestUrl); } } if (httpCode == 0 || string.IsNullOrWhiteSpace(requestUrl)) { return(null); } var pos = requestUrl.IndexOf("?"); if (pos != -1) { requestUrl = requestUrl.Remove(pos); } return(new ErrorHashCode { HashCode = HashCodeUtility.GetPersistentHashCode($"{httpCode}-{requestUrl}").ToString("X"), CollisionIdentifier = $"{httpCode}-{requestUrl}" }); }
/// <summary> /// Add another report. /// </summary> /// <param name="entity">entity</param> /// <exception cref="ArgumentNullException">entity</exception> public void AddReport(ErrorReportEntity entity) { if (entity == null) { throw new ArgumentNullException("entity"); } if (string.IsNullOrWhiteSpace(StackTrace) && entity.Exception != null) { Description = entity.Exception.Message; FullName = entity.Exception.FullName; StackTrace = entity.Exception.StackTrace; } if (UpdatedAtUtc < entity.CreatedAtUtc) { UpdatedAtUtc = entity.CreatedAtUtc; } ReportCount++; }
public void CreateUserAndApp() { using (var uow = CreateUnitOfWork()) { var accountRepos = new AccountRepository(uow); var account = new Account("arne", "123456") { Email = "*****@*****.**" }; accountRepos.Create(account); var userRepos = new UserRepository(uow); var user = new User(account.Id, "arne") { EmailAddress = "*****@*****.**" }; userRepos.CreateAsync(user).GetAwaiter().GetResult(); var appRepos = new ApplicationRepository(uow); var app = new Application(account.Id, "MinApp"); appRepos.CreateAsync(app).GetAwaiter().GetResult(); var member = new ApplicationTeamMember(app.Id, account.Id, "Admin"); appRepos.CreateAsync(member).GetAwaiter().GetResult(); var report = new ErrorReportEntity(app.Id, Guid.NewGuid().ToString("N"), DateTime.UtcNow, new ErrorReportException(new Exception("mofo")), new List <ErrorReportContext> { new ErrorReportContext("Maps", new Dictionary <string, string>()) }); report.Title = "Missing here"; report.Init(report.GenerateHashCodeIdentifier()); var incident = new ReportAnalyzer.Domain.Incidents.IncidentBeingAnalyzed(report); var incRepos = new AnalyticsRepository(new AnalysisDbContext(uow), ConfigStore); incRepos.CreateIncident(incident); report.IncidentId = incident.Id; incRepos.CreateReport(report); uow.SaveChanges(); } }
private ReportDTO ConvertToCoreReport(ErrorReportEntity report) { var dto = new ReportDTO { ApplicationId = report.ApplicationId, ContextCollections = report.ContextInfo.Select(x => new ContextCollectionDTO(x.Name, x.Properties)).ToArray(), CreatedAtUtc = report.CreatedAtUtc, Id = report.Id, IncidentId = report.IncidentId, RemoteAddress = report.RemoteAddress, ReportId = report.ClientReportId, ReportVersion = "1" }; if (report.Exception != null) { dto.Exception = ConvertToCoreException(report.Exception); } return(dto); }
/// <summary> /// Generate a new hash code /// </summary> /// <param name="entity">entity</param> /// <returns>hash code</returns> /// <exception cref="ArgumentNullException">entity</exception> public ErrorHashCode GenerateHashCode(ErrorReportEntity entity) { if (entity == null) { throw new ArgumentNullException("entity"); } foreach (var generator in _generators) { if (generator.CanGenerateFrom(entity)) { // forgiving ones so that we can get the report and process it with a default hash code instead. var code = generator.GenerateHashCode(entity); if (code != null) { return(code); } } } return(DefaultCreateHashCode(entity)); }
public void should_match_line_numbers_and_without_line_numbers_to_reduce_the_number_of_incidents() { var ex1 = new ErrorReportException { StackTrace = @"at System.Web.Compilation.AssemblyBuilder.Compile():line 64 at System.Web.Compilation.BuildProvidersCompiler.PerformBuild():line 65 at System.Web.Compilation.BuildManager.CompileWebFile(VirtualPath virtualPath):line 67" }; var ex2 = new ErrorReportException { StackTrace = @"at System.Web.Compilation.AssemblyBuilder.Compile() at System.Web.Compilation.BuildProvidersCompiler.PerformBuild() at System.Web.Compilation.BuildManager.CompileWebFile(VirtualPath virtualPath)" }; var report1 = new ErrorReportEntity(1, "fjkkfjjkf", DateTime.UtcNow, ex1, new List <ErrorReportContextCollection>()); var report2 = new ErrorReportEntity(1, "fjkkfjjkf", DateTime.UtcNow, ex2, new List <ErrorReportContextCollection>()); var sut = new HashCodeGenerator(new IHashCodeSubGenerator[0]); var actual1 = sut.GenerateHashCode(report1); var actual2 = sut.GenerateHashCode(report2); actual1.Should().Be(actual2); }
public void Should_load_ignored_state_into_class_correctly() { var report = new ErrorReportEntity(FirstApplicationId, Guid.NewGuid().ToString("N"), DateTime.UtcNow, new ErrorReportException(new Exception("mofo")), new List <ErrorReportContext> { new ErrorReportContext("Maps", new Dictionary <string, string>()) }) { Title = "Missing here" }; report.Init(report.GenerateHashCodeIdentifier()); using (var uow = new AnalysisDbContext(CreateUnitOfWork())) { var incident = new IncidentBeingAnalyzed(report); var incRepos = new AnalyticsRepository(uow, new TestConfigStore()); incRepos.CreateIncident(incident); report.IncidentId = incident.Id; incRepos.CreateReport(report); } }
public async Task HandleAsync(IMessageContext context, ProcessReport message) { try { ErrorReportException ex = null; if (message.Exception != null) { ex = ConvertException(message.Exception); } var contexts = message.ContextCollections.Select(ConvertContext).ToArray(); var entity = new ErrorReportEntity(message.ApplicationId, message.ReportId, message.CreatedAtUtc, ex, contexts) { RemoteAddress = message.RemoteAddress }; await _analyzer.Analyze(context, entity); } catch (Exception ex) { _logger.Error("Failed to analyze report ", ex); } }
public void should_strip_line_numbers_to_reduce_the_number_of_incidents() { var locator = Substitute.For <IServiceLocator>(); var ex1 = new ErrorReportException { StackTrace = @" at System.Web.Compilation.AssemblyBuilder.Compile():line 64 at System.Web.Compilation.BuildProvidersCompiler.PerformBuild():line 65 at System.Web.Compilation.BuildManager.CompileWebFile(VirtualPath virtualPath):line 67" }; var ex2 = new ErrorReportException { StackTrace = @" at System.Web.Compilation.AssemblyBuilder.Compile():line 12 at System.Web.Compilation.BuildProvidersCompiler.PerformBuild():line 23 at System.Web.Compilation.BuildManager.CompileWebFile(VirtualPath virtualPath):line 53" }; var report1 = new ErrorReportEntity(1, "fjkkfjjkf", DateTime.UtcNow, ex1, new List <ErrorReportContext>()); var report2 = new ErrorReportEntity(1, "fjkkfjjkf", DateTime.UtcNow, ex2, new List <ErrorReportContext>()); var sut = new HashCodeGenerator(locator); var actual1 = sut.GenerateHashCode(report1); var actual2 = sut.GenerateHashCode(report2); actual1.Should().Be(actual2); }
/// <summary> /// Generate a new hash code /// </summary> /// <param name="entity">entity</param> /// <returns>hashcode</returns> /// <exception cref="ArgumentNullException">entity</exception> public string GenerateHashCode(ErrorReportEntity entity) { var props = entity.ContextInfo.FirstOrDefault(x => x.Name == "ExceptionProperties") ?? entity.ContextInfo.FirstOrDefault(x => x.Name == "Properties"); if (props == null) { _logger.Error("Failed to find ExceptionProperties collection for entity " + entity.Id); return(null); } string value; if (!props.Properties.TryGetValue("HttpCode", out value)) { return(null); } var statusCode = int.Parse(value); var headerProps = entity.ContextInfo.FirstOrDefault(x => x.Name == "HttpHeaders"); if (headerProps == null) { _logger.Error("Failed to find HttpHeaders collection for entity " + entity.Id); return(null); } var url = headerProps.Properties["Url"].ToLower(); var pos = url.IndexOf("?"); if (pos != -1) { url = url.Remove(pos); } return(HashCodeUtility.GetPersistentHashCode(statusCode + "-" + url).ToString("X")); }
/// <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> /// 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> /// Determines if this instance can generate a hashcode for the given entity. /// </summary> /// <param name="entity">entity to examine</param> /// <returns><c>true</c> for <c>HttpException</c>; otherwise <c>false</c>.</returns> /// <exception cref="ArgumentNullException">entity</exception> public bool CanGenerateFrom(ErrorReportEntity entity) { return(entity.Exception.Name == "HttpException" || entity.Exception.BaseClasses.Any(x => x.EndsWith("HttpException"))); }
/// <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(); }