Sentry’s feature flag tracking helps you understand how feature flags correlate with errors and performance issues, making it easier to identify problematic feature rollouts.
Setup
Enable feature flag tracking with the integration:
import * as Sentry from '@sentry/browser' ;
Sentry . init ({
dsn: 'your-dsn' ,
integrations: [
Sentry . featureFlagsIntegration ()
]
});
The feature flags integration automatically tracks flag evaluations and includes them with error events and spans.
Recording Flag Evaluations
Manually record feature flag evaluations:
import { recordFeatureFlag } from '@sentry/browser' ;
// Record a flag evaluation
recordFeatureFlag ( 'new-checkout' , true );
// Use the flag in your code
if ( flags . newCheckout ) {
renderNewCheckout ();
} else {
renderOldCheckout ();
}
Recording Multiple Flags
import { recordFeatureFlag } from '@sentry/browser' ;
// Record multiple flags
recordFeatureFlag ( 'dark-mode' , true );
recordFeatureFlag ( 'beta-features' , false );
recordFeatureFlag ( 'new-dashboard' , true );
Integration with Feature Flag Services
LaunchDarkly
import * as Sentry from '@sentry/browser' ;
import * as LDClient from 'launchdarkly-js-client-sdk' ;
// Initialize Sentry
Sentry . init ({
dsn: 'your-dsn' ,
integrations: [ Sentry . featureFlagsIntegration ()]
});
// Initialize LaunchDarkly
const ldClient = LDClient . initialize ( 'your-client-id' , {
key: 'user-key'
});
await ldClient . waitForInitialization ();
// Record all flag evaluations
ldClient . on ( 'change' , ( settings ) => {
Object . entries ( settings ). forEach (([ flagKey , flagValue ]) => {
Sentry . recordFeatureFlag ( flagKey , flagValue . current );
});
});
// Or record on each evaluation
function getFlagValue ( flagKey , defaultValue ) {
const value = ldClient . variation ( flagKey , defaultValue );
Sentry . recordFeatureFlag ( flagKey , value );
return value ;
}
Split.io
import * as Sentry from '@sentry/browser' ;
import { SplitFactory } from '@splitsoftware/splitio' ;
Sentry . init ({
dsn: 'your-dsn' ,
integrations: [ Sentry . featureFlagsIntegration ()]
});
const factory = SplitFactory ({
core: {
authorizationKey: 'your-api-key'
}
});
const client = factory . client ();
await client . ready ();
// Wrap getTreatment to record flags
const originalGetTreatment = client . getTreatment . bind ( client );
client . getTreatment = function ( splitName ) {
const treatment = originalGetTreatment ( splitName );
Sentry . recordFeatureFlag ( splitName , treatment === 'on' );
return treatment ;
};
GrowthBook
Sentry provides built-in GrowthBook integration:
import * as Sentry from '@sentry/browser' ;
import { GrowthBook } from '@growthbook/growthbook' ;
Sentry . init ({
dsn: 'your-dsn' ,
integrations: [
// Automatic GrowthBook tracking
Sentry . growthbookIntegration ()
]
});
// Initialize GrowthBook
const growthbook = new GrowthBook ({
apiHost: 'https://cdn.growthbook.io' ,
clientKey: 'your-client-key'
});
await growthbook . loadFeatures ();
// Flags are automatically tracked
const showNewFeature = growthbook . isOn ( 'new-feature' );
Use the GrowthBook integration for automatic flag tracking without manual instrumentation.
Feature Flag Context
Flags are automatically added to error and performance events:
import { recordFeatureFlag } from '@sentry/browser' ;
// Record flags
recordFeatureFlag ( 'new-checkout' , true );
recordFeatureFlag ( 'discount-banner' , false );
try {
// Error will include flag context
processCheckout ();
} catch ( error ) {
Sentry . captureException ( error );
// Error in Sentry shows:
// Flags: { new-checkout: true, discount-banner: false }
}
Flags on Spans
Flags are also attached to performance spans:
import { recordFeatureFlag } from '@sentry/browser' ;
Sentry . startSpan ({ name: 'load_dashboard' , op: 'ui.load' }, () => {
// Record flag evaluation
const useNewDashboard = getFeatureFlag ( 'new-dashboard' );
recordFeatureFlag ( 'new-dashboard' , useNewDashboard );
if ( useNewDashboard ) {
loadNewDashboard ();
} else {
loadOldDashboard ();
}
});
// Span includes: flag.evaluation.new-dashboard: true
Up to 10 unique flags per span are recorded. Additional flags are silently dropped.
Flag Buffer
Sentry maintains a buffer of the last 100 flag evaluations:
// These flags are tracked in order
recordFeatureFlag ( 'flag-1' , true );
recordFeatureFlag ( 'flag-2' , false );
recordFeatureFlag ( 'flag-3' , true );
// ... (up to 100 flags)
// When an error occurs, all 100 flags are included
Sentry . captureException ( error );
Flag Buffer Behavior
LRU (Least Recently Used) : Old flags are removed when buffer is full
Unique flags : Re-evaluating a flag updates its value
Ordered : Flags are sorted by recency
Practical Examples
A/B Testing
import { recordFeatureFlag } from '@sentry/browser' ;
function getExperimentVariant ( experimentName ) {
const variant = abTestingService . getVariant ( experimentName );
// Record the variant as a flag
recordFeatureFlag ( `experiment. ${ experimentName } ` , variant );
return variant ;
}
// Use in code
const checkoutVariant = getExperimentVariant ( 'checkout-flow' );
if ( checkoutVariant === 'variant-b' ) {
renderCheckoutB ();
} else {
renderCheckoutA ();
}
Progressive Rollout
import { recordFeatureFlag } from '@sentry/browser' ;
function shouldEnableFeature ( featureName , rolloutPercent ) {
const userId = getCurrentUser (). id ;
const hash = hashCode ( userId + featureName );
const enabled = ( hash % 100 ) < rolloutPercent ;
// Record for Sentry
recordFeatureFlag ( featureName , enabled );
return enabled ;
}
// 10% rollout
const newFeatureEnabled = shouldEnableFeature ( 'new-search' , 10 );
if ( newFeatureEnabled ) {
// New feature code
// If this crashes, Sentry knows the flag was enabled
}
Feature Gating
import { recordFeatureFlag } from '@sentry/browser' ;
function checkFeatureAccess ( feature ) {
const user = getCurrentUser ();
const hasAccess = user . plan === 'premium' || user . betaFeatures ;
recordFeatureFlag ( `feature. ${ feature } .access` , hasAccess );
return hasAccess ;
}
if ( checkFeatureAccess ( 'advanced-analytics' )) {
showAdvancedAnalytics ();
} else {
showUpgradePrompt ();
}
Conditional Features
import { recordFeatureFlag } from '@sentry/browser' ;
function evaluateFeatureFlags () {
const user = getCurrentUser ();
const environment = getEnvironment ();
const flags = {
darkMode: user . preferences . darkMode ,
betaFeatures: user . betaAccess && environment === 'staging' ,
newDashboard: environment === 'production' || user . betaAccess ,
aiFeatures: user . plan === 'enterprise'
};
// Record all flags
Object . entries ( flags ). forEach (([ key , value ]) => {
recordFeatureFlag ( key , value );
});
return flags ;
}
const flags = evaluateFeatureFlags ();
Analyzing Flag Impact
In Sentry, you can:
Filter issues by flags : See errors that occurred when a flag was enabled
Compare performance : Measure performance impact of flags
Correlate with releases : Track flag changes across releases
Group by flags : Aggregate errors by flag combinations
Example Queries
// Errors where new-checkout was enabled
flag.new-checkout:true
// Performance issues with beta features
flag.beta-features:true AND transaction.duration:>1s
// Compare error rates
flag.new-dashboard:true vs flag.new-dashboard:false
Best Practices
Record early : Track flags before using them
Use descriptive names : Clear flag names help debugging
Track all evaluations : Record flags even if they’re false
Include experiments : Track A/B test variants as flags
Clean up old flags : Remove tracking for deprecated flags
Monitor flag impact : Watch error rates when enabling flags
Custom Integration
Create a wrapper for your feature flag service:
import { recordFeatureFlag } from '@sentry/browser' ;
class FeatureFlagManager {
constructor ( flagService ) {
this . flagService = flagService ;
}
isEnabled ( flagName , defaultValue = false ) {
const value = this . flagService . getValue ( flagName , defaultValue );
// Automatically record in Sentry
recordFeatureFlag ( flagName , value );
return value ;
}
getVariant ( experimentName ) {
const variant = this . flagService . getVariant ( experimentName );
// Record experiment variant
recordFeatureFlag ( `experiment. ${ experimentName } ` , variant );
return variant ;
}
}
// Use throughout app
const flags = new FeatureFlagManager ( yourFlagService );
if ( flags . isEnabled ( 'new-feature' )) {
// Feature code
}
TypeScript Support
import { recordFeatureFlag } from '@sentry/browser' ;
type FeatureFlag = 'new-checkout' | 'dark-mode' | 'beta-features' ;
function recordFlag ( flag : FeatureFlag , value : boolean ) : void {
recordFeatureFlag ( flag , value );
}
// Type-safe flag recording
recordFlag ( 'new-checkout' , true );
// recordFlag('invalid-flag', true); // TypeScript error
Troubleshooting
Flags Not Appearing
// Check integration is loaded
import { getClient } from '@sentry/browser' ;
const client = getClient ();
const integration = client ?. getIntegrationByName ( 'FeatureFlags' );
if ( ! integration ) {
console . error ( 'Feature flags integration not found' );
}
// Check flags are being recorded
import { getCurrentScope } from '@sentry/browser' ;
const scope = getCurrentScope ();
const context = scope . getScopeData (). contexts . flags ;
console . log ( 'Recorded flags:' , context ?. values );
Too Many Flags
// Only record important flags
function shouldRecordFlag ( flagName ) {
const importantFlags = [ 'new-checkout' , 'payment-provider' , 'ab-test-variant' ];
return importantFlags . includes ( flagName );
}
function recordFlagConditionally ( flagName , value ) {
if ( shouldRecordFlag ( flagName )) {
recordFeatureFlag ( flagName , value );
}
}
Next Steps
Error Monitoring See how flags impact errors
Performance Measure flag performance impact
Context Learn about context data
User Feedback Collect feedback on new features