Open source · MIT License

Error tracking for Rails, on your own server.

Good Sorcery groups your exceptions into issues, emails you when something new breaks, watches your uptime and shows where request time goes. It’s open source under the MIT License: self-host it free, with no limits.

One Ruby gem in your app. One Rails app on your server, with Postgres and Redis.

The Good Sorcery issue list for a demo app called storefront: 23 unresolved issues, summary tiles for events, new issues, regressions and users affected, query chips such as is:unresolved and env:production, and one row per issue with a 24-hour histogram, event and user counts.

The gem covers, out of the box

  • Rails
  • Active Job
  • Sidekiq
  • Action Mailer
  • Action Cable
  • Rack and Sinatra
  • Rake

Features

Calm until it matters.

You open an error tracker when you’re already stressed. Good Sorcery keeps the screen quiet and makes whatever is new or getting worse stand out.

Error tracking

Every exception, grouped into an issue

Events with the same exception class and top app frame become one issue, with a short ID such as GS-1042. Triage from one list with 24-hour histograms that tell a spike from steady noise.

  • App-first backtraces. Your frames open with the code around them; framework and gem frames fold away.
  • Breadcrumbs. The request, SQL queries, jobs and mail leading up to the error.
  • Full context. Request, params, headers, session, job and environment, plus tag breakdowns and affected users.
  • Search with query chips such as is:unresolved and env:production.
  • Live updates. New issues appear on the list without a reload.
An issue detail page for NoMethodError, undefined method price_cents for nil: 1,284 events from 212 users, an occurrence chart climbing over the last hours, an app-first backtrace with the failing line highlighted, and a side rail with the affected user and tag breakdowns such as 94% coupon FALL25.

Email alerts

Hear about it when it matters, not every time

Each app has its own alert rules and environments. Everyone on the team chooses which apps they hear about, and every email has a one-click mute link.

  • New issue, regression and reached production alerts as they happen.
  • Still happening when an issue passes your hourly threshold, at most once every six hours.
  • A daily digest at 09:00 in each person’s own time zone.
  • A hard cap of 20 alert emails per person per hour; the rest fold into one summary.

Uptime monitoring

Know when a site is down before your users tell you

Add monitors to any app and Good Sorcery checks their URLs every 1, 5, 10 or 60 minutes. Self-hosted, every app can have as many monitors as you like.

  • Uptime at a glance for the last 24 hours, 7 days and 30 days, with status bars.
  • Incident history on each monitor’s page, and a Check now button.
  • Down and recovered emails, one per incident, sent after an immediate retry.
  • Safe by default. Checks refuse private, loopback and cloud-metadata addresses.

Performance monitoring

See which endpoints are slow, and why

Turn on config.performance_monitoring and the gem measures every controller action and background job. It sends one small batch a minute from a background thread.

  • Average, p75, p90, p95 and p99 for each endpoint and job, and a Slower flag when p95 jumps.
  • Where time goes: database, external HTTP, views and Ruby.
  • Slow-request traces with every query, HTTP call and render on a timeline.
  • N+1 detection. Repeated queries are grouped with their count and the line of your code that ran them.

A few microseconds per request, about 2 µs per query. How it works

The slowest request to each endpoint, every minute, keeps a trace. Here, one query ran 38 times while totalling a cart. The trace groups the repeats, shows the line of your code that ran them and suggests the fix.

Part of a slow trace for POST /checkout: 3.95 seconds, 200 OK, 39 SQL queries, 1 HTTP call and 1 N+1 pattern. A callout reads One query, run 38 times: SELECT products WHERE id, 38 calls taking 2.21 seconds in total, 58 ms on average, called from app/services/cart_total.rb line 27 in total_cents, with the suggestion to load the products in one query with includes(:product).

Privacy

Captured data is scrubbed, then encrypted at rest

Anything captured with an error can hold personal data, so Good Sorcery treats all of it as sensitive. Self-hosted, it’s stored only in your own Postgres, and alert emails carry just the redacted title and where it happened.

  • Filtered in your app first

    The gem replaces passwords, tokens, keys, cookies, card numbers and other sensitive values with [FILTERED] before anything is sent, and applies Rails’ filter_parameters too.

  • Scrubbed again on the server

    Ingest scrubs secrets a second time. Messages kept for search are redacted: emails, IPs, UUIDs and tokens are replaced.

  • Encrypted at rest

    Backtraces, code, params, headers, session, breadcrumbs, environment and traces are stored with Active Record Encryption.

  • Kept out of Redis and logs

    Only an id goes through Redis and Sidekiq, never the payload. Users are matched by HMAC digests, and dashboard pages are never cached.

# what the gem sends for a checkout request
params: {
  "cart_id"     => 88123,
  "coupon"      => "FALL25",
  "card_number" => "[FILTERED]",
  "password"    => "[FILTERED]"
}

Install

Add it to your Rails app

The goodsorcery gem needs Ruby 3.2 or newer and has no runtime dependencies. Events are filtered in your process, then sent in gzip’d batches from one background thread, so your requests don’t wait on the network.

  1. Add the gem

    $ bundle add goodsorcery
  2. Run the generator with your app’s API key

    It writes config/initializers/goodsorcery.rb with every option commented out, and stores the key in your encrypted credentials.

    $ bin/rails generate goodsorcery:install \
        --api-key=gs_live_...
  3. Point it at your server

    Performance monitoring is optional and off until you turn it on.

    # config/initializers/goodsorcery.rb
    GoodSorcery.configure do |config|
      config.endpoint = "https://errors.example.com"
      config.performance_monitoring = true
    end

    Signed up at app.goodsorcery.com? Leave endpoint out: that’s the default.

  4. Send a test error

    It sends a test exception and prints its id. The issue appears on your dashboard a moment later.

    $ bin/rails goodsorcery:test

Not on Rails? The gem works with Rack and Sinatra too. Read the gem’s README

Self-host

Self-host it

Good Sorcery is one Rails app. Run it on your own server, free, with no limits on events, apps or uptime monitors.

What you need

  • A Linux server with Docker. The Kamal config targets Ubuntu LTS on amd64.
  • PostgreSQL (14 or newer) and Redis (6 or newer). Kamal can run both on the same server.
  • A few secrets: SECRET_KEY_BASE, three Active Record encryption keys and your public URL.
  • HTTPS in front of it. The app expects TLS. The Kamal config gets it from Cloudflare: a domain on Cloudflare and a Cloudflare Tunnel token.
  • For Kamal: an SSH deploy user on the server and a GitHub token that can pull the image from GHCR.
  • Outgoing email through Postmark or any SMTP server, for password resets and alerts.
  1. Get the code and generate the secrets

    The secret commands need the repo’s Ruby (see .ruby-version) and its gems. Keep the secrets in a password manager: losing the encryption keys makes stored error data unreadable.

    $ git clone https://github.com/creativesorcery/goodsorcery_web
    $ cd goodsorcery_web && bundle install
    $ bin/rails secret               # SECRET_KEY_BASE
    $ bin/rails db:encryption:init   # the three ACTIVE_RECORD_ENCRYPTION_* keys
  2. Deploy with Kamal, or plain Docker

    The repo ships a Kamal config that runs the web app, Sidekiq, Postgres, Redis and a Cloudflare Tunnel on one server, with no public HTTP ports. Export the env vars, registry login and tunnel token that the deployment guide lists, boot the accessories once, and the included GitHub Actions workflow deploys every push to main of your fork. Or run the image yourself: one web container and one Sidekiq container, with HTTPS in front (a reverse proxy or Cloudflare). Migrations run when the web container boots.

    # Kamal, from your fork with its Actions secrets set:
    # boot Postgres, Redis and the tunnel once
    $ bin/kamal accessory boot all
    # then push to main: CI builds, pushes to GHCR and deploys
    $ git push origin main
    
    # or Docker: put the secrets, DATABASE_URL, REDIS_URL and
    # GOODSORCERY_PUBLIC_URL=https://... in an env file, and
    # serve port 80 through your HTTPS proxy
    $ docker build -t goodsorcery_web .
    $ docker run -d --env-file .env -p 80:80 goodsorcery_web
    $ docker run -d --env-file .env goodsorcery_web \
        bundle exec sidekiq -C config/sidekiq.yml
  3. Create your account and add an app

    Open your URL and sign up. The first account owns the instance, and sign-up closes after it unless you set GOODSORCERY_ENABLE_SIGNUPS=1. Add an app, copy its API key and install the gem with config.endpoint set to your URL.

Open source

MIT licensed, all of it

The server and the gem are both on GitHub under the MIT License. Read the code that handles your data, run it on your own hardware, fork it, or send a pull request.