tutorial

How to Monitor LiveSvelte with Vigilmon

Monitor Phoenix LiveView apps that embed Svelte components with LiveSvelte — track LiveView socket health, Svelte bundle serving, client-side hydration errors, and background worker liveness with Vigilmon uptime and heartbeat checks.

How to Monitor LiveSvelte with Vigilmon

LiveSvelte lets you embed reactive Svelte components directly inside Phoenix LiveView templates. You get Svelte's local state management and smooth DOM updates on the client, backed by LiveView's real-time server push — without committing to a full SPA architecture or duplicating routing and auth logic.

That two-layer architecture is its strength, but also its monitoring surface: a LiveSvelte app can fail at the Phoenix layer (LiveView socket unavailable), the asset layer (Svelte bundle not served or failing to hydrate), or the data layer (underlying database or API). External monitoring needs to cover all three.

This tutorial covers:

  • A health endpoint that checks LiveView socket availability and database connectivity
  • HTTP uptime monitoring with Vigilmon
  • Keyword monitoring for Svelte bundle presence on your HTML pages
  • Heartbeat monitoring for background workers that feed LiveView state
  • Alerts and a status page

Why Monitor LiveSvelte?

| Failure mode | Symptom without monitoring | |---|---| | Phoenix node unreachable | All LiveView sockets drop; Svelte components show stale state | | LiveView WebSocket endpoint down | Components render server HTML but lose reactivity | | Svelte JS bundle not served (404) | Hydration fails silently; components render static | | Background worker stalls | LiveView pushes stop; UI freezes without an error | | Database connection pool exhausted | LiveView assigns fail; components receive nil data |


Step 1: Add a health check endpoint

# lib/my_app_web/plugs/health_check.ex
defmodule MyAppWeb.Plugs.HealthCheck do
  import Plug.Conn

  def init(opts), do: opts

  def call(%Plug.Conn{request_path: "/health"} = conn, _opts) do
    checks = %{
      database:       check_database(),
      live_socket:    check_live_socket(),
      memory:         check_memory()
    }

    status = if Enum.all?(checks, fn {_, v} -> v == :ok end), do: 200, else: 503

    body = Jason.encode!(%{
      status: if(status == 200, do: "ok", else: "degraded"),
      checks: checks
    })

    conn
    |> put_resp_content_type("application/json")
    |> send_resp(status, body)
    |> halt()
  end

  def call(conn, _opts), do: conn

  defp check_database do
    case Ecto.Adapters.SQL.query(MyApp.Repo, "SELECT 1", []) do
      {:ok, _} -> :ok
      _ -> :error
    end
  end

  defp check_live_socket do
    # Confirm the LiveView socket handler is registered
    case Phoenix.LiveView.Socket |> Code.ensure_loaded() do
      {:module, _} -> :ok
      _ -> :error
    end
  end

  defp check_memory do
    total_mb = :erlang.memory(:total) / 1_048_576
    if total_mb < 1_500, do: :ok, else: :error
  end
end

Register before your router:

# lib/my_app_web/endpoint.ex
plug MyAppWeb.Plugs.HealthCheck
plug MyAppWeb.Router

Test it:

curl http://localhost:4000/health | jq .
# {"status":"ok","checks":{"database":"ok","live_socket":"ok","memory":"ok"}}

Step 2: Set up HTTP monitoring in Vigilmon

  1. Sign up at vigilmon.online.
  2. Click New Monitor → HTTP.
  3. Set URL to https://your-app.example.com/health.
  4. Set check interval to 60 seconds.
  5. Add a keyword check for "ok" to catch degraded responses.
  6. Save.

Add a second monitor for the LiveView WebSocket endpoint:

  1. Click New Monitor → HTTP.
  2. URL: https://your-app.example.com/live/websocket.
  3. Interval: 60 seconds.
  4. Expected status: 101 (WebSocket upgrade) or 200 depending on your server configuration. If Vigilmon reports 400, that is expected for a plain HTTP GET — set it to keyword-check the response body for the absence of an error page instead.

Step 3: Keyword monitor for Svelte bundle presence

LiveSvelte bundles your Svelte components into the standard Phoenix asset pipeline. If the JS bundle is missing or the CDN edge cache serves a stale 404, components silently render as static HTML.

Add a keyword monitor targeting your main page:

  1. In Vigilmon, click New Monitor → HTTP.
  2. URL: https://your-app.example.com/ (or any LiveSvelte-heavy page).
  3. Set keyword check to match the Svelte bundle script tag — typically something like app.js or the hash-appended filename from your assets/ build.
  4. Interval: 5 minutes.

If the bundle is missing from the HTML, the keyword check fails and you are alerted before a single user notices.

You can also match on a landmark string rendered by your LiveSvelte root component:

<!-- lib/my_app_web/components/svelte/App.svelte -->
<div data-testid="live-svelte-root">
  <!-- component content -->
</div>

Then set the keyword to live-svelte-root. A 200 response without that string means the component failed to render.


Step 4: Heartbeat monitoring for background workers

LiveSvelte UIs are only as live as the data pushing them. Background workers — Oban jobs, GenServers polling external APIs — silently failing means your components show stale data with no visible error.

# lib/my_app/feed_worker.ex
defmodule MyApp.FeedWorker do
  use GenServer
  require Logger

  @interval :timer.minutes(5)

  def start_link(_), do: GenServer.start_link(__MODULE__, %{}, name: __MODULE__)

  @impl true
  def init(state) do
    schedule_tick()
    {:ok, state}
  end

  @impl true
  def handle_info(:tick, state) do
    case refresh_feed() do
      :ok ->
        broadcast_updates()
        ping_heartbeat()
      {:error, reason} ->
        Logger.error("FeedWorker failed: #{inspect(reason)}")
        # No heartbeat ping → Vigilmon alerts after grace period
    end

    schedule_tick()
    {:noreply, state}
  end

  defp refresh_feed do
    # Fetch from external API, update ETS or Repo, etc.
    :ok
  end

  defp broadcast_updates do
    Phoenix.PubSub.broadcast(MyApp.PubSub, "feed:updates", {:new_data, []})
  end

  defp ping_heartbeat do
    url = Application.get_env(:my_app, :vigilmon)[:feed_heartbeat_url]
    if url do
      Task.start(fn ->
        :httpc.request(:get, {String.to_charlist(url), []}, [], [])
      end)
    end
  end

  defp schedule_tick, do: Process.send_after(self(), :tick, @interval)
end

Register in your supervision tree:

children = [
  MyApp.Repo,
  MyApp.PubSub,
  MyAppWeb.Endpoint,
  MyApp.FeedWorker,
]

Configure the URL:

# config/runtime.exs
config :my_app, :vigilmon,
  feed_heartbeat_url: System.get_env("VIGILMON_FEED_HEARTBEAT_URL")

In Vigilmon, create a Heartbeat Monitor:

  1. Click New Monitor → Heartbeat.
  2. Grace period: 10 minutes (5-min interval + buffer).
  3. Copy the ping URL into VIGILMON_FEED_HEARTBEAT_URL.

Step 5: Slack alerts

In Vigilmon, go to Notifications → New Channel → Slack and paste your Slack incoming webhook URL.

When your LiveSvelte app goes down:

🔴 DOWN: your-app.example.com/health
Status: 503 Service Unavailable
Detected: EU-West, US-East

When the Svelte bundle disappears from the page:

🔴 KEYWORD MISSING: your-app.example.com/
Keyword "live-svelte-root" not found in response

When the feed worker stalls:

🔴 HEARTBEAT MISSED: Feed Worker
Last ping: 14 minutes ago (grace: 10 minutes)

Step 6: Key metrics to alert on

| Metric | Alert threshold | Why | |---|---|---| | /health HTTP status | Non-200 for 2 checks | Phoenix node down | | /live/websocket reachability | Unreachable for 2 checks | LiveView sockets unavailable | | Svelte bundle keyword | Missing from page response | Client-side hydration broken | | Feed worker heartbeat | Grace period exceeded | LiveView updates stopped |


Step 7: Status page

  1. Go to Status Pages → New in Vigilmon.
  2. Add your monitors with descriptive names: "API & Phoenix", "LiveView WebSocket", "Svelte bundle", "Feed worker".
  3. Copy the public URL into your team's runbook and error pages.

A status page is especially useful for LiveSvelte apps because a partial failure (socket up, bundle missing) can look like a bug to users when it is actually an infrastructure issue.


Conclusion

LiveSvelte's two-layer architecture — Svelte on the client, LiveView on the server — means you have more surface area to monitor than a simple SSR app. With Vigilmon you cover it all:

  • HTTP uptime on the health endpoint and LiveView socket
  • Keyword monitors that catch missing Svelte bundles before users notice
  • Heartbeat monitors that detect stalled background workers feeding your live UI

Start with the health endpoint and the bundle keyword check, then add heartbeat monitors for each critical worker that drives your LiveSvelte components.

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 →