Enable Discovery debug logging for production troubleshooting
- Add DISCOVERY_LOG_LEVEL=debug - Add DISCOVERY_SHOW_PROGRESS=true - Temporary changes for debugging InitializerProcessor fixes on production
This commit is contained in:
341
resources/views/admin/database/dashboard.blade.php
Normal file
341
resources/views/admin/database/dashboard.blade.php
Normal file
@@ -0,0 +1,341 @@
|
||||
@extends('admin.layout')
|
||||
|
||||
@section('title', 'Database Dashboard')
|
||||
|
||||
@section('content')
|
||||
<div class="container-fluid">
|
||||
<h1 class="h3 mb-4 text-gray-800">Database Dashboard</h1>
|
||||
|
||||
<div class="row">
|
||||
<!-- Connection Selector -->
|
||||
<div class="col-md-12 mb-4">
|
||||
<div class="card shadow">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 font-weight-bold text-primary">Database Connection</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<select id="connection-selector" class="form-control">
|
||||
@foreach($connections as $conn)
|
||||
<option value="{{ $conn }}" {{ $conn === $activeConnection ? 'selected' : '' }}>
|
||||
{{ $conn }}
|
||||
</option>
|
||||
@endforeach
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Connection Stats -->
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card shadow">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 font-weight-bold text-primary">Connection Information</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered" id="connection-info">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Driver</th>
|
||||
<td>{{ $stats['driver'] ?? 'Unknown' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Version</th>
|
||||
<td>{{ $stats['version'] ?? 'Unknown' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Connection Status</th>
|
||||
<td>{{ $stats['connection_status'] ?? 'Unknown' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Tables</th>
|
||||
<td>{{ $stats['table_count'] ?? 'Unknown' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Indexes</th>
|
||||
<td>{{ $stats['index_count'] ?? 'Unknown' }}</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Total Rows (approx.)</th>
|
||||
<td>{{ number_format($stats['total_rows'] ?? 0) }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Query Stats -->
|
||||
<div class="col-md-6 mb-4">
|
||||
<div class="card shadow">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 font-weight-bold text-primary">Query Statistics</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered" id="query-stats">
|
||||
<tbody>
|
||||
<tr>
|
||||
<th>Total Queries</th>
|
||||
<td id="total-queries">Loading...</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Total Query Time</th>
|
||||
<td id="total-time">Loading...</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Average Query Time</th>
|
||||
<td id="average-time">Loading...</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th>Slow Queries</th>
|
||||
<td id="slow-queries">Loading...</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Recent Queries -->
|
||||
<div class="col-md-12 mb-4">
|
||||
<div class="card shadow">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 font-weight-bold text-primary">Recent Queries</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered" id="recent-queries">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>SQL</th>
|
||||
<th>Parameters</th>
|
||||
<th>Time (ms)</th>
|
||||
<th>Timestamp</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="4" class="text-center">Loading...</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Slow Queries -->
|
||||
<div class="col-md-12 mb-4">
|
||||
<div class="card shadow">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 font-weight-bold text-primary">Slow Queries</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-bordered" id="slow-queries-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>SQL</th>
|
||||
<th>Parameters</th>
|
||||
<th>Time (ms)</th>
|
||||
<th>Timestamp</th>
|
||||
<th>Connection</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td colspan="5" class="text-center">Loading...</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<!-- Database-Specific Stats -->
|
||||
<div class="col-md-12 mb-4">
|
||||
<div class="card shadow">
|
||||
<div class="card-header py-3">
|
||||
<h6 class="m-0 font-weight-bold text-primary">Database-Specific Statistics</h6>
|
||||
</div>
|
||||
<div class="card-body" id="specific-stats">
|
||||
<div class="text-center">
|
||||
<div class="spinner-border" role="status">
|
||||
<span class="sr-only">Loading...</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@endsection
|
||||
|
||||
@section('scripts')
|
||||
<script>
|
||||
$(document).ready(function() {
|
||||
// Load initial data
|
||||
loadQueryStats();
|
||||
loadSpecificStats('{{ $activeConnection }}');
|
||||
|
||||
// Set up connection selector
|
||||
$('#connection-selector').change(function() {
|
||||
const connection = $(this).val();
|
||||
window.location.href = '/admin/database/dashboard?connection=' + connection;
|
||||
});
|
||||
|
||||
// Auto-refresh data every 10 seconds
|
||||
setInterval(function() {
|
||||
loadQueryStats();
|
||||
loadSpecificStats('{{ $activeConnection }}');
|
||||
}, 10000);
|
||||
});
|
||||
|
||||
function loadQueryStats() {
|
||||
$.ajax({
|
||||
url: '/admin/database/queries',
|
||||
method: 'GET',
|
||||
success: function(data) {
|
||||
// Update query stats
|
||||
$('#total-queries').text(data.total_queries);
|
||||
$('#total-time').text(data.total_time.toFixed(2) + ' ms');
|
||||
$('#average-time').text(data.average_time.toFixed(2) + ' ms');
|
||||
$('#slow-queries').text(data.slow_queries);
|
||||
|
||||
// Update recent queries table
|
||||
const recentQueriesHtml = data.recent_queries.map(query => `
|
||||
<tr>
|
||||
<td><code>${escapeHtml(query.sql)}</code></td>
|
||||
<td><code>${JSON.stringify(query.parameters)}</code></td>
|
||||
<td>${query.time.toFixed(2)} ms</td>
|
||||
<td>${new Date(query.timestamp * 1000).toLocaleString()}</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
|
||||
$('#recent-queries tbody').html(recentQueriesHtml || '<tr><td colspan="4" class="text-center">No queries found</td></tr>');
|
||||
|
||||
// Update slow queries table
|
||||
const slowQueriesHtml = data.slow_query_list.map(query => `
|
||||
<tr>
|
||||
<td><code>${escapeHtml(query.sql)}</code></td>
|
||||
<td><code>${JSON.stringify(query.parameters)}</code></td>
|
||||
<td>${query.time.toFixed(2)} ms</td>
|
||||
<td>${new Date(query.timestamp * 1000).toLocaleString()}</td>
|
||||
<td>${query.connection}</td>
|
||||
</tr>
|
||||
`).join('');
|
||||
|
||||
$('#slow-queries-table tbody').html(slowQueriesHtml || '<tr><td colspan="5" class="text-center">No slow queries found</td></tr>');
|
||||
},
|
||||
error: function() {
|
||||
console.error('Failed to load query statistics');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function loadSpecificStats(connection) {
|
||||
$.ajax({
|
||||
url: '/admin/database/specific/' + connection,
|
||||
method: 'GET',
|
||||
success: function(data) {
|
||||
let html = '';
|
||||
|
||||
// MySQL-specific stats
|
||||
if (data.query_cache) {
|
||||
html += '<h5>Query Cache</h5>';
|
||||
html += '<div class="table-responsive"><table class="table table-bordered">';
|
||||
html += '<tbody>';
|
||||
for (const [key, value] of Object.entries(data.query_cache)) {
|
||||
html += `<tr><th>${key}</th><td>${value}</td></tr>`;
|
||||
}
|
||||
html += '</tbody></table></div>';
|
||||
}
|
||||
|
||||
if (data.buffer_pool) {
|
||||
html += '<h5>Buffer Pool</h5>';
|
||||
html += '<div class="table-responsive"><table class="table table-bordered">';
|
||||
html += '<tbody>';
|
||||
for (const [key, value] of Object.entries(data.buffer_pool)) {
|
||||
html += `<tr><th>${key}</th><td>${value}</td></tr>`;
|
||||
}
|
||||
html += '</tbody></table></div>';
|
||||
}
|
||||
|
||||
// PostgreSQL-specific stats
|
||||
if (data.cache) {
|
||||
html += '<h5>Cache Statistics</h5>';
|
||||
html += '<div class="table-responsive"><table class="table table-bordered">';
|
||||
html += '<tbody>';
|
||||
for (const [key, value] of Object.entries(data.cache)) {
|
||||
html += `<tr><th>${key}</th><td>${value}</td></tr>`;
|
||||
}
|
||||
html += '</tbody></table></div>';
|
||||
}
|
||||
|
||||
if (data.long_running && data.long_running.length > 0) {
|
||||
html += '<h5>Long-Running Queries</h5>';
|
||||
html += '<div class="table-responsive"><table class="table table-bordered">';
|
||||
html += '<thead><tr><th>Query</th><th>Duration</th><th>User</th></tr></thead>';
|
||||
html += '<tbody>';
|
||||
for (const query of data.long_running) {
|
||||
html += `<tr>
|
||||
<td><code>${escapeHtml(query.query)}</code></td>
|
||||
<td>${query.duration.toFixed(2)} seconds</td>
|
||||
<td>${query.usename}</td>
|
||||
</tr>`;
|
||||
}
|
||||
html += '</tbody></table></div>';
|
||||
}
|
||||
|
||||
// SQLite-specific stats
|
||||
if (data.database) {
|
||||
html += '<h5>Database Statistics</h5>';
|
||||
html += '<div class="table-responsive"><table class="table table-bordered">';
|
||||
html += '<tbody>';
|
||||
for (const [key, value] of Object.entries(data.database)) {
|
||||
html += `<tr><th>${key}</th><td>${value}</td></tr>`;
|
||||
}
|
||||
html += '</tbody></table></div>';
|
||||
}
|
||||
|
||||
if (data.pragmas) {
|
||||
html += '<h5>PRAGMA Settings</h5>';
|
||||
html += '<div class="table-responsive"><table class="table table-bordered">';
|
||||
html += '<tbody>';
|
||||
for (const [key, value] of Object.entries(data.pragmas)) {
|
||||
html += `<tr><th>${key}</th><td>${value}</td></tr>`;
|
||||
}
|
||||
html += '</tbody></table></div>';
|
||||
}
|
||||
|
||||
$('#specific-stats').html(html || '<div class="text-center">No specific statistics available</div>');
|
||||
},
|
||||
error: function() {
|
||||
console.error('Failed to load database-specific statistics');
|
||||
$('#specific-stats').html('<div class="alert alert-danger">Failed to load database-specific statistics</div>');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function escapeHtml(unsafe) {
|
||||
return unsafe
|
||||
.replace(/&/g, "&")
|
||||
.replace(/</g, "<")
|
||||
.replace(/>/g, ">")
|
||||
.replace(/"/g, """)
|
||||
.replace(/'/g, "'");
|
||||
}
|
||||
</script>
|
||||
@endsection
|
||||
Reference in New Issue
Block a user