Files
Server_Monitorizare_v2/templates/logs.html

336 lines
12 KiB
HTML

{% extends "base.html" %}
{% block title %}Logs - Server Monitoring{% endblock %}
{% block page_title %}Log Viewer{% endblock %}
{% block extra_css %}
<style>
.filter-container {
background-color: #f8f9fa;
padding: 20px;
border-radius: 8px;
margin-bottom: 20px;
}
.log-entry {
border-left: 4px solid #dee2e6;
margin-bottom: 10px;
transition: all 0.2s ease;
}
.log-entry.severity-error {
border-left-color: #dc3545;
}
.log-entry.severity-warning {
border-left-color: #ffc107;
}
.log-entry.severity-info {
border-left-color: #17a2b8;
}
.log-entry.severity-debug {
border-left-color: #6c757d;
}
.log-entry:hover {
background-color: #f8f9fa;
cursor: pointer;
}
.log-meta {
font-size: 0.9rem;
color: #6c757d;
}
.severity-badge {
font-size: 0.8rem;
padding: 0.3rem 0.6rem;
border-radius: 50px;
}
.pagination-container {
margin-top: 30px;
}
.log-message {
font-family: 'Courier New', monospace;
font-size: 0.9rem;
margin: 10px 0;
word-break: break-word;
}
</style>
{% endblock %}
{% block content %}
<!-- Filter Section -->
<div class="filter-container">
<form method="GET" class="row g-3">
<div class="col-md-3">
<label for="device_id" class="form-label">Device</label>
<select class="form-select" name="device_id" id="device_id">
<option value="">All Devices</option>
{% for device in devices %}
<option value="{{ device.id }}" {% if current_device_id == device.id %}selected{% endif %}>
{{ device.hostname }} ({{ device.device_ip }})
</option>
{% endfor %}
</select>
</div>
<div class="col-md-2">
<label for="severity" class="form-label">Severity</label>
<select class="form-select" name="severity" id="severity">
<option value="">All Levels</option>
<option value="error" {% if current_severity == 'error' %}selected{% endif %}>Error</option>
<option value="warning" {% if current_severity == 'warning' %}selected{% endif %}>Warning</option>
<option value="info" {% if current_severity == 'info' %}selected{% endif %}>Info</option>
<option value="debug" {% if current_severity == 'debug' %}selected{% endif %}>Debug</option>
</select>
</div>
<div class="col-md-4">
<label for="search" class="form-label">Search Message</label>
<input type="text" class="form-control" name="search" id="search"
placeholder="Search in log messages..." value="{{ current_search }}">
</div>
<div class="col-md-2">
<label for="per_page" class="form-label">Per Page</label>
<select class="form-select" name="per_page" id="per_page">
<option value="25" {% if pagination.per_page == 25 %}selected{% endif %}>25</option>
<option value="50" {% if pagination.per_page == 50 %}selected{% endif %}>50</option>
<option value="100" {% if pagination.per_page == 100 %}selected{% endif %}>100</option>
</select>
</div>
<div class="col-md-1">
<label class="form-label">&nbsp;</label>
<button type="submit" class="btn btn-primary w-100">
<i class="fas fa-search"></i>
</button>
</div>
</form>
</div>
<!-- Results Summary -->
<div class="d-flex justify-content-between align-items-center mb-3">
<div>
<strong>Showing {{ logs|length }} of {{ pagination.total|default(0) }} log entries</strong>
{% if current_device_id or current_severity or current_search %}
<a href="{{ url_for('main.logs') }}" class="btn btn-sm btn-outline-secondary ms-2">
<i class="fas fa-times"></i> Clear Filters
</a>
{% endif %}
</div>
<div>
<button class="btn btn-success btn-sm" onclick="refreshLogs()">
<i class="fas fa-sync-alt"></i> Refresh
</button>
<button class="btn btn-info btn-sm" onclick="exportLogs()">
<i class="fas fa-download"></i> Export
</button>
</div>
</div>
<!-- Log Entries -->
<div class="log-entries">
{% if logs %}
{% for log in logs %}
<div class="card log-entry severity-{{ log.severity|default('info') }}"
onclick="toggleLogDetail('{{ loop.index }}')">
<div class="card-body">
<div class="d-flex justify-content-between align-items-start">
<div class="flex-grow-1">
<div class="d-flex align-items-center mb-2">
<span class="severity-badge bg-{% if log.severity == 'error' %}danger{% elif log.severity == 'warning' %}warning{% elif log.severity == 'info' %}primary{% else %}secondary{% endif %} text-white">
{{ log.severity|default('info')|upper }}
</span>
<strong class="ms-2">{{ log.device.hostname }}</strong>
<span class="text-muted ms-2">({{ log.device.device_ip }})</span>
{% if log.device.nume_masa %}
<span class="badge bg-info ms-2">{{ log.device.nume_masa }}</span>
{% endif %}
</div>
<div class="log-message">
{{ log.resolved_message|default(log.full_message)|truncate(200) }}
</div>
<div class="log-meta mt-2">
<i class="fas fa-clock"></i> {{ log.timestamp | local_dt('%Y-%m-%d %H:%M:%S') if log.timestamp else 'N/A' }}
{% if log.template_hash %}
<span class="ms-3"><i class="fas fa-tag"></i> Template: {{ log.template_hash[:8] }}</span>
{% endif %}
</div>
</div>
<div class="text-end">
<button class="btn btn-sm btn-outline-primary"
onclick="event.stopPropagation(); viewDevice('{{ log.device_id }}')">
<i class="fas fa-desktop"></i>
</button>
</div>
</div>
<!-- Detailed log view (initially hidden) -->
<div id="detail-{{ loop.index }}" class="log-detail mt-3" style="display: none;">
<hr>
<h6>Full Message:</h6>
<pre class="bg-light p-3 rounded">{{ log.full_message|default('No detailed message available') }}</pre>
{% if log.resolved_message != log.full_message %}
<h6>Resolved Message:</h6>
<div class="bg-info bg-opacity-10 p-3 rounded">
{{ log.resolved_message }}
</div>
{% endif %}
</div>
</div>
</div>
{% endfor %}
{% else %}
<div class="text-center py-5">
<i class="fas fa-inbox fa-3x text-muted mb-3"></i>
<h4 class="text-muted">No Logs Found</h4>
<p class="text-muted">No log entries match the current filters.</p>
{% if current_device_id or current_severity or current_search %}
<a href="{{ url_for('main.logs') }}" class="btn btn-primary">
<i class="fas fa-arrow-left"></i> View All Logs
</a>
{% endif %}
</div>
{% endif %}
</div>
<!-- Pagination -->
{% if pagination and pagination.total_pages > 1 %}
<div class="pagination-container">
<nav aria-label="Log pagination">
<ul class="pagination justify-content-center">
{% if pagination.has_prev %}
<li class="page-item">
<a class="page-link" href="{{ url_for('main.logs', page=pagination.prev_num, device_id=current_device_id, severity=current_severity, search=current_search, per_page=pagination.per_page) }}">
<i class="fas fa-chevron-left"></i> Previous
</a>
</li>
{% endif %}
{% for page_num in range(1, pagination.total_pages + 1) %}
{% if page_num == pagination.page %}
<li class="page-item active">
<span class="page-link">{{ page_num }}</span>
</li>
{% elif page_num <= pagination.page + 2 and page_num >= pagination.page - 2 %}
<li class="page-item">
<a class="page-link" href="{{ url_for('main.logs', page=page_num, device_id=current_device_id, severity=current_severity, search=current_search, per_page=pagination.per_page) }}">
{{ page_num }}
</a>
</li>
{% endif %}
{% endfor %}
{% if pagination.has_next %}
<li class="page-item">
<a class="page-link" href="{{ url_for('main.logs', page=pagination.next_num, device_id=current_device_id, severity=current_severity, search=current_search, per_page=pagination.per_page) }}">
Next <i class="fas fa-chevron-right"></i>
</a>
</li>
{% endif %}
</ul>
</nav>
<div class="text-center text-muted">
Page {{ pagination.page }} of {{ pagination.total_pages }}
({{ pagination.total }} total entries)
</div>
</div>
{% endif %}
{% endblock %}
{% block extra_js %}
<script>
function toggleLogDetail(index) {
const detail = document.getElementById(`detail-${index}`);
if (detail.style.display === 'none') {
detail.style.display = 'block';
} else {
detail.style.display = 'none';
}
}
function viewDevice(deviceId) {
window.location.href = `{{ url_for('main.device_detail', device_id=0) }}`.replace('0', deviceId);
}
function refreshLogs() {
window.location.reload();
}
function exportLogs() {
// Create export parameters from current filters
const params = new URLSearchParams(window.location.search);
params.set('export', 'csv');
// Create download link
const downloadUrl = `{{ url_for('main.logs') }}?${params.toString()}`;
const link = document.createElement('a');
link.href = downloadUrl;
link.download = 'logs-export.csv';
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
// Auto-refresh option
let autoRefreshInterval;
function startAutoRefresh(seconds = 30) {
autoRefreshInterval = setInterval(() => {
refreshLogs();
}, seconds * 1000);
}
function stopAutoRefresh() {
if (autoRefreshInterval) {
clearInterval(autoRefreshInterval);
}
}
// Initialize page
document.addEventListener('DOMContentLoaded', function() {
console.log('Logs page loaded');
// Add keyboard shortcuts
document.addEventListener('keydown', function(event) {
if (event.ctrlKey || event.metaKey) {
if (event.key === 'f') {
event.preventDefault();
document.getElementById('search').focus();
}
if (event.key === 'r') {
event.preventDefault();
refreshLogs();
}
}
});
// Auto-submit form when filters change
const filterForm = document.querySelector('.filter-container form');
const autoSubmitElements = ['device_id', 'severity', 'per_page'];
autoSubmitElements.forEach(id => {
const element = document.getElementById(id);
if (element) {
element.addEventListener('change', () => filterForm.submit());
}
});
});
</script>
{% endblock %}