Packages
Example integration — monitors this application's own BEAM VM health, with its dashboard panel bundled in the same package as a separate module (Integrations.Self.Display) — one install, both halves; a release without raven_web simply runs the monitor headless.
Current section
Files
Jump to
Current section
Files
lib/integrations/self/display.ex
defmodule Integrations.Self.Display do
@moduledoc """
Dashboard panel for `Integrations.Self` (same package).
Nested under the monitor's own namespace deliberately — the package's
top-level module name never changes, and this module's name is
guaranteed distinct from any separately-published standalone display
package. `Display.BundledDefault` auto-hooks this whenever
`Integrations.Self` starts, the same as if they were still one module.
"""
use CodeNameRaven.Display, category: :system, size_hint: :large
def display_timezone, do: "America/Chicago"
@impl true
def display_name, do: "Application Health"
@impl true
def compatible_monitors, do: [Integrations.Self]
@impl true
def render(assigns) do
~H"""
<div class="space-y-6">
<%!-- Header --%>
<div class="flex items-start justify-between">
<div>
<h2 class="text-lg font-semibold text-base-content">Application Health</h2>
<p class="text-sm text-base-content/50 font-mono"><%= @result && @result.node %></p>
</div>
<.status_chip status={@status} />
</div>
<%!-- Version / uptime row --%>
<div :if={@result} class="grid grid-cols-3 gap-4">
<.info_card label="Elixir" value={"v#{@result.elixir_version}"} />
<.info_card label="OTP" value={"v#{@result.otp_version}"} />
<.info_card label="Uptime" value={format_uptime(@result.uptime_seconds)} />
</div>
<%!-- Memory --%>
<div :if={@result}>
<p class="text-xs font-semibold uppercase tracking-widest text-base-content/40 mb-3">Memory</p>
<div class="grid grid-cols-2 gap-3">
<.memory_row label="Total" bytes={@result.memory.total} />
<.memory_row label="Processes" bytes={@result.memory.processes} />
<.memory_row label="Binary" bytes={@result.memory.binary} />
<.memory_row label="ETS" bytes={@result.memory.ets} />
</div>
</div>
<%!-- Process stats --%>
<div :if={@result} class="grid grid-cols-2 gap-4">
<.stat_card label="Processes" value={@result.process_count} />
<.stat_card label="Run Queue" value={@result.run_queue} />
</div>
<%!-- Footer --%>
<p class="text-xs text-base-content/30">
Checked <%= CodeNameRaven.Timezone.format_datetime(@last_checked_at, __MODULE__) %>
</p>
</div>
"""
end
# ---------------------------------------------------------------------------
# Private components
# ---------------------------------------------------------------------------
defp status_chip(%{status: :up} = assigns) do
~H"""
<span class="badge badge-success gap-1 px-3 py-3 text-xs font-semibold">
<span class="w-1.5 h-1.5 rounded-full bg-current"></span> Up
</span>
"""
end
defp status_chip(%{status: :degraded} = assigns) do
~H"""
<span class="badge badge-warning gap-1 px-3 py-3 text-xs font-semibold">
<span class="w-1.5 h-1.5 rounded-full bg-current"></span> Degraded
</span>
"""
end
defp status_chip(%{status: :down} = assigns) do
~H"""
<span class="badge badge-error gap-1 px-3 py-3 text-xs font-semibold">
<span class="w-1.5 h-1.5 rounded-full bg-current"></span> Down
</span>
"""
end
defp status_chip(assigns) do
~H"""
<span class="badge badge-neutral gap-1 px-3 py-3 text-xs font-semibold">
<span class="w-1.5 h-1.5 rounded-full bg-current"></span> Unknown
</span>
"""
end
defp info_card(assigns) do
~H"""
<div class="bg-base-200 rounded-xl p-3 text-center">
<p class="text-xs text-base-content/40 mb-1"><%= @label %></p>
<p class="text-sm font-mono font-semibold text-base-content"><%= @value %></p>
</div>
"""
end
defp memory_row(assigns) do
~H"""
<div class="flex justify-between items-center py-1.5 border-b border-base-200">
<span class="text-xs text-base-content/50"><%= @label %></span>
<span class="text-xs font-mono font-medium text-base-content"><%= format_bytes(@bytes) %></span>
</div>
"""
end
defp stat_card(assigns) do
~H"""
<div class="bg-base-200 rounded-xl p-4">
<p class="text-xs text-base-content/40 mb-1"><%= @label %></p>
<p class="text-2xl font-mono font-bold text-base-content"><%= @value %></p>
</div>
"""
end
# ---------------------------------------------------------------------------
# Formatting helpers
# ---------------------------------------------------------------------------
defp format_bytes(bytes) when bytes >= 1_073_741_824,
do: "#{Float.round(bytes / 1_073_741_824, 1)} GB"
defp format_bytes(bytes) when bytes >= 1_048_576,
do: "#{Float.round(bytes / 1_048_576, 1)} MB"
defp format_bytes(bytes) when bytes >= 1_024,
do: "#{Float.round(bytes / 1_024, 1)} KB"
defp format_bytes(bytes),
do: "#{bytes} B"
defp format_uptime(seconds) do
days = div(seconds, 86_400)
hours = div(rem(seconds, 86_400), 3_600)
minutes = div(rem(seconds, 3_600), 60)
cond do
days > 0 -> "#{days}d #{hours}h"
hours > 0 -> "#{hours}h #{minutes}m"
true -> "#{minutes}m #{rem(seconds, 60)}s"
end
end
end