defmodule Plausible.Ingestion.Persistor.TelemetryHandler do @moduledoc """ Sets up telemetry for remote calls to persistor via `Finch.Telemetry` events. Emits persistor specific telemetry events for tracking metrics. """ @finch_request_event [:finch, :request, :stop] @finch_connect_event [:finch, :connect, :stop] @finch_send_event [:finch, :send, :stop] @finch_receive_event [:finch, :recv, :stop] @persistor_request_event [:persistor, :remote, :request] @persistor_connect_event [:persistor, :remote, :connect] @persistor_send_event [:persistor, :remote, :send] @persistor_receive_event [:persistor, :remote, :receive] @telemetry_events [ @finch_request_event, @finch_connect_event, @finch_send_event, @finch_receive_event ] @telemetry_handler &__MODULE__.handle_event/4 def request_event(), do: @persistor_request_event def connect_event(), do: @persistor_connect_event def send_event(), do: @persistor_send_event def receive_event(), do: @persistor_receive_event @spec install() :: :ok def install() do if persistor_backend() in [ Plausible.Ingestion.Persistor.Remote, Plausible.Ingestion.Persistor.EmbeddedWithRelay ] do persistor_host = persistor_url() |> URI.parse() |> Map.fetch!(:host) persistor_count = persistor_count() if is_binary(persistor_host) do :ok = :telemetry.attach_many( "persistor-remote-finch-metrics", @telemetry_events, @telemetry_handler, %{remote_host: persistor_host, pool_size: persistor_count} ) else :ok end else :ok end end @spec handle_event([atom()], any(), map(), any()) :: :ok def handle_event( @finch_request_event, %{duration: duration}, %{request: request, result: result}, config ) do if request.host == config.remote_host do result = case result do {:ok, %Finch.Response{status: status}} when status >= 200 and status < 300 -> "ok" {:ok, %Finch.Response{}} -> "bad_status" {:ok, _} -> "stream" {:error, %{reason: :timeout}} -> "timeout_error" {:error, _} -> "unknown_error" end :telemetry.execute( @persistor_request_event, %{duration: duration}, %{result: result, path: normalize_path(request.path)} ) end :ok end def handle_event( @finch_connect_event, %{duration: duration}, %{host: host} = meta, config ) do if host == config.remote_host do :telemetry.execute( @persistor_connect_event, %{duration: duration}, %{status: if(meta[:error], do: "error", else: "ok")} ) end :ok end def handle_event( @finch_send_event, %{duration: duration}, %{request: request} = meta, config ) do if request.host == config.remote_host do :telemetry.execute( @persistor_send_event, %{duration: duration}, %{status: if(meta[:error], do: "error", else: "ok")} ) end :ok end def handle_event( @finch_receive_event, %{duration: duration}, %{request: request} = meta, config ) do if request.host == config.remote_host do status = meta[:status] || 0 :telemetry.execute( @persistor_receive_event, %{duration: duration}, %{ status: if(meta[:error] || status < 200 || status > 299, do: "error", else: "ok") } ) end :ok end defp normalize_path(path) do if path == "/event" do "/event" else "unknown" end end defp persistor_url() do :plausible |> Application.fetch_env!(Plausible.Ingestion.Persistor.Remote) |> Keyword.fetch!(:url) end defp persistor_backend() do :plausible |> Application.fetch_env!(Plausible.Ingestion.Persistor) |> Keyword.fetch!(:backend) end defp persistor_count() do :plausible |> Application.fetch_env!(Plausible.Ingestion.Persistor.Remote) |> Keyword.fetch!(:count) end end