featureflag

package
v2.0.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Apr 20, 2026 License: MIT Imports: 13 Imported by: 0

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

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

func New(ctx context.Context) (*Client, error)

New creates a Client using the Flipt endpoint from the FEATURE_FLAG_ENDPOINT environment variable with no tracing.

func NewWithConfig

func NewWithConfig(ctx context.Context, cfg *Config) (*Client, error)

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) Name

func (c *Client) Name() string

Name returns the component name for use in logs and error messages.

func (*Client) Shutdown

func (c *Client) Shutdown(ctx context.Context) error

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

func (c *Client) Start(context.Context) error

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL