Skip to content

PostHog/posthog-elixir

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

PostHog Elixir Client

Hex.pm Documentation

A powerful Elixir client for PostHog, providing seamless integration with PostHog's analytics and feature flag APIs.

Features

  • Event Capture: Track user actions and custom events
  • Feature Flags: Manage feature flags and multivariate tests
  • Batch Processing: Send multiple events efficiently
  • Custom Properties: Support for user, group, and person properties
  • Flexible Configuration: Customizable JSON library and API version
  • Environment Control: Disable tracking in development/test environments
  • Configurable HTTP Client: Customizable timeouts, retries, and HTTP client implementation

Installation

Add posthog to your list of dependencies in mix.exs:

def deps do
  [
    {:posthog, "~> 1.0"}
  ]
end

You'll also need to include this library under your application tree. You can do so by including :posthog under your :extra_applications key inside mix.exs

# mix.exs
def application do
  [
    extra_applications: [
      # ... your existing applications
      :posthog
    ]
  ]

or if you already have a YourApp.Application application, you can also add Posthog.Application under your supervision tree:

# lib/my_app/application.ex
defmodule MyApp.Application do
  use Application

  def start(_type, _args) do
    children = [
      # Your other children...
      {Posthog.Application, []}
    ]

    opts = [strategy: :one_for_one, name: MyApp.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

Application Customization

This library includes a Posthog.Application because we bundle Cachex to allow you to track inside PostHog your FF usage.

This cache is located under :posthog_feature_flag_cache. If you want more control over the application, you can init it yourself in your own application.ex

# lib/my_app/application.ex

defmodule MyApp.Application do
  use Application

  def start(_type, _args) do
    children = [
      # Your other application children...
      {Posthog.Application, []}
    ]

    opts = [strategy: :one_for_one, name: MyApp.Supervisor]
    Supervisor.start_link(children, opts)
  end
end

Configuration

Add your PostHog configuration to your application's config:

# config/config.exs
config :posthog,
  api_url: "https://us.posthog.com",  # Or `https://eu.posthog.com` or your self-hosted PostHog instance URL
  api_key: "phc_your_project_api_key"

# Optional configurations
config :posthog,
  json_library: Jason,   # Default JSON parser (optional)
  capture_enabled: true, # Whether to enable PostHog tracking (optional, defaults to true)
  http_client: Posthog.HTTPClient.Hackney,  # Default HTTP client (optional)
  http_client_opts: [    # HTTP client options (optional)
    timeout: 5_000,      # Request timeout in milliseconds (default: 5_000)
    retries: 3,          # Number of retries on failure (default: 3)
    retry_delay: 1_000   # Delay between retries in milliseconds (default: 1_000)
  ]

HTTP Client Configuration

The library uses Hackney as the default HTTP client, but you can configure its behavior or even swap it for a different implementation by simply implementing the Posthog.HTTPClient behavior:

# config/config.exs
config :posthog,
  # Use a different HTTP client implementation
  http_client: MyCustomHTTPClient,

  # Configure HTTP client options
  http_client_opts: [
    timeout: 10_000,   # 10 seconds timeout
    retries: 5,        # 5 retries
    retry_delay: 2_000 # 2 seconds between retries
  ]

For testing, you might want to use a mock HTTP client:

# test/support/mocks.ex
defmodule Posthog.HTTPClient.Test do
  @behaviour Posthog.HTTPClient

  def post(url, body, headers, _opts) do
    # Return mock responses for testing
    {:ok, %{status: 200, headers: [], body: %{}}}
  end
end

# config/test.exs
config :posthog,
  http_client: Posthog.HTTPClient.Test

Disabling PostHog capture

You can disable PostHog tracking by setting enabled_capture: false in your configuration. This is particularly useful in development or test environments where you don't want to send actual events to PostHog.

When enabled_capture is set to false:

  • All Posthog.capture/3 and Posthog.batch/2 calls will succeed silently
  • PostHog will still communicate with the server for Feature Flags

This is useful for:

  • Development and test environments where you don't want to pollute your PostHog instance
  • Situations where you need to temporarily disable tracking

Example configuration for development:

# config/dev.exs
config :posthog,
  enabled_capture: false  # Disable tracking in development

Example configuration for test:

# config/test.exs
config :posthog,
  enabled_capture: false  # Disable tracking in test environment

Usage

Capturing Events

Simple event capture:

# Basic event with `event` and `distinct_id`, both required
Posthog.capture("page_view", "user_123")

# Event with properties
Posthog.capture("purchase", "user_123", %{
    product_id: "prod_123",
    price: 99.99,
    currency: "USD"
})

# Event with custom timestamp
Posthog.capture("signup_completed", "user_123", %{}, timestamp: DateTime.utc_now())

# Event with custom UUID
uuid = "..."
Posthog.capture("signup_completed", "user_123", %{}, uuid: uuid)

# Event with custom headers
Posthog.capture(
  "login",
  "user_123",
  %{},
  headers: [{"x-forwarded-for", "127.0.0.1"}]
)

Batch Processing

Send multiple events in a single request:

events = [
  {"page_view", "user_123", %{}},
  {"button_click", "user_123", %{button_id: "signup"}}
]

Posthog.batch(events)

Feature Flags

Get all feature flags for a user:

{:ok, flags} = Posthog.feature_flags("user_123")

# Response format:
# %{
#   "featureFlags" => %{"flag-1" => true, "flag-2" => "variant-b"},
#   "featureFlagPayloads" => %{
#     "flag-1" => true,
#     "flag-2" => %{"color" => "blue", "size" => "large"}
#   }
# }

Check specific feature flag:

# Boolean feature flag
{:ok, flag} = Posthog.feature_flag("new-dashboard", "user_123")
# Returns: %Posthog.FeatureFlag{name: "new-dashboard", payload: true, enabled: true}

# Multivariate feature flag
{:ok, flag} = Posthog.feature_flag("pricing-test", "user_123")
# Returns: %Posthog.FeatureFlag{
#   name: "pricing-test",
#   payload: %{"price" => 99, "period" => "monthly"},
#   enabled: "variant-a"
# }

# Quick boolean check
if Posthog.feature_flag_enabled?("new-dashboard", "user_123") do
  # Show new dashboard
end

Feature flags with group properties:

Posthog.feature_flags("user_123",
  groups: %{company: "company_123"},
  group_properties: %{company: %{industry: "tech"}},
  person_properties: %{email: "[email protected]"}
)

Stop sending $feature_flag_called

We automatically send $feature_flag_called events so that you can properly keep track of which feature flags you're accessing via Posthog.feature_flag() calls. If you wanna save some events, you can disable this by adding send_feature_flag_event: false to the call:

# Boolean feature flag
{:ok, flag} = Posthog.feature_flag("new-dashboard", "user_123", send_feature_flag_event: false)

Local Development

Run bin/setup to install development dependencies or run the following commands manually:

We recommend using asdf to manage Elixir and Erlang versions:

# Install required versions
asdf install

# Install dependencies
mix deps.get
mix compile

Run tests:

bin/test

(This runs mix test).

Format code:

bin/fmt

Troubleshooting

If you encounter WX library issues during Erlang installation:

# Disable WX during installation
export KERL_CONFIGURE_OPTIONS="--without-wx"

To persist this setting, add it to your shell configuration file (~/.bashrc, ~/.zshrc, or ~/.profile).

Examples

There's an example console project in examples/feature_flag_demo that shows how to use the client. Follow the instructions in the README to run it.

Development Tools

This project uses several development tools to maintain code quality and security:

Credo

Credo is a static code analysis tool that helps enforce coding standards and catch potential issues. Run it with:

mix credo --strict

For more detailed output:

mix credo --strict --verbose

Mix Audit

Mix Audit helps identify security vulnerabilities in dependencies. Run it with:

mix deps.audit

Contributing

When contributing to this project, please ensure your code passes all the development tool checks:

  1. Run Credo to ensure code style consistency
  2. Run Mix Audit to check for security vulnerabilities
  3. Run Mix Unused to identify any unused code
  4. Run the test suite with mix test

License

This project is licensed under the MIT License - see the LICENSE file for details.