Error Tracking for Games
When releasing a game for mobile, the web, VR, PC, whether it's a live service or single-player game, error tracking is a must-have. When a new release goes out, you want to know if players are experiencing issues due to that release. You will want to know where the error occurred and if there are commonalities to the players who are experiencing the issue.
Tools for Error Tracking and Application Performance Monitoring
This is where error tracking tools come into play. There are many to choose from and they have different features and price points.
Here are some great options for error tracking tools, in a table for easier comparison. Note that each of the products and services may offer more than just error tracking in its feature set.
However, not all error tracking tools have official support for some programming languages and frameworks. Not all of them have clear documentation, tutorials and guides. It's important to evaluate error tracking tools based on whether they support the programming languages your team is using, the user interface to view the errors, whether it supports SAML and Single Sign On, and whether the documentation is helpful. For example, PostHog has an official Android SDK but does not have an SDK for iOS although that gap can be filled by capturing errors and exceptions and manually sending them via HTTPS. Self-hosted open source versions of the error tracking service may also differ in the feature set and offer fewer features than the cloud-hosted version.
Implementing Error Tracking
Typically, the SDKs for each error tracking service will provide a way to capture all errors and exceptions and send them to the service, the only requirement being that you create an account and supply an API key. Other parameters such as the version release number and user information can be included when sending data about the error that occurred.
The following is a reference with links to packages for each error tracking tool along with code samples to show how to add them to your project.
BugSnag:
- BugSnag for Android
- BugSnag for iOS
- BugSnag package for Unity
- BugSnag package for Unreal Engine
- BugSnag integration for Cocos2d-x
BugSink supports the Sentry SDKs:
- Sentry SDK for Android
- Sentry SDK for iOS
- Sentry SDK for Unity
- Sentry SDK for Unreal Engine
- Sentry SDK for Godot Engine
- Sentry SDK for browser JavaScript
DataDog:
GlitchTip:
- GlitchTip for Android
- GlitchTip for Objective-C
- GlitchTip for C#
- GlitchTip for C/C++
- GlitchTip for browser JavaScript
Highlight / LaunchDarkly:
PostHog:
Raygun:
Rollbar:
Implementing Error Tracking for a Python Server
The following are some code samples for implementing Error
Rollbar for Python
Rollbar can be implemented for FastAPI, Django, Celery, or directly in Python like this:
import os
import rollbar
rollbar.init(os.getenv('ROLLBAR_API_KEY'), os.getenv('SERVER_ENVIRONMENT'), code_version=os.getenv('COMMIT_SHA'))
try:
main_app_loop()
except IOError:
rollbar.report_message('Got an IOError in the main loop', 'warning')
except:
# catch-all
rollbar.report_exc_info()
BugSnag for Python
BugSnag for standalone Python scripts:
import os
import os.path
import bugsnag
bugsnag.configure(
api_key=os.getenv("BUGSNAG_API_KEY"),
project_root=os.path.abspath(os.path.dirname(__file__)),
)
BugSnag for Django applications:
# settings.py
import os
import os.path
import bugsnag
BUGSNAG = {
"api_key": os.getenv("BUGSNAG_API_KEY"),
"project_root": os.path.abspath(os.path.join(os.path.dirname(__file__), "..")),
}
# ...
MIDDLEWARE = (
"bugsnag.django.middleware.BugsnagMiddleware",
)
Implementing Error Tracking in JavaScript for Browser-Based Games
Rollbar for JavaScript and TypeScript
Rollbar can be added to React, Angular, and Vue:
import Rollbar from 'rollbar';
const rollbar = new Rollbar({
accessToken: 'ROLLBAR_ACCESS_TOKEN',
environment: 'production',
});
BugSnag for JavaScript
import Bugsnag from '@bugsnag/js';
Bugsnag.start({
apiKey: 'BUGSNAG_API_KEY',
onError: (event) => {
event.setUser('user-id', 'user-email@localhost', 'User Name');
}
);
Datadog for JavaScript
import { datadogRum } from '@datadog/browser-rum';
datadogRum.init({
applicationId: 'APP_ID',
clientToken: 'CLIENT_TOKEN',
service: 'SERVICE_NAME',
env: 'ENVIRONMENT_NAME',
version: '1.0.0',
trackUserInteractions: true,
trackResources: true
});
Migrating From Sentry to BugSink or GlitchTip
The advantage of BugSink and GlitchTip are that they are open source and can be self-hosted. In addition, both error tracking services are compatible with Sentry SDKs. That means you can use the Sentry SDK with any language that they support, set the dsn URL endpoint to BugSink or GlitchTip and start tracking.
If you choose to use the cloud version of BugSink or GlitchTip, here's how their error tracking plans compare to Sentry (as of February 2026), with the number of error events the plan can track per month:
| Service | Plan | Monthly Price | # of Errors Included |
|---|---|---|---|
| Sentry | Developer | Free | 5k |
| BugSink | Developer | Free | 5k |
| GlitchTip | Developer | Free | 1k |
| Sentry | Team | $26 | 50k |
| BugSink | Team | $18 (€15) | 50k |
| GlitchTip | Small | $15 | 100k |
| Sentry | Business | $80 | 50k |
| GlitchTip | Medium | $50 | 500k |
| GlitchTip | Large | $250 | 3 million |
| BugSink | Enterprise | $600+ (€500+) | Millions of events |
This is the example code in Python that will send error traces to BugSink by configuring the Sentry SDK:
import sentry_sdk
import time
sentry_sdk.init(
dsn="http://your_key@localhost:8001/1",
traces_sample_rate=1.0
)
print("Causing an error")
1 / 0
This is JavaScript/TypeScript/HTML example code that will send errors to GlitchTip, again by configuring the Sentry SDK to use a different dsn URL endpoint value:
<!DOCTYPE html>
<html>
<body>
<h1>GlitchTip Test</h1>
<button onclick="throwError()">Throw Error</button>
<script
src="https://browser.sentry-cdn.com/10.38.0/bundle.min.js"
integrity="sha384-/oTG1DxxBD0jT/ShtVYNvpwDmS2j9zVqquIKPRo4LxMLUCnwdUywlKzgEIwS1mEY"
crossorigin="anonymous"
></script>
<script>
const GLITCHTIP_DSN = "http://your_key@localhost:8000/1";
Sentry.init({ dsn: GLITCHTIP_DSN });
function throwError() { throw new Error("Hello GlitchTip!"); }
</script>
</body>
</html>
The following is a Docker compose that will run both BugSink and GlitchTip for testing the Python and JavaScript code above:
x-environment: &default-environment
DATABASE_URL: postgres://postgres:postgres@postgres:5432/postgres
VALKEY_URL: "" # Set to enable Valkey. Ex: redis://valkey:6379
SECRET_KEY: change_me_to_something_random # best to run openssl rand -hex 32
EMAIL_URL: consolemail:// # Example smtp://email:password@smtp_url:port https://glitchtip.com/documentation/install#configuration
GLITCHTIP_DOMAIN: https://glitchtip.example.com # Change this to your domain
DEFAULT_FROM_EMAIL: email@example.com # Change this to your email
ENABLE_ADMIN: "False" # Set to True to enable Django Admin at /admin/
ENABLE_OPENAPI: "False" # Set to True to enable OpenAPI spec at /api/docs
# PORT: 8000 # If changing, change the web service port too
services:
# GlitchTip
# https://glitchtip.com/assets/compose.minimal.yml
glitchtip_postgres:
image: postgres:18
environment:
POSTGRES_HOST_AUTH_METHOD: "trust"
restart: unless-stopped
volumes:
- pg-data:/var/lib/postgresql
glitchtip:
image: glitchtip/glitchtip:6 # Check https://glitchtip.com/blog/ for new major versions
depends_on:
- glitchtip_postgres
ports:
- "8000:8000"
environment: *default-environment
command: ./bin/run-all-in-one.sh # Runs web/worker/migrate all in one process
restart: unless-stopped
volumes:
- uploads:/code/uploads
# BugSink
# https://github.com/bugsink/bugsink/blob/main/docker-compose-sample.yaml
bugsink_db:
image: postgres:17-alpine
restart: unless-stopped
environment:
POSTGRES_USER: bugsinkuser
POSTGRES_PASSWORD: your_super_secret_password # Change this
POSTGRES_DB: bugsink
volumes:
- db-data:/var/lib/postgresql/data
healthcheck:
test: pg_isready -h db
retries: 5
start_period: 10s
interval: 5s
timeout: 5s
bugsink:
image: bugsink/bugsink:2
depends_on:
bugsink_db:
condition: service_healthy
restart: unless-stopped
ports:
- "8001:8000"
environment:
SECRET_KEY: django-insecure-RMLYThim9NybWgXiUGat32Aa0Qbgqscf4NPDQuZO2glcZPOiXn # Change this (and remove django-insecure prefix), e.g. openssl rand -base64 50
CREATE_SUPERUSER: email:password # Change this (or remove it and execute 'createsuperuser' against the running container)
PORT: 8000
DATABASE_URL: postgresql://bugsinkuser:your_super_secret_password@db:5432/bugsink # Change password to match POSTGRES_PASSWORD above
BEHIND_HTTPS_PROXY: "false" # Change this for setups behind a proxy w/ ssl enabled
BASE_URL: "http://localhost:8000"
healthcheck:
test: ["CMD-SHELL", "python -c 'import requests; requests.get(\"http://localhost:8000/\").raise_for_status()'"]
interval: 5s
timeout: 20s
retries: 10
volumes:
db-data:
pg-data:
uploads: