Impression handler

CloudBees Feature Flags SDK allows you to configure a function to be called when a flag is being evaluated. This function can be used to report the flag state to your analytics (e.g Google Analytics, Mixpanel, Amplitude ,etc) and application performance management system (e.g. New Relic, AppD, DataDog, etc).

On Rox.setup method, you can provide an impressionHandler which is called when the code checks the flag value. The function is called with two arguments on the client side, and if the context was supplied to the flag evaluation, it will also be sent as a third argument to the function.

  • reporting - an object describing the flag name, value, and an indication whether the evaluation was a result of dashboard configuration or just the default value

  • context - the context that was passed to the flag evaluation function

The following code examples show how to add the impressionHandler across the different SDKs:

With SDK version >= 5.0 see examples below:

React Native JavaScript Node.js JavaScript SSR
Rox.setup(environmentKey, {
 impressionHandler: (reporting) => {
    if (reporting.targeting) {
      console.log('flag ' + reporting.name + ' value is ' + reporting.value);
      } else {
      console.log('flag ' + reporting.name + ' has no conditions, or targeting is off. default value ' + reporting.value + ' was used');
      }
 }
})
Rox.setup(environmentKey, {
 impressionHandler: (reporting) => {
    if (reporting.targeting) {
      console.log('flag ' + reporting.name + ' value is ' + reporting.value);
      } else {
      console.log('flag ' + reporting.name + ' has no conditions, or targeting is off. default value ' + reporting.value + ' was used');
      }
 }
})
await Rox.setup(environmentKey, {
  impressionHandler: (reporting, experiment, context) => {
    if (experiment){
      console.log('flag ' + reporting.name + ' for context ' + context + ' value is ' + reporting.value + ', it is part of ' + experiment.name +' experiment which has the following labels:' + experiment.labels.join(','));
    } else {
      console.log('No experiment configured for flag ' + reporting.name + '. default value ' + reporting.value + ' was used');
    }
  }
})
import {Rox} from 'rox-ssr';

await Rox.setup(environmentKey, {
   impressionHandler: (reporting, context) => {
    if (reporting.targeting) {
      console.log('flag ' + reporting.name + ' value is ' + reporting.value + ' evaluated using context: ' + context);
    } else {
      console.log('flag ' + reporting.name + ' has no conditions, or targeting is off. default value ' + reporting.value + ' was used');
    }
  }
})

With SDK version 4.9 see examples below:

Swift Objective-C Android React Native JavaScript Node.js JavaScript SSR JVM .NET Python Go Ruby PHP C C++
let options = RoxOptions()
options.impressionHandler = {reporting, experiment in
  if (experiment != nil){
    print("flag \(reporting.name!) value is \(reporting.value!), it is part of '\(experiment!.name!)' experiment")
    print("experiment labels:\((experiment?.labels ?? Set<String>()).joined(separator: ","))")
  } else {
    print("no experiment configured for flag \(reporting.name!). default value \(reporting.value!) was used")
  }
}
ROX.setup(withKey:environmentKey, options: options  )
ROXOptions* options = [[ROXOptions alloc] init];
options.impressionHandler =^(ROXReportingValue * _Nonnull reporting, ROXExperiment * _Nullable experiment) {
  if (experiment){
    NSLog(@"flag %@ value is %@, it is part of '%@' experiment",reporting.name, reporting.value, experiment.name);
    NSLog(@"experiment labels: %@", [[experiment.labels allObjects] componentsJoinedByString:@","]);
  } else {
    NSLog(@"no experiment configured for flag %@. default value %@ was used", reporting.name, reporting.value);
  }
};
[ROX setupWithKey:environmentKey options:options]
RoxOptions options = new RoxOptions.Builder()
  .withImpressionHandler(new ImpressionHandler() {
    @Override
    public void onImpression(ReportingValue reporting, Experiment experiment) {
      if (experiment != null){
        Log.i(TAG,"flag " + reporting.getName() + " value is " + reporting.getValue() + ", it is part of " + experiment.getName() +" experiment which has the following labels:" + String.Join(",", experiment.getLabels));
      } else {
        Log.i(TAG,"No experiment configured for flag " + reporting.getName() + ". default value " + reporting.getValue() + " was used");
      }
    }
  })
  .build();

Rox.setup(context, options);
Rox.setup(environmentKey, {
  impressionHandler: (reporting, experiment) => {
    if (experiment){
      console.log('flag ' + reporting.name + ' value is ' + reporting.value + ', it is part of ' + experiment.name +' experiment which has the following labels:' + experiment.labels.join(','));
    } else {
      console.log('No experiment configured for flag ' + reporting.name + '. default value ' + reporting.value + ' was used');
    }
  }
})
Rox.setup(environmentKey, {
  impressionHandler: (reporting, experiment) => {
    if (experiment){
      console.log('flag ' + reporting.name + ' value is ' + reporting.value + ', it is part of ' + experiment.name +' experiment');
    } else {
      console.log('No experiment configured for flag ' + reporting.name + '. default value ' + reporting.value + ' was used');
    }
  }
});
await Rox.setup(environmentKey, {
  impressionHandler: (reporting, experiment, context) => {
    if (experiment){
      console.log('flag ' + reporting.name + ' for context ' + context + ' value is ' + reporting.value + ', it is part of ' + experiment.name +' experiment which has the following labels:' + experiment.labels.join(','));
    } else {
      console.log('No experiment configured for flag ' + reporting.name + '. default value ' + reporting.value + ' was used');
    }
  }
})
import {Rox} from 'rox-ssr';

await Rox.setup(environmentKey, {
  impressionHandler: (reporting, experiment, context) => {
    if (experiment){
      console.log('flag ' + reporting.name + ' for context ' + context + ' value is ' + reporting.value + ', it is part of ' + experiment.name +' experiment which has the following labels:' + experiment.labels.join(','));
    } else {
      console.log('No experiment configured for flag ' + reporting.name + '. default value ' + reporting.value + ' was used');
    }
  }
});
RoxOptions options = new RoxOptions.Builder()
  .withImpressionHandler(new ImpressionHandler() {
    @Override
    public void onImpression(ReportingValue reporting, Experiment experiment) {
        if (experiment != null){
        System.out.println("flag " + reporting.getName() + " value is " + reporting.getValue() + ", it is part of " + experiment.getName() +" experiment which has the following labels:" + String.join(",", experiment.getLabels()));
      } else {
        System.out.println("No experiment configured for flag " + reporting.getName() + ". default value " + reporting.getValue() + " was used");
      }
    }
  })
  .build();

Rox.setup(ROLLOUT_KEY, options).get();
var Options = new RoxOptions(new RoxOptions.RoxOptionsBuilder
{
    ImpressionHandler = (sender, e) =>
    {
        if (e.Experiment != null)
        {
            Console.WriteLine("flag " + e.ReportingValue.Name + " value is " + e.ReportingValue.Value + ", it is part of " + e.Experiment.Name +" experiment which has the following labels:" + string.Join(",", e.Experiment.Labels));
        }
        else
        {
            Console.WriteLine("No experiment configured for flag " + e.ReportingValue.Name + ". default value " + e.ReportingValue.Value + " was used");
        }
    }
}

await Rox.Setup(ROLLOUT_KEY, Options);
def on_impression(e):
    if e.experiment is not None :
        print('flag %s value is %s, it is part of "%s" experiment, that has those labels: %s' % (e.reporting_value.name, e.reporting_value.value, e.experiment.name, ','.join(e.experiment.labels)))
    else :
        print('no experiment configured for flag %s. default value %s was used' % (e.reporting_value.name, e.reporting_value.value))

options = RoxOptions(
  impression_handler=on_impression
)

Rox.setup(<ROLLOUT_KEY>, options).result()
options := server.NewRoxOptions(server.RoxOptionsBuilder {
    ImpressionHandler: func(e model.ImpressionArgs) {
        if e.Experiment != nil {
            fmt.Println("flag", e.ReportingValue.Name, "value is", e.ReportingValue.Value, ", it is part of", e.Experiment.Name, "experiment, which has the following labels:", strings.Join(e.Experiment.Labe    ls, ","))
        } else {
            fmt.Println("No experiment configured for flag ", e.ReportingValue.Name, ". default value ", e.ReportingValue.Value, " was used")
        }
    },
})

<-rox.Setup(ROLLOUT_KEY, options);
impression_handler = proc do

e

if !e.experiment.nil? puts "flag #{e.reporting_value.name} value is #{e.reporting_value.value}, it is part of #{e.experiment.name} experiment, that has those labels: #{e.experiment.labels.join(',')}" else puts "no experiment configured for flag #{e.reporting_value.name}. default value #{e.reporting_value.value} was used" end end

options = Rox::Server::RoxOptions.new( impression_handler: impression_handler )

Rox::Server::RoxServer.setup(<ROLLOUT_KEY>, option).join ----

use Rox\Server\Rox;
use Rox\Server\RoxOptions;
use Rox\Server\RoxOptionsBuilder;
use Rox\Core\Impression\ImpressionArgs;
use Rox\Core\Impression\ImpressionInvokerInterface;

$roxOptionsBuilder = (new RoxOptionsBuilder())
    ->setImpressionHandler(function (ImpressionInvokerInterface $sender, ImpressionArgs $args) {
        if ($args != null && $args->getExperiment() != null) {
            echo 'flag ' . $args->getReportingValue()->getName() . ' value is ' . $args->getReportingValue()->getValue();
            echo ' it is part of ' . $args->getExperiment()->getName() . ' experiment that has those labels: ' . implode(', ', $args->getExperiment()->getLabels());
        } else {
            echo 'no experiment configured for flag ' . $args->getReportingValue()->getName() . '. default value ' . $args->getReportingValue()->getValue() . ' was used';
        }
    })

Rox::setup(ROLLOUT_KEY, new RoxOptions($roxOptionsBuilder));