51336e346a
Daily logs created by root cron were blocking www from appending, which aborted webhook ingest and bot pushes. File channels now use 0664 plus exception-safe stacks, and writes go through SafeLog. Co-authored-by: Cursor <cursoragent@cursor.com>
94 lines
2.7 KiB
PHP
94 lines
2.7 KiB
PHP
<?php
|
|
|
|
namespace App\Support;
|
|
|
|
use Illuminate\Support\Facades\Log;
|
|
use Throwable;
|
|
|
|
/**
|
|
* Logging that never throws. File permission errors on a dedicated channel
|
|
* (root-owned daily logs vs php-fpm www) must not abort webhooks or Telegram.
|
|
*/
|
|
final class SafeLog
|
|
{
|
|
public static function channel(string $name): SafeLogChannel
|
|
{
|
|
return new SafeLogChannel($name);
|
|
}
|
|
}
|
|
|
|
final class SafeLogChannel
|
|
{
|
|
public function __construct(private readonly string $name) {}
|
|
|
|
/** @param array<string, mixed> $context */
|
|
public function debug(string $message, array $context = []): void
|
|
{
|
|
$this->write('debug', $message, $context);
|
|
}
|
|
|
|
/** @param array<string, mixed> $context */
|
|
public function info(string $message, array $context = []): void
|
|
{
|
|
$this->write('info', $message, $context);
|
|
}
|
|
|
|
/** @param array<string, mixed> $context */
|
|
public function warning(string $message, array $context = []): void
|
|
{
|
|
$this->write('warning', $message, $context);
|
|
}
|
|
|
|
/** @param array<string, mixed> $context */
|
|
public function error(string $message, array $context = []): void
|
|
{
|
|
$this->write('error', $message, $context);
|
|
}
|
|
|
|
/** @param array<string, mixed> $context */
|
|
public function critical(string $message, array $context = []): void
|
|
{
|
|
$this->write('critical', $message, $context);
|
|
}
|
|
|
|
/** @param array<int, mixed> $arguments */
|
|
public function __call(string $level, array $arguments): void
|
|
{
|
|
$message = (string) ($arguments[0] ?? '');
|
|
$context = is_array($arguments[1] ?? null) ? $arguments[1] : [];
|
|
$this->write($level, $message, $context);
|
|
}
|
|
|
|
/** @param array<string, mixed> $context */
|
|
private function write(string $level, string $message, array $context): void
|
|
{
|
|
try {
|
|
Log::channel($this->name)->{$level}($message, $context);
|
|
} catch (Throwable $e) {
|
|
$this->fallback($level, $message, $context, $e);
|
|
}
|
|
}
|
|
|
|
/** @param array<string, mixed> $context */
|
|
private function fallback(string $level, string $message, array $context, Throwable $e): void
|
|
{
|
|
$context['safe_log_channel'] = $this->name;
|
|
$context['safe_log_error'] = $e->getMessage();
|
|
|
|
try {
|
|
$default = (string) config('logging.default', 'stack');
|
|
if ($default !== '' && $default !== $this->name) {
|
|
Log::channel($default)->warning($message, $context);
|
|
|
|
return;
|
|
}
|
|
} catch (Throwable) {
|
|
}
|
|
|
|
try {
|
|
error_log('['.$this->name.'] '.$level.': '.$message.' '.$e->getMessage());
|
|
} catch (Throwable) {
|
|
}
|
|
}
|
|
}
|