alpha version of cache debug information

This commit is contained in:
HB9HIL
2026-02-02 11:39:51 +01:00
parent 937f09eae7
commit 4d23c75781
4 changed files with 367 additions and 0 deletions

View File

@@ -105,6 +105,9 @@ class Debug extends CI_Controller
$data['userdata_status'] = $userdata_status;
}
// Cache Info
$data['cache_info'] = $this->debug_model->get_cache_info();
$data['dxcc_update'] = $this->cron_model->cron('update_dxcc')->row();
$data['dok_update'] = $this->cron_model->cron('update_update_dok')->row();
$data['lotw_user_update'] = $this->cron_model->cron('update_lotw_users')->row();
@@ -288,6 +291,22 @@ class Debug extends CI_Controller
echo json_encode($commit_hash);
}
public function clear_cache() {
$this->load->model('user_model');
if ($this->user_model->authorize(2) == false) {
header('Content-Type: application/json');
echo json_encode(['status' => false, 'message' => __("You're not allowed to do that!")]);
return;
}
$this->load->model('Debug_model');
$status = $this->Debug_model->clear_cache();
header('Content-Type: application/json');
echo json_encode(['status' => (bool) $status]);
return;
}
public function migrate_userdata() {
// Check if users logged in
$this->load->model('user_model');

View File

@@ -185,4 +185,261 @@ class Debug_model extends CI_Model
$result = $query->result_array();
return $result;
}
function get_cache_info() {
$response = [];
$cache_path = $this->config->item('cache_path') ?? NULL;
if (!$cache_path && $cache_path !== '') {
$cache_path = ''; // default path is application/cache
$response['config']['cache_path'] = sprintf(__(" %s not set in config, using default path: %s"), "'cache_path'", "application/cache");
} else {
$response['config']['cache_path'] = $cache_path;
}
$cache_adapter = $this->config->item('cache_adapter') ?? NULL;
if (!$cache_adapter) {
$cache_adapter = 'file'; // default adapter is file
$response['config']['cache_adapter'] = sprintf(__(" %s not set in config, using default adapter: %s"), "'cache_adapter'", "file");
} else {
$response['config']['cache_adapter'] = $cache_adapter;
}
$cache_backup = $this->config->item('cache_backup') ?? NULL;
if (!$cache_backup) {
$cache_backup = 'file'; // default backup is file
$response['config']['cache_backup'] = sprintf(__(" %s not set in config, using default backup: %s"), "'cache_backup'", "file");
} else {
$response['config']['cache_backup'] = $cache_backup;
}
$cache_key_prefix = $this->config->item('cache_key_prefix') ?? NULL;
if (!$cache_key_prefix) {
$cache_key_prefix = ''; // default key prefix is empty
$response['config']['cache_key_prefix'] = sprintf(__(" %s not set in config, using default: empty string %s"), "'cache_key_prefix'", "('')");
} else {
$response['config']['cache_key_prefix'] = $cache_key_prefix;
}
// Load cache driver
$this->load->driver('cache', [
'adapter' => $cache_adapter,
'backup' => $cache_backup,
'key_prefix' => $cache_key_prefix
]);
// Get cache details
$cache_size = $this->get_cache_size();
$cache_keys_count = $this->get_cache_keys_count();
$response['details']['size'] = $this->format_bytes($cache_size);
$response['details']['size_bytes'] = $cache_size;
$response['details']['keys_count'] = $cache_keys_count;
$available_adapters = ['file', 'redis', 'memcached', 'apcu'];
foreach ($available_adapters as $adapter) {
$response['adapters'][$adapter] = $this->cache->is_supported($adapter);
}
return $response;
}
public function clear_cache() {
$cache_adapter = $this->config->item('cache_adapter') ?? 'file';
$cache_backup = $this->config->item('cache_backup') ?? 'file';
$cache_key_prefix = $this->config->item('cache_key_prefix') ?? '';
$this->load->driver('cache', [
'adapter' => $cache_adapter,
'backup' => $cache_backup,
'key_prefix' => $cache_key_prefix
]);
if ($cache_adapter === 'file') {
$cache_path = $this->config->item('cache_path') ?: 'application/cache';
$cache_path = realpath(APPPATH . '../') . '/' . $cache_path;
if (!is_dir($cache_path)) {
return false;
}
$files = new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($cache_path, RecursiveDirectoryIterator::SKIP_DOTS),
RecursiveIteratorIterator::CHILD_FIRST
);
foreach ($files as $file) {
if ($file->isFile() && !in_array($file->getFilename(), ['index.html', '.htaccess'])) {
@unlink($file->getRealPath());
}
}
return true;
}
if (method_exists($this->cache, 'clean')) {
return $this->cache->clean();
}
return false;
}
function get_cache_size($adapter = NULL) {
$cache_adapter = $adapter ?? ($this->config->item('cache_adapter') ?? 'file');
switch ($cache_adapter) {
case 'file':
$cache_path = $this->config->item('cache_path') ?: 'application/cache';
$cache_path = realpath(APPPATH . '../') . '/' . $cache_path;
if (!is_dir($cache_path)) {
return 0;
}
$size = 0;
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($cache_path, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST);
foreach ($files as $file) {
if ($file->isFile() && !in_array($file->getFilename(), ['index.html', '.htaccess'])) {
$size += $file->getSize();
}
}
return $size;
case 'redis':
if ($this->cache->is_supported('redis')) {
$redis_info = $this->cache->cache_info('redis');
// Note: This returns total Redis server memory usage, not just cache keys with prefix
// used_memory_dataset excludes overhead and is more accurate for data size
if (isset($redis_info['used_memory_dataset'])) {
return (int)$redis_info['used_memory_dataset'];
}
return isset($redis_info['used_memory']) ? (int)$redis_info['used_memory'] : 0;
}
return 0;
case 'memcached':
if ($this->cache->is_supported('memcached')) {
$memcached_info = $this->cache->cache_info('memcached');
log_message('debug', 'Memcached Info: ' . print_r($memcached_info, true));
// Memcached returns array of servers, each with stats
if (is_array($memcached_info)) {
$total_bytes = 0;
foreach ($memcached_info as $server_stats) {
if (is_array($server_stats)) {
// bytes is the current bytes used in the cache
if (isset($server_stats['bytes'])) {
$total_bytes += (int) $server_stats['bytes'];
}
}
}
return $total_bytes;
}
// Fallback for single server format
if (isset($memcached_info['bytes'])) {
return (int) $memcached_info['bytes'];
}
return 0;
}
return 0;
case 'apcu':
if ($this->cache->is_supported('apcu')) {
$apcu_info = apcu_cache_info();
return isset($apcu_info['mem_size']) ? (int)$apcu_info['mem_size'] : 0;
}
return 0;
default:
return 0;
}
}
function get_cache_keys_count($adapter = NULL) {
$cache_adapter = $adapter ?? ($this->config->item('cache_adapter') ?? 'file');
switch ($cache_adapter) {
case 'file':
$cache_path = $this->config->item('cache_path') ?: 'application/cache';
$cache_path = realpath(APPPATH . '../') . '/' . $cache_path;
if (!is_dir($cache_path)) {
return 0;
}
$count = 0;
$files = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($cache_path, RecursiveDirectoryIterator::SKIP_DOTS), RecursiveIteratorIterator::CHILD_FIRST);
foreach ($files as $file) {
if ($file->isFile() && !in_array($file->getFilename(), ['index.html', '.htaccess'])) {
$count++;
}
}
return $count;
case 'redis':
if ($this->cache->is_supported('redis')) {
$redis_info = $this->cache->cache_info('redis');
$total_keys = 0;
// Parse keyspace info (db0, db1, etc.)
foreach ($redis_info as $key => $value) {
if (preg_match('/^db(\d+)$/', $key) && is_string($value)) {
// Parse "keys=4,expires=4,avg_ttl=43131246" format
if (preg_match('/keys=(\d+)/', $value, $matches)) {
$total_keys += (int)$matches[1];
}
}
}
return $total_keys;
}
return 0;
case 'memcached':
if ($this->cache->is_supported('memcached')) {
$memcached_info = $this->cache->cache_info('memcached');
if (isset($memcached_info['curr_items'])) {
return (int) $memcached_info['curr_items'];
}
if (is_array($memcached_info)) {
$total_items = 0;
foreach ($memcached_info as $server_stats) {
if (is_array($server_stats) && isset($server_stats['curr_items'])) {
$total_items += (int) $server_stats['curr_items'];
}
}
return $total_items;
}
return 0;
}
return 0;
case 'apcu':
if ($this->cache->is_supported('apcu')) {
$apcu_info = apcu_cache_info();
return isset($apcu_info['num_entries']) ? (int)$apcu_info['num_entries'] : 0;
}
return 0;
default:
return 0;
}
}
function format_bytes($bytes, $precision = 2) {
$units = ['B', 'KB', 'MB', 'GB'];
$bytes = max($bytes, 0);
$pow = floor(($bytes ? log($bytes) : 0) / log(1024));
$pow = min($pow, count($units) - 1);
$bytes /= (1 << (10 * $pow));
return round($bytes, $precision) . ' ' . $units[$pow];
}
}

View File

@@ -399,6 +399,74 @@
</div>
</div>
</div>
<!-- Cache Information Card -->
<div class="card">
<div class="card-header"><?= __("Cache Configuration"); ?></div>
<div class="card-body">
<div class="row">
<div class="col-md-6">
<p><u><?= __("Current Configuration"); ?></u></p>
<table width="100%" class="table table-sm table-striped">
<tr>
<td><strong><?= __("Adapter"); ?></strong></td>
<td>
<span class="badge text-bg-info"><?php echo ucfirst($cache_info['config']['cache_adapter'] ?? 'file'); ?></span>
</td>
</tr>
<tr>
<td><strong><?= __("Backup"); ?></strong></td>
<td>
<span class="badge text-bg-secondary"><?php echo ucfirst($cache_info['config']['cache_backup'] ?? 'file'); ?></span>
</td>
</tr>
<tr>
<td><strong><?= __("Path"); ?></strong></td>
<td><code><?php echo $cache_info['config']['cache_path'] ?: 'application/cache'; ?></code></td>
</tr>
<tr>
<td><strong><?= __("Key Prefix"); ?></strong></td>
<td><code><?php echo $cache_info['config']['cache_key_prefix'] ?: __("(empty)"); ?></code></td>
</tr>
</table>
</div>
<div class="col-md-6">
<p><u><?= __("Cache Details"); ?></u></p>
<table width="100%" class="table table-sm table-striped">
<tr>
<td><strong><?= __("Total Size"); ?></strong></td>
<td>
<span class="badge text-bg-primary"><?php echo $cache_info['details']['size'] ?? '0 B'; ?></span>
</td>
</tr>
<tr>
<td><strong><?= __("Number of Keys"); ?></strong></td>
<td>
<span class="badge text-bg-primary"><?php echo $cache_info['details']['keys_count'] ?? '0'; ?></span>
</td>
</tr>
</table>
</div>
</div>
<div class="d-flex justify-content-end">
<button type="button" id="clear_cache_button" class="btn btn-sm btn-danger">
<?= __("Clear Cache"); ?>
</button>
</div>
<div class="border-top pt-3 mt-3">
<p><u><?= __("Available Adapters"); ?></u></p>
<div>
<?php foreach ($cache_info['adapters'] as $adapter => $supported) { ?>
<?php if ($supported) { ?>
<span class="badge text-bg-success"><?php echo ucfirst($adapter); ?></span>
<?php } else { ?>
<span class="badge text-bg-danger"><?php echo ucfirst($adapter); ?></span>
<?php } ?>
<?php } ?>
</div>
</div>
</div>
</div>
<!-- HIER -->
<?php if (file_exists(realpath(APPPATH . '../') . '/.git') && function_usable('exec')) { ?>
<?php
//Below is a failsafe where git commands fail

View File

@@ -101,3 +101,26 @@ function update_version_check(local_branch) {
$('#last_version_check').text("Last version check: " + new Date(timestamp).toUTCString());
});
}
$(document).ready(function () {
$('#clear_cache_button').on('click', function () {
if (!confirm('Cache wirklich löschen?')) {
return;
}
$.ajax({
url: base_url + 'index.php/debug/clear_cache',
type: 'post',
success: function (resu) {
if (resu && resu.status) {
location.reload();
} else {
alert('Cache konnte nicht geleert werden.');
}
},
error: function () {
alert('Cache konnte nicht geleert werden.');
}
});
});
});