mirror of
https://github.com/SigNoz/signoz.git
synced 2026-03-15 01:22:14 +00:00
Use the new `flagger` package to power the following features flags in the codebase: - [x] `use_span_metrics` - [x] `kafka_span_eval` - [x] `interpolation_enabled`
135 lines
4.7 KiB
Markdown
135 lines
4.7 KiB
Markdown
# Flagger
|
|
|
|
Flagger is SigNoz's feature flagging system built on top of the [OpenFeature](https://openfeature.dev/) 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.
|
|
|
|
## How does it work?
|
|
|
|
Flagger consists of three main components:
|
|
|
|
1. **Registry** (`pkg/flagger/registry.go`) - Contains all available feature flags with their metadata and default values
|
|
2. **Flagger** (`pkg/flagger/flagger.go`) - The consumer-facing interface for evaluating feature flags
|
|
3. **Providers** (`pkg/flagger/<provider>flagger/`) - Implementations that supply feature flag values (e.g., `configflagger` for config-based flags)
|
|
|
|
The evaluation flow works as follows:
|
|
|
|
1. The caller requests a feature flag value via the `Flagger` interface
|
|
2. Flagger checks the registry to validate the flag exists and get its default value
|
|
3. Each registered provider is queried for an override value
|
|
4. If a provider returns a value different from the default, that value is returned
|
|
5. Otherwise, the default value from the registry is returned
|
|
|
|
## How to add a new feature flag?
|
|
|
|
### 1. Register the flag in the registry
|
|
|
|
Add your feature flag definition in `pkg/flagger/registry.go`:
|
|
|
|
```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).
|
|
|
|
### 2. Configure the feature flag value (optional)
|
|
|
|
To override the default value, add an entry in your configuration file:
|
|
|
|
```yaml
|
|
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` |
|
|
|
|
## How to evaluate a feature flag?
|
|
|
|
Use the `Flagger` interface to evaluate feature flags. The interface provides typed methods for each value type:
|
|
|
|
```go
|
|
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
|
|
}
|
|
```
|
|
|
|
### Empty variants
|
|
|
|
For cases where you want to use a default value on error (and log the error), use the `*OrEmpty` methods:
|
|
|
|
```go
|
|
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
|
|
}
|
|
}
|
|
```
|
|
|
|
### Available evaluation methods
|
|
|
|
| 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{}{}` |
|
|
|
|
## What should I remember?
|
|
|
|
- Always define feature flags in the registry (`pkg/flagger/registry.go`) before using them
|
|
- Use descriptive feature names that clearly indicate what the flag controls
|
|
- Prefer `*OrEmpty` methods for non-critical features to avoid error handling overhead
|
|
- Export feature name variables (e.g., `FeatureMyNewFeature`) for type-safe usage across packages
|
|
- Consider the feature's lifecycle stage (`Alpha`, `Beta`, `Stable`) when defining it
|
|
- Providers are evaluated in order; the first non-default value wins
|