Impression handler

7 minute readData analytics

CloudBees Feature Management 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
.NET
Python
Java
Android
C
C++
Go
Ruby
Swift
Objective C
PHP
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, 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'); } } });
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'); } } })
var Options = new RoxOptions(new RoxOptions.RoxOptionsBuilder { ImpressionHandler = (sender, e) => { if (e.ReportingValue.Targeting) { Console.WriteLine("flag " + e.ReportingValue.Name + " value is " + e.ReportingValue.Value); if (e.Context != null) { Console.WriteLine("Evaluated using context:" + e.Context.ToString()); } } else { Console.WriteLine("flag " + e.ReportingValue.Name + " has no conditions, or targeting is off. default value " + e.ReportingValue.Value + " was used"); } } } await Rox.Setup(ROLLOUT_KEY, Options);
def handler(reporting): if reporting.targeting is not None: print('flag {} value is {}'.format(reporting.name, reporting.value)) else: print('flag {} has no conditions, or targeting is off. Default value {} was used'.format(reporting.name, reporting.value)) options = RoxOptions(impression_handler=handler) Rox.setup(environment_key, options)
import io.rollout.client.ImpressionHandler; RoxOptions options = new RoxOptions.Builder() .withImpressionHandler(new ImpressionHandler() { @Override public void onImpression(io.rollout.flags.ImpressionEvent event) { if (event.isTargeting()) { System.out.println("Impression: " + event.getName()); } else { System.out.println("No experiment: " + event.getName()); } } }) .build(); Rox.setup(this, options);
import io.rollout.client.ImpressionHandler; RoxOptions options = new RoxOptions.Builder() .withImpressionHandler(new ImpressionHandler() { @Override public void onImpression(io.rollout.flags.ImpressionEvent event) { if (event.isTargeting()) { System.out.println("Impression: " + event.getName()); } else { System.out.println("No experiment: " + event.getName()); } } }) .build(); Rox.setup(this, options);
static void rox_impression_handler_imp(void *target, RoxReportingValue *value, RoxContext *context) { if (value->targeting) { printf("flag %s value is %s", value->name, value->value); } else { printf("flag %s has no conditions, or targeting is off, default value %s was used", value->name, value->value); } } int main(int argc, char **argv) { RoxOptions *options = rox_options_create(); rox_options_set_impression_handler(options, NULL, &rox_impression_handler_imp); rox_setup(DEFAULT_API_KEY, options); rox_shutdown(); }
using namespace Rox; class ImpressionHandler : public ImpressionHandlerInterface { public: explicit ImpressionHandler() { } public: void HandleImpression(ReportingValue *value, Context *context) { if (value->targeting) { printf("flag %s value is %s", value->name , value->value); } else { printf("flag %s targeting is off, default value %s was used", value->name, value->value); } } }; int main(int argc, char **argv) { ImpressionHandler imp = ImpressionHandler(); Rox::Options *options = Rox::OptionsBuilder() .SetImpressionHandler(&imp) .Build(); Rox::Setup(DEFAULT_API_KEY, options); Rox::Shutdown(); }
impressionHandler := func(e model.ImpressionArgs) { if e.ReportingValue != nil { if e.ReportingValue.Targeting { fmt.Println("Impression: ", e.ReportingValue.Name) } else { fmt.Println("targeting off") } } }
impression_handler = proc do |e| if !e.reporting_value.targeting? puts "flag #{e.reporting_value.name} value is #{e.reporting_value.value}" else puts "#{Time.now}: flag #{e.reporting_value.name} has no conditions, or targeting is off. default value #{!!e.reporting_value.value == e.reporting_value.value ? (e.reporting_value.value ? 'true' : 'false') : 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
let options = RoxOptions() options.impressionHandler = {reporting in if (reporting.targeting) { print("flag \(reporting.name!) value is \(reporting.value!), it is part of experiment") } else { print("flag \(reporting.name!) has no conditions, or targeting is off. default value \(reporting.value!) was used") } } ROX.setup(withKey:environmentKey, options: options )
ROXOptions* options = [[ROXOptions alloc] init]; options.impressionHandler = ^(ROXReportingValue * _Nonnull reporting) { if (reporting.targeting) { NSLog(@"flag %@ value is %@, it is part of experiment", reporting.name, reporting.value); } else { NSLog(@"flag %@ has no conditions, or targeting is off. default value %@ was used", reporting.name, reporting.value); } }; [ROX setupWithKey:environmentKey options:options]
use Rox\Server\Rox; use Rox\Server\RoxOptions; use Rox\Server\RoxOptionsBuilder; use Rox\Core\Impression\ImpressionArgs; $roxOptionsBuilder = (new RoxOptionsBuilder()) ->setImpressionHandler(function (ImpressionArgs $args) { if ($args->getReportingValue()->isTargeting()) { echo 'flag ' . $args->getReportingValue()->getName() . ' value is ' . $args->getReportingValue()->getValue(); } else { echo 'flag ' . $args->getReportingValue()->getName() . '. has no conditions, or targeting is off, default value ' . $args->getReportingValue()->getValue() . ' was used'; } }) Rox::setup(ROLLOUT_KEY, new RoxOptions($roxOptionsBuilder));

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; $roxOptionsBuilder = (new RoxOptionsBuilder()) ->setImpressionHandler(function (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));
void rox_impression_handler_imp( void *target, RoxReportingValue *value, RoxExperiment *experiment, RoxContext *context) { if (value != NULL && experiment != NULL) { printf("flag %s value is %s", value->name , value->value); printf(" it is part of %s experiment", experiment->name); } else { printf("no experiment configured for flag %s default value %s was used", value->name, value->value); } } int main(int argc, char **argv) { RoxOptions *options = rox_options_create(); rox_options_set_impression_handler(options, NULL, &rox_impression_handler_imp); rox_setup(DEFAULT_API_KEY, options); rox_shutdown(); }
class ImpressionHandler : public Rox::ImpressionHandlerInterface { public: explicit ImpressionHandler() { } public: void HandleImpression( RoxReportingValue *value, RoxExperiment *experiment, RoxContext *context) { if (value != NULL && experiment != NULL) { printf("flag %s value is %s", value->name , value->value); printf(" it is part of %s experiment", experiment->name); } else { printf("no experiment configured for flag %s default value %s was used", value->name, value->value); } } }; int main(int argc, char **argv) { ImpressionHandler imp = ImpressionHandler(); Rox::Options *options = Rox::OptionsBuilder() .SetImpressionHandler(&imp) .Build(); Rox::Setup(DEFAULT_API_KEY, options); Rox::Shutdown(); }