Skip to content

Instantly share code, notes, and snippets.

@kevbuchanan
Last active August 4, 2023 16:06
Show Gist options
  • Save kevbuchanan/b30561a2ae0b0262f0ddb321d8c98404 to your computer and use it in GitHub Desktop.
Save kevbuchanan/b30561a2ae0b0262f0ddb321d8c98404 to your computer and use it in GitHub Desktop.
Phoenix Health Endpoint
defmodule MyAppWeb.HealthController do
use MyAppWeb, :controller
alias MyAppWeb.Heartbeat
def root(conn, _) do
send_resp(conn, :no_content, "")
end
def show(conn, _) do
case Heartbeat.status() do
:running ->
conn
|> put_status(200)
|> json(%{
"app" => %{
"message" => "Application is running.",
"success" => true
},
"db" => %{
"message" => "DB is connected.",
"success" => MyApp.Repo.connected?()
},
"cluster" => %{
"self" => Node.self(),
"nodes" => Node.list()
},
"version" => %{
"sha" => revision(),
"build" => build_number()
}
})
:stopping ->
conn
|> put_status(503)
|> json(%{
"app" => %{
"message" => "Application is shutting down.",
"success" => false
}
})
_ ->
conn
|> put_status(500)
|> json(%{
"app" => %{
"message" => "Unknown application status.",
"success" => false
}
})
end
end
defp revision do
:code.priv_dir(:my_app)
|> Path.join("revision")
|> File.read()
|> case do
{:ok, revision} -> String.trim(revision)
_ -> nil
end
end
defp build_number do
:code.priv_dir(:my_app)
|> Path.join("build_number")
|> File.read()
|> case do
{:ok, build_number} -> String.trim(build_number)
_ -> nil
end
end
end
defmodule MyAppWeb.Heartbeat do
@shutdown_delay 20_000
use GenServer, shutdown: @shutdown_delay + 10
require Logger
def start_link(opts) do
GenServer.start_link(__MODULE__, opts, name: __MODULE__)
end
def init(_) do
Process.flag(:trap_exit, true)
:ets.new(:heartbeat, [:set, :named_table, :public, {:read_concurrency, true}])
:ets.insert(:heartbeat, {:status, :running})
{:ok, :running}
end
def stop do
GenServer.cast(__MODULE__, :stop)
end
def status do
case :ets.lookup(:heartbeat, :status) do
[{:status, status}] -> status
_ -> :unknown
end
end
def handle_cast(:stop, :running = state) do
stop_heartbeat(state)
{:noreply, :stopping}
end
def handle_cast(:stop, state) do
{:noreply, state}
end
def terminate(:normal, state), do: stop_heartbeat(state)
def terminate(:shutdown, state), do: stop_heartbeat(state)
def terminate({:shutdown, _}, state), do: stop_heartbeat(state)
def terminate(_, _state), do: :ok
defp stop_heartbeat(_state) do
delay = @shutdown_delay
Logger.info("Heartbeat received shutdown. Delaying shutdown for #{delay} ms...")
:ets.insert(:heartbeat, {:status, :stopping})
:timer.sleep(delay)
:ok
end
end
defmodule MyAppWeb.Router do
use MyAppWeb, :router
use Plug.ErrorHandler
get "/", WilsonWeb.HealthController, :root
get "/health", MyAppWeb.HealthController, :show
end
@derpycoder
Copy link

derpycoder commented Aug 4, 2023

Thank you @kevbuchanan, your gist helped me a lot.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment