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

Current Activity

Executing

{@metrics.executing_count}

Queued

{@metrics.queued_count}

Completed (1h)

{@metrics.completed_last_hour}

Failed (1h)

{@metrics.failed_last_hour}

Success Rates

Polling (1h) <%= Float.round(@metrics.polling_success_rate_1h * 100, 1) %>%
Discovery (1h) <%= Float.round(@metrics.discovery_success_rate_1h * 100, 1) %>%

Performance

Avg Execution Time
<%= if @metrics.avg_execution_time_seconds do %> <%= Float.round(@metrics.avg_execution_time_seconds, 1) %>s <% else %> N/A <% end %>
Jobs/Hour
{@metrics.completed_last_hour}

Updates every 10 seconds