Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(feature) Integration with Datadog RUM #1877

Open
thomaspoignant opened this issue May 13, 2024 · 14 comments
Open

(feature) Integration with Datadog RUM #1877

thomaspoignant opened this issue May 13, 2024 · 14 comments
Labels
enhancement New feature or request good first issue Good for newcomers p3 Longer term priority

Comments

@thomaspoignant
Copy link
Owner

Requirements

Datadog is now supporting RUM integration for feature flags (https://docs.datadoghq.com/real_user_monitoring/guide/setup-feature-flag-data-collection/?tab=browser).

It will be great to be able to integrate GO Feature Flag with Datadog.
This will probably be through an OpenFeature Hook.

@thomaspoignant thomaspoignant added enhancement New feature or request good first issue Good for newcomers p3 Longer term priority labels May 13, 2024
@thomaspoignant thomaspoignant removed their assignment May 14, 2024
@mbezhanov
Copy link
Contributor

Hello there!

To better understand, are you looking into adding this as an extra initialization option in the JavaScript / TypeScript client provider, or did you have something else in mind?

e.g. something like:

const goFeatureFlagWebProvider = new GoFeatureFlagWebProvider({
  endpoint: endpoint,
  listener: (key, value) => {
     datadogRum.addFeatureFlagEvaluation(key, value);
  },
}, logger);

@thomaspoignant
Copy link
Owner Author

Hello @mbezhanov,
Yes, something like that could work.
I haven't looked at it in detail yet, but this sounds like an elegant solution.

@mbezhanov
Copy link
Contributor

I'll give it a shot.

@mbezhanov
Copy link
Contributor

/assign-me

Copy link
Contributor

👋 Hey @mbezhanov, thanks for your interest in this issue! 🎉

⚠ Note that this issue will become unassigned if it isn't closed within 10 days.

🔧 A maintainer can also add the 📌 Pinned label to prevent it from being unassigned automatically.

@mbezhanov
Copy link
Contributor

mbezhanov commented Jun 20, 2024

Just want to make sure I'm on the right track here. Consider the following potential changes made to the go-feature-flag-web-provider.ts file in the open-telemetry/js-sdk-contrib repo: mbezhanov/js-sdk-contrib@9308bff

This allows us to initialize the provider in the following way:

const goFeatureFlagWebProvider = new GoFeatureFlagWebProvider({
    endpoint,
    listener: (key, value) => {
      datadogRum.addFeatureFlagEvaluation(key, value)
    }
  }, logger);

Then, if we assume we have the following flags defined in the relay proxy:

some-boolean-flag:
  variations:
    A: true
    B: false
  targeting:
    - query: ff eq true
      percentage:
        A: 50
        B: 50
  defaultRule:
    variation: A
some-numeric-flag:
  variations:
    A: 3
    B: 5
    C: 7
  targeting:
    - query: ff eq true
      percentage:
        A: 30
        B: 30
        C: 40
  defaultRule:
    variation: A
some-string-flag:
  variations:
    A: "foo"
    B: "bar"
    C: "baz"
  targeting:
    - query: ff eq true
      percentage:
        A: 30
        B: 30
        C: 40
  defaultRule:
    variation: A

And we perform similar evaluations in our code:

if (client.getBooleanValue('some-boolean-flag', false)) {
  // ...
}

if (client.getStringValue('some-string-flag', 'qux')) {
  // ...
}

if (client.getNumberValue('some-numeric-flag', 9))) {
  // ...
}

The following information gets displayed in Datadog:

image

Is that sufficient?

@thomaspoignant
Copy link
Owner Author

That looks great regarding results, but I was wondering if we should not leverage hooks for that.

Having a built-in provider hook can probably make it work.
But in the same way I don't want to tight the provider to a Datadog dependency. So I am not 100% sure of the best solution here 🤔

@mbezhanov
Copy link
Contributor

To clarify this a little bit, there is no direct dependency on @datadog/browser-rum in @openfeature/go-feature-flag-web-provider itself.

So in a JS app, the entire code goes somewhat like this:

import {datadogRum} from "@datadog/browser-rum";
import {GoFeatureFlagWebProvider} from "@openfeature/go-feature-flag-web-provider";
import {OpenFeature} from "@openfeature/web-sdk";

// init the RUM browser SDK
datadogRum.init({
  // . . .
  enableExperimentalFeatures: ['feature_flags'],
});

// . . .

// init  the GO Feature Flag web provider
const goFeatureFlagWebProvider = new GoFeatureFlagWebProvider({
  // . . .
  listener: (key, value) => {
    datadogRum.addFeatureFlagEvaluation(key, value)
  }
}, logger);

// . . .

// init OpenFeature client
await OpenFeature.setContext(ctx);
OpenFeature.setProvider(goFeatureFlagWebProvider);
const client = OpenFeature.getClient();

// . . .

// evaluate some feature flag
if (client.getBooleanValue('some-boolean-flag', false)) {
  // . . .
}

This mirrors some of the other listeners listed on the Datadog integrations page in the documentation.

listener can effectively be anything:

const goFeatureFlagWebProvider = new GoFeatureFlagWebProvider({
  endpoint: endpoint,
  listener: (key, value) => {
    console.log(key, value)
  }
}, logger);

The way I understand it, with hooks we would define a new Hook class somewhere in our application:

import {EvaluationDetails, FlagValue, Hook, HookContext} from '@openfeature/web-sdk';
import {RumPublicApi} from "@datadog/browser-rum-core";

export class DatadogHook implements Hook {
  private _datadogRum;
  
  constructor(datadogRum: RumPublicApi ) {
    this._datadogRum = datadogRum;
  }

  after(hookContext: HookContext, evaluationDetails: EvaluationDetails<FlagValue>) {
    this._datadogRum.addFeatureFlagEvaluation(evaluationDetails.flagKey, evaluationDetails.value)
  }
}

And then we'll instruct the OpenFeature SDK to use that hook as follows:

import {datadogRum} from "@datadog/browser-rum";
import {DatadogHook} from "./datadog-hook";
import {GoFeatureFlagWebProvider} from "@openfeature/go-feature-flag-web-provider";
import {OpenFeature} from "@openfeature/web-sdk";

// init the RUM browser SDK
datadogRum.init({
  // . . .
  enableExperimentalFeatures: ['feature_flags'],
});

// . . .

// init  the GO Feature Flag web provider
const goFeatureFlagWebProvider = new GoFeatureFlagWebProvider({
  // . . .
}, logger);

// . . .

// init OpenFeature client and add hook
await OpenFeature.setContext(ctx);
OpenFeature.setProvider(goFeatureFlagWebProvider);
const client = OpenFeature.getClient();
client.addHooks(new DatadogHook(datadogRum))

// . . .

// evaluate some feature flag
if (client.getBooleanValue('some-boolean-flag', false)) {
  // . . .
}

In that case, it doesn't seem that any changes are necessary in the @openfeature/go-feature-flag-web-provider package itself.

Am I understanding this correctly, or did you have something else in mind?

@mbezhanov
Copy link
Contributor

I'll unassign myself for now, but I'll be ready to take over again whenever you need me 🙂

@mbezhanov mbezhanov removed their assignment Jul 2, 2024
Copy link
Contributor

This issue has been automatically unassigned due to inactivity.

@github-actions github-actions bot added the Stale When an issue is open for too long label Oct 15, 2024
@github-actions github-actions bot removed 📍 Assigned Stale When an issue is open for too long labels Oct 25, 2024
@EvansMutwiri
Copy link

Can I try solving this issue?

@mbezhanov
Copy link
Contributor

@EvansMutwiri I am not actively working on this atm, so no problem on my end 🙂

@LuisDi98
Copy link

Understanding the issue, we need to integrate GO Feature Flag with Datadog's RUM feature flag evaluations. So the goal is to track feature flag evaluations in Datadog by connecting Datadog's RUM with GO Feature Flag using OpenFeature hooks. As I can see, this integration should allow evaluations of flags (boolean, numeric, string) defined in the relay proxy to be captured and displayed in Datadog without a direct dependency on Datadog within the GO Feature Flag web provider.

What about implementing a custom OpenFeature Hook to manage this integration? So we can drive it without modifying the GO Feature Flag web provider, enhancing the modularity and avoiding a hard dependency on Datadog.

If it doesn't sound good, alternatively, we can implement a listener within the GoFeatureFlagWebProvider that would send feature flag evaluations to Datadog.

Here I created a table so we can decide better between listener vs hook solutions, personally I would think on the hook because of SOLID design principles

Feature Listener Hook
Coupling Tightly coupled to the provider Loosely coupled via OpenFeature client
Modularity Limited; embedded in the provider High; implemented as a separate class
Flexibility Less flexible; harder to extend to other systems Highly flexible; reusable across integrations
Ease of Setup Easier; straightforward configuration More setup needed; requires extra hook code
Use Case Suitability Simple integrations or single-monitoring tool Complex projects or multiple monitoring tools

Also I am part of Dojo Coding community looking to contribute to new open source projects, it would be nice to work with go-feature-flag project.

@teskpicoin
Copy link

I’d love to work on this task.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
enhancement New feature or request good first issue Good for newcomers p3 Longer term priority
Projects
None yet
Development

No branches or pull requests

5 participants