...

Source file src/github.com/launchdarkly/go-server-sdk/v6/ldclient_events.go

Documentation: github.com/launchdarkly/go-server-sdk/v6

     1  package ldclient
     2  
     3  import (
     4  	"github.com/launchdarkly/go-sdk-common/v3/ldcontext"
     5  	"github.com/launchdarkly/go-sdk-common/v3/ldreason"
     6  	"github.com/launchdarkly/go-sdk-common/v3/ldvalue"
     7  	ldevents "github.com/launchdarkly/go-sdk-events/v2"
     8  	ldeval "github.com/launchdarkly/go-server-sdk-evaluation/v2"
     9  	"github.com/launchdarkly/go-server-sdk/v6/interfaces"
    10  	"github.com/launchdarkly/go-server-sdk/v6/interfaces/flagstate"
    11  	"github.com/launchdarkly/go-server-sdk/v6/ldcomponents"
    12  	"github.com/launchdarkly/go-server-sdk/v6/subsystems"
    13  )
    14  
    15  // This file contains internal support code for LDClient's interactions with the analytics event pipeline.
    16  //
    17  // General implementation notes:
    18  //
    19  // Under normal circumstances, an analytics event is generated whenever 1. a flag is evaluated explicitly
    20  // with a Variation method, 2. a flag is evaluated indirectly as a prerequisite, or 3. the application
    21  // explicitly generates an event by calling Identify or Track. This event is submitted to the configured
    22  // EventProcessor's SendEvent method; the EventProcessor then does any necessary processing and eventually
    23  // delivers the event data to LaunchDarkly, either as a full event or in summary data. The implementation
    24  // of that logic is all in go-sdk-events (since it can be used outside of the SDK, as in ld-relay).
    25  //
    26  // When events are disabled, the EventProcessor is a null implementation that does nothing. It is safe to
    27  // use that object, but LDClient still refrains from doing so if it knows events are disabled so that we
    28  // can avoid a little bit of computational overhead. That's the reason for the IsNullEventProcessorFactory
    29  // method.
    30  
    31  type nullEventProcessorFactoryDescription interface {
    32  	IsNullEventProcessorFactory() bool
    33  }
    34  
    35  func isNullEventProcessorFactory(f subsystems.ComponentConfigurer[ldevents.EventProcessor]) bool {
    36  	if nf, ok := f.(nullEventProcessorFactoryDescription); ok {
    37  		return nf.IsNullEventProcessorFactory()
    38  	}
    39  	return false
    40  }
    41  
    42  func getEventProcessorFactory(config Config) subsystems.ComponentConfigurer[ldevents.EventProcessor] {
    43  	if config.Offline {
    44  		return ldcomponents.NoEvents()
    45  	}
    46  	if config.Events == nil {
    47  		return ldcomponents.SendEvents()
    48  	}
    49  	return config.Events
    50  }
    51  
    52  // This struct is used during evaluations to keep track of the event generation strategy we are using
    53  // (with or without evaluation reasons). It captures all of the relevant state so that we do not need to
    54  // create any more stateful objects, such as closures, to generate events during an evaluation. See
    55  // CONTRIBUTING.md for performance issues with closures.
    56  type eventsScope struct {
    57  	disabled                  bool
    58  	factory                   ldevents.EventFactory
    59  	prerequisiteEventRecorder ldeval.PrerequisiteFlagEventRecorder
    60  }
    61  
    62  func newDisabledEventsScope() eventsScope {
    63  	return eventsScope{disabled: true}
    64  }
    65  
    66  func newEventsScope(client *LDClient, withReasons bool) eventsScope {
    67  	factory := ldevents.NewEventFactory(withReasons, nil)
    68  	return eventsScope{
    69  		factory: factory,
    70  		prerequisiteEventRecorder: func(params ldeval.PrerequisiteFlagEvent) {
    71  			client.eventProcessor.RecordEvaluation(factory.NewEvaluationData(
    72  				ldevents.FlagEventProperties{
    73  					Key:                  params.PrerequisiteFlag.Key,
    74  					Version:              params.PrerequisiteFlag.Version,
    75  					RequireFullEvent:     params.PrerequisiteFlag.TrackEvents,
    76  					DebugEventsUntilDate: params.PrerequisiteFlag.DebugEventsUntilDate,
    77  				},
    78  				ldevents.Context(params.Context),
    79  				params.PrerequisiteResult.Detail,
    80  				params.PrerequisiteResult.IsExperiment,
    81  				ldvalue.Null(),
    82  				params.TargetFlagKey,
    83  			))
    84  		},
    85  	}
    86  }
    87  
    88  // This implementation of interfaces.LDClientInterface delegates all client operations to the
    89  // underlying LDClient, but suppresses the generation of analytics events.
    90  type clientEventsDisabledDecorator struct {
    91  	client *LDClient
    92  	scope  eventsScope
    93  }
    94  
    95  func newClientEventsDisabledDecorator(client *LDClient) interfaces.LDClientInterface {
    96  	return &clientEventsDisabledDecorator{client: client, scope: newDisabledEventsScope()}
    97  }
    98  
    99  func (c *clientEventsDisabledDecorator) BoolVariation(
   100  	key string,
   101  	context ldcontext.Context,
   102  	defaultVal bool,
   103  ) (bool, error) {
   104  	detail, err := c.client.variation(key, context, ldvalue.Bool(defaultVal), true, c.scope)
   105  	return detail.Value.BoolValue(), err
   106  }
   107  
   108  func (c *clientEventsDisabledDecorator) BoolVariationDetail(key string, context ldcontext.Context, defaultVal bool) (
   109  	bool, ldreason.EvaluationDetail, error) {
   110  	detail, err := c.client.variation(key, context, ldvalue.Bool(defaultVal), true, c.scope)
   111  	return detail.Value.BoolValue(), detail, err
   112  }
   113  
   114  func (c *clientEventsDisabledDecorator) IntVariation(
   115  	key string,
   116  	context ldcontext.Context,
   117  	defaultVal int,
   118  ) (int, error) {
   119  	detail, err := c.client.variation(key, context, ldvalue.Int(defaultVal), true, c.scope)
   120  	return detail.Value.IntValue(), err
   121  }
   122  
   123  func (c *clientEventsDisabledDecorator) IntVariationDetail(key string, context ldcontext.Context, defaultVal int) (
   124  	int, ldreason.EvaluationDetail, error) {
   125  	detail, err := c.client.variation(key, context, ldvalue.Int(defaultVal), true, c.scope)
   126  	return detail.Value.IntValue(), detail, err
   127  }
   128  
   129  func (c *clientEventsDisabledDecorator) Float64Variation(key string, context ldcontext.Context, defaultVal float64) (
   130  	float64, error) {
   131  	detail, err := c.client.variation(key, context, ldvalue.Float64(defaultVal), true, c.scope)
   132  	return detail.Value.Float64Value(), err
   133  }
   134  
   135  func (c *clientEventsDisabledDecorator) Float64VariationDetail(
   136  	key string,
   137  	context ldcontext.Context,
   138  	defaultVal float64,
   139  ) (
   140  	float64, ldreason.EvaluationDetail, error) {
   141  	detail, err := c.client.variation(key, context, ldvalue.Float64(defaultVal), true, c.scope)
   142  	return detail.Value.Float64Value(), detail, err
   143  }
   144  
   145  func (c *clientEventsDisabledDecorator) StringVariation(key string, context ldcontext.Context, defaultVal string) (
   146  	string, error) {
   147  	detail, err := c.client.variation(key, context, ldvalue.String(defaultVal), true, c.scope)
   148  	return detail.Value.StringValue(), err
   149  }
   150  
   151  func (c *clientEventsDisabledDecorator) StringVariationDetail(
   152  	key string,
   153  	context ldcontext.Context,
   154  	defaultVal string,
   155  ) (
   156  	string, ldreason.EvaluationDetail, error) {
   157  	detail, err := c.client.variation(key, context, ldvalue.String(defaultVal), true, c.scope)
   158  	return detail.Value.StringValue(), detail, err
   159  }
   160  
   161  func (c *clientEventsDisabledDecorator) JSONVariation(key string, context ldcontext.Context, defaultVal ldvalue.Value) (
   162  	ldvalue.Value, error) {
   163  	detail, err := c.client.variation(key, context, defaultVal, true, c.scope)
   164  	return detail.Value, err
   165  }
   166  
   167  func (c *clientEventsDisabledDecorator) JSONVariationDetail(
   168  	key string,
   169  	context ldcontext.Context,
   170  	defaultVal ldvalue.Value,
   171  ) (
   172  	ldvalue.Value, ldreason.EvaluationDetail, error) {
   173  	detail, err := c.client.variation(key, context, defaultVal, true, c.scope)
   174  	return detail.Value, detail, err
   175  }
   176  
   177  func (c *clientEventsDisabledDecorator) AllFlagsState(
   178  	context ldcontext.Context,
   179  	options ...flagstate.Option,
   180  ) flagstate.AllFlags {
   181  	// Currently AllFlagsState never generates events anyway, so nothing is different here
   182  	return c.client.AllFlagsState(context, options...)
   183  }
   184  
   185  func (c *clientEventsDisabledDecorator) Identify(context ldcontext.Context) error {
   186  	return nil
   187  }
   188  
   189  func (c *clientEventsDisabledDecorator) TrackEvent(eventName string, context ldcontext.Context) error {
   190  	return nil
   191  }
   192  
   193  func (c *clientEventsDisabledDecorator) TrackData(
   194  	eventName string,
   195  	context ldcontext.Context,
   196  	data ldvalue.Value,
   197  ) error {
   198  	return nil
   199  }
   200  
   201  func (c *clientEventsDisabledDecorator) TrackMetric(eventName string, context ldcontext.Context, metricValue float64,
   202  	data ldvalue.Value) error {
   203  	return nil
   204  }
   205  
   206  func (c *clientEventsDisabledDecorator) WithEventsDisabled(disabled bool) interfaces.LDClientInterface {
   207  	if disabled {
   208  		return c
   209  	}
   210  	return c.client
   211  }
   212  

View as plain text