kibana/packages/core/feature-flags
Alejandro Fernández Haro 8020063671
[8.16] [Feature Flags] ECS-compliant logger (#214231) (#214353)
# Backport

This will backport the following commits from `main` to `8.16`:
- [[Feature Flags] ECS-compliant logger
(#214231)](https://github.com/elastic/kibana/pull/214231)

<!--- Backport version: 9.6.6 -->

### Questions ?
Please refer to the [Backport tool
documentation](https://github.com/sorenlouv/backport)

<!--BACKPORT [{"author":{"name":"Alejandro Fernández
Haro","email":"alejandro.haro@elastic.co"},"sourceCommit":{"committedDate":"2025-03-12T23:24:40Z","message":"[Feature
Flags] ECS-compliant logger (#214231)\n\n## Summary\n\nThe OpenFeature
clients receive a logger, but it logs errors like\n`log('something went
wrong', error)`. Our core logger then removes the\n`error.message` as it
prefers the message provided as the first\nargument.\n\nThis PR wraps
the logger to make sure that we handle this type of usage.\n\ncc
@pmuellr as he found out about this bug\n\n\n### Checklist\n\n- [x]
[Unit or
functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere
updated or added to match the most common
scenarios\n\n---------\n\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"98986a86a1086d0047d60a14425920b3c5fbc343","branchLabelMapping":{"^v9.1.0$":"main","^v8.19.0$":"8.x","^v(\\d+).(\\d+).\\d+$":"$1.$2"}},"sourcePullRequest":{"labels":["Team:Core","release_note:skip","v9.0.0","backport:prev-minor","backport:prev-major","v9.1.0"],"title":"[Feature
Flags] ECS-compliant
logger","number":214231,"url":"https://github.com/elastic/kibana/pull/214231","mergeCommit":{"message":"[Feature
Flags] ECS-compliant logger (#214231)\n\n## Summary\n\nThe OpenFeature
clients receive a logger, but it logs errors like\n`log('something went
wrong', error)`. Our core logger then removes the\n`error.message` as it
prefers the message provided as the first\nargument.\n\nThis PR wraps
the logger to make sure that we handle this type of usage.\n\ncc
@pmuellr as he found out about this bug\n\n\n### Checklist\n\n- [x]
[Unit or
functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere
updated or added to match the most common
scenarios\n\n---------\n\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"98986a86a1086d0047d60a14425920b3c5fbc343"}},"sourceBranch":"main","suggestedTargetBranches":[],"targetPullRequestStates":[{"branch":"9.0","label":"v9.0.0","branchLabelMappingKey":"^v(\\d+).(\\d+).\\d+$","isSourceBranch":false,"url":"https://github.com/elastic/kibana/pull/214304","number":214304,"state":"MERGED","mergeCommit":{"sha":"fa04dc0e0536ced502e3eb8993aa7ba227f7299d","message":"[9.0]
[Feature Flags] ECS-compliant logger (#214231) (#214304)\n\n#
Backport\n\nThis will backport the following commits from `main` to
`9.0`:\n- [[Feature Flags] ECS-compliant
logger\n(#214231)](https://github.com/elastic/kibana/pull/214231)\n\n\n\n###
Questions ?\nPlease refer to the [Backport
tool\ndocumentation](https://github.com/sorenlouv/backport)\n\n\n\nCo-authored-by:
Alejandro Fernández Haro
<alejandro.haro@elastic.co>"}},{"branch":"main","label":"v9.1.0","branchLabelMappingKey":"^v9.1.0$","isSourceBranch":true,"state":"MERGED","url":"https://github.com/elastic/kibana/pull/214231","number":214231,"mergeCommit":{"message":"[Feature
Flags] ECS-compliant logger (#214231)\n\n## Summary\n\nThe OpenFeature
clients receive a logger, but it logs errors like\n`log('something went
wrong', error)`. Our core logger then removes the\n`error.message` as it
prefers the message provided as the first\nargument.\n\nThis PR wraps
the logger to make sure that we handle this type of usage.\n\ncc
@pmuellr as he found out about this bug\n\n\n### Checklist\n\n- [x]
[Unit or
functional\ntests](https://www.elastic.co/guide/en/kibana/master/development-tests.html)\nwere
updated or added to match the most common
scenarios\n\n---------\n\nCo-authored-by: kibanamachine
<42973632+kibanamachine@users.noreply.github.com>","sha":"98986a86a1086d0047d60a14425920b3c5fbc343"}}]}]
BACKPORT-->
2025-03-13 13:50:49 +00:00
..
core-feature-flags-browser [8.x] [Feature Flags Service] Hello world 👋 (#188562) (#193519) 2024-09-20 12:21:41 +02:00
core-feature-flags-browser-internal [8.16] [Feature Flags] Remove apm.captureError on slow setup (#212037) (#212056) 2025-02-21 14:32:40 +00:00
core-feature-flags-browser-mocks [8.x] [Feature Flags Service] Hello world 👋 (#188562) (#193519) 2024-09-20 12:21:41 +02:00
core-feature-flags-server [8.x] [Feature Flags Service] Hello world 👋 (#188562) (#193519) 2024-09-20 12:21:41 +02:00
core-feature-flags-server-internal [8.16] [Feature Flags] ECS-compliant logger (#214231) (#214353) 2025-03-13 13:50:49 +00:00
core-feature-flags-server-mocks [8.x] [Feature Flags Service] Hello world 👋 (#188562) (#193519) 2024-09-20 12:21:41 +02:00
README.mdx [8.x] [Feature Flags Service] Hello world 👋 (#188562) (#193519) 2024-09-20 12:21:41 +02:00

---
id: kibFeatureFlagsService
slug: /kibana-dev-docs/tutorials/feature-flags-service
title: Feature Flags service
description: The Feature Flags service provides the necessary APIs to evaluate dynamic feature flags.
date: 2024-07-26
tags: ['kibana', 'dev', 'contributor', 'api docs', 'a/b testing', 'feature flags', 'flags']
---

# Feature Flags Service

The Feature Flags service provides the necessary APIs to evaluate dynamic feature flags.

The service is always enabled, however, it will return the fallback value if a feature flags provider hasn't been attached.
Kibana only registers a provider when running on Elastic Cloud Hosted/Serverless.

For a code example, refer to the [Feature Flags Example plugin](../../../examples/feature_flags_example)

## Registering a feature flag

Kibana follows a _gitops_ approach when managing feature flags. To declare a feature flag, add your flags definitions in
your plugin's `server/index.ts` file:

```typescript
// <plugin>/server/index.ts
import type { FeatureFlagDefinitions } from '@kbn/core-feature-flags-server';
import type { PluginInitializerContext } from '@kbn/core-plugins-server';

export const featureFlags: FeatureFlagDefinitions = [
  {
    key: 'my-cool-feature',
    name: 'My cool feature',
    description: 'Enables the cool feature to auto-hide the navigation bar',
    tags: ['my-plugin', 'my-service', 'ui'],
    variationType: 'boolean',
    variations: [
      {
        name: 'On',
        description: 'Auto-hides the bar',
        value: true,
      },
      {
        name: 'Off',
        description: 'Static always-on',
        value: false,
      },
    ],
  },
  {...},
];

export async function plugin(initializerContext: PluginInitializerContext) {
  const { FeatureFlagsExamplePlugin } = await import('./plugin');
  return new FeatureFlagsExamplePlugin(initializerContext);
}
```

After merging your PR, the CI will create/update the flags in our third-party feature flags provider.

### Deprecation/removal strategy

When your code doesn't use the feature flag anymore, it is recommended to clean up the feature flags when possible.
There are a few considerations to take into account when performing this clean-up:

1. Always deprecate first, remove after
2. When to remove?

#### Always deprecate first, remove after

Just because the CI syncs the state of `main` to our feature flag provider, there is a high probability that the
previous version of the code that still relied on the feature flag is still running out there.

For that reason, the recommendation is to always deprecate before removing the flags. This will keep evaluating the flags,
according to the segmentation rules configured for the flag.

#### When to remove?

After deprecation, we need to consider when it's safe to remove the flag. There are different scenarios that come with
different recommendations:

* The segmentation rules of my flag are set up to return the fallback value 100% of the time: it should be safe to
remove the flag at any time.
* My flag only made it to Serverless (it never made it to Elastic Cloud Hosted): it should be safe to remove the flag
after 2 releases have been rolled out (roughly 2-3 weeks later). This is to ensure that all Serverless projects have 
been upgraded and that we won't need to rollback to the previous version.
* My flag made it to Elastic Cloud Hosted: if we want to remove the flag, we should approach the affected customers to
fix the expected values via [config overrides](#config-overrides).

In general, the recommendation is to check our telemetry to validate the usage of our flags.

## Evaluating feature flags

This service provides 2 ways to evaluate your feature flags, depending on the use case:

1. **Single evaluation**: performs the evaluation once, and doesn't react to updates. These APIs are synchronous in the
browser, and asynchronous in the server.
2. **Observed evaluation**: observes the flag for any changes so that the code can adapt. These APIs return an RxJS observable.

Also, the APIs are typed, so you need to use the appropriate API depending on the `variationType` you defined your flag:

|  Type   | Single evaluation                                       | Observed evaluation                                      |
|:-------:|:--------------------------------------------------------|:---------------------------------------------------------|
| Boolean | `core.featureFlags.getBooleanValue(flagName, fallback)` | `core.featureFlags.getBooleanValue$(flagName, fallback)` |
| String  | `core.featureFlags.getStringValue(flagName, fallback)`  | `core.featureFlags.getStringValue$(flagName, fallback)`  |
| Number  | `core.featureFlags.getNumberValue(flagName, fallback)`  | `core.featureFlags.getNumberValue$(flagName, fallback)`  |

### Request handler context

Additionally, to make things easier in our HTTP handlers, the _Single evaluation_ APIs are available as part of the core
context provided to the handlers:

```typescript
async (context, request, response) => {
  const { featureFlags } = await context.core;
  return response.ok({
    body: {
      number: await featureFlags.getNumberValue('example-number', 1),
    },
  });
}
```

## Extending the evaluation context

The <DocLink id="kibCloudExperimentsPlugin" section="evaluation-context" text="current evaluation context"/> should have
enough information to declare the segmentation rules for your feature flags. However, if your use case requires additional
context, feel free to call the API `core.featureFlags.setContext()` from your plugin.

At the moment, we use 2 levels of context: `kibana` and `organization` that we can use for segmentation purposes at
different levels. By default, the API appends the context to the `kibana` scope. If you need to extend the `organization`
scope, make sure to add `kind: 'organization'` to the object provided to the `setContext` API.

## Config overrides

To help with testing, and to provide an escape hatch in cases where the flag evaluation is not behaving as intended,
the Feature Flags Service provides a way to force the values of a feature flag without attempting to resolve it via the
provider. In the `kibana.yml`, the following config sets the overrides:

```yaml
feature_flags.overrides:
  my-feature-flag: 'my-forced-value'
```

> [!WARNING]  
> There is no validation regarding the variations nor the type of the flags. Use these overrides with caution.

### Dynamic config

When running in our test environments, the overrides can be updated without restarting Kibana via the HTTP `PUT /internal/core/_settings`:

```
PUT /internal/core/_settings
{
  "feature_flags.overrides": {
    "my-feature-flag": "my-forced-value"
  }
}
```