Documentation
¶
Overview ¶
Package featureflag provides a tracing-aware decorator around the OpenFeature Go SDK configured to use Flipt as its provider.
The Client mirrors the OpenFeature client's value-details methods (Client.BooleanValueDetails, Client.StringValueDetails) so callers have full access to resolution metadata (variant, reason, error code, flag metadata). The only addition is optional OTel tracing: when a [Config.Tracer] is set, every evaluation is recorded as a child span.
Setup ¶
The Flipt server address is configured via the FEATURE_FLAG_ENDPOINT environment variable:
export FEATURE_FLAG_ENDPOINT=http://flipt:8080
Use New in production code to pick up the endpoint automatically:
client, err := featureflag.New(ctx)
if err != nil {
return err
}
Use NewWithConfig when the endpoint or namespace must be supplied programmatically:
client, err := featureflag.NewWithConfig(ctx, &featureflag.Config{
Endpoint: "http://localhost:8080",
Namespace: "my-service",
})
Evaluating flags ¶
Client.BooleanValueDetails and Client.StringValueDetails evaluate a flag and return the full openfeature.BooleanEvaluationDetails or openfeature.StringEvaluationDetails together with any provider error:
details, err := client.BooleanValueDetails(ctx, "enable-new-api", false, openfeature.EvaluationContext{})
if err != nil {
// defaultValue (false) is returned; log or handle the error.
}
if details.Value {
// new API path
}
details, err := client.StringValueDetails(ctx, "ui-theme", "light", openfeature.EvaluationContext{})
Observability ¶
When a [Config.Tracer] is provided, every flag evaluation is instrumented with an OpenTelemetry child span carrying the following attributes:
- feature_flag.key - the flag key
- feature_flag.value - the resolved value
- feature_flag.variant - the matched variant name
- feature_flag.reason - the reason returned by the provider
Errors are recorded on the span via RecordError so the failure is visible in traces.
Testing ¶
Use memprovider.NewInMemoryProvider via [Config.Provider] to test flag evaluation without a running Flipt server:
import "github.com/open-feature/go-sdk/openfeature/memprovider"
provider := memprovider.NewInMemoryProvider(map[string]memprovider.InMemoryFlag{
"my-flag": {
Key: "my-flag",
State: memprovider.Enabled,
DefaultVariant: "on",
Variants: map[string]any{"on": true, "off": false},
},
})
client, _ := featureflag.NewWithConfig(ctx, &featureflag.Config{
Name: "test",
Provider: provider,
})
details, _ := client.BooleanValueDetails(ctx, "my-flag", false, openfeature.EvaluationContext{})
// details.Value == true
// details.Variant == "on"
Example ¶
Example shows basic boolean flag evaluation using an in-memory provider.
package main
import (
"context"
"fmt"
"github.com/open-feature/go-sdk/openfeature"
"github.com/open-feature/go-sdk/openfeature/memprovider"
"gitlab.com/gitlab-org/labkit/v2/featureflag"
)
func main() {
provider := memprovider.NewInMemoryProvider(map[string]memprovider.InMemoryFlag{
"enable-new-api": {
Key: "enable-new-api",
State: memprovider.Enabled,
DefaultVariant: "on",
Variants: map[string]any{"on": true, "off": false},
},
})
client, err := featureflag.NewWithConfig(context.Background(), &featureflag.Config{
Name: "example",
Provider: provider,
})
if err != nil {
panic(err)
}
defer func() {
if err := client.Shutdown(context.Background()); err != nil {
panic(err)
}
}()
details, err := client.BooleanValueDetails(context.Background(), "enable-new-api", false, openfeature.EvaluationContext{})
if err != nil {
panic(err)
}
fmt.Println(details.Value)
}
Output: true
Index ¶
- type Client
- func (c *Client) BooleanValueDetails(ctx context.Context, flag string, defaultValue bool, ...) (openfeature.BooleanEvaluationDetails, error)
- func (c *Client) Name() string
- func (c *Client) Shutdown(ctx context.Context) error
- func (c *Client) Start(context.Context) error
- func (c *Client) StringValueDetails(ctx context.Context, flag string, defaultValue string, ...) (openfeature.StringEvaluationDetails, error)
- type Config
- type Evaluator
Examples ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client is a tracing-aware decorator around an openfeature.Client configured to evaluate flags via Flipt. When a [Config.Tracer] is provided, every evaluation is recorded as a child span with flag key, value, variant, and reason attributes.
Client implements app.Component (Start, Shutdown, Name) for lifecycle integration. Start is a no-op since the provider is initialised eagerly in NewWithConfig.
func New ¶
New creates a Client using the Flipt endpoint from the FEATURE_FLAG_ENDPOINT environment variable with no tracing.
func NewWithConfig ¶
NewWithConfig creates a Client using the supplied Config. A nil cfg is treated identically to an empty Config.
func (*Client) BooleanValueDetails ¶
func (c *Client) BooleanValueDetails( ctx context.Context, flag string, defaultValue bool, evalCtx openfeature.EvaluationContext, options ...openfeature.Option, ) (openfeature.BooleanEvaluationDetails, error)
BooleanValueDetails evaluates a boolean feature flag and returns the full resolution details including variant, reason, and flag metadata. When a tracer is configured, the evaluation is recorded as a child span. defaultValue is returned when the flag cannot be evaluated.
func (*Client) Shutdown ¶
Shutdown cleanly shuts down this client's OpenFeature provider by replacing it with a no-op. This only affects the domain-scoped provider registered under this client's [Name]; other clients in the same process are unaffected.
func (*Client) Start ¶
Start is a no-op. The provider is initialised eagerly in NewWithConfig. This method exists to satisfy the app.Component interface.
func (*Client) StringValueDetails ¶
func (c *Client) StringValueDetails( ctx context.Context, flag string, defaultValue string, evalCtx openfeature.EvaluationContext, options ...openfeature.Option, ) (openfeature.StringEvaluationDetails, error)
StringValueDetails evaluates a string feature flag and returns the full resolution details including variant, reason, and flag metadata. When a tracer is configured, the evaluation is recorded as a child span. defaultValue is returned when the flag cannot be evaluated.
type Config ¶
type Config struct {
// Endpoint is the address of the Flipt server (e.g. "http://flipt:8080").
// When empty, the FEATURE_FLAG_ENDPOINT environment variable is used.
// Not required when Provider is set.
Endpoint string
// Name identifies this client in OpenFeature and in error messages.
// Defaults to "featureflag". Must be unique per process: the OpenFeature
// SDK uses it as a domain key in a process-global provider registry.
Name string
// Namespace is the Flipt namespace for flag lookup and evaluation.
// When empty, uses the Flipt provider's own default (typically "default").
Namespace string
// Tracer is used to create spans for flag evaluations. When nil, tracing
// is disabled.
Tracer *trace.Tracer
// Provider overrides the default Flipt provider. This is primarily
// useful for testing (e.g. with [memprovider.NewInMemoryProvider]).
// When set, Endpoint is ignored.
Provider openfeature.FeatureProvider
// CacheTTL sets how long a flag evaluation result is cached. Zero (default)
// disables caching entirely — no cache is allocated and behaviour is unchanged.
CacheTTL time.Duration
// CacheSize caps the number of entries in the evaluation cache.
// Defaults to 1000 when CacheTTL > 0 and CacheSize is zero.
CacheSize int
// HTTPClient is the HTTP client used by the Flipt provider for all requests.
// When nil, a default client with a 2-second timeout is created. This timeout
// is aggressive to ensure that slow or unresponsive Flipt services fail fast
// and allow graceful fallback to default flag values.
// Override this to configure a different timeout or custom transport:
//
// cfg.HTTPClient = &http.Client{Timeout: 5 * time.Second}
//
// Ignored when Provider is set.
HTTPClient *http.Client
}
Config holds optional configuration for NewWithConfig.
type Evaluator ¶
type Evaluator interface {
BooleanValueDetails(
ctx context.Context, flag string, defaultValue bool,
evalCtx openfeature.EvaluationContext, options ...openfeature.Option,
) (openfeature.BooleanEvaluationDetails, error)
StringValueDetails(
ctx context.Context, flag string, defaultValue string,
evalCtx openfeature.EvaluationContext, options ...openfeature.Option,
) (openfeature.StringEvaluationDetails, error)
}
Evaluator is the interface for feature flag evaluation. It mirrors the OpenFeature client's value-details methods so callers have full access to resolution metadata (variant, reason, error code, flag metadata).
Consumers should program to this interface rather than the concrete Client type to enable test doubles.