mirror of
https://codeberg.org/PostERG/xamxam.git
synced 2026-09-25 01:53:03 +02:00
logs: standardise log filenames to xamxam-{service}-{date}.log
This commit is contained in:
@@ -53,17 +53,24 @@ if ($activeTab === 'status' || !array_key_exists($activeTab, SystemController::L
|
||||
$activeTab = 'app';
|
||||
}
|
||||
|
||||
// Optional date selection (YYYY-MM-DD) for daily app logs.
|
||||
$selectedDate = $_GET['date'] ?? null;
|
||||
if ($selectedDate !== null && !preg_match('/^\d{4}-\d{2}-\d{2}$/', $selectedDate)) {
|
||||
$selectedDate = null;
|
||||
}
|
||||
|
||||
$selectedN = isset($_GET['n']) ? (int) $_GET['n'] : 100;
|
||||
if (!in_array($selectedN, SystemController::ALLOWED_LINES, true)) {
|
||||
$selectedN = 100;
|
||||
}
|
||||
|
||||
$logData = $_controller->getLogData($activeTab, $selectedN);
|
||||
$logData = $_controller->getLogData($activeTab, $selectedN, $selectedDate);
|
||||
$logLines = $logData['lines'];
|
||||
$logError = $logData['error'];
|
||||
$logFileMeta = $logData['meta'];
|
||||
$logIsJson = $logData['isJson'] ?? false;
|
||||
$notYet = $logData['notYet'] ?? false;
|
||||
$logDates = SystemController::listLogDates($activeTab);
|
||||
|
||||
$collapsed = $_COOKIE['sys_collapsed'] ?? null;
|
||||
$statusInitiallyCollapsed = $collapsed === '1';
|
||||
|
||||
@@ -33,6 +33,12 @@ if (!in_array($selectedN, SystemController::ALLOWED_LINES, true)) {
|
||||
$selectedN = 100;
|
||||
}
|
||||
|
||||
// Optional date selection (YYYY-MM-DD) for daily app logs.
|
||||
$selectedDate = $_GET['date'] ?? null;
|
||||
if ($selectedDate !== null && !preg_match('/^\d{4}-\d{2}-\d{2}$/', $selectedDate)) {
|
||||
$selectedDate = null;
|
||||
}
|
||||
|
||||
header('Content-Type: text/html; charset=utf-8');
|
||||
header('X-Robots-Tag: noindex');
|
||||
|
||||
@@ -42,11 +48,12 @@ $_cache = new SystemCache($_db->getPDO());
|
||||
$_controller = new SystemController($_db, $_cache);
|
||||
|
||||
// ── Render ─────────────────────────────────────────────────────────────────
|
||||
$logData = $_controller->getLogData($activeTab, $selectedN);
|
||||
$logData = $_controller->getLogData($activeTab, $selectedN, $selectedDate);
|
||||
$logLines = $logData['lines'];
|
||||
$logError = $logData['error'];
|
||||
$logFileMeta = $logData['meta'];
|
||||
$logIsJson = $logData['isJson'] ?? false;
|
||||
$notYet = $logData['notYet'] ?? false;
|
||||
$logDates = SystemController::listLogDates($activeTab);
|
||||
|
||||
include APP_ROOT . '/templates/admin/partials/system-log-panel.php';
|
||||
|
||||
@@ -3,8 +3,8 @@
|
||||
/**
|
||||
* Admin audit logger.
|
||||
*
|
||||
* Writes JSON-lines to /var/log/xamxam.log (production) or
|
||||
* storage/logs/admin.log (dev / cli-server).
|
||||
* Writes JSON-lines to /var/log/xamxam/xamxam-admin-YYYY-MM-DD.log (production)
|
||||
* or storage/logs/xamxam-admin.log (dev / cli-server).
|
||||
*
|
||||
* Each entry: timestamp, actor (admin IP/UA), action, resource, status, context.
|
||||
*
|
||||
|
||||
@@ -26,25 +26,39 @@ class SystemController
|
||||
'admin' => ['label' => 'Admin — actions', 'path' => null, 'json' => true],
|
||||
'error' => ['label' => 'Erreurs — application', 'path' => null, 'json' => true],
|
||||
'audit' => ['label' => 'Audit — données', 'path' => null, 'json' => true],
|
||||
'nginx_access' => ['label' => 'nginx — accès', 'path' => '/var/log/nginx/xamxam_access.log', 'json' => false],
|
||||
'nginx_error' => ['label' => 'nginx — erreurs','path' => '/var/log/nginx/xamxam_error.log', 'json' => false],
|
||||
'nginx_access' => ['label' => 'nginx — accès', 'path' => '/var/log/nginx/xamxam-nginx-access.log', 'json' => false],
|
||||
'nginx_error' => ['label' => 'nginx — erreurs','path' => '/var/log/nginx/xamxam-nginx-error.log', 'json' => false],
|
||||
];
|
||||
|
||||
/**
|
||||
* Resolve a log file path — app logs live under STORAGE_ROOT, system logs
|
||||
* have hard-coded paths (only valid in production).
|
||||
* Resolve a log file path — app logs live under /var/log/xamxam in
|
||||
* production (and storage/logs in dev/cli-server); nginx logs have
|
||||
* hard-coded paths (only valid in production).
|
||||
*
|
||||
* A $date (YYYY-MM-DD) selects a specific retained daily file for app
|
||||
* channels; when null the most recent file is returned.
|
||||
*/
|
||||
private static function resolveLogPath(string $tab): string
|
||||
private static function resolveLogPath(string $tab, ?string $date = null): string
|
||||
{
|
||||
$def = self::LOG_FILES[$tab];
|
||||
if ($def['path'] !== null) {
|
||||
return $def['path'];
|
||||
}
|
||||
// App logs: storage/logs/{channel}.log (Monolog RotatingFileHandler uses
|
||||
// this as base name; the current log is always at {channel}-YYYY-MM-DD.log)
|
||||
$dir = defined('STORAGE_ROOT') ? STORAGE_ROOT . '/logs' : APP_ROOT . '/storage/logs';
|
||||
// App logs: /var/log/xamxam/xamxam-{channel}.log (production) or
|
||||
// storage/logs/xamxam-{channel}.log (dev / cli-server). Monolog
|
||||
// RotatingFileHandler uses this as base name; the current log is always
|
||||
// at xamxam-{channel}-YYYY-MM-DD.log.
|
||||
$dir = php_sapi_name() === 'cli-server'
|
||||
? APP_ROOT . '/storage/logs'
|
||||
: '/var/log/xamxam';
|
||||
$base = $dir . '/xamxam-' . $tab;
|
||||
|
||||
// Explicit date selection (validated YYYY-MM-DD)
|
||||
if ($date !== null && preg_match('/^\d{4}-\d{2}-\d{2}$/', $date)) {
|
||||
return $base . '-' . $date . '.log';
|
||||
}
|
||||
|
||||
// Find the most recent dated log file for this channel
|
||||
$base = $dir . '/' . $tab;
|
||||
$dated = glob($base . '-20[0-9][0-9]-[0-9][0-9]-[0-9][0-9].log');
|
||||
if (!empty($dated)) {
|
||||
rsort($dated); // newest first
|
||||
@@ -54,6 +68,49 @@ class SystemController
|
||||
return $base . '.log';
|
||||
}
|
||||
|
||||
/**
|
||||
* List the retained daily log files (dates) for an app channel.
|
||||
*
|
||||
* Returns [['date' => 'YYYY-MM-DD', 'label' => '...'], …] newest-first.
|
||||
* Empty array for system logs (nginx) which have fixed names.
|
||||
*/
|
||||
public static function listLogDates(string $tab): array
|
||||
{
|
||||
$def = self::LOG_FILES[$tab];
|
||||
if (($def['json'] ?? false) !== true) {
|
||||
return []; // nginx logs are fixed-name, not daily
|
||||
}
|
||||
|
||||
$dir = php_sapi_name() === 'cli-server'
|
||||
? APP_ROOT . '/storage/logs'
|
||||
: '/var/log/xamxam';
|
||||
$base = $dir . '/xamxam-' . $tab;
|
||||
$dated = glob($base . '-20[0-9][0-9]-[0-9][0-9]-[0-9][0-9].log');
|
||||
if (empty($dated)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
$dates = [];
|
||||
foreach ($dated as $path) {
|
||||
if (preg_match('/(\d{4}-\d{2}-\d{2})\.log$/', $path, $m)) {
|
||||
$dates[] = $m[1];
|
||||
}
|
||||
}
|
||||
rsort($dates); // newest first
|
||||
|
||||
$out = [];
|
||||
foreach ($dates as $d) {
|
||||
$isToday = $d === date('Y-m-d');
|
||||
$out[] = [
|
||||
'date' => $d,
|
||||
'label' => $isToday
|
||||
? 'Aujourd\'hui (' . date('d/m/Y', strtotime($d)) . ')'
|
||||
: date('d/m/Y', strtotime($d)),
|
||||
];
|
||||
}
|
||||
return $out;
|
||||
}
|
||||
|
||||
public const ALLOWED_LINES = [50, 100, 200, 500];
|
||||
|
||||
// ── TTLs ──────────────────────────────────────────────────────────────────
|
||||
@@ -157,11 +214,13 @@ class SystemController
|
||||
/**
|
||||
* Read and return data for a log tab.
|
||||
*
|
||||
* @param string|null $date YYYY-MM-DD to select a specific daily file
|
||||
* (app channels); null = most recent.
|
||||
* @return array{lines: ?array, error: ?string, meta: ?array}
|
||||
*/
|
||||
public function getLogData(string $tab, int $n): array
|
||||
public function getLogData(string $tab, int $n, ?string $date = null): array
|
||||
{
|
||||
$logPath = self::resolveLogPath($tab);
|
||||
$logPath = self::resolveLogPath($tab, $date);
|
||||
$isJson = self::LOG_FILES[$tab]['json'] ?? false;
|
||||
$error = null;
|
||||
$rawLines = null;
|
||||
|
||||
+10
-4
@@ -55,7 +55,7 @@ class Logger
|
||||
|
||||
try {
|
||||
$handler = new RotatingFileHandler(
|
||||
$logDir . '/' . $channel . '.log',
|
||||
$logDir . '/xamxam-' . $channel . '.log',
|
||||
30, // keep 30 days of logs
|
||||
self::level()
|
||||
);
|
||||
@@ -99,12 +99,18 @@ class Logger
|
||||
|
||||
/**
|
||||
* Resolve the log directory.
|
||||
*
|
||||
* Production: /var/log/xamxam/ (standard system log root, provisioned by
|
||||
* setup-server.sh; owned by www-data:xamxam). Dev (cli-server): the
|
||||
* app's local storage/logs/ so no root provisioning is needed.
|
||||
*/
|
||||
private static function logDir(): string
|
||||
{
|
||||
if (defined('STORAGE_ROOT')) {
|
||||
return STORAGE_ROOT . '/logs';
|
||||
// cli-server (dev) → app-local storage/logs; everything else (php-fpm
|
||||
// production) → the standard system log directory.
|
||||
if (php_sapi_name() === 'cli-server') {
|
||||
return __DIR__ . '/../storage/logs';
|
||||
}
|
||||
return __DIR__ . '/../storage/logs';
|
||||
return '/var/log/xamxam';
|
||||
}
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ just migrate # run pending migrations
|
||||
| `schema.sql` | Full, fully-migrated schema + seed data (regenerated from the local DB) |
|
||||
| `backups/` | `just backup-snapshot` hot-backups (`*.db.gz`) |
|
||||
| `cache/` | Runtime cache (rate limits, etc.) |
|
||||
| `logs/` | Runtime logs (admin, audit) — **outside the webroot** |
|
||||
| `logs/` | Dev-only runtime logs (cli-server); production logs live in `/var/log/xamxam/` |
|
||||
| `covers/` | Cover images |
|
||||
| `theses/`, `tfe/`, `tmp/` | Uploaded files / staging |
|
||||
|
||||
|
||||
@@ -386,8 +386,8 @@
|
||||
|
||||
<p class="sys-refresh-note">
|
||||
Affiché le <?= date('d/m/Y à H:i:s') ?> —
|
||||
<a href="?tab=<?= htmlspecialchars($activeTab) ?>&n=<?= $selectedN ?>">Rafraîchir</a> —
|
||||
<a href="?tab=<?= htmlspecialchars($activeTab) ?>&n=<?= $selectedN ?>&refresh=1">Forcer actualisation</a>
|
||||
<a href="?tab=<?= htmlspecialchars($activeTab) ?>&date=<?= htmlspecialchars($selectedDate ?? '') ?>&n=<?= $selectedN ?>">Rafraîchir</a> —
|
||||
<a href="?tab=<?= htmlspecialchars($activeTab) ?>&date=<?= htmlspecialchars($selectedDate ?? '') ?>&n=<?= $selectedN ?>&refresh=1">Forcer actualisation</a>
|
||||
</p>
|
||||
|
||||
<div class="sys-status-header">
|
||||
@@ -460,11 +460,11 @@
|
||||
|
||||
<nav class="sys-tabs" aria-label="Journaux et configuration">
|
||||
<?php foreach (SystemController::LOG_FILES as $key => $def): ?>
|
||||
<a href="?tab=<?= htmlspecialchars($key) ?>&n=<?= $selectedN ?>"
|
||||
<a href="?tab=<?= htmlspecialchars($key) ?>&date=<?= htmlspecialchars($selectedDate ?? '') ?>&n=<?= $selectedN ?>"
|
||||
class="sys-tab <?= $activeTab === $key ? 'active' : '' ?>"
|
||||
hx-get="/admin/system-fragment.php?tab=<?= htmlspecialchars($key) ?>&n=<?= $selectedN ?>"
|
||||
hx-get="/admin/system-fragment.php?tab=<?= htmlspecialchars($key) ?>&date=<?= htmlspecialchars($selectedDate ?? '') ?>&n=<?= $selectedN ?>"
|
||||
hx-target="#sys-tab-panel"
|
||||
hx-push-url="?tab=<?= htmlspecialchars($key) ?>&n=<?= $selectedN ?>"
|
||||
hx-push-url="?tab=<?= htmlspecialchars($key) ?>&date=<?= htmlspecialchars($selectedDate ?? '') ?>&n=<?= $selectedN ?>"
|
||||
hx-swap="innerHTML"
|
||||
hx-indicator="#sys-tab-panel"
|
||||
data-tab="<?= htmlspecialchars($key) ?>"
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
hx-swap="innerHTML"
|
||||
hx-indicator="#sys-tab-panel"
|
||||
hx-trigger="change"
|
||||
hx-vals='{"tab":"<?= htmlspecialchars($activeTab) ?>"}'>
|
||||
hx-vals='{"tab":"<?= htmlspecialchars($activeTab) ?>","date":"<?= htmlspecialchars($selectedDate ?? '') ?>"}'>
|
||||
<label for="lines-select">Afficher</label>
|
||||
<select id="lines-select" name="n" aria-label="Nombre de lignes">
|
||||
<?php foreach (SystemController::ALLOWED_LINES as $opt): ?>
|
||||
@@ -12,6 +12,23 @@
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</form>
|
||||
|
||||
<?php if (!empty($logDates)): ?>
|
||||
<form id="date-form" hx-get="/admin/system-fragment.php"
|
||||
hx-target="#sys-tab-panel"
|
||||
hx-swap="innerHTML"
|
||||
hx-indicator="#sys-tab-panel"
|
||||
hx-trigger="change"
|
||||
hx-vals='{"tab":"<?= htmlspecialchars($activeTab) ?>","n":<?= $selectedN ?>}'>
|
||||
<label for="date-select">Jour</label>
|
||||
<select id="date-select" name="date" aria-label="Jour du journal">
|
||||
<?php foreach ($logDates as $d): ?>
|
||||
<option value="<?= htmlspecialchars($d['date']) ?>" <?= ($selectedDate ?? null) === $d['date'] ? 'selected' : '' ?>><?= htmlspecialchars($d['label']) ?></option>
|
||||
<?php endforeach; ?>
|
||||
</select>
|
||||
</form>
|
||||
<?php endif; ?>
|
||||
|
||||
<?php if ($logLines !== null && count($logLines) > 0): ?>
|
||||
<span class="log-count-badge"><?= count($logLines) ?> ligne(s)</span>
|
||||
<?php endif; ?>
|
||||
|
||||
Reference in New Issue
Block a user