Best Practices for C# for Exception Reporting

Since the early days of .NET and C#, developers have been using logging libraries like log4Net or NLog as a way to report on errors and exceptions in applications. This is a great place to start, but logs can be very noisy, and don’t often contain the context needed to assist in the detection and resolution process. Logs also require quite a bit of digging through before they become useful. Error monitoring and crash reporting platforms like Backtrace have evolved in the last few years to allow developers to gain more actionable intelligence from their exception reports and make more informed decisions when it comes to allocating resources.

In this blog post, we’ll discuss how you can better handle exceptions in your code in an effort to help you prioritize issues based on information that is important to you and your application.

Let’s Review C# Exceptions

In languages like C#, an exception is an error that occurs during runtime of your application. Exceptions can be the result of faulty arithmetic (i.e. divide by zero), invalid array access (i.e. accessing an out of bounds region), error loading a file (i.e. incorrect directory path), or a timeout when connecting to an external data source (i.e. down site), amongst other broad error types.

Managed languages like C# capture these types of errors as exceptions, allowing you an opportunity to handle the situation. You can add exception handling code in a try-catch-finally block or by registering a listener for unhandled exceptions, like with Backtrace’s backtraceClient.HandleApplicationException(); method. A try-catch-finally block is placed around code that could throw an exception, and it allows the developer to implement code to handle specific or generic exceptions that the code could throw. Following is a simple example of a try-catch-finally block:

This exception handling code shows us how a developer can handle different types of exceptions with different code and logic.

What Should I do with Exceptions?

There are two main things you can do with exceptions.

  1. Try to recover. This means including appropriate retry or user control. For example, if the file selected didn’t exist, tell the user to try to load a different file and cancel the operation. Or if a network timeout occurred, let the user know how many times the application will retry before failing.
  2. Log your exceptions. As stated earlier, log4net and NLog are two popular logging libraries. Logs are useful because they record all the errors that occurred and provide a common definition of log severities. This makes it relatively easy to filter and identify issues captured in a log.

Something to keep in mind, however, is that logs are far from a panacea. This is especially true for native applications running on Windows or Mac desktops, or mobile devices like iOS or Android. Logs don’t do a good job of capturing application state, device information, or external factors. Without this type of information, triaging and understanding possible root cause becomes more difficult, as you must rely on incomplete data.

Some logging libraries provide the ability to write to a central location, such as a remote database or error monitoring platform. While this allows you to filter and search all your log information from one location, the entries still lack the additional context to provide clues as to what the root cause could be. There is a lot of information and noise to sift through, and log-based systems are hampered in their ability to accurately group similar crashes together due to lack of sophisticated deduplication or grouping engines.

Enter Backtrace for C# Exceptions

Error monitoring solutions have become popular in recent years to address some of the deficiencies in log management and other APM solutions. Backtrace offers software teams an advanced approach to detecting and resolving issues that cause application exceptions or crashes.

Backtrace takes the idea of centralized exception logging to the next level, with a reporting library that can collect application state, system and custom metadata, call stacks, dump files, and other file attachments. Backtrace provides ways to customize views and collect more in-depth data that would be possible with other error monitoring or log management solutions.

Let’s look at a few common examples of what software teams are doing with Backtrace:

  1. Identify a top 10 list of issues based on the number of occurrences or affected users.
  2. Know which user has the most amount of instability, and the types of issues they are encountering.
  3. Search across all your exception report data, including call stacks and attributes with powerful operators like not-contains and inverse-regex.
  4. Detect regressions immediately while monitoring your CI or regression builds.
  5. View call stack for symbolicated code to make it easier to pinpoint what code may be the culprit.
  6. Integrate with existing workflows for ticket management, collaboration, monitoring and more.
Image of Backtrace Query Builder with C# exception info.

Dynamic top 10 lists displaying the information most important to you.

 

Investigate all exceptions and crashes reported by a specific user.

 

Image of Backtrace Query Operators

Collect and Search any system or custom attribute using powerful search operators.

 

Image of the C# Exception Report Viewer

The Backtrace Web Debugger highlights important signals and allows you to download ancillary data.

 

Best Practices for How to Integrate

The Backtrace C# error and crash capture tool allows you to report on handled and unhandled exceptions. It’s supported on .NET Core 2.0, Xamarin, .NET 4.5+, and some other common runtimes. You can get started by installing the Backtrace C# Reporting Library via NuGet:

Windows NuGet CLI: Install-Package Backtrace
Linux/Mac OS X .NET Core CLI: dotnet add package Backtrace

Once installed, create a BacktraceCredentials and BacktraceClientConfiguration object to specify how to connect to Backtrace, which custom attributes you want to submit alongside all reports, and optional client side rate limiting. The custom attributes are how you begin to capture state and context. Current customers are storing information like applicationVersion, deviceType, deviceID, and userID, amongst other custom data here.
Backtrace also supports a flexible offline database (BacktraceDatabase) for local storage of errors that are not sent successfully due to network outage or server unavailability. Below is some sample code to initialize error reporting with the Backtrace C# reporting library.

You can see that the BacktraceDatabaseSettings provides a flexible way to configure your offline store. You can specify the maximum number of records or the maximum size of the database, and how it behaves when trying to send, or retry sending, errors. The BacktraceDatabase has a special power as well. Once it is enabled, it will automatically generate and attach a minidump to the error reports that are submitted to Backtrace. Engineers can load the minidump into Visual Studio to identify specific areas in the source code that were part of the call stack, and understand local variable values at the time of error.

The BacktraceDatabase has a special power as well. Once it is enabled, it will automatically generate and attach a minidump to the error reports that are submitted to Backtrace. Engineers can load the minidump into Visual Studio to identify specific areas in the source code that were part of the call stack, and understand local variable values at the time of error.

Now that you see how to initialize your integration, let’s explore how you can embed code to generate and send a BacktraceReport object. Usually, this is done in a try-catch-finally statement (below) or by enabling the reporting of unhandled application exceptions (using
backtraceClient.HandleApplicationException();)

You’ll note a few items in the above code. First, you can add additional runtime context or state in the form of more attributes. Teams might use this to capture breadcrumb related information, or other system state changes since the application started. Second, you can specify file attachments, such as log file, config files, or system information files to be submitted alongside the report to assist in debugging. Third, Backtrace implemented the SendAsync method for sending. This is the recommended approach for .NET 4.5+, however sync post methods are available as well.

The Backtrace C# reporting library offers more customizations, such as custom event handlers to trigger actions before and after certain events. It also offers an overloaded SendAsync() method to allow you to send generic string messages, or the exception object (rather than a BacktraceReport).

Find out more about how to integrate by checking out the Backtrace C# Reporting Library on
Github.

Actionable Intelligence for C# and More

Now that you are set up to capture your C# exceptions in a more holistic way, you can start to explore 5 ways that Backtrace improves your exception reporting. In that post, you’ll see how Backtrace provides automated symbolication, industry-leading deduplication techniques, and rich query and group by features.

Stay tuned for a future blog post on mixed mode call stacks. Mixed mode call stacks will allow you to capture error and crash reports from your C# application and the managed or unmanaged C++ libraries that it calls.

By | 2018-08-14T13:52:11+00:00 August 13th, 2018|Backtrace, Features|