Job Monitoring Dashboard

Real-time monitoring of polling and discovery jobs

Completed (1h)

{Map.get(@health_metrics, :completed_last_hour, 0)}

jobs completed

Failed (1h)

{Map.get(@health_metrics, :failed_last_hour, 0)}

failures

Avg Duration

<%= if Map.get(@health_metrics, :avg_execution_time_seconds) do %> {Map.get(@health_metrics, :avg_execution_time_seconds) |> Float.round(1)}s <% else %> N/A <% end %>

last hour

Active Jobs

{length(@active_jobs)}

executing now

Active Operations ({length(@executing_jobs)})

<%= if length(@executing_jobs) == 0 do %>

No jobs currently executing

<% else %>
<%= for job <- @executing_jobs do %>

<%= if job.device do %> <%= job.device.name %> <% else %> Device #{get_in(job.args, ["device_id"])} <% end %>

<%= worker_name(job.worker) %>

Started <%= ToweropsWeb.TimeHelpers.format_time_ago(job.attempted_at) %>

<.icon name="hero-arrow-path" class="w-4 h-4 animate-spin text-blue-600" />
<% end %>
<% end %>
<%= if length(@stuck_jobs) > 0 or length(@failed_jobs) > 0 do %>

⚠️ Problems Detected

<%= if length(@stuck_jobs) > 0 do %>

Stuck Jobs ({length(@stuck_jobs)})

<%= for job <- @stuck_jobs do %>

<%= if job.device do %> <%= job.device.name %> <% else %> Device #{get_in(job.args, ["device_id"])} <% end %>

<%= worker_name(job.worker) %>

Running for <%= duration_in_words(job.attempted_at) %>

<% end %>
<% end %> <%= if length(@failed_jobs) > 0 do %>

Failed Jobs ({length(@failed_jobs)})

<%= for job <- Enum.take(@failed_jobs, 5) do %>

<%= if job.device do %> <%= job.device.name %> <% else %> Device #{get_in(job.args, ["device_id"])} <% end %>

<%= worker_name(job.worker) %> - Attempt <%= job.attempt %>/<%= job.max_attempts %>

<%= if job.errors && length(job.errors) > 0 do %>

<%= hd(job.errors)["error"] || "Unknown error" %>

<% end %>
<% end %>
<% end %>
<% end %>

Health Metrics

Detailed metrics coming soon