tutorial

How to Monitor Tesla with Vigilmon

Tesla is Elixir's flexible HTTP client with composable middleware — but HTTP clients are invisible failure points. Here's how to monitor Tesla-powered integrations end-to-end with Vigilmon.

How to Monitor Tesla with Vigilmon

Tesla is Elixir's composable HTTP client library. Its middleware stack — retry, logging, caching, OAuth — makes it the go-to choice for building integrations in Phoenix and Umbrella apps. But HTTP clients are silent failure points: if an external API your Tesla client depends on goes down, your app may degrade silently with no immediate error surfaced to users.

This tutorial wires up monitoring for Tesla-dependent services:

  • A health endpoint that exercises your Tesla adapters
  • HTTP uptime monitoring with Vigilmon
  • Heartbeat monitors to catch silent adapter failures
  • Telemetry integration for request latency tracking
  • Alerts when upstream APIs degrade

Why Monitor Tesla?

| Signal | What it catches | |---|---| | Adapter availability | Finch/Hackney/Mint adapter startup failures | | Upstream API health | Third-party API downtime your app depends on | | Retry exhaustion | Middleware retry storms that mask upstream failures | | Request latency | P95 spikes from slow external services | | SSL/TLS errors | Certificate issues on upstream endpoints |

A standard uptime check only confirms your Phoenix app responds — it won't catch a Tesla client that can't reach a payment gateway or authentication service.


Step 1: Add a Tesla Health Check Endpoint

Create a dedicated health check that actually exercises your Tesla clients:

# lib/my_app/health/tesla_health.ex
defmodule MyApp.Health.TeslaHealth do
  @moduledoc """
  Exercises configured Tesla clients to verify upstream connectivity.
  """

  def check_all do
    checks = [
      {"payments_api", &check_payments_api/0},
      {"auth_service", &check_auth_service/0}
    ]

    results = Enum.map(checks, fn {name, check_fn} ->
      case check_fn.() do
        :ok -> {name, :ok}
        {:error, reason} -> {name, {:error, reason}}
      end
    end)

    failed = Enum.filter(results, fn {_, status} -> status != :ok end)

    if Enum.empty?(failed) do
      {:ok, %{status: "ok", checks: length(checks)}}
    else
      {:error, %{status: "degraded", failed: Enum.map(failed, &elem(&1, 0))}}
    end
  end

  defp check_payments_api do
    case MyApp.PaymentsClient.health_check() do
      {:ok, _} -> :ok
      error -> {:error, inspect(error)}
    end
  end

  defp check_auth_service do
    case MyApp.AuthClient.ping() do
      {:ok, _} -> :ok
      error -> {:error, inspect(error)}
    end
  end
end

Wire it into a Phoenix controller:

# lib/my_app_web/controllers/health_controller.ex
defmodule MyAppWeb.HealthController do
  use MyAppWeb, :controller

  def tesla(conn, _params) do
    case MyApp.Health.TeslaHealth.check_all() do
      {:ok, result} ->
        json(conn, result)
      {:error, result} ->
        conn
        |> put_status(503)
        |> json(result)
    end
  end
end

Add the route:

# lib/my_app_web/router.ex
scope "/health", MyAppWeb do
  get "/tesla", HealthController, :tesla
end

Step 2: Set Up a Tesla Client with Telemetry Middleware

Tesla ships with a telemetry middleware you can attach to track request durations:

# lib/my_app/clients/payments_client.ex
defmodule MyApp.PaymentsClient do
  use Tesla

  plug Tesla.Middleware.BaseUrl, Application.compile_env(:my_app, :payments_api_url)
  plug Tesla.Middleware.JSON
  plug Tesla.Middleware.Telemetry
  plug Tesla.Middleware.Retry,
    delay: 500,
    max_retries: 3,
    max_delay: 4_000,
    should_retry: fn
      {:ok, %{status: status}} when status in [429, 500, 502, 503] -> true
      {:ok, _} -> false
      {:error, _} -> true
    end
  plug Tesla.Middleware.Logger

  adapter Tesla.Adapter.Finch, name: MyApp.Finch

  def health_check do
    get("/health")
  end
end

Attach a telemetry handler to capture latency:

# lib/my_app/telemetry.ex
defmodule MyApp.Telemetry do
  def setup do
    :telemetry.attach(
      "tesla-request-stop",
      [:tesla, :request, :stop],
      &handle_request_stop/4,
      nil
    )
  end

  def handle_request_stop(_event, %{duration: duration}, metadata, _config) do
    url = metadata[:env] && metadata.env.url || "unknown"
    status = metadata[:env] && metadata.env.status || 0
    duration_ms = System.convert_time_unit(duration, :native, :millisecond)

    :telemetry.execute(
      [:my_app, :http_client, :request],
      %{duration: duration_ms},
      %{url: url, status: status}
    )
  end
end

Step 3: Add Vigilmon Uptime Monitoring

  1. Log in to vigilmon.online and click Add Monitor.
  2. Set Type to HTTP / HTTPS.
  3. Enter your health endpoint: https://yourapp.com/health/tesla.
  4. Set Check interval to 1 minute.
  5. Set Expected HTTP status to 200.
  6. Under Advanced, add a Response body contains check: "status":"ok".
  7. Click Save.

Vigilmon now verifies your Tesla clients can reach their upstream services every minute.


Step 4: Add a Heartbeat for Background Tesla Workers

If you use Tesla in background jobs or GenServers that poll external APIs, add a heartbeat that stops pinging Vigilmon when the integration fails:

# lib/my_app/workers/api_sync_worker.ex
defmodule MyApp.Workers.ApiSyncWorker do
  use GenServer

  @interval :timer.minutes(1)
  @vigilmon_heartbeat_url "https://vigilmon.online/api/heartbeat/YOUR_HEARTBEAT_ID"

  def start_link(_), do: GenServer.start_link(__MODULE__, nil, name: __MODULE__)

  def init(_) do
    schedule()
    {:ok, %{consecutive_failures: 0}}
  end

  def handle_info(:sync, %{consecutive_failures: failures} = state) do
    new_state =
      case sync_data() do
        :ok ->
          ping_vigilmon()
          %{state | consecutive_failures: 0}

        {:error, _reason} ->
          %{state | consecutive_failures: failures + 1}
      end

    schedule()
    {:noreply, new_state}
  end

  defp sync_data do
    case MyApp.PaymentsClient.get("/transactions/recent") do
      {:ok, %{status: 200}} -> :ok
      error -> {:error, error}
    end
  end

  defp ping_vigilmon do
    Tesla.get!(vigilmon_client(), @vigilmon_heartbeat_url)
  end

  defp vigilmon_client do
    Tesla.client([Tesla.Middleware.FollowRedirects])
  end

  defp schedule, do: Process.send_after(self(), :sync, @interval)
end

To create the heartbeat in Vigilmon:

  1. Click Add MonitorCron / Heartbeat.
  2. Set the expected ping interval to 1 minute.
  3. Copy the generated heartbeat URL into @vigilmon_heartbeat_url.

Step 5: Set Up Alerts

  1. Go to Alert ChannelsAdd Channel.
  2. Choose Slack, Email, or PagerDuty.
  3. Set alert thresholds: notify after 2 consecutive failures.
  4. Assign the channel to your HTTP monitor and heartbeat monitor.

For critical payment or auth integrations, also add an on-call escalation:

  1. Create a second alert channel with PagerDuty or OpsGenie.
  2. Set it to trigger after 1 failure (no grace period for critical services).
  3. Assign it only to monitors for critical upstream dependencies.

Key Metrics to Watch

| Metric | Vigilmon feature | What to alert on | |---|---|---| | Upstream API availability | HTTP monitor on /health/tesla | Any 5xx or timeout | | Background sync health | Heartbeat monitor | Missing ping for > 2 min | | Response time | Response time chart | P95 > 3s over 5 minutes | | SSL certificate | Cert expiry monitor | Expires in < 14 days | | Retry storm detection | Heartbeat gap | Heartbeat missing during retry window |


Conclusion

Tesla's middleware architecture makes HTTP integrations elegant in Elixir — but it also means failures can be absorbed silently by retry middleware before they surface. With a health endpoint that exercises real Tesla clients, a heartbeat that stops when sync fails, and Vigilmon monitoring both, you'll catch upstream API degradation before it affects your users.

Get started free at vigilmon.online.

Monitor your app with Vigilmon

Free plan — 5 monitors, no credit card required. Up and running in 60 seconds.

Start free →