How Sentry is Integrated for Error Tracking in Ghost: Backend and Frontend Implementation

Ghost implements Sentry error tracking across both its Node.js backend and Ember-based admin frontend using a dual-layer architecture that filters noise, enriches events with Ghost-specific context, and optionally traces database queries.

The TryGhost/Ghost repository employs a sophisticated, full-stack approach to error monitoring that captures server crashes, client-side UI failures, and performance bottlenecks. By splitting the integration between ghost/core/core/shared/sentry.js for the backend and ghost/admin/app/utils/sentry.js for the frontend, Ghost ensures comprehensive visibility while maintaining strict control over data quality and relevance.

Backend Sentry Integration Architecture

The backend implementation resides in ghost/core/core/shared/sentry.js and serves as the central hub for server-side error capture and performance monitoring.

Initialization and Configuration

Sentry initializes only when a valid sentry configuration block exists in Ghost's config and is not explicitly disabled. The module sets critical metadata to ensure errors are properly categorized:

  • Release: Formatted as ghost@<full-version> using the @tryghost/version package
  • Environment: Derived from PRO_ENV or the standard env setting
  • DSN: Pulled from config.get('sentry').dsn
// Configuration structure in ghost/core/core/shared/sentry.js
const sentryConfig = {
    dsn: config.get('sentry').dsn,
    release: `ghost@${version}`,
    environment: config.get('proEnv') || config.get('env'),
    // Additional hooks and integrations below
};

Error Enrichment with beforeSend

The beforeSend hook in the backend implementation performs intelligent error enrichment and filtering. When Ghost-specific errors from the @tryghost/errors library are detected, the hook extracts custom fields—including type, code, id, and status_code—and maps them to Sentry tags for improved searchability.

For database failures, the hook normalizes MySQL errors by adding a dedicated mysql context containing the raw errno, SQL statement, and other diagnostic information. Critically, the implementation filters out non-500 status codes, ensuring only serious server failures are transmitted to Sentry and reducing alert fatigue from client-caused errors.

Transaction Filtering and Performance Monitoring

Ghost implements a beforeSendTransaction hook that aggressively whitelists HTTP transactions to prevent noisy performance data. Only requests belonging to specific routes—Ghost's public API, members API, or frontend routes—are retained. This selective approach ensures performance monitoring focuses on user-facing functionality rather than internal health checks or administrative endpoints.

When sentry.tracing.enabled is set to true, Ghost activates two additional components:

  1. The standard HTTP integration for request tracing
  2. A custom SentryKnexTracingIntegration (defined in ghost/core/core/shared/sentry-knex-tracing-integration.js) that captures database query spans

Express Middleware Implementation

The Sentry module exports three critical Express middleware functions and utility methods that integrate seamlessly into Ghost's server stack:

// Example: Implementing Sentry middleware in the Express stack
const {
    requestHandler, 
    errorHandler, 
    tracingHandler, 
    initQueryTracing
} = require('./shared/sentry');

// Start a Sentry transaction per request
app.use(requestHandler());

// Optional: Enable database query tracing
app.use(tracingHandler());

// Route handlers would go here...

// Capture and send errors to Sentry
app.use(errorHandler());

// Initialize Knex query tracing if configured
initQueryTracing(knexInstance);

Frontend Sentry Integration in the Ghost Admin

The client-side implementation in ghost/admin/app/utils/sentry.js handles error tracking for Ghost's Ember-based administrative interface.

Client-Side Configuration Factory

The frontend exposes a getSentryConfig factory function that returns a configuration object tailored for the Ember-compatible Sentry SDK (@sentry/ember). This factory accepts the DSN, environment, application version, and an optional custom transport for testing environments.

// Example: Initializing Sentry in the admin app (admin/app.js)
import * as Sentry from '@sentry/ember';
import {getSentryConfig} from './utils/sentry';

const sentryConfig = getSentryConfig(
    process.env.SENTRY_DSN,
    process.env.NODE_ENV,
    APP_VERSION,
    null // optional custom transport for tests
);

Sentry.init(sentryConfig);

Environment-Aware Integrations

The configuration dynamically loads environment-specific integrations:

  • Debug integration activates in development environments for verbose logging
  • Replay integration enables session replay capabilities in non-testing environments
  • Custom ignore patterns filter known harmless browser errors that would otherwise clutter reports

Error Sanitization and Filtering

The frontend beforeSend hook performs several sanitization steps to ensure high signal-to-noise ratios:

  • Strips "handled" UI errors marked with shown_to_user to focus on uncaught exceptions
  • Filters out requests matching FILTERED_URL_REGEX (specifically Ghost's analytics endpoint)
  • Normalizes model-id strings (e.g., <(post|page):...>) to improve error grouping in Sentry's UI
  • Enhances AJAX error reports with the original server-side payload, including error type, context, and message

Database Query Tracing with Knex

For deployments requiring deep performance insights, Ghost includes a custom tracing integration in ghost/core/core/shared/sentry-knex-tracing-integration.js. When enabled via the tracing configuration, this integration wraps Knex queries to create detailed spans showing database execution times and query patterns. This allows developers to identify N+1 queries or slow database operations directly within Sentry's performance monitoring dashboard.

Summary

  • Ghost employs a dual-layer Sentry integration splitting backend (ghost/core/core/shared/sentry.js) and frontend (ghost/admin/app/utils/sentry.js) concerns for comprehensive error tracking.
  • The backend implementation uses beforeSend hooks to normalize MySQL errors, enrich Ghost-specific error contexts, and filter out non-500 status codes.
  • Transaction whitelisting via beforeSendTransaction ensures only relevant API and frontend route performance data is captured, eliminating noise from internal endpoints.
  • The frontend factory function getSentryConfig provides environment-aware configurations including session replay and debug integrations.
  • Optional Knex query tracing via SentryKnexTracingIntegration enables database performance monitoring when sentry.tracing.enabled is true.

Frequently Asked Questions

How do I enable Sentry error tracking in Ghost?

Enable Sentry by adding a sentry configuration block to your Ghost configuration file with a valid DSN. Set sentry.dsn to your project’s DSN, and optionally enable performance tracing by setting sentry.tracing.enabled to true. The backend initializes automatically when the configuration is present and not disabled, while the frontend picks up the DSN from environment variables during the admin build process.

What types of errors does Ghost filter out from Sentry reports?

The backend explicitly filters out errors with non-500 HTTP status codes, ensuring only server-side failures are reported while ignoring client errors like 404s or 400s. The frontend filters handled UI errors marked as shown_to_user and requests to Ghost’s internal analytics endpoint. Both implementations use beforeSend hooks to drop events matching specific ignore patterns, ensuring reports contain only actionable exceptions.

How does Ghost handle database query tracing with Sentry?

When tracing is enabled in the configuration, Ghost loads a custom SentryKnexTracingIntegration from ghost/core/core/shared/sentry-knex-tracing-integration.js. This integration wraps the Knex query builder to create spans for each database operation, allowing Sentry to display query execution times and identify performance bottlenecks. Developers must call initQueryTracing(knexInstance) after initializing the database connection to activate this feature.

What is the difference between the backend and frontend Sentry implementations in Ghost?

The backend implementation in ghost/core/core/shared/sentry.js uses the Node.js Sentry SDK and provides Express middleware for request handling, while normalizing server errors and MySQL failures. The frontend implementation in ghost/admin/app/utils/sentry.js uses the Ember-specific Sentry SDK and focuses on sanitizing browser errors, normalizing model identifiers, and integrating session replay capabilities. The backend handles transaction filtering for API routes, whereas the frontend manages environment-specific integrations like Debug and Replay.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →