Debugging a .NET Serverless App

Serverless development has become the hot thing in tech. Renting time on a virtual machine only when you need it saves money and resources. However, there are many gotchas that can make working with serverless technology difficult if you're not prepared. One such gotcha is event handling.

Unlike a Web Server applications, events in a serverless function act very similarly to console applications. Take AWS Lambda, for example. A Lambda function is going to spin up, execute, and spin down as quickly as it can. You want this. This is one of the key selling points of serverless, and how developers can ultimately save money. However, this can lead to problems when trying to process asynchronous events. We'll cover how to solve that in this post.

Today, we're going to build a simple .NET Hello World serverless application and we're going to implement event handling to log errors and other events. We're going to use the dotnet cli to install a new Lambda template, but first we need to make sure we have the Amazon Lambda Templates Package.

Once that's installed, create a new directory. I'm calling mine MyFunction, but you can call yours whatever you'd like. Change into that directory and run the following command:

dotnet new lambda.EmptyFunction --name MyFunction

This will create your new serverless function project in .NET. If you open up your project in Visual Studio or whatever IDE or code editor you prefer, you'll see your main file is Function.cs. You'll also have a very helpful README, and a json file with some default configuration. We're not going to be editing anything besides the Function.cs file, but it's good to know what's provided out of the box when generating a new lambda project.

We now need to add Exceptionless to the project. Exceptionless is an open-source event monitoring service available to all programming languages, but dedicated to .NET. Let's install it by running this command at the command line:

dotnet add package Exceptionless

This will install the most recent version of Exceptionless and is the foundation to you adding event handling in your serverless app. Once Exceptionless is installed, we can take a look at our code and see about where we might want to utilize Exceptionless.

Open up the Function.cs file again and add the Exceptionless namespace like this:

using Exceptionless;

Now, let's take a look at the function itself. It's a pretty simple function that returns a string in its uppercase form. We're going to change this around to instead return "Hello" plus the string passed in. Of course, that's not the focus of the tutorial, so we're going to also wire up error handling and event processing.

So, let's start with error handling. We can manually force an error that will fall into the catch block of a try/catch by not passing in a string to our function. Update your function to look like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Exceptionless;

using Amazon.Lambda.Core;

[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]

namespace MyFunction
{
public class Function
{

public string FunctionHandler(string input, ILambdaContext context)
{
var client = new ExceptionlessClient(c =>
{
c.ApiKey = "YOUR EXCEPTIONLESS API KEY";
});

using var _ = client.ProcessQueueDeferred();

try
{
if (input == null)
{
throw new Exception("input value is required");
}
return "Hello, " + input + "!";
}
catch (Exception ex)
{
client.SubmitException(ex);
return ex.Message;
}
}
}
}

We've added the Exceptionless namespace, we've converted the function to take in a string and return a hello world string.

To test error handling, we have set up a try/catch that will throw if no string is passed into our function. If that happens, we send the exception to Exceptionless.

Because we need to make sure events are sent to Exceptionless before the function ends, we are using the Exceptionless method ProcessQueueDeferred().

Now, it's time to test. Fortunately, there's a super easy way to test our code before we deploy to AWS. In your root directory, find the test folder. Nested in that folder is a test file called MyFunction.Test.cs. We're going to edit this file a bit and run our tests from the command line.

Update the file to look like this:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

using Xunit;
using Amazon.Lambda.Core;
using Amazon.Lambda.TestUtilities;

using MyFunction;

namespace MyFunction.Tests
{
public class FunctionTest
{
[Fact]
public void HelloWorldFunction()
{
var function = new Function();
var context = new TestLambdaContext();
var upperCase = function.FunctionHandler("John", context);

Assert.Equal("Hello, John!", upperCase);
}
}
}

This test should pass and we should not be sending anything to Exceptionless. To test it, switch into MyFunction/test/MyFunction.Tests and run dotnet test.

If your test passed, you're in great shape! But we don't just want to test our function. We want to make sure errors are sent to Exceptionless. So let's force an error.

Add the following to your test file:

 public class ErrorTest
{
[Fact]
public void ErrorFunctionTest()
{
var function = new Function();
var context = new TestLambdaContext();
var helloworld = function.FunctionHandler(null, context);

Assert.NotEqual("Hello, John!", helloworld);
}
}

The test should pass because we don't get a response of "Hello, John!". But what we want to do is check to see if the exception was sent to Exceptionless.

Open up your Exceptionless dashboard and you shoud see something like Exception in FunctionHandler input value is required. If you click on it, you'll see more details like this:

There are three tabs on the details page. In the above screenshot, I have selected the "Overview" tab. However, if you explore the other tabs, you'll see value data captured for you automatically.

Cool, so we created an exception. What about something a little more complex?

Let's say we want to track the usage of our new serverless function. We can do that pretty easily with Exceptionless. Let's change the current try/catch block to look like this:

 try
{
if (input == null)
{
throw new Exception("input value is required");
}
client.SubmitFeatureUsage("Serverless Function");
return "Hello, " + input + "!";
}
catch (Exception ex)
{
client.SubmitException(ex);
return ex.Message;
}

Here we are sending a feature usage event to Exceptionless as soon as the serverless function is triggered and we're sure the input is not null. The nice thing about this is that it doesn't rely on any errors being thrown and the rest of your function can continue to execute.

Let's run our tests again. We should get the feature usage event as well as an additional error because, remember, one of our tests forces a null exception.

In our dashboard, if we look at all event, we will see our feature usage event logged.

Feature usage event example

Conclusion #

These types of events (features, logs, errors) because especially useful in serverless environments. Often, default logging in a serverless environment is either non-existent or much more difficult to set up. By dropping Exceptionless into your serverless application, you can track just about anything you're interested in.

Why You Should be Practicing Exception Driven Development

You've heard of test-driven development. You've heard of behavior-driven development. Maybe you've even heard of acceptance-driven development. But you probably haven't heard much about exception-driven development. Let's take a look at what that type of development process is and why you should be practicing it.

Exception Driven Development #

Jeff Atwood, of Coding Horror, wrote a blog post almost 12 years ago about this topic. In those dozen years since his post, the need for exception-driven development has only increased, but developers and companies need a clear understanding of how and why to practice this type of development.

At a high-level, exception-driven development is simply the practice of monitoring for exceptions in your code and shipping fixes quickly. To ensure you are monitoring the right things and shipping the right fixes, all without burdening your users, you have to make sure you are collecting the right data.

Contextual data is the key to exception-driven development. Arbitrary data from various points of input may not be useful. Data that is highly contextual to user actions, user experiences, and points in time during the use of your app will allow you to target better solutions and ship faster.

With your contextual data in hand, you can start to fix issues. But here's the rub: You can't wait until you "have enough" fixed for a deployment. Part of the exception-driven development process is deploying often. Have a single bug that took one line to fix? Cool, ship it. Know that you'll have additional features to put out in a few days and you're considering holding your bug fix for that deployment? Don't. Ship the bug fix, then ship the features.

Exception-driven development isn't a mysterious new process for most developers, but it is a mindset shift within many organizations. Bug fixes, listening to the data, and responsiveness must take priority if you are to transition to an exception-driven development cycle. While it may not take much effort to change your company's internal process, it may still take some convincing. So, why should you and your company practice exception-driven development?

Why Exception Driven Development #

The first reason you should care is the most obvious reason. Your code has bugs. It does. No matter how good you are, you're going to ship bugs and you need to be ready to fix those bugs. Jeff Atwood probably said it best in his post about exception-driven development:

The question isn't how many bugs you will ship but how fast you can fix them.

Bugs are a part of code. Some never get fixed. But the ones that need to be fixed need to be fixed quickly. The trouble, of course, is that if you are relying on your users to tell you about these bugs, you've already taken too long to fix the issue.

Wait, what?

It's true. If your users have told you about bugs through email or Slack or Discord, you have already taken too long to resolve the issue. This is where the heart of exception-driven development comes in. You need to know about issues the second they arise.

You don't want to turn into this guy, right?

Picture of an angry cat acting as tech support

Think about it this way. How many lines of code does your application have? Got that number in mind? Ok, now divide it by 1000. Take your resulting number and multiply it by 15. That is possibly how many bugs you have in your software right now. Knowing this, don't you think it's important to try to find these bugs before your users have to tell you about them?

By practicing exception-driven development, you make your customers happier which protects your brand image. Do you want to be the brand that has a ton of bugs or do you want to be the brand that solves problems? You also make your developers happier. Would you rather fix a bug on a piece of code shortly after you were working on it, or do you want to try to fix it six months later after you've long forgotten about that code?

Customer retention, customer conversion, productivity, morale, and more are the reasons exception-driven development is important. Convinced yet? Good, now how do you do it?

How to Practice Exception Driven Development #

If you remember, the first step of exception-driven development is to collect contextual data. Contextual data means data more than the stack trace itself. You should be collecting those traces, but the stack trace is just part of the picture. Some of the questions your contextual data should be answering include:

  • What user had this problem?
  • Where in the application did the problem occur?
  • Has the problem occurred previously?
  • What actions did the user take leading up to the issue?

You can probably think of other questions you'd like answered to help you with the bugs that come in. But notice that the suggestion here is not to collect this information directly from your users. Don't wait for an email from them. Don't follow-up with questions about their operating system and browser. Collect that information upfront without the user having to take action, and you will have your contextual data. Services like Exceptionless make this easy.

But why use a service like Exceptionless? You're a developer, you can just build the solution yourself. Sure you can, but then you have to manage that solution. You have to fix bugs on that solution while also fixing bugs you've found from your actual production application. You're not in the business of running an error monitoring service. But Exceptionless is.

Once you've collected your contextual data, you should know what to fix, where to fix it, and why it needs to be fixed. So, the logical thing to do is—well—fix it. The trick, though, is not to allow code creep to slow your release down. It's easy (trust me we all do it) to say, "oh, I'll just add this little feature or change this one other thing" when you're trying to fix a bug. You have to fight that urge, fix the bug, commit the code, and ship it. Then, you can go back and work on the shiny thing that almost distracted you.

Once your code is shipped, if you are using Exceptionless, you can mark the version that fixed the bug right within your dashboard. By doing this, you'll automatically know if you have any regressions. Exceptionless will track new occurrences of the error and compare the occurrence against the version of software the user is using and the version you marked as solving the problem. If those versions match, it's a regression.

Conclusion #

The whole flow of collecting data, analyzing the data, writing code, and shipping should not be foreign to you. Yet, exception-driven development is still not practiced everywhere. Hopefully, this article helps you understand why you should change your ways and how to do it.

Comparing Monitoring Services

Error monitoring is vital to the success of any application. Console logs only get you so far. Once your app is out in the wild, asking your customers to open up the developer tools window and inspect it for errors is a deal-breaker. This is where error monitoring services come in. Let's take a look at three different services:

Each service, generally speaking, handles errors and logging for your applications, but not all services are created equally. Let's take a closer look at each and see how they stack up.

Log Rocket #

Log Rocket was founded in 2015 and is an error monitoring service that focuses on replaying the events that led up to the error. While some services focus on digging into the error details themselves, Log Rocket uses HTML from the user's session to reconstruct a playback of what the user experienced.

What LogRocket does is we capture a recording in real time of all the user activity so the developer on the other end can replay exactly what went wrong and troubleshoot issues faster.

Log Rocket example

Log Rocket is a venture-backed company that has raised $30 million in its history. It provides its tools exclusively as a for-profit, hosted solution. They do offer self-hosted options, but only as part of their enterprise, custom pricing.

Their focus is on user experience through the tracking of everything a user does on the site. While this is helpful, it is also a bit invasive in the fact that the default installation of Log Rocket captures just about everything a user does in the application.

Sentry #

Sentry was founded in 2012 and is focused on capturing errors at the code-level and sending them to a repository for review and organization. Sentry allows you to configure the groupings of errors and presents them in an interface designed to surface the most-pressing errors first.

In addition to surfacing errors, Sentry allows users to create custom alerts. For example, should a specific event occur, you can set up a trigger to send an email to your team. This is generally used for errors, but it could be used for just about any event sent through to Sentry.

Sentry Example

Sentry, like Log Rocket, is also a venture-backed company. They have raised over $66 million in their history. Unlike Log Rocket, Sentry provides an open-source and self-hosted solution for free. For users that do not want to pay for Sentry's hosted option, they can utilize Docker to run a self-hosted, containerized version of the Sentry platform.

One big differentiator for Sentry is its focus on providing services for a wide array of platforms.

Sentry differentiates from legacy application performance monitoring solutions by focusing on software that runs on devices its developers have no control over, including mobile and IoT devices and smart sensor networks.

Exceptionless #

Exceptionless has been around the longest of the three services reviewed here. Created as a software offering from the parent company CodeSmith Tools, Exceptionless was established in 2010 and is privately held. It is positioned as a tool to help first and a product second. This is why one of Exceptionless's main focuses is the open-source community.

Exceptionless lets developers capture logs, events, and errors. However, Exceptionless's real differentiator is its real-time delivery mechanisms. Rather than costly code deployments to change your error monitoring configurations, Exceptionless allows you to make changes from their user interface and those changes will apply instantly in your application. This is key when it becomes clear that additional errors need to be surfaced quickly.

From the user experience and customer experience front, Exceptionless works hard to stand out from the competition. In Exceptionless 7.0, customers are now able to mark stacks of errors and logs as "discarded", the events will no longer count against plan quotas. Combine that with their chat, email, and Discord support, and Exceptionless proves its focus on customers.

Digging into the product features, Exceptionless provides one of the cleanest views for understanding events and errors in your application.

Example error details

All the information you need about errors and events are captured in a simple, easy to read view. From your dashboard, you can consolidate events into stacks to help group relevant issues. You can also indicate the resolution of events based not just on the fact that the issue was resolve but based on the version of your software that fixed the event. Exceptionless will automatically recognize any new errors that come in with an older version and group them in the resolved bucket. However, should the error surface on the version of your software in which you marked the issue fixed, Exceptionless will classify this event as a regression without you having to manually do so.

So Which One is Right For You? #

The answer to this often comes down to what's most important for your application. Each service has its merits, but the value provided differs based on use-cases. Log Rocket offers full session replays, but may not be the right solution for digging deep into stack traces. Sentry offers full stack trace reviews and error categorization, but it may not be the right solution if you need the ability to update your error handling configuration in real-time. Exceptionless provides real-time error monitoring and configuration, and it is committed to the open-source community.

In the end, the choice is yours.

Set Default Log Levels

Customize Your Log Levels on the Fly #

Imagine this scenario. You're debugging your application. You KNOW there is a problem, but your logs aren't showing you anything helpful. You've bounced your head off your desk at least 34 times. Then it hits you!

You've configured your error monitoring service to only capture FATAL errors, but this error is surely some other level. Your log level settings need to be updated.

The good news is you've figured out what the problem is (at least what the problem is that's keeping you from debugging the real problem). The bad news is you now have to update your applications code to ensure the right log levels are being passed through to your error monitoring service. You have to save your code, re-deploy, then start re-testing.

That's a pretty terrible experience, but fortunately, it's one that Exceptionless has solved for you.

You can customize the log levels Exceptionless will track for you. ON. THE. FLY. You don't have to touch your code. Instead, should you run into the problem above, you can change your default log level and it will be applied immediately. Any new events coming through that match the level you set will now be stacked up and tracked for you.

We recognize that every application is different. Every developer is different. Being able to easily customize the data captured through Exceptionless is an important part of our mission. This is why in v7.0.5, we have updated the user interface to support default log level selections.

Log Level Settings

As you can see in the image above, the current example default is "Warn." You can override that default on the Stacks page any time you'd like. But if you want to customize that default log level from the start and have it apply across the board, you can do so on your settings page.

Set Default Log Level on the Settings Page

Why Do Log Levels Matter #

Logging is key to understanding problems in your application, but it can also act as a historical record useful for post-mortems and general analysis. However, not all logs are important or necessary. And even if they are, you may want to filter them out in certain scenarios.

Enter log levels.

Log levels control what types of messages are logged. The general definition of log levels, and what we use at Exceptionless is:

TRACE
DEBUG
INFO
WARN
ERROR
FATAL
OFF

By setting your log levels, you are essentially saying you'd like to see information grouped into the buckets defined by the log level. For example, if you'd only like to log the most severe issues, you might select a log level of FATAL.

We want to help you with this flow by automatically accepting or ignoring errors and messages that don't match the log levels you've defined in Exceptionless. We think this will help you better manage your exceptions, reporting, and overall development flow. We can't wait to hear your feedback. If there's any questions, suggestions, or general feedback, we'd love to hear from you. You can reply here or open an issue on Github.

Introducing Exceptionless 7

Feedback and community support is the key to growing a successful open-source company, and you all have provided us some of the best feedback possible. With your feedback, we just released one of our biggest improvements yet. This release includes changes on both the application interface and the core functionality. We wanted to highlight some of the main improvements and why we did it.

New Statuses #

new statuses

This improvement is more than just visual. We heard the feedback from you loud and clear. There are stacks you don't want to track, but filtering them out on your end is difficult. For that reason, we are introducing the "Discarded" status. If you mark a stack with this status, it will not be tracked in your dashboards and, more importantly, it won't count against your plan limits 🎉.

actions example

As part of this improvement, we also consolidated statuses. Operations that were duplicative or didn't make sense were removed or combined.

Adding a status means your dashboards will be updated in real-time with these statuses as quick indicators to help you understand your stacks. You can read more about this feature and the goals we had here.

Dashboards #

All dashboards now have a both high level stats and a timeline view by default. This gives you better information at a glance and allows you to go deeper with your stacks and events as needed.

example dashboard

You'll also notice we've renamed some of our stats labels to make things a little more clear.

We've made some minor adjustments to navigation icons, spacing, and more to make the experience in Exceptionless better as you move throughout the application.

example navigation

We want to hear from you #

We are constantly improving based on user feedback. If you'd like to reach out, please do so any time. You can also add your feedback at either of the two links below:

Happy building!

How to Self-Host Your Error Monitoring Service

The beauty of open-source solutions is that you are often given the option to self-host or pay for an easy hosted solution by the company or people who created the project. This is true of many projects. Ghost is a popular example of this from the blogging world. Some analytics serves, including Matomo, allow users to choose between a hosted and a self-hosted solution. In that same vein, Exceptionless provides a self-hosted option for those who would like to host their own error monitoring.

Today, we're going to walk through setting up a self-hosted Exceptionless instance. Let's get started!

Exceptionless provides a simple Docker image to help get started with self-hosting. We're going to make use of that, so the first step is to download Docker for desktop if you don't already have it. Once you're able to download that and start it, you should then be able to execute docker commands from the command line. Test it out by running docker stats in the command line.

Pretty cool, right? Just by downloading and running the desktop application, you also have access to the Docker CLI. That CLI is what we'll need to run our self-hosted instance of Docker.

Let's make sure we can get Exceptionless running locally. Ready for how easy this is? Are you?

Ok, start up Docker Desktop, then in your command line, run:

docker run --rm -it -p 5000:80 exceptionless/exceptionless:latest

This will check to see if you've already downloaded the latest Exceptionless release, and if not, it will install all of the necessary dependencies. This is important because Exceptionless is split into a client-side front-end and a server-side back-end. Docker lets all of this be combined.

When the process in your command line finishes up, open your browser and navigate to http://localhost:5000. If all went well, you should see the Exceptionless login page.

Exceptionless self-hosted login page

Go ahead and sign up for an account. Keep in mind, this is not a good production solution, but it's a great way to get started with a self-hosted error monitoring solution.

Along with the front-end that we're looking at now, you also have a full Exceptionless server running. To prove it, let's run a simple cURL command.

curl --location --request POST 'http://localhost:5000/api/v2/auth/login' \
--header 'Content-Type: application/json' \
--data-raw '{
"email": EMAIL_ADDRESS,
"password": PASSWORD
}'

Make sure to use the email and password you just signed up with. You should get a token back. This shows you that the API is running successfully and you can now do everything you would with a hosted Exceptionless instance, but locally. Go ahead and try it out. Here are the full API docs for Exceptionless.

The problem here is the data you save in this run of your self-hosted Exceptionless instance will not be saved between runs. As soon as you shut down Docker and exit the application, your data will be deleted and you'll be starting from scratch. That's no fun. Let's fix it.

Go ahead and shut down Exceptionless either by exiting in the command line or by going into your Docker Desktop Dashboard and clicking the stop button your Exceptionless instance. Once it's stopped, open up your command line again and run:

docker run --rm -it -p 5000:80 \
-v $(pwd)/esdata:/usr/share/elasticsearch/data \
exceptionless/exceptionless:latest

If you're using PowerShell, you'll instead want to run:

docker run --rm -it -p 5000:80 `
-v ${PWD}/esdata:/usr/share/elasticsearch/data `

exceptionless/exceptionless:latest

Now, when you sign up at http://localhost:5000, your data will be persisted. You can start tracking errors locally and that data will be shown in your Exceptionless dashboard even after you shut down Docker/Exceptionless and restart it.

Your homework: Try running this locally with SSL and SMTP enabled. It's just as simple as everything else we've done so far because Exceptionless has really taken the time to make sure the developer experience is top-notch. Check out the instructions here.

Error monitoring is incredibly important to both your customer's experience and your own sanity. To give yourself true control over error monitoring, you can choose a solution like Exceptionless, and as you've seen here, self-host it pretty easily. Of course, if you'd rather let Exceptionless take care of hosting, that's covered too.

Now, you can go and experiment with your newly installed, self-hosted, error monitoring service. Will you let it run on your machine? Will you tinker and get it production-ready? Will you deploy it to a remote server somewhere? (That last one might be the subject of a future post 😉)

Why We Upgraded Our Production Application to .NET 5.0


Update: Microsoft has officially released .NET 5.0! #


For anyone who has built an application, you've probably built it on some library or framework that changes over time. To keep up, you have to upgrade your application. However, there are varying schools of thought around when you should upgrade. At Exceptionless, we like to be on the bleeding edge. As an open-source company, we feel a responsibility to the community to know and understand the open-source tools we use. As such, we have already upgraded Exceptionless to use .NET 5.0.

To give you a little background, .NET 5.0 was introduced in May of 2019. The announcement was a big one as Microsoft chose to drop the .NET Core distinction. Going forward, we will just see cross-platform support in the form of ".NET X.X". The first release candidate for .NET 5.0 was announced September 13, 2020. We chose to upgrade and begin using .NET 5.0 immediately. That decision was driven by Microsoft's commitment to supporting production usage of the rc1 release. And as it turned out, the upgrade process was not too painful at all.

All in, the upgrade took about one hour and was a very small commit. You can actually see the commit here. This is really a testament to the foundation we've built here combined with the long-running foundation Microsoft has built with the .NET framework. Exceptionless is no small application and yet we were able to upgrade to an early release candidate in order to capitalize on new capabilities. To highlight the scale of Exceptionless and the relatively minor impact the upgrade process had, let's take a look at some of our numbers.

  • 1.4 TB Elasticsearch Cluster
  • 173M Elasticsearch Documents
  • 384M Redis Operations/Day
  • 122M HTTP Requests/Day
  • 2,476 GitHub Stars
  • 568 GitHub Forks

There are always multiple schools of thought around running pre-release code on production applications, but for us, the decision was a no-brainer. The top motivators were performance improvements, availability of new C# features, and Docker improvements for our self-hosted solution.

Performance Improvements #

We are a developer tool, and as such, performance is important. .NET 5.0 allows us to leverage the performance boosts associated with the upgrade and pass that along to our customers and the community around us. We compared our memory and performance from .NET Core 3.1 to the .NET 5.0 rc-1 release and saw enough gains to help support our decision to move forward with rolling this out to production.

The .NET team's focus on pushing the boundaries of garbage collection was an important factor for us. GC is such a critical component to performance, and it impacts almost everything within the framework. We were excited to see the focus Microsoft put on continuing to improve performance in this area and felt the gains were enough to really tilt us towards our production release of Exceptionless using .NET 5.0.

As a quick, visual example, of other improvements, here's a table Ben Adams tweeted that gives us glimpse into the performance gains of .NET 5.0 over .NET 3.1

New C# Features #

With the release of C# 9, we, once again, get significant improvements. Anytime a programming language releases new features, it's important to ask yourself whether those features are necessary for your application. In the case of C# 9, there are multiple features we believe will help improve the code legibility, overall codebase size, and ultimately performance. It always comes back to performance!

Pattern matching in C# 9 is a feature we are particularly excited about. If you're interested in a deep dive into the improvements here, Anthony Giretti has a great post highlighting the new functionality. For Exceptionless, pattern matching represents a better way for us to execute logical operations we already support. In doing so, we can reduce code complexity, improve performance, and deliver a better experience.

Records are an exciting new feature in C# 9 as well. Data immutability is important, once again, for—you guessed it—performance. The way Dave Brock puts it on his blog is apt:

Immutable types reduce risk, are safer, and help to prevent a lot of nasty bugs that occur when you update your object.

Data records give us immutability in the form of a dedicated struct. Rather than extending the functionality of C#'s existing structs, records give us the ability to reach for a data-specific type that offers built-in immutability.

Docker Improvements #

We are proud of our open-source roots. We want to make self-hosting Exceptionless as easy as possible, and Docker has made this a reality. Our Docker image is the fastest and easiest way to get started with self-hosting and .NET 5.0 only improves the Docker experience.

.NET 5.0 enables better resource compaction which, in turn, reduces the cost associated with Docker images. This is important to the bottom line, but .NET 5.0's improvements go beyond dollar-savings with Docker. The new features also improve memory constraints which—say it with me now—improve performance.

One additional benefit of the .NET 5.0 rc-1 release candidate is how our Docker image now works better with Kubernetes resource constraints. We're pretty big fans of Docker and Kubernetes, so anything to improve the experience around both is a win in our eyes.

Conclusion #

We took an early release candidate from a massive framework and rolled it out to production almost immediately after it was announced. Are we crazy? We don't think so, but you decide.

Using an Error Monitoring Service to Track User Experience

In development, we tend to think of errors as things that are thrown when our code does not execute properly. Errors can be caught and handled or they can be missed and result in uncaught exceptions. But how do we classify errors that are not directly caused by the code we write? How do we identify and address errors that are caused by the design decisions we made (or didn't make)?

You may already be using an error monitoring service, and if you are, you could continue using that and reach for yet another tool to help you with your user experience woes. Or, rather than adding another product to your endless list of tools that keep your application running, you can use the error monitoring service to both monitor for traditional errors and user experience problems.

Let's take a look at how we can do this. There are plenty of logging services out there, and most of them do the same thing. However, we're going to take a look at Exceptionless. Exceptionless is an especially attractive choice for three reasons:

  1. Fair pricing on their hosted version
  2. It's open-source and can be totally self-hosted
  3. The API allows us to do exactly what I'm proposing in this article.

While self-hosting may be an attractive option (and one that I will surely write about ina future post), we're going to sign up for a free account using Exceptionless's hosted platform. To do so, go to https://exceptionless.com and click the Sign Up button in the top-right:

Exceptionless home page

Once you sign up, you'll be prompted to name your team and project. You'll also need to choose the language in which your project is written. I'm choosing NodeJS, but you can choose whatever language applies to you because I'll be referencing cURL commands to keep our solution as general-purpose and adaptable as possible. Once you've created a project, you will be provided an API Key. Hold on to that, we'll need to use it as a bearer token later.

*Pro-tip: To convert a cURL command to the language of your choice, use Postman and import the raw command. You can then choose the code option and see how to run the API call in the language you prefer.

You'll want to follow the documentation to set up your codebase to send errors appropriately to Exceptionless, but we will also need to think about how we are going to handle these UX errors.

To do this, let's first think about some of the problems a user may face on a site and how we can handle them. A simple example that I can think of is what I'm going to call "Happy Path Slippage." We build applications with a happy path in mind. It's how we test, naturally. We have to force ourselves to test outside of the happy path, so it's also important to monitor how often our users deviate from the happy path.

Let's say we have a simple e-commerce application. The happy path, in this case, would be:

  1. User signs up
  2. User searches for a product
  3. User adds product to shopping cart
  4. User checks out

That is the ideal flow, but we know users won't always follow that flow. However, what we don't know is how often users will deviate and if they do deviate. To track this with Exceptionless, we are going to use simple GET requests with a query parameter to build a funnel analysis. We will want to track product searches, shopping cart adds, and checkouts.

Let's start with the setup for product searches. Remember, we're going to use a GET request. You can read more of the Exceptionless documentation here, but the request is pretty simple. We will want to pass in an indicator that the event is a productSearch and what the product is. We can do that like this:

curl --location --request GET 'https://api.exceptionless.io/api/v2/events/submit/usage?source=productSearch&message=YOUR_PRODUCT' \
--header 'Authorization: Bearer YOUR_API_KEY'

Feel free to add whatever product name you'd like in the query. Just replace YOUR_PRODUCT with the name of the product you'd like to track. You can run the cURL command from the command line or you can use it to build a real request you would use in your app. If we run that then take a look at our dashboard in Exceptionless, we can start to make use of the data.

The Exceptionless dashboard takes you to a handy chart of most frequent exceptions/errors. However, we're tracking User Experience issues tied to features in our application, so those events won't appear on the Exceptions dashboard. Instead, if you click the Features Usage link on the left navigation, then click Events, you should see your new productSearch event.

Features Usage Dashboard Example

Pretty cool! This alone starts to become useful. We can cut out a separate analytics tracking tool by using our error monitoring service (Exceptionless in this case) to track events outside the normal error reporting. But we can take it a step further.

Remember, we want to track the funnel from search to checkout. So, let's send through another event representing a cartAdd when a user adds a product to their shopping cart. Here we are adding an extra parameter to also track how many of the product is added to the cart.

curl --location --request GET 'https://api.exceptionless.io/api/v2/events/submit/usage?source=cartAdd&value=QUANTITY_ADDED&message=YOUR_PRODUCT' \
--header 'Authorization: Bearer YOUR_API_KEY'

Exceptionless has real-time monitoring, so if you flip back to your dashboard after running the above command, you should already see the event in the list:

Cart Add Example

I think you're already seeing how easy this is, but let's round this out by adding a checkout event to track.

curl --location --request GET 'https://api.exceptionless.io/api/v2/events/submit/usage?source=checkout&message=YOUR_PRODUCT' \
--header 'Authorization: Bearer YOUR_API_KEY'

Again, your Exceptionless dashboard should update in real-time. This is starting to shape up! Now, let's dive into the events because right now we have the start of a nice funnel analysis, but we don't know yet what products were searched for, added to the cart, and checked out. The cool thing here is we can click into an event like productSearch for example and get detailed info.

Go ahead and try it. Click on the event and you'll be taken to a dedicated Event Occurrence page.

Event Occurrence Example

This is useful information. Combined with our user experience funnel analysis, we can start to make product decisions. Just for fun, I want to show you what this could look like when leveraging the Most Frequent view.

Again, we should click on the Features Usage link on the side navigation. This time, we'll choose the Most Frequent option. I've created a bunch of events so that we can see how useful the Most Frequent view can be.

Funnel Example

Now we have the makings of a useful way of tracking the user experience right from within our error reporting tool. The benefit here is that we can use a single tool to help us with monitoring, bugs, event tracking, and user experience. Exceptionless makes this incredibly easy, is self-hostable, is open source, and if you choose the hosted option is very affordable.

Go forth and track errors AND user experience all in one place.

Exceptionless 5.0 Release - ASP.NET Core & Localization Support, and more!

Exceptionless 5.0 Localization

Exceptionless 5.0 is here and we wanted to make a quick blog post highlighting the new features, bug fixes, and of course upgrading.

In this release, we focused primarily on migrating the application to ASP.NET Core, localization, along with ongoing performance enhancements and bug fixes.

More details, below:

Exceptionless 5.0 New Features #

  1. Exceptionless now runs on ASP.NET Core! This brings in many performance advantages as well as cross platform support.
  2. Docker/Kubernetes based hosting is now the default hosting model. This will bring seamless and quick upgrades while reducing the amount of environmental related errors.
  3. Added Chinese localization support. Thanks @Varorbc@edwardmeng for that contribution!
  4. Added support for using various different cloud hosted services (e.g., Aliyun, Minio, S3) and metric providers (e.g., InfluxDB). Thanks @edwardmeng for that contribution!
  5. When viewing 404 event types, you will now see a grid column for IP addresses. This will allow you to quickly identify any bots or security scans that might be happening to your applications.
  6. In addition to client side plugins that will remove sensitive user data, we've added server side code as well to remove any missed sensitive user data.
  7. Added the ability to delete your account on the manage account page.

Version 5.0 Bug Fixes #

  • Various user interface usability issues have been fixed in this release. Please view the UI release notes (v2.8.0 for more info).
  • Fixed a bug where notifications and web hooks would be sent for fixed events.
  • Updated Foundatio which uses a task queue to resolve dead locking and thread exhaustion.

Upgrading to Exceptionless 5.0 #

If you are using our hosted service, you do not need to upgrade anything on your end. If you are self hosting Exceptionless and upgrading from version 4 or 5, a little work is needed to get up and running using the new docker images and configuration. See our upgrade guide for more information.

Check out the official release notes here, or view the full changelog if you are interested in a complete list of changes.

How are we doing? #

As always, we want to know what you think! If you have questions, concerns, or any feedback, please submit an issue over on the GitHub repo.