Automated Bug Reporting with Sentry for GameMaker games

Automated Bug Reporting with Sentry for GameMaker games

Picture this: you're an indie dev, you've sunk 3 or more years of your life into making a game, and against all odds, you make it to release day. With must trepidation, you hit the launch button and all the download links become available. Eagerly you await the first reviews and comments from your fans. You start receiving messages and reviews. It's bugs! You scramble to fire up IDE, hoping to do a quick hotfix and fire that out before too many people start playing it, but you can't reproduce the problem. It must be something specific they're doing, but you don't have enough information to fix it.

Sound familiar? Following the indie gamedev community closely, I see a lot of these. Indie releases that don't have a good way to collect logs, and rely on players submitting bug reports manually, usually via screenshots sent to Discord or the Steam communities.

Fortunately, automating and receiving bug reports and debugging data is actually quite easy to do in gamemaker, and for those releasing games, this is very likely to be worth your while to do.

In the previous blog post I talked about structured logging, and made two points:

  • Logs should be a permanent part of code, and not just added temporarily to help debug something
  • Logs should be structured so that we can have other programs be able to read those log messages

The biggest reason for both of those things is that both of these are important for automated bug reporting. Having logs as permanent part of code helps generate the debugging info that may help you understand what was going on in the game just before the crash, while structured logging helps you make that data consistently formatted and searchable.

I'm spending this blog post talking about how to set up Sentry to serve as a hub to receive logs and crash reports that your game may generate. You can grab a copy of the script from my Sentry library over here. Grab the downloadable yymps file from the latest version

Releases · meseta/gmlogging-suite
Contribute to meseta/gmlogging-suite development by creating an account on GitHub.

Note: this contains the same Logger from my structured logging page. If you import both, you will have duplicates. It is safe to delete Logger.gml and use this instead.

Sentry as a bug handling tool

There are lots of bug report tools out there, the one I'm going to talk about is sentry.io. Sentry is a web service (it has a free account you can sign up to, as well as an open source release if you wanted to run your own) that serves as a command center for bug reports. You can write code in GameMaker that intercepts crashes, and instead of just displaying it on the player's computer, it can send that report over to your account on Sentry, for you to look at later.

And, if you are smart about logging, this report would contain not only the crash message, but a record of everything that the game was doing up to that point.

An example of a crash report looks like this:

Let me explain what all these things are:

Tags

This top part of the report shows a bunch of "tags". Tags are key/value fields that allow you to group reports by OS, type, version, and even user. If you use my Sentry library, it'll automatically fill some information in here like the detected OS and runtime version, and the game's release version. But you can add more information here if needed.

The Release version is important, so you can make sure common crashes are being fixed by subsequent releases

Crash and Stack Trace

Of course the main thing we want to know about the crash is the error message and stacktrace. This part of the report contains both, and it's the same message you'll get from the GameMaker crash dialogue

Note: the context lines (the line number and line that the code is on) isn't always available post-release due to the way GameMaker strips debugging information. So depending on how you build the game, this may or may not be available

Sentry is smart enough to group together similar crashes, so you'll be able to get a quick overview of which crashes are common, and which are rare from your players

Breadcrumbs are an underappreciated feature of logging. If you use my Sentry library, every time you use the logger (stuff like logger.info() ) it'll save that log line to an array, ready to be included in a report if it crashes. This will give you some important contextual information about what might have been going on in the game just before the crash.

Build and OS info

At the very bottom of the report is a bunch of build and OS information that gives you a bit of extra information about the report. If you use my Setnry library, this stuff gets added automatically.

Issue list

While the above is what an individual bug report looks like, that's not the only benefit of using bug handling tools like Sentry. The other benefit is you get a nice overview of all the reports that are coming in.

Here's an example of that in action: this is a screenshot I like to share (with permission) from the game Forager, which used an early version of my library.

This was a view of their bug list a week into their beta test release. It showed that their top bug got 2000 submitted reports across 731 users, but importantly, if you look at the frequency histogram, every one of those bugs except for the third in the list was fixed by a release that came out 2 days before the screenshot was taken.

Forager also went further, and I helped them modify my Sentry library to also package the player's savefile together with the report, so that they could load the file and investigate the player's save to see what was going on.

Setting Up Sentry

To start collecting bug reports like this, first step is to go register for an account at sentry.io. Then, when it comes to creating a new project, you'll see the following:

Unfortunately GameMaker isn't on the list, but also it doesn't matter a whole lot, since the platform mainly changes the icon in your project list, it doesn't change much else. For the irony factor, I'd say go with Unity.

In the next screen, it'll show you your DSN. This is the unique ID for your project which you need to use to make sure that reports submitted from your game end up in your account. Copy this. This value is also available in the project settings should you need it later.

Setting Up Your Game

Once you have a sentry account, grab my Sentry logging library, here is the link again:

Releases · meseta/gmlogging-suite
Contribute to meseta/gmlogging-suite development by creating an account on GitHub.

Download the .yymps file, and from your gamemaker project use "Import Local Package"

Once you do that, this is how you set it up. Put these somewhere early on in your project.

// create a sentry instance
global.sentry = new Sentry("https://<snip>.ingest.sentry.io/<snip>");

// set gamemaker to use sentry's exception handler
exception_unhandled_handler(global.sentry.get_exception_handler());

// create the root logger
global.logger = new Logger();

// Tell the root logger to send breadcrumbs and stuff to sentry
global.logger.use_sentry(global.sentry);

Now, as the game is running, and you use the logger (global.logger, or any child created from it), the log messages will be buffered as breadcrumbs, and whenever your game crashes, the Sentry handler will be invoked, and the player will be asked if they want to submit a crash report that'll include these log messages, and the error message. Hitting Yes will fire off the bug report.

This is the default behaviour of my Sentry library, but there are several things you can tune/adjust.

Advanced usage

The Sentry library has a few values that can be used to customize it, see the self._options value inside it. you can adjust things like turning on and off the dialogue to ask whether to send the bug report (if you want to always send it automatically without warning the user). You can turn on and off the stacktrace. You can customize the message, and so on.

global.sentry.set_option("ask_to_send", false);

You may also want to set custom tags. These can help you filter your logs

global.sentry.add_tag("release_mode", "demo");

If your game has user accounts or a way to identify a user, you can set the user information. This user information gets attached to Sentry and helps you figure out how many people are experiencing a particular error. This can be done at any time

global.sentry.set_user("abc123");

In addition to automatically sending bug reports, you can also manually send a report. This is useful if you want to send a non-crashing error that you want to be informed about.

global.sentry.send_report("error", "Hello this is a report");

This will show up on the Sentry dashboard just like all the other crashes.

There's more information about how to use this library over in the Wiki:

Home
Contribute to meseta/gmlogging-suite development by creating an account on GitHub.

So, there you have it, a way to automatically collect crash reports for your released GameMaker games.

I really encourage people releasing GameMaker games to use some way of easily collecting bug reports. If you have a game you're releasing and would like my help to make sure that your game has a way to collect bug reports, reach out to me on Twitter or find me on the GameMaker discord