fix: log every silent error path so failures surface in k8s logs

Sweep of remaining swallowed-error sites flagged by an audit pass:

- weather/gefs_client.ex: byte-range download task exits log url + reason
  before being surfaced as {:error, _} (matches the HRRR fix).
- live/path_live.ex: terrain-profile load failure, native-duct lookup
  failure, and ProfilesFile.read failure now log warnings with path
  endpoints / midpoint / valid_time. Previously rendered a degraded
  result silently.
- live/rover_live.ex: station-mutation {:error, _} now logs id + reason
  instead of dropping into {:noreply, socket}.
- application.ex: the boot-time backfill_missing_home_qth Task.start is
  now wrapped in try/rescue. A DB issue at boot would have crashed the
  task silently (no Logger handler attached pre-Logger crash format).
This commit is contained in:
Graham McIntire 2026-04-27 14:31:30 -05:00
parent 301935f8c1
commit f26fbafc29
No known key found for this signature in database
GPG key ID: F4ABF488E6029E59
4 changed files with 49 additions and 9 deletions

View file

@ -5,6 +5,8 @@ defmodule Microwaveprop.Application do
use Application use Application
require Logger
@build_timestamp DateTime.utc_now() @build_timestamp DateTime.utc_now()
@impl true @impl true
@ -66,7 +68,15 @@ defmodule Microwaveprop.Application do
# already set, so re-running on every boot is safe (and useful: any # already set, so re-running on every boot is safe (and useful: any
# users registered while the worker module was unavailable get # users registered while the worker module was unavailable get
# picked up the next time the pod restarts). # picked up the next time the pod restarts).
{:ok, _pid} = Task.start(fn -> Microwaveprop.Accounts.backfill_missing_home_qth() end) {:ok, _pid} =
Task.start(fn ->
try do
Microwaveprop.Accounts.backfill_missing_home_qth()
rescue
e ->
Logger.error("Application: backfill_missing_home_qth crashed: #{Exception.format(:error, e, __STACKTRACE__)}")
end
end)
# Load ML model in dev/test only (Nx/Axon not compiled for prod) # Load ML model in dev/test only (Nx/Axon not compiled for prod)
if Application.get_env(:microwaveprop, :load_ml_model, false) do if Application.get_env(:microwaveprop, :load_ml_model, false) do

View file

@ -19,6 +19,8 @@ defmodule Microwaveprop.Weather.GefsClient do
derivation (GEFS pgrb2a publishes RH rather than Td). derivation (GEFS pgrb2a publishes RH rather than Td).
""" """
require Logger
@base_url "https://nomads.ncep.noaa.gov/pub/data/nccf/com/gens/prod" @base_url "https://nomads.ncep.noaa.gov/pub/data/nccf/com/gens/prod"
@run_hours [0, 6, 12, 18] @run_hours [0, 6, 12, 18]
@ -273,8 +275,13 @@ defmodule Microwaveprop.Weather.GefsClient do
timeout: 120_000 timeout: 120_000
) )
|> Enum.map(fn |> Enum.map(fn
{:ok, result} -> result {:ok, result} ->
{:exit, reason} -> {:error, reason} result
{:exit, reason} ->
Logger.error("GEFS grib range download crashed: url=#{url} reason=#{inspect(reason)}")
{:error, reason}
end) end)
case Enum.find(results, &match?({:error, _}, &1)) do case Enum.find(results, &match?({:error, _}, &1)) do

View file

@ -284,7 +284,11 @@ defmodule MicrowavepropWeb.PathLive do
%{profile: profile, analysis: analysis} %{profile: profile, analysis: analysis}
{:error, _} -> {:error, reason} ->
Logger.warning(
"PathLive terrain profile load failed: src=#{src.lat},#{src.lon} dst=#{dst.lat},#{dst.lon} reason=#{inspect(reason)}"
)
nil nil
end end
@ -346,8 +350,15 @@ defmodule MicrowavepropWeb.PathLive do
# Richardson so turbulent duct readings don't inflate the score. # Richardson so turbulent duct readings don't inflate the score.
native_duct = native_duct =
case Weather.nearest_native_duct_info(midlat, midlon, now) do case Weather.nearest_native_duct_info(midlat, midlon, now) do
{:ok, info} -> info {:ok, info} ->
{:error, _} -> %{best_duct_band_ghz: nil, bulk_richardson: nil} info
{:error, reason} ->
Logger.warning(
"PathLive nearest_native_duct_info failed: midpoint=#{midlat},#{midlon} reason=#{inspect(reason)}"
)
%{best_duct_band_ghz: nil, bulk_richardson: nil}
end end
# Build conditions and score # Build conditions and score
@ -474,8 +485,18 @@ defmodule MicrowavepropWeb.PathLive do
defp profile_grid_for(valid_time) do defp profile_grid_for(valid_time) do
case ProfilesFile.read(valid_time) do case ProfilesFile.read(valid_time) do
{:ok, grid} -> grid {:ok, grid} ->
_ -> nil grid
{:error, reason} ->
Logger.warning("PathLive ProfilesFile.read failed: valid_time=#{inspect(valid_time)} reason=#{inspect(reason)}")
nil
other ->
Logger.warning("PathLive ProfilesFile.read unexpected: valid_time=#{inspect(valid_time)} got=#{inspect(other)}")
nil
end end
end end

View file

@ -395,7 +395,9 @@ defmodule MicrowavepropWeb.RoverLive do
{:ok, stations} -> {:ok, stations} ->
{:noreply, socket |> assign_stations(stations) |> sync_url()} {:noreply, socket |> assign_stations(stations) |> sync_url()}
{:error, _} -> {:error, reason} ->
Logger.error("RoverLive station mutation failed: id=#{inspect(id)} reason=#{inspect(reason)}")
{:noreply, socket} {:noreply, socket}
end end
end end