Error tracking with Sentry on Roblox

Got some reports of obscure errors with no console screenshot? Can’t reproduce that one rare bug to get that sweet, sweet stack trace? Fear not, for Sentry Claus has come to town!

This module, called Raven, will help you log and keep track of errors on Sentry. This is how to set it up.

First, register an account and make an organization on sentry.io.
Then, create a project:

image

After naming your project, you should be met with this page:

Click on “Get your DSN.”
This should show you your project’s DSN, or Data Source Name, which you need to copy. Don’t share this with the public. If your DSN does get leaked, you can delete it and create a new one in your project’s settings.

After copying your DSN, get to your project’s live feed by clicking on your project’s name at the top.

Now, on to Studio!

Get the module from the GitLab project.

There is documentation in the module itself describing everything in detail, but I’ll give you a basic rundown here.

This module is meant to be used only on the server to prevent players from spamming requests to your Sentry project and potentially risking its termination, or the termination of your Sentry account.

To integrate Raven into your game, require the module and create a new Raven client with your project’s DSN that you copied earlier. A Raven client should not be confused with a ROBLOX client (player); a Raven client describes a configured instance of Raven.

local Raven = require(script.Raven)
local client = Raven:Client("<your DSN here>")

You can now send events to your Sentry project! There are two functions to do this. Here’s an example of both of them:

local success, err = pcall(function() error("test server error") end)
if (not success) then
    client:SendException(raven.ExceptionType.Server, err, debug.traceback())
end

client:SendMessage("Fatal error", raven.EventLevel.Fatal)
client:SendMessage("Basic error", raven.EventLevel.Error)
client:SendMessage("Warning message", raven.EventLevel.Warning)
client:SendMessage("Info message", raven.EventLevel.Info)
client:SendMessage("Debug message", raven.EventLevel.Debug)

SendMessage is for basic errors, messages, or information, whereas SendException is for more complicated errors optionally paired with tracebacks from debug.traceback().

Since this module is supposed to be used only on the server, there is functionality for client > server error reporting built-in. Here’s how to use it, first on the server:

client:ConnectRemoteEvent(Instance.new("RemoteEvent", game.ReplicatedStorage))

Now, on the client:

local success, err = pcall(function() error("test client error") end)
if (not success) then
    game.ReplicatedStorage.RemoteEvent:FireServer(err, debug.traceback())
end

The arguments sent are the same as SendException, excluding a few options.
Make sure the RemoteEvent you pass isn’t used for any other purposes!

Each ROBLOX client can report a (configurable) maximum of 5 events per server, however if Raven detects spoofed data being sent, it disables their ability to report errors in that server entirely.

Raven also tries to anonymize data received from ROBLOX clients before sending it to Sentry.

If you ran the above code, you can check your Sentry project’s live feed to find:

And there you go! You’re able to integrate Raven into your game, and you no longer have to spend hours trying to get that beautiful stack trace yourself- it’s right there in the live feed, in all its verbose, formatted glory.

125 Likes

Yeah, I was going to make one of these for bugsnag because it offers more features for free.

nice work tho

6 Likes

The free tier for Sentry seems rather low even for low-traffic Roblox games

image

That’s only 13 events per hour, or 0.22 per minute. Even at the next tier (26$/mo), you only get 100k so 2.2 per minute for your entire game.

The module is cool though! But maybe you can extend it to work with other services too? (I can imagine 90% of the source of your module can be reused)

EDIT: For reference: our game with ~50-200 concurrent players generates about 270k records per month already, all logging levels combined. I would need to pay ~180-200$/mo to Sentry to support that, while we have our own database + interface for basically free at the moment.

EDIT2: Yikes, bugsnag is even more expensive for the number of events per month that they offer… I think we’re going to stick with what we have for now :stuck_out_tongue:

9 Likes

Yeah, their rate limiting is awful, especially for the free tiers. GameAnalytics might be a much better option since AFAIK there is no limit aside from unique events. As long as you generalize errors (i.e. game.Players.EchoReaper.Backpack.Tool.Script : 40 -> <PLAYER>.Backpack.Tool.Script : 40) you should be fine.

Another alternative would be to log errors in the DataStore, and if you can’t save to DataStores, use Sentry/etc as a fallback.

3 Likes

You mention 270k records per month, all logging levels combined. Are you tracking records other than errors, or are all those events in some way another an error?

1 Like

If you just don’t log errors, it’s like they never happen

60 Likes

That’s why Farming among Friends has no bugs!

Well, I do have it linked to google analytics but I never check it.

6 Likes

All of those 270k are errors or warnings or debugging-related info logs (minor amount) in some way. A big part of it (recently) came from chat-related CoreScripts and other Roblox-managed things though so we could theoretically filter out those messages to reduce the load.

Sentry doesn’t have very large limits for its free tier, but the major sellers for me are: its interface, (relative) ease of use, and the fact that events are, most of the time, instantly processed.

It’s definitely not for logging a lot of debug information or general statistics (there’s GameAnalytics for that), but under the right circumstances I believe it can be invaluable.

Sentry should probably be integrated in a public/private testing/release stage after most surface bugs have been fixed, when the more contextual bugs rarely rear their heads. At that stage, I doubt a large amount of developers would be encountering >10k events/month, as long as they stay on top of frequently-occurring bugs.

Even if that limit were reached, you’d have the last seven days of potentially a month’s worth of data to work with, which is infinitely better than nothing.

1 Like

I’ve just used a Discord webhook that makes a message in the server’s Error Log channel. It posts the error and it’s trace. I never seem to run into issues with it. The only problem would be if a game becomes popular and there a could be multiple errors and I have to search for the right one.

3 Likes

Reply 1 (below)

See also:
Reply 2
Reply 3
And the rest of that thread.

5 Likes

It’s fine actually so long as they don’t hit the rate limits.

https://devforum.roblox.com/t/discord-and-trello-api-communication-bots/49670/14

7 Likes

Definitely don’t use the free Sentry SaaS. The best option for using Sentry is to host it yourself. They provide a Docker image which requires a fair bit of setup and might be advanced for some users, but if you’re willing to go through that, you can log as many events as you want for as much as your server costs.

P.S. @nomer888 I would highly recommend changing eventLevel.fatal to EventLevel.Fatal, ExceptionType.Server, etc. to stay aligned with the ROBLOX “enum” conventions.

P.P.S. If anyone’s interested in a tutorial on how to set it up, I might be able to dedicate some time to writing one.

10 Likes

I agree with you on the enum conventions, I’ve fixed that.

However, why do you recommend not using Sentry’s free offerings? It’s definitely not as good as hosting it yourself, but for most it’s a quick, easy, and a good enough solution you can pop into your game in < 1 hour and forget about it. Like Crazyman said, if you don’t log errors at all, it’s like they never happen- and this can get you started, at the least.

That being said, I am interested in a tutorial on how to set something like that up.

I don’t recommend using it because of the low quotas. However, if you don’t have that many events and the free tier works for you, go for it. It’s just that hosting it yourself is much better because there’s no limitations.

2 Likes

Found this thread because I ended up getting a Sponsored Sentry plan through the Github Student Developer Pack, which gives me 500k events/mo for free while I’m a student. (I highly suggest signing up for this if you’re in school, and even if you aren’t you can still apply and get some free goodies)

Thanks for this module, I plan to use it soon for an upcoming project!

5 Likes

Nice! Lemme know if there are any features you want or bugs with this and I’ll address them as soon as I can.

Aha Same, Got the pack a month ago and was wondering if anyone made a tutorial for sentry.

1 Like

So I’m using ScriptContext.Error as a catch-all handler for all Errors that can occur in the game and then sending them to Sentry. The only problem I’ve come across is that the trace that is passed by ScriptContext.Error is not the same format as debug.traceback().

So I ended up adding this to the StringTraceToTable() function.
The main addition is matching for the trace format that ScriptContext.Error uses, and then recording them as path2, lineNum2, value2 respectively.

local function StringTraceToTable(trace)
	local stacktrace = {}

	for line in trace:gmatch("[^\n\r]+") do
		if (not line:match("^Stack Begin$") and not line:match("^Stack End$")) then
			local path, lineNum, value = line:match("^Script '(.-)', Line (%d+)%s?%-?%s?(.*)$")
			local path2, lineNum2, value2 = line:match("^(.-), line (%d+)%s?%-?%s?(.*)$")
			print(path2, lineNum2, value2)
			if (path and lineNum and value) then
				stacktrace[#stacktrace + 1] = {
					filename = path;
					["function"] = value or "nil";
					lineno = lineNum;
				}
			elseif (path2 and lineNum2 and value2) then
				stacktrace[#stacktrace + 1] = {
					filename = path2;
					["function"] = value2 or "nil";
					lineno = lineNum2;
				}
			else
				return false, "invalid traceback"
			end
		end
	end

	if (#stacktrace == 0) then
		return false, "invalid traceback"
	end

	local sorted = {}
	for i = #stacktrace, 1, -1 do
		sorted[i] = stacktrace[i]
	end

	return true, sorted
end
5 Likes

I figured out how you can log all errors and send it to raven. Here’s how:
Make a new script in serverscriptservice to require the module and set the client like so

local Raven = require(game.ReplicatedStorage.Raven)
     
local client = Raven:Client("DSN here")

then use scriptcontext to log all errors that happen like so

game:GetService("ScriptContext").Error:Connect(function(message,trace,script)

    client:SendException(Raven.ExceptionType.Server, message, debug.traceback())

end)

then for the remote event for client you would type this:

client:ConnectRemoteEvent(Instance.new("RemoteEvent", game.ReplicatedStorage))

for client errors you would put a local script in starterplayer.starterplayerscripts and put the following code to send the info to the server

game:GetService("ScriptContext").Error:Connect(function(message,trace,script)

    game.ReplicatedStorage.RemoteEvent:FireServer(message, debug.traceback())

end)

Hope this helps you guys who are struggling with this :slight_smile:

5 Likes