View Source Unleash
Unleash is a client for the
Unleash Toggle Service.
The primary use is Unleash.enabled?/2 or Unleash.enabled?/3 to check whether
or not the given feature flag is enabled.
iex> Unleash.enabled?(:my_feature)
false
iex> Unleash.enabled?(:my_feature, true)
true
iex> context = %{user_id: "ID", custom_field: "Unleash"}
iex> Unleash.enabled?(:my_feature, context)
true
iex> Unleash.enabled?(:my_feature, context, false)
trueContext fields are transformed internally when validated against constraints,
e.g. :user_id becomes "userId".
Installation
If available in Hex, the package can be installed
by adding unleash_ex to your list of dependencies in mix.exs:
def deps do
[
{:unleash, "~> 1.11"}
]
endUnleash works by default with Req, so make sure to explicit declare it in your list of dependencies.
For backward compatibility reasons, Unleash also works with Mojito.
You can use your own HTTP client module by implementing the Unleash.HTTPClientAdapter behaviour and passing the module name in the :http_client configuration.
Configuration
There are many configuration options available, and they are listed below with
their defaults. These go into the relevant config/*.exs file.
config :unleash, Unleash,
url: "", # The URL of the Unleash server to connect to, should include up to http://base.url/api
appname: "", # The app name, used for registration
instance_id: "", # The instance ID, used for metrics tracking
metrics_period: 10 * 60 * 1000, # Send metrics every 10 minutes, in milliseconds
features_period: 15 * 1000, # Poll for new flags every 15 seconds, in milliseconds
strategies: Unleash.Strategies, # Which module to request for toggle strategies
backup_file: nil, # Backup file in the event that contacting the server fails
custom_http_headers: [], # A keyword list of custom headers to send to the server
disable_client: false, # Whether or not to enable the client
disable_metrics: false, # Whether or not to send metrics,
retries: -1 # How many times to retry on failure, -1 disables limit
app_env: :dev # Which environment we're in
http_client: Unleash.ReqClient # What http client will be used. can be Unleash.ReqClient or Unleash.MojitoClient out of the box:custom_http_headers should follow the format prescribed by
t:Unleash.HTTPClientAdapter/0
:strategies should be a module that implements
Unleash.Strategies.strategies/0. See Extensibility
for more information.
Extensibility
If you need to create your own strategies, you can extend the Unleash client
by implementing the callbacks in both Unleash.Strategy and
Unleash.Strategies as well as passing your new Strategies module in as
configuration:
Create your new strategy. See
Unleash.Strategyfor details on the correct API andUnleash.Strategy.Utilsfor helpful utilities.defmodule MyApp.Strategy.Environment do use Unleash.Strategy def enabled?(%{"environments" => environments}, _context) do with {:ok, environment} <- MyApp.Config.get_environment(), environment = List.to_string(environment) do {Utils.in_list?(environments, environment, &String.downcase/1), %{environment: environment, environments: environments}} end end endCreate a new strategies module. See
Unleash.Strategiesfor details on the correct API.defmodule MyApp.Strategies do @behaviour Unleash.Strategies def strategies do [{"environment", MyApp.Strategy.Environment}] ++ Unleash.Strategies.strateges() end endConfigure your application to use your new strategies list.
config :unleash, Unleash, strategies: MyApp.Strategies
Telemetry events
From Unleash 1.9, telemetry events are emitted by the Unleash client
library. You can attach to these events and collect metrics or use the Logger,
for example:
# An example of checking if Unleash server is reachable during the periodic
# features fetch.
:ok =
:telemetry.attach_many(
:duffel_core_feature_heatbeat_metric,
[
[:unleash, :client, :fetch_features, :stop],
[:unleash, :client, :fetch_features, :exception]
],
fn [:unleash, :client, :fetch_features, action],
_measurements,
metadata,
_config ->
require Logger
http_status = metadata[:http_response_status]
if action == :stop and http_status in [200, 304] do
Logger.info("Fetching features are ok")
else
Logger.info("Error on fetching features!!!")
end
end,
%{}
)The following events are emitted by the Unleash library:
[:unleash, :feature, :enabled?, :start]- dispatched byUnleashwhenever a feature state has been requested.- Measurement:
%{system_time: system_time, monotonic_time: monotonic_time} - Metadata:
%{appname: String.t(), instance_id: String.t(), feature: String.t()}
- Measurement:
[:unleash, :feature, :enabled?, :stop]- dispatched byUnleashwhenever a feature check has successfully returned a result.- Measurement:
%{duration: native_time, monotonic_time: monotonic_time} - Metadata:
%{appname: String.t(), instance_id: String.t(), feature: String.t(), result: boolean(), reason: atom(), strategy_evaluations: [{String.t(), boolean()}], feature_enabled: boolean()}
- Measurement:
[:unleash, :feature, :enabled?, :exception]- dispatched byUnleashafter exceptions on fetching a feature's activation state.- Measurement:
%{duration: native_time, monotonic_time: monotonic_time} Metadata:
%{appname: String.t(), instance_id: String.t(), feature: String.t(), kind: :throw | :error | :exit, reason: term(), stacktrace: Exception.stacktrace()}
- Measurement:
[:unleash, :client, :fetch_features, :start]- dispatched byUnleash.Clientwhenever it start to fetch features from a remote Unleash server.- Measurement:
%{system_time: system_time, monotonic_time: monotonic_time} Metadata:
%{appname: String.t(), instance_id: String.t(), etag: String.t() | nil, url: String.t()}
- Measurement:
[:unleash, :client, :fetch_features, :stop]- dispatched byUnleash.Clientwhenever it finishes to fetch features from a remote Unleash server.- Measurement:
%{duration: native_time, monotonic_time: monotonic_time} Metadata:
%{appname: String.t(), instance_id: String.t(), etag: String.t() | nil, url: String.t(), http_response_status: pos_integer | nil, error: struct() | nil}
- Measurement:
[:unleash, :client, :fetch_features, :exception]- dispatched byUnleash.Clientafter exceptions on fetching features.- Measurement:
%{duration: native_time, monotonic_time: monotonic_time} Metadata:
%{appname: String.t(), instance_id: String.t(), etag: String.t() | nil, url: String.t(), kind: :throw | :error | :exit, reason: term(), stacktrace: Exception.stacktrace()}
- Measurement:
[:unleash, :client, :register, :start]- dispatched byUnleash.Clientwhenever it starts to register in an Unleash server.- Measurement:
%{system_time: system_time, monotonic_time: monotonic_time} - Metadata:
%{appname: String.t(), instance_id: String.t(), url: String.t(), sdk_version: String.t(), strategies: [String.t()], interval: pos_integer}
- Measurement:
[:unleash, :client, :register, :stop]- dispatched byUnleash.Clientwhenever it finishes to register in an Unleash server.- Measurement:
%{duration: native_time, monotonic_time: monotonic_time} Metadata:
%{appname: String.t(), instance_id: String.t(), url: String.t(), sdk_version: String.t(), strategies: [String.t()], interval: pos_integer, http_response_status: pos_integer | nil, error: struct() | nil}
- Measurement:
[:unleash, :client, :register, :exception]- dispatched byUnleash.Clientafter exceptions on registering in an Unleash server.- Measurement:
%{duration: native_time, monotonic_time: monotonic_time} Metadata:
%{appname: String.t(), instance_id: String.t(), url: String.t(), sdk_version: String.t(), strategies: [String.t()], interval: pos_integer, kind: :throw | :error | :exit, reason: term(), stacktrace: Exception.stacktrace()}
- Measurement:
[:unleash, :client, :push_metrics, :start]- dispatched byUnleash.Clientwhenever it starts to push metrics to an Unleash server.- Measurement:
%{system_time: system_time, monotonic_time: monotonic_time} - Metadata:
%{appname: String.t(), instance_id: String.t(), url: String.t(), metrics_payload: %{ :bucket => %{:start => String.t(), :stop => String.t(), toggles: %{ String.t() => %{ :yes => pos_integer(), :no => pos_integer() } } } } }
- Measurement:
[:unleash, :client, :push_metrics, :stop]- dispatched byUnleash.Clientwhenever it finishes to push metrics to an Unleash server.- Measurement:
%{duration: native_time, monotonic_time: monotonic_time} Metadata:
%{appname: String.t(), instance_id: String.t(), url: String.t(), http_response_status: pos_integer | nil, error: struct() | nil, metrics_payload: %{ :bucket => %{:start => String.t(), :stop => String.t(), toggles: %{ String.t() => %{ :yes => pos_integer(), :no => pos_integer() } } } } }
- Measurement:
[:unleash, :client, :push_metrics, :exception]- dispatched byUnleash.Clientafter exceptions on pushing metrics to an Unleash server.- Measurement:
%{duration: native_time, monotonic_time: monotonic_time} Metadata:
%{appname: String.t(), instance_id: String.t(), url: String.t(), kind: :throw | :error | :exit, reason: term(), stacktrace: Exception.stacktrace(), metrics_payload: %{ :bucket => %{:start => String.t(), :stop => String.t(), toggles: %{ String.t() => %{ :yes => pos_integer(), :no => pos_integer() } } } } }
- Measurement:
[:unleash, :repo, :schedule]- dispatched byUnleash.Repowhen scheduling a poll to the server for metrics- Metadata:
%{appname: String.t(), instance_id: String.t(), retries: integer(), etag: String.t(), interval: pos_integer()}
- Metadata:
[:unleash, :repo, :backup_file_update]- dispatched byUnleash.Repowhen it writes features to the backup file.- Metadata:
%{appname: String.t(), instance_id: String.t(), content: String.t(), filename: String.t()}
- Metadata:
[:unleash, :repo, :disable_polling]- dispatched byUnleash.Repowhen polling gets disabled due to retries running out or zero retries being specified initially.- Metadata:
%{appname: String.t(), instance_id: String.t(), retries: integer(), etag: String.t()}
- Metadata:
[:unleash, :repo, :features_update]- dispatched byUnleash.Repowhen features are updated.Metadata:
%{appname: String.t(), instance_id: String.t(), retries: integer(), etag: String.t(), source: :remote | :cache | :backup_file}