Spotlight

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT", "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this document are to be interpreted as described in RFC 2119.

Sentry Spotlight is a local development tool that provides real-time observability for errors, traces, logs, and performance data during development. SDKs SHOULD implement support for Spotlight to unlock the power of Sentry for local development.

SDKs MUST support Spotlight configuration through one of two approaches:

This approach MUST be used if the SDK language allows for a single configuration attribute to have 2 different types (boolean and string).

The SDK MUST accept a single spotlight configuration attribute that can be:

  • false or undefined/null: Spotlight is disabled
  • true: Spotlight is enabled using the default URL (http://localhost:8969/stream)
  • string: Spotlight is enabled using the provided URL

Example:

Copied
# Disabled
spotlight: False

# Enabled with default URL
spotlight: True

# Enabled with custom URL
spotlight: "http://localhost:3000/stream"

The SDK MUST accept two separate configuration attributes:

  • spotlight: Optional[boolean]: Enable or disable Spotlight
  • spotlightUrl: Optional[string]: Specify the Spotlight backend URL

Important: If spotlightUrl is set to any truthy string value, it MUST imply spotlight: true unless spotlight is explicitly set to false.

Example:

Copied
# Disabled
spotlight: False

# Enabled with default URL
spotlight: True

# Enabled with custom URL (spotlightUrl implies spotlight: true)
spotlightUrl: "http://localhost:3000/stream"

# Explicitly disabled even with spotlightUrl set
spotlight: False
spotlightUrl: "http://localhost:3000/stream"  # Ignored

SDKs MUST support the SENTRY_SPOTLIGHT environment variable. This is to enable spotlight run to be used as a development server, which is the preferred way to run Spotlight. In this mode Spotlight runs your development command for you, with a fresh, unique Spotlight server running on a random port. To enable Spotlight on the target application, it sets the SENTRY_SPOTLIGHT environment variable to the URL of the Spotlight server. It also does some accommodations based on the target application, such as altering the host value for Docker Compose while keeping the front-end ones the same. It will also support emulator-based development environments and will set the host value accordingly in the near future.

The value MUST be parsed according to the following rules:

The following values MUST be treated as spotlight: true (enabling Spotlight with the default URL):

  • "true"
  • "t"
  • "y"
  • "yes"
  • "on"
  • "1"

The following values MUST be treated as spotlight: false (disabling Spotlight):

  • "false"
  • "f"
  • "n"
  • "no"
  • "off"
  • "0"

Any other string value MUST be treated as a Spotlight backend URL, enabling Spotlight with that URL.

Example parsing logic (Python reference):

Copied
def parse_spotlight_env(env_value):
    if not env_value:
        return None

    env_value_lower = env_value.lower().strip()

    # Truthy values
    if env_value_lower in ("true", "t", "y", "yes", "on", "1"):
        return True

    # Falsy values
    if env_value_lower in ("false", "f", "n", "no", "off", "0"):
        return False

    # Any other value is treated as a URL
    return env_value

The interaction between configuration options and environment variables MUST follow these precedence rules:

  1. Config option MUST take precedence over env var, except:

    • If spotlight: true (boolean, no URL specified) AND SENTRY_SPOTLIGHT is set to a URL string → MUST use the env var URL This allows developers to enable Spotlight via config but override the URL via environment variable
  2. If spotlight is set to a string URL → it MUST override the env var completely When this happens, the SDK MUST print a warning to the console indicating that the config URL is taking precedence over the env var

  3. If using two-attribute approach:

    • If spotlightUrl config and env var are both set → MUST use config value (spotlightUrl)
    • If spotlight: false is explicitly set → MUST ignore spotlightUrl value, the env var, and any URL configuration
    • In either case the SDK MUST print a warning to the console explaining the reason for deactivation of Spotlight or that the env variable exists but not being used due to hard-coded configuration.

Precedence Examples:

Copied
# Example 1: Config boolean true + env var URL → use env var URL
# Config: spotlight: True
# Env: SENTRY_SPOTLIGHT=http://custom:3000/stream
# Result: Enabled with http://custom:3000/stream

# Example 2: Config URL string → always use config URL
# Config: spotlight: "http://config:3000/stream"
# Env: SENTRY_SPOTLIGHT=http://env:3000/stream
# Result: Enabled with http://config:3000/stream

# Example 3: Config false → disable regardless of env var
# Config: spotlight: False
# Env: SENTRY_SPOTLIGHT=http://localhost:8969/stream
# Result: Disabled

# Example 4: Two-attribute approach with spotlightUrl
# Config: spotlightUrl: "http://config:3000/stream"
# Env: SENTRY_SPOTLIGHT=http://env:3000/stream
# Result: Enabled with http://config:3000/stream (config takes precedence)

When Spotlight is enabled, SDKs MUST implement the following data collection behavior:

General Requirements:

  • SDKs SHOULD use a separate pipeline for Spotlight that does not affect upstream Sentry behavior
  • All Spotlight-specific settings (sample rates, PII collection, etc.) MUST NOT affect upstream Sentry configuration
  • Fallback: If a separate pipeline is not feasible, SDKs SHOULD enable Spotlight-specific settings manually if no DSN is configured in development mode

  • MUST send a copy of every envelope to the Spotlight server
  • This includes errors, transactions, sessions, profiles, replays, and all other envelope types
  • The Spotlight server HTTP semantics are the same as the Sentry server HTTP semantics (e.g. use POST to send envelopes)

  • MUST enable 100% sample rate for all telemetry types for the Spotlight pipeline

  • MUST enable all PII data collection for Spotlight (equivalent to sendDefaultPii: true)
  • Spotlight is intended for local development, so full data visibility is expected
  • To achieve this, the RECOMMENDED approach is to collect all PII data and then scrub it locally before sending it to Sentry while keeping it for Spotlight.

  • MUST enable profiling to Spotlight
  • MUST enable log collection to Spotlight

Ideal Implementation:

Copied
# Separate pipeline that bypasses sampling
def send_to_spotlight(envelope):
    # Clone envelope to avoid affecting upstream
    spotlight_envelope = clone_envelope(envelope)

    # Override sampling - ensure 100% sample rate
    spotlight_envelope.sample_rate = 1.0

    # Enable all PII
    spotlight_envelope.send_default_pii = True

    # Send to Spotlight server
    spotlight_transport.send(spotlight_envelope)

Fallback Implementation:

If separate pipeline is not feasible:

Copied
# Enable sampling if no DSN in development
if not dsn and is_development_mode():
    sample_rate = 1.0
    send_default_pii = True

The default Spotlight backend URL is:

Copied
http://localhost:8969/stream

This URL MUST be used when:

  • spotlight: true is set (boolean, no URL specified) and SENTRY_SPOTLIGHT is not set
  • SENTRY_SPOTLIGHT is set to a truthy value (not a URL string) and spotlight config is not set

  • Spotlight MUST be disabled by default and MUST only be enabled when explicitly configured or when the environment variable is set.

SDKs MUST handle Spotlight server connectivity issues gracefully:

  • If the Spotlight server is unreachable, SDKs:
    • MUST log an error message at least once
    • MUST NOT log an error message for every failed envelope
    • SHOULD implement exponential backoff retry logic
    • MUST continue normal Sentry operation without interruption

RECOMMENDED retry strategy:

Copied
import time
import logging

logger = logging.getLogger(__name__)

class SpotlightTransport:
    def __init__(self, url):
        self.url = url
        self.retry_delay = 1.0  # Start with 1 second
        self.max_retry_delay = 60.0  # Max 60 seconds
        self.error_logged = False

    def send(self, envelope):
        try:
            # Attempt to send
            self._send_envelope(envelope)
            # Reset retry delay on success
            self.retry_delay = 1.0
            self.error_logged = False
        except ConnectionError as e:
            # Exponential backoff
            if not self.error_logged:
                logger.error(f"Spotlight server unreachable at {self.url}: {e}")
                self.error_logged = True

            # Wait before retry
            time.sleep(self.retry_delay)
            self.retry_delay = min(self.retry_delay * 2, self.max_retry_delay)

            # Retry once, then give up for this envelope
            try:
                self._send_envelope(envelope)
                self.retry_delay = 1.0
            except ConnectionError:
                # Silently drop envelope after retry
                pass

  • SHOULD log errors at the appropriate level (typically ERROR or WARNING)
  • MUST avoid logging errors for every failed envelope to prevent log spam
  • MAY consider logging once per connection failure, then periodically if failures persist

  • Spotlight transmission MUST never block normal Sentry operation
  • If Spotlight is unavailable, the SDK MUST continue sending data to Sentry normally
  • Spotlight failures MUST NOT affect event capture, transaction recording, or any other SDK functionality

The Python SDK implementation serves as a reference. Key implementation details:

  • Single attribute approach: spotlight: Optional[Union[str, bool]]
  • Environment variable: SENTRY_SPOTLIGHT
  • Default URL: http://localhost:8969/stream
  • Separate transport pipeline for Spotlight

Configuration Example:

Copied
import sentry_sdk

sentry_sdk.init(
    dsn="https://...@sentry.io/...",
    spotlight=True,  # Enable with default URL
    # or
    spotlight="http://localhost:3000/stream",  # Custom URL
)

Copied
interface SpotlightOptions {
  spotlight?: boolean | string;
}

function init(options: SpotlightOptions) {
  const spotlightEnabled = resolveSpotlightConfig(options);

  if (spotlightEnabled) {
    const spotlightUrl =
      typeof spotlightEnabled === "string"
        ? spotlightEnabled
        : "http://localhost:8969/stream";

    setupSpotlightTransport(spotlightUrl);
  }
}

function resolveSpotlightConfig(
  options: SpotlightOptions,
): boolean | string | null {
  // Config takes precedence
  if (options.spotlight === false) {
    return null;
  }

  if (options.spotlight === true) {
    // Check env var for URL override
    const envValue = process.env.SENTRY_SPOTLIGHT;
    if (envValue && !isTruthyFalsy(envValue)) {
      return envValue; // Use env var URL
    }
    return true; // Use default URL
  }

  if (typeof options.spotlight === "string") {
    return options.spotlight; // Config URL takes precedence
  }

  // Check env var
  const envValue = process.env.SENTRY_SPOTLIGHT;
  if (envValue) {
    return parseSpotlightEnv(envValue);
  }

  return null;
}

Copied
public class SpotlightConfig {
    private Boolean enabled;
    private String url;

    public static SpotlightConfig fromOptions(Options options, String envVar) {
        SpotlightConfig config = new SpotlightConfig();

        // Config takes precedence
        if (options.getSpotlight() != null) {
            if (options.getSpotlight() instanceof Boolean) {
                config.enabled = (Boolean) options.getSpotlight();
                if (config.enabled && envVar != null && !isTruthyFalsy(envVar)) {
                    config.url = envVar; // Env var URL override
                }
            } else if (options.getSpotlight() instanceof String) {
                config.enabled = true;
                config.url = (String) options.getSpotlight();
            }
        } else if (envVar != null) {
            Object parsed = parseSpotlightEnv(envVar);
            if (parsed instanceof Boolean) {
                config.enabled = (Boolean) parsed;
            } else if (parsed instanceof String) {
                config.enabled = true;
                config.url = (String) parsed;
            }
        }

        if (config.enabled == null || !config.enabled) {
            return null; // Spotlight disabled
        }

        config.url = config.url != null ? config.url : "http://localhost:8969/stream";
        return config;
    }
}

Was this helpful?
Help improve this content
Our documentation is open source and available on GitHub. Your contributions are welcome, whether fixing a typo (drat!) or suggesting an update ("yeah, this would be better").