{% extends "base.html" %} {% block title %}Error Monitoring{% endblock %} {% block breadcrumb %}Error Monitoring{% endblock %} {% block extra_head %} {% endblock %} {% block content %} {# ═══════════════════════════════════════════════════════════════════ AQUILIA ADMIN — ERROR MONITORING (v2 — Chart.js) Trend Area Charts · Severity Doughnut · Domain Polar Area Top Errors Bar · Velocity Sparkline · Resolution Tracking ═══════════════════════════════════════════════════════════════════ #}

Error Monitoring

Track errors with stack traces, grouping, frequency analysis, and hourly trends

{# ═══ METRIC CARDS (extended with resolution tracking) ═══ #}
0
Total Errors
{{ error_rate }} /min rate
0
Last Hour
{{ errors_last_24h }} in 24h
0
Unique Errors
Grouped by fingerprint
0
Unresolved
{{ resolved_count }} resolved
{{ "%.1f"|format(error_rate) }}
Errors/min
Current rate
{{ "%.0f"|format(mttr_seconds) }}s
MTTR
Mean time to resolve
{# ═══ CHARTS ROW 1: Error Trend + Severity Doughnut ═══ #}
Error Analytics
{# ── Error Trend Area Chart (hourly) ── #}
Error Trend — Last 24h
{# ── Severity Doughnut ── #}
By Severity
{# ═══ CHARTS ROW 2: Domain Polar + Top Codes Bar ═══ #}
{# ── Domain Polar Area ── #}
Errors by Domain
{# ── Top Error Codes Horizontal Bar ── #}
Top Error Codes
{# ═══ CHARTS ROW 3: Domain Stacked Area + Severity Timeline + Velocity ═══ #}
{# ── Domain Stacked Area ── #}
Domain Timeline
{# ── Severity Timeline ── #}
Severity Timeline
{# ── Error Velocity Sparkline ── #}
Velocity
Errors per 5-min window (last 30 min)
{# ═══ BREAKDOWN GRID (enhanced) ═══ #}
Error Breakdown
{# By Severity (table view — kept as detail supplement) #} {% if by_severity and by_severity|length > 0 %}
By Severity
{% for sev, count in by_severity.items() %}
{{ sev }}
{{ count }}
{% endfor %}
{% endif %} {# By Domain #} {% if by_domain and by_domain|length > 0 %}
By Domain
{% for domain, count in by_domain.items() %}
{{ domain }} {{ count }}
{% endfor %}
{% endif %} {# Top Routes #} {% if top_routes and top_routes|length > 0 %}
Top Error Routes
{% for r in top_routes[:8] %}
{{ r.route|truncate(40) }} {{ r.count }}
{% endfor %}
{% endif %}
{# ═══ ERROR GROUPS (enhanced with severity badge + progress bar) ═══ #} {% if error_groups and error_groups|length > 0 %}
Error Groups {{ error_groups|length }} groups
{% set max_group_count = namespace(val=1) %} {% for g in error_groups[:20] %} {% if g.count > max_group_count.val %}{% set max_group_count.val = g.count %}{% endif %} {% endfor %} {% for g in error_groups[:20] %} {% endfor %}
Error Code Message Severity Domain Count Impact Last Seen
{{ g.code }} {{ g.message|truncate(80) }} {% set sev = g.severity if g.severity is defined else 'UNKNOWN' %} {{ sev }} {{ g.domain }} {{ g.count }}
{{ g.last_seen[:19] if g.last_seen else '—' }}
{% endif %} {# ═══ RECENT ERRORS WITH STACK TRACES ═══ #}
Recent Errors
{% if recent_errors and recent_errors|length > 0 %} {% for err in recent_errors[-30:]|reverse %} {% set sev_lower = (err.severity|lower) if err.severity else 'unknown' %}
{{ err.code }} {{ err.severity }} {% if err.domain %} {{ err.domain }} {% endif %}
{{ err.message|truncate(200) }}
{% if err.exception_type %}
{{ err.exception_type }}: {{ err.exception_message|truncate(150) }}
{% endif %} {% if err.route %}
{{ err.route }} {% if err.app %} · {{ err.app }}{% endif %}
{% endif %}
{{ err.trace_id }}
{{ err.timestamp[:19] if err.timestamp else '' }}
{# Stack Trace (collapsible) #} {% if err.stack_trace or (err.stack_frames and err.stack_frames|length > 0) %}
Stack Trace ({{ err.stack_frames|length if err.stack_frames else 0 }} frames)
{% if err.stack_frames and err.stack_frames|length > 0 %} {% for frame in err.stack_frames %}
{{ frame.filename }}:{{ frame.lineno }} {{ frame.name }} {% if frame.line %}
{{ frame.line }}
{% endif %}
{% endfor %} {% elif err.stack_trace %}
{{ err.stack_trace }}
{% endif %}
{% endif %}
{% endfor %} {% else %}

No errors recorded

Errors will appear here as they are captured by the fault engine.

{% endif %}
{% endblock %} {% block extra_js %} // ── Chart.js Error Analytics ──────────────────────────────────── (function() { 'use strict'; if (typeof Chart === 'undefined') return; var isDark = document.documentElement.getAttribute('data-theme') !== 'light'; var gridColor = isDark ? 'rgba(255,255,255,0.06)' : 'rgba(0,0,0,0.08)'; var textColor = isDark ? 'rgba(255,255,255,0.5)' : 'rgba(0,0,0,0.5)'; Chart.defaults.color = textColor; Chart.defaults.borderColor = gridColor; Chart.defaults.font.family = "'Outfit', system-ui, sans-serif"; Chart.defaults.font.size = 11; Chart.defaults.plugins.legend.labels.boxWidth = 10; Chart.defaults.plugins.legend.labels.padding = 12; var charts = {{ charts | tojson }}; // ── Palette for domains / series ──────────────────────────── var palette = [ '#ef4444', '#f59e0b', '#3b82f6', '#8b5cf6', '#22c55e', '#ec4899', '#06b6d4', '#f97316', '#6366f1', '#14b8a6', '#e11d48', '#84cc16', '#0ea5e9', '#a855f7', '#eab308', ]; // ── Error Trend Area Chart (hourly) ───────────────────────── var hourly = charts.hourly || {}; if (hourly.labels && hourly.labels.length) { new Chart(document.getElementById('chart-error-trend'), { type: 'line', data: { labels: hourly.labels, datasets: [{ label: 'Errors', data: hourly.values, borderColor: '#ef4444', backgroundColor: 'rgba(239,68,68,0.1)', borderWidth: 2, fill: true, tension: 0.35, pointRadius: 0, pointHoverRadius: 4, }], }, options: { responsive: true, maintainAspectRatio: false, interaction: { intersect: false, mode: 'index' }, scales: { x: { grid: { display: false }, ticks: { maxTicksLimit: 8 } }, y: { beginAtZero: true, grid: { color: gridColor }, ticks: { stepSize: 1 } }, }, plugins: { legend: { display: false } }, }, }); } // ── Severity Doughnut ─────────────────────────────────────── var sd = charts.severity_doughnut || {}; if (sd.labels && sd.labels.length) { var sevColorMap = { 'ERROR': '#ef4444', 'error': '#ef4444', 'FATAL': '#991b1b', 'fatal': '#991b1b', 'CRITICAL': '#dc2626', 'critical': '#dc2626', 'WARN': '#f59e0b', 'warn': '#f59e0b', 'WARNING': '#f59e0b', 'warning': '#f59e0b', 'INFO': '#3b82f6', 'info': '#3b82f6', 'DEBUG': '#6b7280', 'debug': '#6b7280', }; var sevColors = sd.labels.map(function(l) { return sevColorMap[l] || '#6b7280'; }); new Chart(document.getElementById('chart-severity-doughnut'), { type: 'doughnut', data: { labels: sd.labels, datasets: [{ data: sd.values, backgroundColor: sevColors, borderWidth: 0, hoverOffset: 6 }], }, options: { responsive: true, maintainAspectRatio: false, cutout: '68%', plugins: { legend: { position: 'bottom', labels: { padding: 10 } } }, }, }); } // ── Domain Polar Area ─────────────────────────────────────── var dp = charts.domain_polar || {}; if (dp.labels && dp.labels.length) { var domColors = dp.labels.map(function(_, i) { return palette[i % palette.length]; }); var domBg = domColors.map(function(c) { return c + '99'; }); // 60% opacity new Chart(document.getElementById('chart-domain-polar'), { type: 'polarArea', data: { labels: dp.labels, datasets: [{ data: dp.values, backgroundColor: domBg, borderColor: domColors, borderWidth: 1 }], }, options: { responsive: true, maintainAspectRatio: false, scales: { r: { ticks: { display: false }, grid: { color: gridColor } } }, plugins: { legend: { position: 'bottom', labels: { padding: 8 } } }, }, }); } // ── Top Error Codes Horizontal Bar ────────────────────────── var tc = charts.top_codes_bar || {}; if (tc.labels && tc.labels.length) { new Chart(document.getElementById('chart-top-codes'), { type: 'bar', data: { labels: tc.labels, datasets: [{ label: 'Occurrences', data: tc.values, backgroundColor: tc.labels.map(function(_, i) { return palette[i % palette.length] + 'cc'; }), borderWidth: 0, borderRadius: 4, }], }, options: { responsive: true, maintainAspectRatio: false, indexAxis: 'y', scales: { x: { beginAtZero: true, grid: { color: gridColor }, ticks: { stepSize: 1 } }, y: { grid: { display: false } }, }, plugins: { legend: { display: false } }, }, }); } // ── Domain Stacked Area ───────────────────────────────────── var ds = charts.domain_stacked || {}; if (ds.labels && ds.labels.length && ds.series) { var dsDatasets = []; var dsKeys = Object.keys(ds.series); dsKeys.forEach(function(domain, i) { dsDatasets.push({ label: domain, data: ds.series[domain], borderColor: palette[i % palette.length], backgroundColor: palette[i % palette.length] + '33', borderWidth: 1.5, fill: true, tension: 0.35, pointRadius: 0, }); }); new Chart(document.getElementById('chart-domain-stacked'), { type: 'line', data: { labels: ds.labels, datasets: dsDatasets }, options: { responsive: true, maintainAspectRatio: false, interaction: { intersect: false, mode: 'index' }, scales: { x: { stacked: true, grid: { display: false }, ticks: { maxTicksLimit: 6 } }, y: { stacked: true, beginAtZero: true, grid: { color: gridColor }, ticks: { stepSize: 1 } }, }, plugins: { legend: { position: 'top', align: 'end' } }, }, }); } // ── Severity Timeline ─────────────────────────────────────── var st = charts.severity_timeline || {}; if (st.labels && st.labels.length && st.series) { var stDatasets = []; var stKeys = Object.keys(st.series); var stColorMap = { 'ERROR': '#ef4444', 'error': '#ef4444', 'FATAL': '#991b1b', 'fatal': '#991b1b', 'CRITICAL': '#dc2626', 'critical': '#dc2626', 'WARN': '#f59e0b', 'warn': '#f59e0b', 'WARNING': '#f59e0b', 'warning': '#f59e0b', 'INFO': '#3b82f6', 'info': '#3b82f6', 'DEBUG': '#6b7280', 'debug': '#6b7280', }; stKeys.forEach(function(sev) { var c = stColorMap[sev] || '#6b7280'; stDatasets.push({ label: sev, data: st.series[sev], borderColor: c, backgroundColor: c + '33', borderWidth: 1.5, fill: true, tension: 0.35, pointRadius: 0, }); }); new Chart(document.getElementById('chart-severity-timeline'), { type: 'line', data: { labels: st.labels, datasets: stDatasets }, options: { responsive: true, maintainAspectRatio: false, interaction: { intersect: false, mode: 'index' }, scales: { x: { stacked: true, grid: { display: false }, ticks: { maxTicksLimit: 6 } }, y: { stacked: true, beginAtZero: true, grid: { color: gridColor }, ticks: { stepSize: 1 } }, }, plugins: { legend: { position: 'top', align: 'end' } }, }, }); } // ── Error Velocity Sparkline ──────────────────────────────── var vel = charts.velocity || []; if (vel.length) { var velLabels = vel.map(function(_, i) { return (i * 5) + 'm'; }); new Chart(document.getElementById('chart-velocity'), { type: 'bar', data: { labels: velLabels, datasets: [{ data: vel, backgroundColor: vel.map(function(v) { return v > 10 ? '#ef4444cc' : (v > 5 ? '#f59e0bcc' : '#22c55ecc'); }), borderWidth: 0, borderRadius: 3, }], }, options: { responsive: true, maintainAspectRatio: false, scales: { x: { grid: { display: false } }, y: { beginAtZero: true, grid: { color: gridColor }, ticks: { stepSize: 1 } }, }, plugins: { legend: { display: false } }, }, }); } // ── Error list filter ─────────────────────────────────────── window.filterErrors = function(severity) { var rows = document.querySelectorAll('.error-row'); rows.forEach(function(row) { if (severity === 'all') { row.style.display = ''; } else if (severity === 'error') { var s = row.getAttribute('data-severity'); row.style.display = (s === 'error' || s === 'fatal' || s === 'critical') ? '' : 'none'; } else if (severity === 'warning') { var s = row.getAttribute('data-severity'); row.style.display = (s === 'warn' || s === 'warning') ? '' : 'none'; } else if (severity === 'info') { var s = row.getAttribute('data-severity'); row.style.display = (s === 'info' || s === 'debug') ? '' : 'none'; } else { row.style.display = row.getAttribute('data-severity') === severity ? '' : 'none'; } }); document.querySelectorAll('.aq-err-filter').forEach(function(btn) { btn.classList.toggle('active', btn.getAttribute('data-filter') === severity); }); }; // ── Auto-refresh ──────────────────────────────────────────── var refreshInterval = 15000; function scheduleRefresh() { setTimeout(function() { fetch('{{ url_prefix }}/errors/api/') .then(function(r) { return r.ok ? r.json() : null; }) .then(function(data) { if (data) { var cards = document.querySelectorAll('.dash-stat-value[data-count]'); cards.forEach(function(el) { var target = parseInt(el.getAttribute('data-count'), 10); if (!isNaN(target)) el.textContent = target; }); } }) .catch(function() {}) .finally(scheduleRefresh); }, refreshInterval); } scheduleRefresh(); })(); {% endblock %}