feat: verbose logging, file persistence, auto websocket log channel
- WebSocketHandler: log connection rejections, unknown app keys, message drops - Logger: persist all output to file via Laravel Log facade - ServiceProvider: auto-register 'websocket' daily log channel
This commit is contained in:
parent
781e329601
commit
859fcb6f89
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
namespace BlaxSoftware\LaravelWebSockets\Server\Loggers;
|
||||
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Symfony\Component\Console\Formatter\OutputFormatterStyle;
|
||||
use Symfony\Component\Console\Output\OutputInterface;
|
||||
|
||||
|
|
@ -15,14 +16,14 @@ class Logger
|
|||
protected $consoleOutput;
|
||||
|
||||
/**
|
||||
* Wether the logger is enabled.
|
||||
* Whether the logger is enabled.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
protected $enabled = false;
|
||||
|
||||
/**
|
||||
* Wether the verbose mode is on.
|
||||
* Whether the verbose mode is on.
|
||||
*
|
||||
* @var bool
|
||||
*/
|
||||
|
|
@ -116,10 +117,38 @@ class Logger
|
|||
$this->line($message, 'error');
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a message to the console and persist it to the websocket log file.
|
||||
*/
|
||||
protected function line(string $message, string $style)
|
||||
{
|
||||
// Console output (existing behavior)
|
||||
$this->consoleOutput->writeln(
|
||||
$style ? "<{$style}>{$message}</{$style}>" : $message
|
||||
);
|
||||
|
||||
// Also persist to log file so errors are visible outside the console
|
||||
$this->fileLog($style, $message);
|
||||
}
|
||||
|
||||
/**
|
||||
* Write a message to the websocket log channel.
|
||||
* Uses the 'websocket' channel if available, falls back to the default.
|
||||
*/
|
||||
protected function fileLog(string $level, string $message): void
|
||||
{
|
||||
// Map console styles to log levels
|
||||
$logLevel = match ($level) {
|
||||
'error' => 'error',
|
||||
'warning' => 'warning',
|
||||
default => 'info',
|
||||
};
|
||||
|
||||
try {
|
||||
$channel = config('logging.channels.websocket') ? 'websocket' : null;
|
||||
Log::channel($channel)->log($logLevel, '[WebSocket] '.$message);
|
||||
} catch (\Throwable) {
|
||||
// Logging must never crash the WS server
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ use BlaxSoftware\LaravelWebSockets\Events\NewConnection;
|
|||
use BlaxSoftware\LaravelWebSockets\Helpers;
|
||||
use BlaxSoftware\LaravelWebSockets\Server\Exceptions\WebSocketException;
|
||||
use Exception;
|
||||
use Illuminate\Support\Facades\Log;
|
||||
use Illuminate\Support\Str;
|
||||
use Ratchet\ConnectionInterface;
|
||||
use Ratchet\RFC6455\Messaging\MessageInterface;
|
||||
|
|
@ -45,6 +46,7 @@ class WebSocketHandler implements MessageComponentInterface
|
|||
public function onOpen(ConnectionInterface $connection)
|
||||
{
|
||||
if (! $this->connectionCanBeMade($connection)) {
|
||||
$this->wsLog('warning', 'Connection rejected: server not accepting new connections');
|
||||
return $connection->close();
|
||||
}
|
||||
|
||||
|
|
@ -64,6 +66,8 @@ class WebSocketHandler implements MessageComponentInterface
|
|||
|
||||
$this->channelManager->connectionPonged($connection);
|
||||
|
||||
$this->wsLog('info', "[{$connection->app->id}][{$connection->socketId}] Connection established (key: {$connection->app->key})");
|
||||
|
||||
NewConnection::dispatch($connection->app->id, $connection->socketId);
|
||||
}
|
||||
} catch (WebSocketException $exception) {
|
||||
|
|
@ -84,6 +88,7 @@ class WebSocketHandler implements MessageComponentInterface
|
|||
public function onMessage(ConnectionInterface $connection, MessageInterface $message)
|
||||
{
|
||||
if (! isset($connection->app)) {
|
||||
$this->wsLog('warning', 'Message dropped: connection has no app (likely failed auth). Payload: '.Str::limit($message->getPayload(), 200));
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -170,6 +175,10 @@ class WebSocketHandler implements MessageComponentInterface
|
|||
$exception->getPayload()
|
||||
));
|
||||
}
|
||||
|
||||
$appId = $connection->app->id ?? 'unknown';
|
||||
$socketId = $connection->socketId ?? 'unknown';
|
||||
$this->wsLog('error', "[{$appId}][{$socketId}] {$exception->getMessage()}");
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -201,6 +210,7 @@ class WebSocketHandler implements MessageComponentInterface
|
|||
App::findByKey($appKey)
|
||||
->then(function ($app) use ($appKey, $connection, $deferred) {
|
||||
if (! $app) {
|
||||
$this->wsLog('error', "Unknown app key: '{$appKey}'. Check that PUSHER_APP_KEY in .env matches the key used by the frontend. Configured apps: ".implode(', ', array_map(fn ($a) => $a['key'] ?? 'null', config('websockets.apps', []))));
|
||||
$deferred->reject(new Exceptions\UnknownAppKey($appKey));
|
||||
}
|
||||
|
||||
|
|
@ -306,4 +316,18 @@ class WebSocketHandler implements MessageComponentInterface
|
|||
{
|
||||
return str_ends_with($event, '.' . $action) || str_ends_with($event, ':' . $action);
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a WebSocket server message.
|
||||
* Uses the 'websocket' channel if configured, falls back to the default channel.
|
||||
*/
|
||||
protected function wsLog(string $level, string $message): void
|
||||
{
|
||||
try {
|
||||
$channel = config('logging.channels.websocket') ? 'websocket' : config('logging.default');
|
||||
Log::channel($channel)->log($level, '[WebSocket] '.$message);
|
||||
} catch (\Throwable) {
|
||||
// Logging must never break the server
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ class WebSocketsServiceProvider extends ServiceProvider
|
|||
__DIR__ . '/Websocket' => app_path('Websocket')
|
||||
]);
|
||||
|
||||
$this->registerWebsocketLogChannel();
|
||||
$this->registerDefaultWebsocketChannels();
|
||||
$this->registerEventLoop();
|
||||
$this->registerWebSocketHandler();
|
||||
|
|
@ -85,6 +86,19 @@ class WebSocketsServiceProvider extends ServiceProvider
|
|||
}
|
||||
}
|
||||
|
||||
protected function registerWebsocketLogChannel()
|
||||
{
|
||||
// Register a dedicated 'websocket' log channel if the app hasn't defined one
|
||||
if (! config('logging.channels.websocket')) {
|
||||
config(['logging.channels.websocket' => [
|
||||
'driver' => 'daily',
|
||||
'path' => storage_path('logs/websocket.log'),
|
||||
'level' => 'debug',
|
||||
'days' => 7,
|
||||
]]);
|
||||
}
|
||||
}
|
||||
|
||||
protected function registerWebSocketHandler()
|
||||
{
|
||||
$this->app->singleton('websockets.handler', function () {
|
||||
|
|
|
|||
Loading…
Reference in New Issue