Current section
Files
Jump to
Current section
Files
lib/logflare_ex/client.ex
defmodule WarehouseEx.Client do
@moduledoc """
A `WarehouseEx.Client` contains all configuration used for making API requests, whether batched or not.
### Application-level Configuration
Application-wide configuration can be set in `config.exs`:
```elixir
config :warehouse_ex,
api_key: "...",
source_token: "..."
```
### Runtime Configuration
All configuration options can be overridden at runtime. This is through the use of the `WarehouseEx.Client` struct.
To create a new client with a custom configuration, use `WarehouseEx.client/1`:
```elixir
# To create a client from the application-level configuration.
iex> default_client = WarehouseEx.client()
%WarehouseEx.Client{...}
# To create a client with runtime overrides
iex> client = WarehouseEx.client(source_token: "...")
%WarehouseEx.Client{...}
# use the runtime client
iex> WarehouseEx.send_batched_event(client, %{...})
:ok
```
### Options
For every configuration, either `:source_token` or `:source_name` must be provided.
- `:api_key`: **Required**. Public API key.
- `:api_url`: Custom server endpoint, for self-hosting. Defaults to `https//api.logflare.app`.
- `:source_token`: Source UUID. Mutually exclusive with `:source_name`
- `:source_name`: Source name. Mutually exclusive with `:source_token`
- `:on_error`: mfa callback for handling API errors. Must be 1 arity.
- `:on_prepare_payload`: mfa callback or anonymous function for preparing the final payload before sending to API. Must be 1 arity.
- `:auto_flush`: Used for batching. Enables automatic flushing. If disabled, `WarehouseEx.flush/1` must be called.
- `:flush_interval`: Used for batching. Flushes cached events at the provided interval.
- `:batch_size`: Used for batching. It is the maximum number of events send per API request.
"""
@default_tesla_adapter {Tesla.Adapter.Finch, name: WarehouseEx.Finch, receive_timeout: 30_000}
@default_batch_size 250
@default_flush_interval 1_500
use TypedStruct
typedstruct do
@typedoc "Logflare client"
field(:tesla_client, Tesla.Client.t(), enforce: true)
field(:api_key, String.t(), enforce: true)
field(:api_url, String.t(), default: "https://api.logflare.app")
field(:source_token, String.t())
field(:source_name, String.t())
field(:on_error, list() | mfa(), default: nil)
field(:on_prepare_payload, list() | mfa(), default: nil)
# batching
field(:auto_flush, :boolean, default: true)
field(:flush_interval, non_neg_integer(), default: @default_flush_interval)
field(:batch_size, non_neg_integer(), default: @default_batch_size)
end
@typep opts :: [api_key: String.t(), api_url: String.t(), tesla_client: Tesla.Client.t()]
@spec new(opts) :: t()
def new(opts \\ []) do
opts =
Enum.into(opts, %{
api_url: get_config_value(:api_url) || "https://api.logflare.app",
api_key: get_config_value(:api_key),
adapter: get_config_value(:adapter) || @default_tesla_adapter,
source_token: get_config_value(:source_token),
source_name: get_config_value(:source_name),
tesla_client: nil,
on_error: get_config_value(:on_error),
on_prepare_payload: get_config_value(:on_prepare_payload),
flush_interval: get_config_value(:flush_interval) || @default_flush_interval,
batch_size: get_config_value(:batch_size) || @default_batch_size
})
tesla_client =
make_tesla_client(
opts.api_url,
opts.api_key,
opts.adapter
)
struct(__MODULE__, %{opts | tesla_client: tesla_client})
end
defp make_tesla_client(
api_url,
api_key,
adapter
) do
middlewares = [
Tesla.Middleware.FollowRedirects,
{Tesla.Middleware.Headers,
[
{"x-api-key", api_key},
{"content-type", "application/bert"}
]},
{Tesla.Middleware.BaseUrl, api_url},
{Tesla.Middleware.Compression, format: "gzip"}
]
Tesla.client(middlewares, adapter)
end
def get_config_value(key) do
Application.get_env(:warehouse_ex, key)
end
def validate_client(%__MODULE__{source_name: nil, source_token: nil}),
do: {:error, :invalid_config}
def validate_client(%__MODULE__{api_key: nil}), do: {:error, :invalid_config}
def validate_client(%__MODULE__{flush_interval: i}) when i < 0, do: {:error, :invalid_config}
def validate_client(%__MODULE__{batch_size: i}) when i < 0, do: {:error, :invalid_config}
def validate_client(%__MODULE__{}), do: :ok
def validate_client(_), do: {:error, :invalid_config}
end