- 필수 기능
- 시작하기
- Glossary
- 표준 속성
- Guides
- Agent
- 통합
- 개방형텔레메트리
- 개발자
- Administrator's Guide
- API
- Datadog Mobile App
- CoScreen
- Cloudcraft
- 앱 내
- 서비스 관리
- 인프라스트럭처
- 애플리케이션 성능
- APM
- Continuous Profiler
- 스팬 시각화
- 데이터 스트림 모니터링
- 데이터 작업 모니터링
- 디지털 경험
- 소프트웨어 제공
- 보안
- AI Observability
- 로그 관리
- 관리
",t};e.buildCustomizationMenuUi=t;function n(e){let t='
",t}function s(e){let n=e.filter.currentValue||e.filter.defaultValue,t='${e.filter.label}
`,e.filter.options.forEach(s=>{let o=s.id===n;t+=``}),t+="${e.filter.label}
`,t+=`This guide walks you through the process of migrating feature flags from Statsig to Eppo by Datadog, as an intermediate step before fully migrating to Datadog’s dedicated Feature Flags product. Follow these general steps:
Log in to Eppo at https://eppo.cloud/.
Generate an SDK key by navigating to Flags > Environments, then clicking Create > SDK Key.
Define a logging function for the Eppo SDK to log assignments so they end up in your data warehouse.
const assignmentLogger: IAssignmentLogger = {
logAssignment(assignment) {
analytics.track({
userId: assignment.subject,
event: "Eppo Randomization Event",
type: "track",
properties: { ...assignment },
});
},
};
Initialize the SDK in your code. For instructions for your language, see SDK guides.
await init({
apiKey: EPPO_SDK_KEY,
assignmentLogger,
});
Create a flag in Eppo by navigating to Flags > Feature Flags, then clicking Create Flag > Feature Flag.
Implement the flag in your application code.
Test the flag in your local development environment to ensure it works as expected.
const variation = getInstance().getBooleanAssignment(
'show-new-feature',
user.id,
{
'country': user.country,
'device': user.device
},
false
);
Deploy the application to your staging or testing environments and verify the flag’s functionality.
Deploy the application to your production environment and test the flag again.
Implement a function that wraps calling Eppo’s SDK to have a fallback mechanism to use the Statsig flag values if the new service is unavailable or experiences issues.
When attempting to retrieve a flag value from Eppo, catch any exceptions or errors that may occur due to service unavailability or issues and return the old value.
Eppo SDKs use only strongly typed assignment functions (for example, getBooleanAssignment
), whereas Statsig SDKs use specific evaluation functions for different types. For such SDKs, it’s recommended to create wrappers for each type. You can then replace uses of the Statsig functions with the typed wrappers in your application. Here are examples:
// After initialization, turn off graceful failure so exceptions are rethrown
getInstance().setIsGracefulFailureMode(false);
// Drop-in wrapper replacement for getting a boolean Statsig gate.
// Replace boolean calls to checkGate() with getBoolVariationWrapper() in the code.
export function getBoolVariationWrapper(
gateKey: string,
user: StatsigUser,
attributes?: Record<string, string | number | boolean | null>
) {
let assignment = false;
try {
assignment = getInstance().getBooleanAssignment(
gateKey,
user.userID,
attributes,
false
);
} catch (e) {
logger.warn(
'Error encountered evaluating boolean assignment from Eppo SDK; falling back to Statsig',
{ gateKey, user, attributes }
);
// Fallback to Statsig gate check
assignment = statsig.checkGate(user, gateKey);
}
return assignment;
}
// Drop-in wrapper replacement for getting a string Statsig config.
// Replace string calls to getConfig() with getStringVariationWrapper() in the code.
export function getStringVariationWrapper(
configKey: string,
parameterName: string,
user: StatsigUser,
attributes?: Record<string, string | number | boolean | null>
) {
let assignment = 'default';
try {
assignment = getInstance().getStringAssignment(
user.userID,
configKey,
attributes,
'default'
);
} catch (e) {
logger.warn(
'Error encountered evaluating string assignment from Eppo SDK; falling back to Statsig',
{ configKey, parameterName, user, attributes }
);
// Fallback to Statsig config parameter
const config = statsig.getConfig(user, configKey);
assignment = config.get(parameterName, 'default');
}
return assignment;
}
// Drop-in wrapper replacement for getting a numeric Statsig config parameter.
// Replace numeric calls to getConfig() with getNumericVariationWrapper() in the code.
export function getNumericVariationWrapper(
configKey: string,
parameterName: string,
user: StatsigUser,
attributes?: Record<string, string | number | boolean | null>
) {
let assignment = 0;
try {
assignment = getInstance().getNumericAssignment(
user.userID,
configKey,
attributes,
0
);
} catch (e) {
logger.warn(
'Error encountered evaluating numeric assignment from Eppo SDK; falling back to Statsig',
{ configKey, parameterName, user, attributes }
);
// Fallback to Statsig config parameter
const config = statsig.getConfig(user, configKey);
assignment = config.get(parameterName, 0);
}
return assignment;
}
// Drop-in wrapper replacement for getting experiment assignments.
// Replace calls to getExperiment() with getExperimentVariationWrapper() in the code.
export function getJSONVariationWrapper(
experimentKey: string,
parameterName: string,
user: StatsigUser,
attributes?: Record<string, string | number | boolean | null>
) {
let assignment: any = null;
try {
// For experiments with multiple parameters, use JSON assignment
const experimentResult = getInstance().getJSONAssignment(
user.userID,
experimentKey,
attributes,
{}
);
assignment = experimentResult?.[parameterName];
} catch (e) {
logger.warn(
'Error encountered evaluating experiment assignment from Eppo SDK; falling back to Statsig',
{ experimentKey, parameterName, user, attributes }
);
// Fallback to Statsig experiment
const experiment = statsig.getExperiment(user, experimentKey);
assignment = experiment.get(parameterName, null);
}
return assignment;
}
FeatureHelper.ts
export function isFeatureEnabled(
featureKey: string,
userId: string,
attributes?: Record<string, string | number | boolean | null>
) {
return getInstance().getBooleanAssignment(userId, featureKey, attributes, false);
}
export function getFeatureConfig(
configKey: string,
userId: string,
attributes?: Record<string, string | number | boolean | null>
) {
return getInstance().getJSONAssignment(userId, configKey, attributes, {});
}
PlaceUsingFlags.ts
const useBigButtons = isFeatureEnabled('use-big-buttons', userId, userAttributes);
const buttonConfig = getFeatureConfig('button-configuration', userId, userAttributes);
Statsig and Eppo have similar interfaces for feature flag evaluation, making the transition straightforward with some key differences in how they handle different data types.
For more details, see the documentation sources above each code example.
Statsig docs: Getting Started
await statsig.initialize('client-key', user, { environment: { tier: 'production' } });
Eppo docs: Initialization
await init({
apiKey: EPPO_SDK_KEY,
assignmentLogger,
});
Statsig docs: Logging an Event
// Statsig automatically logs assignments, but you can add custom logging by
// subscribing to client events like gate_evaluation or experiment_evaluation.
statsig.on('gate_evaluation', (event) => {
// Your custom logging logic here
console.log(event);
});
Eppo docs: Assignment logging
const assignmentLogger: IAssignmentLogger = {
logAssignment(assignment) {
// Send data to analytics provider/warehouse here
analytics.track({
userId: assignment.subject,
event: "Eppo Randomization Event",
type: "track",
properties: { ...assignment },
});
}
};
getInstance().setLogger(assignmentLogger); // Can also be set in init()
For example, check if a feature is enabled.
Statsig docs: Checking a Feature Flag/Gate
const enabled = statsig.checkGate('new_feature_gate');
Eppo docs: Boolean Assignments
const enabled = getInstance().getBooleanAssignment(
user.userID,
'new_feature_gate',
userAttributes,
false
);
Statsig docs: Reading a Dynamic Config
const config = statsig.getConfig('product_config');
Eppo docs: Assignments
// If it's part of a multi-valued configuration (how Statsig organizes values),
// you will have to figure out the type of each parameter, as Eppo uses different
// calls for each variant type.
// For a JSON configuration with multiple parameters:
const config = getInstance().getJSONAssignment(user.userID, 'product_config', userAttributes, {});
const buttonColor = config?.button_color || 'blue';
const maxItems = config?.max_items || 10;
// For individual string parameters:
const buttonColor = getInstance().getStringAssignment(
user.userID,
'button_color_flag',
userAttributes,
'blue'
);
// For individual numeric parameters:
const maxItems = getInstance().getNumericAssignment(
user.userID,
'max_items_flag',
userAttributes,
10
);
For example, get experiment parameter values.
Statsig docs: Getting a Layer/Experiment
// Values using getLayer
const layer = statsig.getLayer("user_promo_experiments");
const promoTitle = layer.get("title", "Welcome to Statsig!");
const discount = layer.get("discount", 0.1);
// or, using getExperiment
const titleExperiment = statsig.getExperiment("new_user_promo_title");
const priceExperiment = statsig.getExperiment("new_user_promo_price");
const promoTitle = titleExperiment.value["title"] ?? "Welcome!";
const discount = priceExperiment.value["discount"] ?? 0.1;
Eppo docs: Assignments
// For experiments with multiple parameters, use JSON assignment.
const variation = getInstance().getJSONAssignment(
user.userID,
'checkout_flow_test',
userAttributes,
{}
);
const checkoutVersion = variation?.checkout_version || 'control';
const showUpsell = variation?.show_upsell || false;
// Alternatively, for individual experiment parameters:
const checkoutVersion = getInstance().getStringAssignment(
user.userID,
'checkout_version',
userAttributes,
'control'
);
Statsig docs: Statsig User
const user = new StatsigUser({
userID: '12345',
email: 'user@example.com',
country: 'US',
custom: {
plan: 'premium',
signup_date: '2023-01-15'
}
});
Eppo docs: Subject attributes
const userAttributes = {
email: 'user@example.com',
country: 'US',
plan: 'premium',
signup_date: '2023-01-15'
};
// Used in assignment calls
const variation = getInstance().getBooleanAssignment(
'12345', // userID as separate parameter
'feature_flag',
userAttributes,
false
);