docs/contributing/go/flagger.md
Flagger is SigNoz's feature flagging system built on top of the OpenFeature standard. It provides a unified interface for evaluating feature flags across the application, allowing features to be enabled, disabled, or configured dynamically without code changes.
💡 Note: OpenFeature is a CNCF project that provides a vendor-agnostic feature flagging API, making it easy to switch providers without changing application code.
Flagger consists of three main components:
pkg/flagger/registry.go) - Contains all available feature flags with their metadata and default valuespkg/flagger/flagger.go) - The consumer-facing interface for evaluating feature flagspkg/flagger/<provider>flagger/) - Implementations that supply feature flag values (e.g., configflagger for config-based flags)The evaluation flow works as follows:
Flagger interfaceAdd your feature flag definition in pkg/flagger/registry.go:
var (
// Export the feature name for use in evaluations
FeatureMyNewFeature = featuretypes.MustNewName("my_new_feature")
)
func MustNewRegistry() featuretypes.Registry {
registry, err := featuretypes.NewRegistry(
// ...existing features...
&featuretypes.Feature{
Name: FeatureMyNewFeature,
Kind: featuretypes.KindBoolean, // or KindString, KindFloat, KindInt, KindObject
Stage: featuretypes.StageStable, // or StageAlpha, StageBeta
Description: "Controls whether my new feature is enabled",
DefaultVariant: featuretypes.MustNewName("disabled"),
Variants: featuretypes.NewBooleanVariants(),
},
)
// ...
}
💡 Note: Feature names must match the regex
^[a-z_]+$(lowercase letters and underscores only).
To override the default value, add an entry in your configuration file:
flagger:
config:
boolean:
my_new_feature: true
Supported configuration types:
| Type | Config Key | Go Type |
|---|---|---|
| Boolean | boolean | bool |
| String | string | string |
| Float | float | float64 |
| Integer | integer | int64 |
| Object | object | any |
Use the Flagger interface to evaluate feature flags. The interface provides typed methods for each value type:
import (
"github.com/SigNoz/signoz/pkg/flagger"
"github.com/SigNoz/signoz/pkg/types/featuretypes"
)
func DoSomething(ctx context.Context, flagger flagger.Flagger) error {
// Create an evaluation context (typically with org ID)
evalCtx := featuretypes.NewFlaggerEvaluationContext(orgID)
// Evaluate with error handling
enabled, err := flagger.Boolean(ctx, flagger.FeatureMyNewFeature, evalCtx)
if err != nil {
return err
}
if enabled {
// Feature is enabled
}
return nil
}
For cases where you want to use a default value on error (and log the error), use the *OrEmpty methods:
func DoSomething(ctx context.Context, flagger flagger.Flagger) {
evalCtx := featuretypes.NewFlaggerEvaluationContext(orgID)
// Returns false on error and logs the error
if flagger.BooleanOrEmpty(ctx, flagger.FeatureMyNewFeature, evalCtx) {
// Feature is enabled
}
}
| Method | Return Type | Empty Variant Default |
|---|---|---|
Boolean() | (bool, error) | false |
String() | (string, error) | "" |
Float() | (float64, error) | 0.0 |
Int() | (int64, error) | 0 |
Object() | (any, error) | struct{}{} |
pkg/flagger/registry.go) before using them*OrEmpty methods for non-critical features to avoid error handling overheadFeatureMyNewFeature) for type-safe usage across packagesAlpha, Beta, Stable) when defining it