Skip to content

Logging, Metrics and Feature Flagging in Elixir. Brought to you by Zappi.

License

Notifications You must be signed in to change notification settings

Intellection/zexbox

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

156 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Zexbox

Hex.pm CI Documentation

Installation

def deps do
  [
    {:zexbox, "~> 1.4.1"}
  ]
end

LaunchDarkly Feature Flags

The Zexbox library provides an idiomatic Elixir wrapper around the LaunchDarkly Erlang SDK with support for contexts, multi-contexts, and all modern LaunchDarkly features.

Configuration

Configuration is fairly simple, with the only required piece of configuration being the sdk_key. For production environments we recommend also including :email as a private attribute:

config :zexbox, :flags,
  sdk_key: System.fetch_env!("LAUNCH_DARKLY_SDK_KEY"),
  private_attributes: [:email]

For local development and testing you'll probably want to read flags from a local file and ensure there is no interaction with the LaunchDarkly API. While the two configurations will be very similar you should have different ones to put to different files. You're configurations will look something like this following:

config :zexbox, :flags,
  sdk_key: "dev-launch-darkly-key",
  file_datasource: true,
  send_events: false,
  file_auto_update: true,
  file_poll_interval: 1000,
  feature_store: :ldclient_storage_map,
  file_paths: ["flags.json"]

Implementing

In order to use feature flags you need to start the LaunchDarkly client. You should do this in the start/2 function of your Application module:

def start(_type, _args) do
  Zexbox.Flags.start()
  ...
end

This will initialise a LauncDarkly client with the :default tag and using the configuration you've defined in your app.

If you wish to use a different tag you can make use of Zexbox.Flags.start/1

Zexbox.Flags.start(:my_tag)

Additionally, if you don't want to make use of the application config you can use Zexbox.Flags.start/2

Zexbox.Flags.start(
  %{
    sdk_key: "my-sdk-key", # This key is required
    private_attributes: [:email]
  },
  :my_tag
)

You can then shut the :default client down when your app does in the Zexbox.Flags.stop/0 function of your Application module:

def stop(_type, _args) do
  Zexbox.Flags.stop()
end

Stopping a client with a custom tag can be done using the Zexbox.Flags.stop/1 function.

Using Contexts

Contexts are the recommended way to evaluate feature flags. They provide type safety and support for multi-entity targeting:

alias Zexbox.Flags.Context

# Simple user context
context = Context.new("user-123")
Zexbox.Flags.variation("my-flag", context, false)

# Context with attributes
context =
  Context.new("user-123")
  |> Context.set("email", "user@example.com")
  |> Context.set("plan", "enterprise")
  |> Context.set_private_attributes(["email"])

Zexbox.Flags.variation("premium-feature", context, false)

# Multi-context (target based on user AND organization)
user = Context.new("user-123", "user")
org = Context.new("org-456", "organization")
multi = Context.new_multi([user, org])

Zexbox.Flags.variation("enterprise-feature", multi, false)

Backward Compatibility: Raw maps are still supported:

Zexbox.Flags.variation(
  "my-flag",
  %{key: "user-hash", email: "user@email.com"},
  "my_default_value"
)

For more details, see the Context Module Guide.

Logging

Default logging can be attached to your controllers by calling Zexbox.Logging.attach_controller_logs! in the start/2 function of your Application module:

def start(_type, _args) do
  Zexbox.Logging.attach_controller_logs!()
  ...
end

This sets up handlers for the [:phoenix, :endpoint, :start] and [:phoenix, :endpoint, :stop] events which are dispatched by Plug.Telemetry at the beginning and end of each request. The handlers are named phoenix_controller_logs_stop and phoenix_controller_logs_start respectively. The handlers log structured data (reports) in the following form [info] [event: <event_params>, measurements: <measurement_data>, metadata: <metadat>, config: <config>].

Adding your own logs

Adding your own logs is as simple as calling the Zexbox.Telementry.attach/4 (which is just a wrapper around :telemetry.attach/4)

Zexbox.Telemetry.attach(:my_event, [:my, :event], &MyAppHandler.my_handler/3, nil)

Metrics

In order to setup metrics with InfluxDB you'll need to add the following configuration:

config :zexbox, Zexbox.Metrics.Connection,
  auth: [
    method: :token,
    token: "token"
  ],
  host: "localhost",
  port: "8086",
  version: :v2,
  org: "zappi",
  bucket: "my_app"

A more indepth explanation on the configuration can be found in the Instream.Connection hexdocs.

In order to make use of metrics you'll need to add the Zexbox module to your application's Supervisor tree

defmodule MyApp.Application do
  use Application

  @impl Application
  def start(_type, args) do
    ...
    children = [
      ...
      {Zexbox.Metrics, []}
    ]
    ...
    Supervisor.start_link(children, opts)
  end
end

This will write metrics to InfluxDB after every Phoenix DB request. The structure of the metrics is defined by the Zexbox.Metrics.ControllerSeries module.

Adding Custom Controller Metrics

You can easily add your own controller metrics using the Zexbox.Metrics.Client module

metric = %Zexbox.Metrics.Series{
  measurement: "my_measurement",
  fields: %{
    "my_field" => 1
  },
  tags: %{
    "my_tag" => "my_value"
  }
}
Zexbox.Metrics.Client.write_metric(metric)

Disabling Metrics For a Single Request/Process

If you want to suppress metrics for a specific request (for example: test traffic, synthetic checks, or health probes), disable metrics for the current process:

Zexbox.Metrics.disable_for_process()

All metric writes from that process will be skipped (including the default controller metrics and any custom calls to Zexbox.Metrics.Client.write_metric/1). If you spawn tasks using Task.async/1, the disabled state is also respected in the spawned task via the caller chain.

To re-enable metrics for the current process:

Zexbox.Metrics.enable_for_process()

Copyright and License

Copyright (c) 2024, Zappistore.

Zexbox source code is licensed under the MIT License.

About

Logging, Metrics and Feature Flagging in Elixir. Brought to you by Zappi.

Topics

Resources

License

Stars

Watchers

Forks

Packages

No packages published

Contributors 4

  •  
  •  
  •  
  •  

Languages