From 3c6fe13f3de1da1d5ab6471fa4984e2581114fad Mon Sep 17 00:00:00 2001 From: Bugsnag Platforms Team Date: Thu, 7 May 2020 10:59:48 +0100 Subject: [PATCH 01/13] Update PR template to match the Android notifier --- .github/PULL_REQUEST_TEMPLATE.md | 77 ++------------------------------ 1 file changed, 4 insertions(+), 73 deletions(-) diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 4bd4829..5ccc929 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,84 +1,15 @@ ## Goal - - - ## Design -Why was this approach to the goal used? - ---> + ## Changeset - + ## Tests - - -## Discussion - -### Alternative Approaches - - - -### Outstanding Questions - - - -### Linked issues - - - -## Review - - - -For the submitter, initial self-review: - -- [ ] Commented on code changes inline explain the reasoning behind the approach -- [ ] Reviewed the test cases added for completeness and possible points for discussion -- [ ] A changelog entry was added for the goal of this pull request -- [ ] Check the scope of the changeset - is everything in the diff required for the pull request? -- This pull request is ready for: - - [ ] Initial review of the intended approach, not yet feature complete - - [ ] Structural review of the classes, functions, and properties modified - - [ ] Final review - -For the pull request reviewer(s), this changeset has been reviewed for: - -- [ ] Consistency across platforms for structures or concepts added or modified -- [ ] Consistency between the changeset and the goal stated above -- [ ] Internal consistency with the rest of the library - is there any overlap between existing interfaces and any which have been added? -- [ ] Usage friction - is the proposed change in usage cumbersome or complicated? -- [ ] Performance and complexity - are there any cases of unexpected O(n^3) when iterating, recursing, flat mapping, etc? -- [ ] Concurrency concerns - if components are accessed asynchronously, what issues will arise -- [ ] Thoroughness of added tests and any missing edge cases -- [ ] Idiomatic use of the language + From 2a2bddc7808c3c72564e54b9e78563614a1e1264 Mon Sep 17 00:00:00 2001 From: Bugsnag Platforms Team Date: Thu, 7 May 2020 11:03:39 +0100 Subject: [PATCH 02/13] Guard against file read exceptions --- src/Bugsnag/Polyfills.cs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/src/Bugsnag/Polyfills.cs b/src/Bugsnag/Polyfills.cs index 36cd0f8..5baade3 100644 --- a/src/Bugsnag/Polyfills.cs +++ b/src/Bugsnag/Polyfills.cs @@ -1,7 +1,7 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.Linq; -using System.Text; using System.IO; namespace Bugsnag.Polyfills @@ -13,7 +13,15 @@ public static IEnumerable ReadLines(string file) #if NET35 return Enumerable.Empty(); #else - return File.ReadLines(file); + try + { + return File.ReadLines(file); + } + catch (Exception exception) + { + Trace.WriteLine(exception); + return Enumerable.Empty(); + } #endif } } From fdf3d5472063eea6a8b00cea6873579cdd0232f7 Mon Sep 17 00:00:00 2001 From: Bugsnag Platforms Team Date: Thu, 7 May 2020 15:38:59 +0100 Subject: [PATCH 03/13] Only send non-empty code segments to avoid false links in the dashboard --- src/Bugsnag/Payload/StackTraceLine.cs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/Bugsnag/Payload/StackTraceLine.cs b/src/Bugsnag/Payload/StackTraceLine.cs index 0684ffc..5392a2d 100644 --- a/src/Bugsnag/Payload/StackTraceLine.cs +++ b/src/Bugsnag/Payload/StackTraceLine.cs @@ -94,7 +94,10 @@ public StackTraceLine(string file, int lineNumber, string methodName, bool inPro this.AddToPayload("lineNumber", lineNumber); this.AddToPayload("method", methodName); this.AddToPayload("inProject", inProject); - this.AddToPayload("code", code); + if (code != null && code.Count > 0) + { + this.AddToPayload("code", code); + } } public string FileName From 3c1d713f4666c4b10fe4b5b18a236facd4f017c0 Mon Sep 17 00:00:00 2001 From: Steve Kirkland Date: Thu, 7 May 2020 18:37:20 +0100 Subject: [PATCH 04/13] Qualify uses of System.Exception Co-authored-by: Tom Longridge --- src/Bugsnag/Polyfills.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Bugsnag/Polyfills.cs b/src/Bugsnag/Polyfills.cs index 5baade3..ccdb36e 100644 --- a/src/Bugsnag/Polyfills.cs +++ b/src/Bugsnag/Polyfills.cs @@ -17,7 +17,7 @@ public static IEnumerable ReadLines(string file) { return File.ReadLines(file); } - catch (Exception exception) + catch (System.Exception exception) { Trace.WriteLine(exception); return Enumerable.Empty(); From a00e85d7bedcda13688e93d142d309b489fa824a Mon Sep 17 00:00:00 2001 From: Bugsnag Platforms Team Date: Thu, 7 May 2020 21:46:17 +0100 Subject: [PATCH 05/13] Changelog updated --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 497cf4b..ff90e1e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,12 @@ Changelog ## Unreleased +### Bug fixes + +* Handle any exceptions raised when reading files for code segments + | [twometresteve](https://github.com/twometresteve) + | [#123](https://github.com/bugsnag/bugsnag-dotnet/pull/123) + ## 2.2.0 (2018-07-19) ### Enhancements From 5429c5f5d9135102acba272cb2e87f3a4bc69378 Mon Sep 17 00:00:00 2001 From: Bugsnag Platforms Team Date: Mon, 11 May 2020 14:24:47 +0100 Subject: [PATCH 06/13] Examples refreshed --- Bugsnag.sln | 11 +- LICENSE.txt | 2 +- .../aspnet35/Bugsnag.Sample.AspNet35.csproj | 12 +- examples/aspnet35/packages.config | 6 +- .../aspnet45-mvc-webapi.csproj | 20 +-- examples/aspnet45-mvc-webapi/packages.config | 12 +- .../Controllers/HomeController.cs | 79 ++++++---- examples/aspnetcore11-mvc/README.md | 4 +- examples/aspnetcore11-mvc/Startup.cs | 91 ++++++----- .../aspnetcore11-mvc/aspnetcore11-mvc.csproj | 2 +- .../Controllers/HomeController.cs | 149 +++++++++--------- examples/aspnetcore20-mvc/Program.cs | 27 ++-- .../aspnetcore20-mvc/aspnetcore20-mvc.csproj | 4 +- examples/net35-console/net35-console.csproj | 8 +- examples/net35-console/packages.config | 4 +- examples/net47-console/Program.cs | 6 +- examples/net47-console/net47-console.csproj | 8 +- examples/net47-console/packages.config | 4 +- examples/netcore11-console/Program.cs | 37 ++--- .../netcore11-console.csproj | 2 +- examples/netcore20-console/Program.cs | 23 +-- .../netcore20-console.csproj | 2 +- 22 files changed, 259 insertions(+), 254 deletions(-) diff --git a/Bugsnag.sln b/Bugsnag.sln index 2acf7ed..76e1b25 100644 --- a/Bugsnag.sln +++ b/Bugsnag.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.27130.2036 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29209.62 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bugsnag", "src\Bugsnag\Bugsnag.csproj", "{6786F339-353C-413B-9165-422ED8D9EDF5}" EndProject @@ -27,12 +27,17 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject appveyor.yml = appveyor.yml build.cake = build.cake + CHANGELOG.md = CHANGELOG.md + CONTRIBUTING.md = CONTRIBUTING.md src\Directory.build.props = src\Directory.build.props + LICENSE.txt = LICENSE.txt + README.md = README.md + UPGRADING.md = UPGRADING.md EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bugsnag.AspNet.WebApi.Tests", "tests\Bugsnag.AspNet.WebApi.Tests\Bugsnag.AspNet.WebApi.Tests.csproj", "{584E892B-5A65-4484-9F0E-CD58EFDA75BD}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Bugsnag.AspNet.Core.Tests", "tests\Bugsnag.AspNet.Core.Tests\Bugsnag.AspNet.Core.Tests.csproj", "{C86A5E8F-5402-49D8-BDD3-0C96D89B6E2E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Bugsnag.AspNet.Core.Tests", "tests\Bugsnag.AspNet.Core.Tests\Bugsnag.AspNet.Core.Tests.csproj", "{C86A5E8F-5402-49D8-BDD3-0C96D89B6E2E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution diff --git a/LICENSE.txt b/LICENSE.txt index 3e2ba45..b6d11b7 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,4 @@ -Copyright (c) 2018 Bugsnag +Copyright (c) 2020 Bugsnag Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the diff --git a/examples/aspnet35/Bugsnag.Sample.AspNet35.csproj b/examples/aspnet35/Bugsnag.Sample.AspNet35.csproj index eb9d291..5530ec3 100644 --- a/examples/aspnet35/Bugsnag.Sample.AspNet35.csproj +++ b/examples/aspnet35/Bugsnag.Sample.AspNet35.csproj @@ -39,14 +39,14 @@ 4 - - packages\Bugsnag.2.0.2\lib\net35\Bugsnag.dll + + packages\Bugsnag.2.2.0\lib\net35\Bugsnag.dll - - packages\Bugsnag.AspNet.2.0.2\lib\net35\Bugsnag.AspNet.dll + + packages\Bugsnag.AspNet.2.2.0\lib\net35\Bugsnag.AspNet.dll - - packages\Bugsnag.ConfigurationSection.2.0.2\lib\net35\Bugsnag.ConfigurationSection.dll + + packages\Bugsnag.ConfigurationSection.2.2.0\lib\net35\Bugsnag.ConfigurationSection.dll diff --git a/examples/aspnet35/packages.config b/examples/aspnet35/packages.config index 018c38e..3df9d65 100644 --- a/examples/aspnet35/packages.config +++ b/examples/aspnet35/packages.config @@ -1,6 +1,6 @@  - - - + + + \ No newline at end of file diff --git a/examples/aspnet45-mvc-webapi/aspnet45-mvc-webapi.csproj b/examples/aspnet45-mvc-webapi/aspnet45-mvc-webapi.csproj index 5eb3b9c..23c8cf0 100644 --- a/examples/aspnet45-mvc-webapi/aspnet45-mvc-webapi.csproj +++ b/examples/aspnet45-mvc-webapi/aspnet45-mvc-webapi.csproj @@ -46,20 +46,20 @@ 4 - - packages\Bugsnag.2.0.2\lib\net45\Bugsnag.dll + + packages\Bugsnag.2.2.0\lib\net45\Bugsnag.dll - - packages\Bugsnag.AspNet.2.0.2\lib\net45\Bugsnag.AspNet.dll + + packages\Bugsnag.AspNet.2.2.0\lib\net45\Bugsnag.AspNet.dll - - packages\Bugsnag.AspNet.Mvc.2.0.2\lib\net45\Bugsnag.AspNet.Mvc.dll + + packages\Bugsnag.AspNet.Mvc.2.2.0\lib\net45\Bugsnag.AspNet.Mvc.dll - - packages\Bugsnag.AspNet.WebApi.2.0.2\lib\net45\Bugsnag.AspNet.WebApi.dll + + packages\Bugsnag.AspNet.WebApi.2.2.0\lib\net45\Bugsnag.AspNet.WebApi.dll - - packages\Bugsnag.ConfigurationSection.2.0.2\lib\net45\Bugsnag.ConfigurationSection.dll + + packages\Bugsnag.ConfigurationSection.2.2.0\lib\net45\Bugsnag.ConfigurationSection.dll packages\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.1.0.7\lib\net45\Microsoft.CodeDom.Providers.DotNetCompilerPlatform.dll diff --git a/examples/aspnet45-mvc-webapi/packages.config b/examples/aspnet45-mvc-webapi/packages.config index 2316f69..b8b088c 100644 --- a/examples/aspnet45-mvc-webapi/packages.config +++ b/examples/aspnet45-mvc-webapi/packages.config @@ -1,12 +1,12 @@ - + - - - - - + + + + + diff --git a/examples/aspnetcore11-mvc/Controllers/HomeController.cs b/examples/aspnetcore11-mvc/Controllers/HomeController.cs index 7e9bbe6..84988ba 100644 --- a/examples/aspnetcore11-mvc/Controllers/HomeController.cs +++ b/examples/aspnetcore11-mvc/Controllers/HomeController.cs @@ -1,42 +1,55 @@ -using System; +using System; using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using Bugsnag; namespace aspnetcore11_mvc.Controllers { - public class HomeController : Controller + public class HomeController : Controller + { + private readonly IClient _client; + private Dictionary _messageLookup = new Dictionary() { - private readonly IClient _client; - - public HomeController(IClient client) - { - _client = client; - } - - public IActionResult Index() - { - return View(); - } - - public IActionResult About() - { - _client.Breadcrumbs.Leave("Here comes the exception..."); - throw new NotImplementedException(); - } - - public IActionResult Contact() - { - ViewData["Message"] = "Your contact page."; - - return View(); - } - - public IActionResult Error() - { - return View(); - } + { + "Contact", "Contact page" + } + }; + + public HomeController(IClient client) + { + _client = client; + + } + + public IActionResult Index() + { + return View(); + } + + public IActionResult About() + { + _client.Breadcrumbs.Leave("Here comes the exception..."); + throw new NotImplementedException(); + } + + public IActionResult Contact() + { + try + { + ViewData["Message"] = _messageLookup["Cotnact"]; + } + catch (Exception e) + { + _client.Notify(e); + ViewData["Message"] = "Your contact page."; + } + + return View(); + } + + public IActionResult Error() + { + return View(); } + } } diff --git a/examples/aspnetcore11-mvc/README.md b/examples/aspnetcore11-mvc/README.md index 17d2967..97d71ed 100644 --- a/examples/aspnetcore11-mvc/README.md +++ b/examples/aspnetcore11-mvc/README.md @@ -19,7 +19,9 @@ services.AddBugsnag(config => { }); ``` -From within Visual Studio you can compile and start the website. +From within Visual Studio you can compile and start the website. Once the index page loads: +- Navigating to About will invoke an unhandled exception +- Navigating to Contact will invoke a handled exception ### Steps taken to install Bugsnag diff --git a/examples/aspnetcore11-mvc/Startup.cs b/examples/aspnetcore11-mvc/Startup.cs index 32e88a0..0dda6d5 100644 --- a/examples/aspnetcore11-mvc/Startup.cs +++ b/examples/aspnetcore11-mvc/Startup.cs @@ -1,7 +1,3 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.Configuration; @@ -11,55 +7,56 @@ namespace aspnetcore11_mvc { - public class Startup + public class Startup + { + public Startup(IHostingEnvironment env) { - public Startup(IHostingEnvironment env) - { - var builder = new ConfigurationBuilder() - .SetBasePath(env.ContentRootPath) - .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) - .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) - .AddEnvironmentVariables(); - Configuration = builder.Build(); - } + var builder = new ConfigurationBuilder() + .SetBasePath(env.ContentRootPath) + .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true) + .AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true) + .AddEnvironmentVariables(); + Configuration = builder.Build(); + } - public IConfigurationRoot Configuration { get; } + public IConfigurationRoot Configuration { get; } - // This method gets called by the runtime. Use this method to add services to the container. - public void ConfigureServices(IServiceCollection services) - { - services.AddBugsnag(config => { - config.ApiKey = ""; - config.ProjectNamespaces = new[]{ "aspnetcore11_mvc" }; - config.ProjectRoots = new[]{ @"C:\app\" }; - }); - // Add framework services. - services.AddMvc(); - } + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddBugsnag(config => + { + config.ApiKey = ""; + config.ProjectNamespaces = new[] { "aspnetcore11_mvc" }; + config.ProjectRoots = new[] { @"C:\app\" }; + }); + // Add framework services. + services.AddMvc(); + } - // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. - public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) - { - loggerFactory.AddConsole(Configuration.GetSection("Logging")); - loggerFactory.AddDebug(); + // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. + public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory) + { + loggerFactory.AddConsole(Configuration.GetSection("Logging")); + loggerFactory.AddDebug(); - if (env.IsDevelopment()) - { - app.UseDeveloperExceptionPage(); - } - else - { - app.UseExceptionHandler("/Home/Error"); - } + if (env.IsDevelopment()) + { + app.UseDeveloperExceptionPage(); + } + else + { + app.UseExceptionHandler("/Home/Error"); + } - app.UseStaticFiles(); + app.UseStaticFiles(); - app.UseMvc(routes => - { - routes.MapRoute( - name: "default", - template: "{controller=Home}/{action=Index}/{id?}"); - }); - } + app.UseMvc(routes => + { + routes.MapRoute( + name: "default", + template: "{controller=Home}/{action=Index}/{id?}"); + }); } + } } diff --git a/examples/aspnetcore11-mvc/aspnetcore11-mvc.csproj b/examples/aspnetcore11-mvc/aspnetcore11-mvc.csproj index 4aa3097..17ebb38 100644 --- a/examples/aspnetcore11-mvc/aspnetcore11-mvc.csproj +++ b/examples/aspnetcore11-mvc/aspnetcore11-mvc.csproj @@ -6,7 +6,7 @@ - + diff --git a/examples/aspnetcore20-mvc/Controllers/HomeController.cs b/examples/aspnetcore20-mvc/Controllers/HomeController.cs index d9d8f2f..fe2bc4a 100644 --- a/examples/aspnetcore20-mvc/Controllers/HomeController.cs +++ b/examples/aspnetcore20-mvc/Controllers/HomeController.cs @@ -1,98 +1,95 @@ using System; using System.Collections.Generic; using System.Diagnostics; -using System.Linq; -using System.Threading.Tasks; using Microsoft.AspNetCore.Mvc; using aspnetcore20_mvc.Models; -using Bugsnag; namespace aspnetcore20_mvc.Controllers { - public class HomeController : Microsoft.AspNetCore.Mvc.Controller - { + public class HomeController : Controller + { - // The Bugsnag client (initialized in Startup.cs) will be injected into your classes where you declare the IClient dependency. - // This allows you to report handled exceptions within your controller. - private readonly Bugsnag.IClient _bugsnag; + // The Bugsnag client (initialized in Startup.cs) will be injected into your classes where you declare the IClient dependency. + // This allows you to report handled exceptions within your controller. + private readonly Bugsnag.IClient _bugsnag; - public HomeController(Bugsnag.IClient bugsnag) + public HomeController(Bugsnag.IClient bugsnag) + { + _bugsnag = bugsnag; + _bugsnag.BeforeNotify(report => + { + if (report.OriginalException is System.NotImplementedException) { - _bugsnag = bugsnag; - _bugsnag.BeforeNotify(report => { - if (report.OriginalException is System.NotImplementedException) - { - report.Event.Metadata.Add("paying account", true); - report.Event.Context = "an-important-context"; - } - // A BeforeNotify callback lets you evaluate, modify, add and remove data before sending the error to bugsnag. The actions here will be applied to *all* errors, handled and unhandled. - _bugsnag.BeforeNotify(report => { - // In order to correlate errors with customer reports, or to see a list of users who experienced each error, you can attach user data in your callback - report.Event.User = new Bugsnag.Payload.User - { - Id = "006", - Name = "Hedy Lamarr", - Email = "h.lamarr@delos.com" - }; - //this example makes some modifications that only apply to reports of error class "System.NotImplementedException". - if (report.OriginalException is System.NotImplementedException) - { - report.Event.Metadata.Add("account", new Dictionary { { "paying", true } }); - report.Event.Context = "an-important-context"; - } - - // note that using report.Ignore() will cancel the entire error report. - }); + report.Event.Metadata.Add("paying account", true); + report.Event.Context = "an-important-context"; } + }); - public IActionResult Index() + // A BeforeNotify callback lets you evaluate, modify, add and remove data before sending the error to bugsnag. The actions here will be applied to *all* errors, handled and unhandled. + _bugsnag.BeforeNotify(report => + { + // In order to correlate errors with customer reports, or to see a list of users who experienced each error, you can attach user data in your callback + report.Event.User = new Bugsnag.Payload.User { - // creates an exception report, but sets the severity to "info". - _bugsnag.Notify(new System.Exception("Home page loaded"), report => - { - // You can modify many attributes directly on a specific report before sending to Bugsnag. - report.Event.Severity = Bugsnag.Severity.Info; - }); - - return View(); + Id = "006", + Name = "Hedy Lamarr", + Email = "h.lamarr@delos.com" + }; + //this example makes some modifications that only apply to reports of error class "System.NotImplementedException". + if (report.OriginalException is System.NotImplementedException) + { + report.Event.Metadata.Add("account", new Dictionary { { "paying", true } }); + report.Event.Context = "an-important-context"; } - public IActionResult Problems() - { - _bugsnag.Breadcrumbs.Leave("Here comes the exception..."); - // You can leave manual breadcrumbs via the Breadcrumbs property on the client object. - _bugsnag.Breadcrumbs.Leave("Something happened!"); - // You can optionally attach a type and metadata to a breadcrumb. - var metadata = new Dictionary { { "message", "wait for it......" } }; - _bugsnag.Breadcrumbs.Leave("Here comes the exception...", Bugsnag.BreadcrumbType.Navigation, metadata); + // note that using report.Ignore() will cancel the entire error report. + }); + } - // below deliberately throws an unhandled excpetion, which will automatically be reported by Bugsnag and crash the app. - throw new NotImplementedException(); - } + public IActionResult Index() + { + // creates an exception report, but sets the severity to "info". + _bugsnag.Notify(new System.Exception("Home page loaded"), report => + { + // You can modify many attributes directly on a specific report before sending to Bugsnag. + report.Event.Severity = Bugsnag.Severity.Info; + }); - public IActionResult Contact() - { - ViewData["Message"] = "Your contact page."; - try - { - throw new System.Exception("Error!"); - } - catch (Exception ex) - { - throw new System.Exception("Error on contact page!"); - } - catch (Exception ex) - { - // To report handled exceptions, make sure to declare the IClient dependency (above), then you can pass the exception object to your client for notification. - _bugsnag.Notify(ex); - } + return View(); + } - return View(); - } + public IActionResult Problems() + { + _bugsnag.Breadcrumbs.Leave("Here comes the exception..."); + // You can leave manual breadcrumbs via the Breadcrumbs property on the client object. + _bugsnag.Breadcrumbs.Leave("Something happened!"); + // You can optionally attach a type and metadata to a breadcrumb. + var metadata = new Dictionary { { "message", "wait for it......" } }; + _bugsnag.Breadcrumbs.Leave("Here comes the exception...", Bugsnag.BreadcrumbType.Navigation, metadata); - public IActionResult Error() - { - return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); - } + // below deliberately throws an unhandled excpetion, which will automatically be reported by Bugsnag and crash the app. + throw new NotImplementedException(); + } + + public IActionResult Contact() + { + ViewData["Message"] = "Your contact page."; + try + { + throw new System.Exception("Error on contact page!"); + } + catch (Exception ex) + { + // To report handled exceptions, make sure to declare the IClient dependency (above), then you can pass the exception object to your client for notification. + _bugsnag.Notify(ex); + } + + return View(); + } + + public IActionResult Error() + { + return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } + } } diff --git a/examples/aspnetcore20-mvc/Program.cs b/examples/aspnetcore20-mvc/Program.cs index b203cd7..9785743 100644 --- a/examples/aspnetcore20-mvc/Program.cs +++ b/examples/aspnetcore20-mvc/Program.cs @@ -1,25 +1,18 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading.Tasks; using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.Logging; namespace aspnetcore20_mvc { - public class Program + public class Program + { + public static void Main(string[] args) { - public static void Main(string[] args) - { - BuildWebHost(args).Run(); - } - - public static IWebHost BuildWebHost(string[] args) => - WebHost.CreateDefaultBuilder(args) - .UseStartup() - .Build(); + BuildWebHost(args).Run(); } + + public static IWebHost BuildWebHost(string[] args) => + WebHost.CreateDefaultBuilder(args) + .UseStartup() + .Build(); + } } diff --git a/examples/aspnetcore20-mvc/aspnetcore20-mvc.csproj b/examples/aspnetcore20-mvc/aspnetcore20-mvc.csproj index f057f2c..e9b4d4e 100644 --- a/examples/aspnetcore20-mvc/aspnetcore20-mvc.csproj +++ b/examples/aspnetcore20-mvc/aspnetcore20-mvc.csproj @@ -5,12 +5,12 @@ - + - + diff --git a/examples/net35-console/net35-console.csproj b/examples/net35-console/net35-console.csproj index 146c998..86580ed 100644 --- a/examples/net35-console/net35-console.csproj +++ b/examples/net35-console/net35-console.csproj @@ -32,11 +32,11 @@ 4 - - packages\Bugsnag.2.0.2\lib\net35\Bugsnag.dll + + packages\Bugsnag.2.2.0\lib\net35\Bugsnag.dll - - packages\Bugsnag.ConfigurationSection.2.0.2\lib\net35\Bugsnag.ConfigurationSection.dll + + packages\Bugsnag.ConfigurationSection.2.2.0\lib\net35\Bugsnag.ConfigurationSection.dll diff --git a/examples/net35-console/packages.config b/examples/net35-console/packages.config index d2eb2a5..b7755e9 100644 --- a/examples/net35-console/packages.config +++ b/examples/net35-console/packages.config @@ -1,5 +1,5 @@  - - + + \ No newline at end of file diff --git a/examples/net47-console/Program.cs b/examples/net47-console/Program.cs index 5b826c9..434b7f5 100644 --- a/examples/net47-console/Program.cs +++ b/examples/net47-console/Program.cs @@ -1,8 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; +using System; using Bugsnag; namespace net47_console diff --git a/examples/net47-console/net47-console.csproj b/examples/net47-console/net47-console.csproj index 18d5f32..7d9bf03 100644 --- a/examples/net47-console/net47-console.csproj +++ b/examples/net47-console/net47-console.csproj @@ -32,11 +32,11 @@ 4 - - packages\Bugsnag.2.0.2\lib\net45\Bugsnag.dll + + packages\Bugsnag.2.2.0\lib\net45\Bugsnag.dll - - packages\Bugsnag.ConfigurationSection.2.0.2\lib\net45\Bugsnag.ConfigurationSection.dll + + packages\Bugsnag.ConfigurationSection.2.2.0\lib\net45\Bugsnag.ConfigurationSection.dll diff --git a/examples/net47-console/packages.config b/examples/net47-console/packages.config index c3f6df2..a130639 100644 --- a/examples/net47-console/packages.config +++ b/examples/net47-console/packages.config @@ -1,5 +1,5 @@  - - + + \ No newline at end of file diff --git a/examples/netcore11-console/Program.cs b/examples/netcore11-console/Program.cs index cbdd387..52bb7e5 100644 --- a/examples/netcore11-console/Program.cs +++ b/examples/netcore11-console/Program.cs @@ -1,27 +1,28 @@ -using System; +using System; using Bugsnag; namespace netcore11_console { - class Program + class Program + { + static void Main(string[] args) { - static void Main(string[] args) - { - var client = new Client(new Configuration("APIKEY") { - ProjectRoots = new[] { @"C:\app\" }, - ProjectNamespaces = new[] { "netcore11_console" } - }); + var client = new Client(new Configuration("APIKEY") + { + ProjectRoots = new[] { @"C:\app\" }, + ProjectNamespaces = new[] { "netcore11_console" } + }); - Console.WriteLine("Hello World!"); + Console.WriteLine("Hello World!"); - try - { - throw new NotImplementedException(); - } - catch (Exception ex) - { - client.Notify(ex); - } - } + try + { + throw new NotImplementedException(); + } + catch (Exception ex) + { + client.Notify(ex); + } } + } } diff --git a/examples/netcore11-console/netcore11-console.csproj b/examples/netcore11-console/netcore11-console.csproj index 06cb7e8..716583b 100644 --- a/examples/netcore11-console/netcore11-console.csproj +++ b/examples/netcore11-console/netcore11-console.csproj @@ -5,6 +5,6 @@ netcoreapp1.1 - + diff --git a/examples/netcore20-console/Program.cs b/examples/netcore20-console/Program.cs index 775ae53..c38771b 100644 --- a/examples/netcore20-console/Program.cs +++ b/examples/netcore20-console/Program.cs @@ -1,20 +1,21 @@ -using System; +using System; using Bugsnag; namespace netcore20_console { - class Program + class Program + { + static void Main(string[] args) { - static void Main(string[] args) - { - var client = new Client(new Configuration("APIKEY") { - ProjectRoots = new[] { @"C:\app\" }, - ProjectNamespaces = new[] { "netcore20_console" } - }); + var client = new Client(new Configuration("APIKEY") + { + ProjectRoots = new[] { @"C:\app\" }, + ProjectNamespaces = new[] { "netcore20_console" } + }); - Console.WriteLine("Hello World!"); + Console.WriteLine("Hello World!"); - throw new NotImplementedException(); - } + throw new NotImplementedException(); } + } } diff --git a/examples/netcore20-console/netcore20-console.csproj b/examples/netcore20-console/netcore20-console.csproj index 37c113b..feb9665 100644 --- a/examples/netcore20-console/netcore20-console.csproj +++ b/examples/netcore20-console/netcore20-console.csproj @@ -5,6 +5,6 @@ netcoreapp2.0 - + From 6180dba837e3694ddcdde86cbc0057af11577a26 Mon Sep 17 00:00:00 2001 From: Bugsnag Platforms Team Date: Mon, 11 May 2020 17:53:38 +0100 Subject: [PATCH 07/13] Drafting out fix --- src/Bugsnag/Bugsnag.csproj | 4 +++- src/Bugsnag/UnhandledException.cs | 28 +++++++++++++++++++++++++++- 2 files changed, 30 insertions(+), 2 deletions(-) diff --git a/src/Bugsnag/Bugsnag.csproj b/src/Bugsnag/Bugsnag.csproj index c3f9b7a..509d649 100644 --- a/src/Bugsnag/Bugsnag.csproj +++ b/src/Bugsnag/Bugsnag.csproj @@ -1,4 +1,4 @@ - + Bugsnag Bugsnag .NET Notifier @@ -19,6 +19,8 @@ + + diff --git a/src/Bugsnag/UnhandledException.cs b/src/Bugsnag/UnhandledException.cs index a36452f..ca7b2b7 100644 --- a/src/Bugsnag/UnhandledException.cs +++ b/src/Bugsnag/UnhandledException.cs @@ -11,9 +11,11 @@ class UnhandledException private readonly object _currentClientLock = new object(); private IClient _currentClient; + private bool _unobservedTerminates; private UnhandledException() { + _unobservedTerminates = DetermineUnobservedTerminates(); AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit; TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; @@ -46,9 +48,33 @@ public void ConfigureClient(IClient client, IConfiguration configuration) } } + /// + /// Determines if an UnobservedTaskException leads to the process terminating, based on the target + /// framework and (when applicable) configuration. + /// + /// + private bool DetermineUnobservedTerminates() + { +#if NET35 || NET40 + return true; +#elif NET45 + System.Xml.Linq.XElement configFile = System.Xml.Linq.XElement.Load(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile); + if (configFile != null) + { + // TODO Null check all the way down + Console.WriteLine(configFile.Element("runtime").Element("ThrowUnobservedTaskExceptions").Attribute("enabled").Value); + } +#elif NETSTANDARD1_3 || NETSTANDARD2_0 + // TODO SetupInformation does not exist in .NET Standard 1.3 + // ConfigurationMAnager _might_ be an option in .NET Standard 2.0 + //AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName + return false; +#endif + } + private void CurrentDomain_ProcessExit(object sender, EventArgs e) { - HandleEvent(null, true); + HandleEvent(null, _unobservedTerminates); } private void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) From f4703ee901a05e4e26b3c18aa206d7a34cee45b4 Mon Sep 17 00:00:00 2001 From: Bugsnag Platforms Team Date: Mon, 11 May 2020 18:05:28 +0100 Subject: [PATCH 08/13] Pass flag from correct call. --- src/Bugsnag/UnhandledException.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/Bugsnag/UnhandledException.cs b/src/Bugsnag/UnhandledException.cs index ca7b2b7..2ca80b8 100644 --- a/src/Bugsnag/UnhandledException.cs +++ b/src/Bugsnag/UnhandledException.cs @@ -74,12 +74,12 @@ private bool DetermineUnobservedTerminates() private void CurrentDomain_ProcessExit(object sender, EventArgs e) { - HandleEvent(null, _unobservedTerminates); + HandleEvent(null, true); } private void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) { - HandleEvent(e.Exception as Exception, !e.Observed); + HandleEvent(e.Exception as Exception, _unobservedTerminates); } [HandleProcessCorruptedStateExceptions] From 6b9af4fbc75040b65cc9792164cf7e7ad2fb7599 Mon Sep 17 00:00:00 2001 From: Bugsnag Platforms Team Date: Tue, 12 May 2020 10:03:36 +0100 Subject: [PATCH 09/13] Reworked for .NET Framework targets --- src/Bugsnag/UnhandledException.cs | 35 +++++++++++++++++++------------ 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/src/Bugsnag/UnhandledException.cs b/src/Bugsnag/UnhandledException.cs index 2ca80b8..7a7c241 100644 --- a/src/Bugsnag/UnhandledException.cs +++ b/src/Bugsnag/UnhandledException.cs @@ -11,11 +11,15 @@ class UnhandledException private readonly object _currentClientLock = new object(); private IClient _currentClient; +#if !(NET35 || NET40) private bool _unobservedTerminates; +#endif private UnhandledException() { +#if !(NET35 || NET40) _unobservedTerminates = DetermineUnobservedTerminates(); +#endif AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit; TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; @@ -53,24 +57,25 @@ public void ConfigureClient(IClient client, IConfiguration configuration) /// framework and (when applicable) configuration. /// /// +#if NET45 private bool DetermineUnobservedTerminates() { -#if NET35 || NET40 - return true; -#elif NET45 System.Xml.Linq.XElement configFile = System.Xml.Linq.XElement.Load(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile); - if (configFile != null) - { - // TODO Null check all the way down - Console.WriteLine(configFile.Element("runtime").Element("ThrowUnobservedTaskExceptions").Attribute("enabled").Value); - } -#elif NETSTANDARD1_3 || NETSTANDARD2_0 - // TODO SetupInformation does not exist in .NET Standard 1.3 - // ConfigurationMAnager _might_ be an option in .NET Standard 2.0 - //AppDomain.CurrentDomain.SetupInformation.TargetFrameworkName + var configValue = configFile?.Element("runtime")?.Element("ThrowUnobservedTaskExceptions")?.Attribute("enabled")?.Value; + bool value; + var success = bool.TryParse(configValue, out value); + return success && value; + } +#endif + +#if NETSTANDARD1_3 || NETSTANDARD2_0 + private bool DetermineUnobservedTerminates() + { + //Console.WriteLine(System.Runtime.InteropServices.RuntimeEnvironment.SystemConfigurationFile); + return false; + } #endif - } private void CurrentDomain_ProcessExit(object sender, EventArgs e) { @@ -79,7 +84,11 @@ private void CurrentDomain_ProcessExit(object sender, EventArgs e) private void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) { +#if NET35 || NET40 + HandleEvent(e.Exception as Exception, !e.Observed); +#else HandleEvent(e.Exception as Exception, _unobservedTerminates); +#endif } [HandleProcessCorruptedStateExceptions] From 616837c91c6dc88cc87c0b498899ec3637ca74ae Mon Sep 17 00:00:00 2001 From: Bugsnag Platforms Team Date: Tue, 12 May 2020 12:17:27 +0100 Subject: [PATCH 10/13] Hard code .NET Standard to non-terminating --- src/Bugsnag/UnhandledException.cs | 13 +++---------- 1 file changed, 3 insertions(+), 10 deletions(-) diff --git a/src/Bugsnag/UnhandledException.cs b/src/Bugsnag/UnhandledException.cs index 7a7c241..947eab4 100644 --- a/src/Bugsnag/UnhandledException.cs +++ b/src/Bugsnag/UnhandledException.cs @@ -57,25 +57,18 @@ public void ConfigureClient(IClient client, IConfiguration configuration) /// framework and (when applicable) configuration. /// /// -#if NET45 private bool DetermineUnobservedTerminates() { +#if NET45 System.Xml.Linq.XElement configFile = System.Xml.Linq.XElement.Load(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile); var configValue = configFile?.Element("runtime")?.Element("ThrowUnobservedTaskExceptions")?.Attribute("enabled")?.Value; bool value; var success = bool.TryParse(configValue, out value); return success && value; - } -#endif - -#if NETSTANDARD1_3 || NETSTANDARD2_0 - private bool DetermineUnobservedTerminates() - { - //Console.WriteLine(System.Runtime.InteropServices.RuntimeEnvironment.SystemConfigurationFile); - +#else //NETSTANDARD1_3 || NETSTANDARD2_0 return false; - } #endif + } private void CurrentDomain_ProcessExit(object sender, EventArgs e) { From 064d9a84e5c028e28cea1f9f8f58ffa862a74375 Mon Sep 17 00:00:00 2001 From: Bugsnag Platforms Team Date: Tue, 12 May 2020 13:37:55 +0100 Subject: [PATCH 11/13] CHANEGLOG updated --- CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ff90e1e..22969fd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,10 +5,14 @@ Changelog ### Bug fixes -* Handle any exceptions raised when reading files for code segments +* Handle any exceptions raised when reading files for code segments. | [twometresteve](https://github.com/twometresteve) | [#123](https://github.com/bugsnag/bugsnag-dotnet/pull/123) +* Account for process termination behavior when handling UnobservedTaskExceptions. + | [twometresteve](https://github.com/twometresteve) + | [#125](https://github.com/bugsnag/bugsnag-dotnet/pull/125) + ## 2.2.0 (2018-07-19) ### Enhancements From 9915f93a3a0240ca44d0651811e894f50038f02e Mon Sep 17 00:00:00 2001 From: Bugsnag Platforms Team Date: Wed, 13 May 2020 16:39:38 +0100 Subject: [PATCH 12/13] Conditional compilation logic simplified --- src/Bugsnag/UnhandledException.cs | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/Bugsnag/UnhandledException.cs b/src/Bugsnag/UnhandledException.cs index 947eab4..d884f29 100644 --- a/src/Bugsnag/UnhandledException.cs +++ b/src/Bugsnag/UnhandledException.cs @@ -11,15 +11,11 @@ class UnhandledException private readonly object _currentClientLock = new object(); private IClient _currentClient; -#if !(NET35 || NET40) private bool _unobservedTerminates; -#endif private UnhandledException() { -#if !(NET35 || NET40) _unobservedTerminates = DetermineUnobservedTerminates(); -#endif AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; AppDomain.CurrentDomain.ProcessExit += CurrentDomain_ProcessExit; TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; @@ -59,7 +55,9 @@ public void ConfigureClient(IClient client, IConfiguration configuration) /// private bool DetermineUnobservedTerminates() { -#if NET45 +#if NET35 || NET40 + return true; +#elif NET45 System.Xml.Linq.XElement configFile = System.Xml.Linq.XElement.Load(AppDomain.CurrentDomain.SetupInformation.ConfigurationFile); var configValue = configFile?.Element("runtime")?.Element("ThrowUnobservedTaskExceptions")?.Attribute("enabled")?.Value; bool value; @@ -77,11 +75,7 @@ private void CurrentDomain_ProcessExit(object sender, EventArgs e) private void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e) { -#if NET35 || NET40 - HandleEvent(e.Exception as Exception, !e.Observed); -#else - HandleEvent(e.Exception as Exception, _unobservedTerminates); -#endif + HandleEvent(e.Exception as Exception, _unobservedTerminates && !e.Observed); } [HandleProcessCorruptedStateExceptions] From f0f0698dd0a82c96b266cd727fbdb9bb9b7d85de Mon Sep 17 00:00:00 2001 From: Bugsnag Platforms Team Date: Wed, 13 May 2020 21:45:39 +0100 Subject: [PATCH 13/13] Release 2.2.1 --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 22969fd..38c10ee 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,7 +1,7 @@ Changelog ========= -## Unreleased +## 2.2.1 (2019-05-14) ### Bug fixes