diff --git a/.editorconfig b/.editorconfig index cd8eb86efa..32de2af6aa 100644 --- a/.editorconfig +++ b/.editorconfig @@ -11,5 +11,8 @@ end_of_line = lf insert_final_newline = true trim_trailing_whitespace = true +[*.blade.php] +indent_size = 2 + [*.md] trim_trailing_whitespace = false diff --git a/config/websockets.php b/config/websockets.php index 1c9f61f2f7..13aac01bc9 100644 --- a/config/websockets.php +++ b/config/websockets.php @@ -191,9 +191,13 @@ | store them into an array and then store them into the database | on each interval. | + | You can opt-in to avoid any statistics storage by setting the logger + | to the built-in NullLogger. + | */ - 'logger' => BeyondCode\LaravelWebSockets\Statistics\Logger\HttpStatisticsLogger::class, + 'logger' => \BeyondCode\LaravelWebSockets\Statistics\Logger\HttpStatisticsLogger::class, + // 'logger' => \BeyondCode\LaravelWebSockets\Statistics\Logger\NullStatisticsLogger::class, /* |-------------------------------------------------------------------------- diff --git a/resources/views/dashboard.blade.php b/resources/views/dashboard.blade.php index e4a761b905..33a69b17c1 100644 --- a/resources/views/dashboard.blade.php +++ b/resources/views/dashboard.blade.php @@ -1,265 +1,423 @@ - - WebSockets Dashboard - - - - - + + + WebSockets Dashboard + + + + + + + + + + + - -
-
-
-
- - - - - - -
-
+ +
+
+
+ Connect to app +
+ +
+
+ +
+ +
-
-
-

Realtime Statistics

-
-
-
-

Event Creator

-
-
-
- -
-
- -
-
-
-
-
- -
-
-
-
-
- -
-
-
-
-

Events

- - - - - - - - - - - - - - - -
TypeDetailsTime
@{{ log.type }}
@{{ log.details }}
@{{ log.time }}
+
+ + +
+
+
+ +
+
+ Live statistics
-
+ +
+
+ +
+
+ Send payload event to channel +
+ +
+
+ + +
+
+ + +
+
+ +
+ + + +
+ +
+ +
+
+ +
+
+ Server activity +
+ +
+
+ + + + + + + + + + + + + + + +
+ Type + + Details + + Time +
+
+ @{{ log.type }} +
+
+
@{{ log.details }}
+
+ @{{ log.time }} +
+
+
+
+
diff --git a/src/Dashboard/DashboardLogger.php b/src/Dashboard/DashboardLogger.php index 2b00d3f0d6..f5d0980873 100644 --- a/src/Dashboard/DashboardLogger.php +++ b/src/Dashboard/DashboardLogger.php @@ -3,16 +3,14 @@ namespace BeyondCode\LaravelWebSockets\Dashboard; use BeyondCode\LaravelWebSockets\WebSockets\Channels\ChannelManager; -use Ratchet\ConnectionInterface; -use stdClass; class DashboardLogger { const LOG_CHANNEL_PREFIX = 'private-websockets-dashboard-'; - const TYPE_DISCONNECTION = 'disconnection'; + const TYPE_DISCONNECTED = 'disconnected'; - const TYPE_CONNECTION = 'connection'; + const TYPE_CONNECTED = 'connected'; const TYPE_VACATED = 'vacated'; @@ -20,7 +18,7 @@ class DashboardLogger const TYPE_SUBSCRIBED = 'subscribed'; - const TYPE_CLIENT_MESSAGE = 'client-message'; + const TYPE_WS_MESSAGE = 'ws-message'; const TYPE_API_MESSAGE = 'api-message'; @@ -28,101 +26,36 @@ class DashboardLogger const TYPE_REPLICATOR_UNSUBSCRIBED = 'replicator-unsubscribed'; - public static function connection(ConnectionInterface $connection) - { - /** @var \GuzzleHttp\Psr7\Request $request */ - $request = $connection->httpRequest; - - static::log($connection->app->id, static::TYPE_CONNECTION, [ - 'details' => [ - 'origin' => "{$request->getUri()->getScheme()}://{$request->getUri()->getHost()}", - 'socketId' => $connection->socketId, - ], - ]); - } - - public static function occupied(ConnectionInterface $connection, string $channelName) - { - static::log($connection->app->id, static::TYPE_OCCUPIED, [ - 'details' => [ - 'channel' => $channelName, - ], - ]); - } - - public static function subscribed(ConnectionInterface $connection, string $channelName) - { - static::log($connection->app->id, static::TYPE_SUBSCRIBED, [ - 'details' => [ - 'socketId' => $connection->socketId, - 'channel' => $channelName, - ], - ]); - } - - public static function clientMessage(ConnectionInterface $connection, stdClass $payload) - { - static::log($connection->app->id, static::TYPE_CLIENT_MESSAGE, [ - 'details' => [ - 'socketId' => $connection->socketId, - 'channel' => $payload->channel, - 'event' => $payload->event, - 'data' => $payload, - ], - ]); - } - - public static function disconnection(ConnectionInterface $connection) - { - static::log($connection->app->id, static::TYPE_DISCONNECTION, [ - 'details' => [ - 'socketId' => $connection->socketId, - ], - ]); - } - - public static function vacated(ConnectionInterface $connection, string $channelName) - { - static::log($connection->app->id, static::TYPE_VACATED, [ - 'details' => [ - 'socketId' => $connection->socketId, - 'channel' => $channelName, - ], - ]); - } - - public static function apiMessage($appId, string $channel, string $event, string $payload) - { - static::log($appId, static::TYPE_API_MESSAGE, [ - 'details' => [ - 'channel' => $connection, - 'event' => $event, - 'payload' => $payload, - ], - ]); - } - - public static function replicatorSubscribed(string $appId, string $channel, string $serverId) - { - static::log($appId, static::TYPE_REPLICATOR_SUBSCRIBED, [ - 'details' => [ - 'serverId' => $serverId, - 'channel' => $channel, - ], - ]); - } - - public static function replicatorUnsubscribed(string $appId, string $channel, string $serverId) - { - static::log($appId, static::TYPE_REPLICATOR_UNSUBSCRIBED, [ - 'details' => [ - 'serverId' => $serverId, - 'channel' => $channel, - ], - ]); - } - - public static function log($appId, string $type, array $attributes = []) + const TYPE_REPLICATOR_JOINED_CHANNEL = 'replicator-joined'; + + const TYPE_REPLICATOR_LEFT_CHANNEL = 'replicator-left'; + + const TYPE_REPLICATOR_MESSAGE_PUBLISHED = 'replicator-message-published'; + + const TYPE_REPLICATOR_MESSAGE_RECEIVED = 'replicator-message-received'; + + /** + * The list of all channels. + * + * @var array + */ + public static $channels = [ + self::TYPE_DISCONNECTED, + self::TYPE_CONNECTED, + self::TYPE_VACATED, + self::TYPE_OCCUPIED, + self::TYPE_SUBSCRIBED, + self::TYPE_WS_MESSAGE, + self::TYPE_API_MESSAGE, + self::TYPE_REPLICATOR_SUBSCRIBED, + self::TYPE_REPLICATOR_UNSUBSCRIBED, + self::TYPE_REPLICATOR_JOINED_CHANNEL, + self::TYPE_REPLICATOR_LEFT_CHANNEL, + self::TYPE_REPLICATOR_MESSAGE_PUBLISHED, + self::TYPE_REPLICATOR_MESSAGE_RECEIVED, + ]; + + public static function log($appId, string $type, array $details = []) { $channelName = static::LOG_CHANNEL_PREFIX.$type; @@ -134,7 +67,8 @@ public static function log($appId, string $type, array $attributes = []) 'data' => [ 'type' => $type, 'time' => strftime('%H:%M:%S'), - ] + $attributes, + 'details' => $details, + ], ]); } } diff --git a/src/Dashboard/Http/Controllers/ShowDashboard.php b/src/Dashboard/Http/Controllers/ShowDashboard.php index 47088ef515..7f22a45dd3 100644 --- a/src/Dashboard/Http/Controllers/ShowDashboard.php +++ b/src/Dashboard/Http/Controllers/ShowDashboard.php @@ -3,6 +3,7 @@ namespace BeyondCode\LaravelWebSockets\Dashboard\Http\Controllers; use BeyondCode\LaravelWebSockets\Apps\AppManager; +use BeyondCode\LaravelWebSockets\Dashboard\DashboardLogger; use Illuminate\Http\Request; class ShowDashboard @@ -12,6 +13,8 @@ public function __invoke(Request $request, AppManager $apps) return view('websockets::dashboard', [ 'apps' => $apps->all(), 'port' => config('websockets.dashboard.port', 6001), + 'channels' => DashboardLogger::$channels, + 'logPrefix' => DashboardLogger::LOG_CHANNEL_PREFIX, ]); } } diff --git a/src/Facades/StatisticsLogger.php b/src/Facades/StatisticsLogger.php index 9aadfa7425..518334279a 100644 --- a/src/Facades/StatisticsLogger.php +++ b/src/Facades/StatisticsLogger.php @@ -6,7 +6,7 @@ use Illuminate\Support\Facades\Facade; /** - * @see \BeyondCode\LaravelWebSockets\Statistics\Logger\HttpStatisticsLogger + * @see \BeyondCode\LaravelWebSockets\Statistics\Logger\HttpStatisticsLogger * @mixin \BeyondCode\LaravelWebSockets\Statistics\Logger\HttpStatisticsLogger */ class StatisticsLogger extends Facade diff --git a/src/HttpApi/Controllers/TriggerEventController.php b/src/HttpApi/Controllers/TriggerEventController.php index bc921e4f1f..819d417878 100644 --- a/src/HttpApi/Controllers/TriggerEventController.php +++ b/src/HttpApi/Controllers/TriggerEventController.php @@ -21,12 +21,11 @@ public function __invoke(Request $request) 'data' => $request->json()->get('data'), ], $request->json()->get('socket_id'), $request->appId); - DashboardLogger::apiMessage( - $request->appId, - $channelName, - $request->json()->get('name'), - $request->json()->get('data') - ); + DashboardLogger::log($request->appId, DashboardLogger::TYPE_API_MESSAGE, [ + 'channel' => $channelName, + 'event' => $request->json()->get('name'), + 'payload' => $request->json()->get('data'), + ]); StatisticsLogger::apiMessage($request->appId); } diff --git a/src/PubSub/Drivers/LocalClient.php b/src/PubSub/Drivers/LocalClient.php index 3e24c73f8b..8209e83803 100644 --- a/src/PubSub/Drivers/LocalClient.php +++ b/src/PubSub/Drivers/LocalClient.php @@ -78,7 +78,7 @@ public function unsubscribe(string $appId, string $channel): bool */ public function joinChannel(string $appId, string $channel, string $socketId, string $data) { - $this->channelData["$appId:$channel"][$socketId] = $data; + $this->channelData["{$appId}:{$channel}"][$socketId] = $data; } /** @@ -92,10 +92,10 @@ public function joinChannel(string $appId, string $channel, string $socketId, st */ public function leaveChannel(string $appId, string $channel, string $socketId) { - unset($this->channelData["$appId:$channel"][$socketId]); + unset($this->channelData["{$appId}:{$channel}"][$socketId]); - if (empty($this->channelData["$appId:$channel"])) { - unset($this->channelData["$appId:$channel"]); + if (empty($this->channelData["{$appId}:{$channel}"])) { + unset($this->channelData["{$appId}:{$channel}"]); } } @@ -108,7 +108,7 @@ public function leaveChannel(string $appId, string $channel, string $socketId) */ public function channelMembers(string $appId, string $channel): PromiseInterface { - $members = $this->channelData["$appId:$channel"] ?? []; + $members = $this->channelData["{$appId}:{$channel}"] ?? []; $members = array_map(function ($user) { return json_decode($user); @@ -130,8 +130,8 @@ public function channelMemberCounts(string $appId, array $channelNames): Promise // Count the number of users per channel foreach ($channelNames as $channel) { - $results[$channel] = isset($this->channelData["$appId:$channel"]) - ? count($this->channelData["$appId:$channel"]) + $results[$channel] = isset($this->channelData["{$appId}:{$channel}"]) + ? count($this->channelData["{$appId}:{$channel}"]) : 0; } diff --git a/src/PubSub/Drivers/RedisClient.php b/src/PubSub/Drivers/RedisClient.php index ef48149414..11a479edd6 100644 --- a/src/PubSub/Drivers/RedisClient.php +++ b/src/PubSub/Drivers/RedisClient.php @@ -12,7 +12,7 @@ use React\Promise\PromiseInterface; use stdClass; -class RedisClient implements ReplicationInterface +class RedisClient extends LocalClient { /** * The running loop. @@ -90,49 +90,30 @@ public function boot(LoopInterface $loop, $factoryClass = null): ReplicationInte } /** - * Handle a message received from Redis on a specific channel. + * Publish a message to a channel on behalf of a websocket user. * - * @param string $redisChannel - * @param string $payload - * @return void + * @param string $appId + * @param string $channel + * @param stdClass $payload + * @return bool */ - protected function onMessage(string $redisChannel, string $payload) + public function publish(string $appId, string $channel, stdClass $payload): bool { - $payload = json_decode($payload); - - // Ignore messages sent by ourselves. - if (isset($payload->serverId) && $this->serverId === $payload->serverId) { - return; - } - - // Pull out the app ID. See RedisPusherBroadcaster - $appId = $payload->appId; - - // We need to put the channel name in the payload. - // We strip the app ID from the channel name, websocket clients - // expect the channel name to not include the app ID. - $payload->channel = Str::after($redisChannel, "{$appId}:"); - - $channelManager = app(ChannelManager::class); - - // Load the Channel instance to sync. - $channel = $channelManager->find($appId, $payload->channel); + $payload->appId = $appId; + $payload->serverId = $this->getServerId(); - // If no channel is found, none of our connections want to - // receive this message, so we ignore it. - if (! $channel) { - return; - } + $payload = json_encode($payload); - $socket = $payload->socket ?? null; + $this->publishClient->__call('publish', ["{$appId}:{$channel}", $payload]); - // Remove fields intended for internal use from the payload. - unset($payload->socket); - unset($payload->serverId); - unset($payload->appId); + DashboardLogger::log($appId, DashboardLogger::TYPE_REPLICATOR_MESSAGE_PUBLISHED, [ + 'channel' => $channel, + 'serverId' => $this->getServerId(), + 'payload' => $payload, + 'pubsub' => "{$appId}:{$channel}", + ]); - // Push the message out to connected websocket clients. - $channel->broadcastToEveryoneExcept($payload, $socket, $appId, false); + return true; } /** @@ -144,16 +125,20 @@ protected function onMessage(string $redisChannel, string $payload) */ public function subscribe(string $appId, string $channel): bool { - if (! isset($this->subscribedChannels["$appId:$channel"])) { + if (! isset($this->subscribedChannels["{$appId}:{$channel}"])) { // We're not subscribed to the channel yet, subscribe and set the count to 1 - $this->subscribeClient->__call('subscribe', ["$appId:$channel"]); - $this->subscribedChannels["$appId:$channel"] = 1; + $this->subscribeClient->__call('subscribe', ["{$appId}:{$channel}"]); + $this->subscribedChannels["{$appId}:{$channel}"] = 1; } else { // Increment the subscribe count if we've already subscribed - $this->subscribedChannels["$appId:$channel"]++; + $this->subscribedChannels["{$appId}:{$channel}"]++; } - DashboardLogger::replicatorSubscribed($appId, $channel, $this->serverId); + DashboardLogger::log($appId, DashboardLogger::TYPE_REPLICATOR_SUBSCRIBED, [ + 'channel' => $channel, + 'serverId' => $this->getServerId(), + 'pubsub' => "{$appId}:{$channel}", + ]); return true; } @@ -167,39 +152,25 @@ public function subscribe(string $appId, string $channel): bool */ public function unsubscribe(string $appId, string $channel): bool { - if (! isset($this->subscribedChannels["$appId:$channel"])) { + if (! isset($this->subscribedChannels["{$appId}:{$channel}"])) { return false; } // Decrement the subscription count for this channel - $this->subscribedChannels["$appId:$channel"]--; + $this->subscribedChannels["{$appId}:{$channel}"]--; // If we no longer have subscriptions to that channel, unsubscribe - if ($this->subscribedChannels["$appId:$channel"] < 1) { - $this->subscribeClient->__call('unsubscribe', ["$appId:$channel"]); + if ($this->subscribedChannels["{$appId}:{$channel}"] < 1) { + $this->subscribeClient->__call('unsubscribe', ["{$appId}:{$channel}"]); - unset($this->subscribedChannels["$appId:$channel"]); + unset($this->subscribedChannels["{$appId}:{$channel}"]); } - DashboardLogger::replicatorUnsubscribed($appId, $channel, $this->serverId); - - return true; - } - - /** - * Publish a message to a channel on behalf of a websocket user. - * - * @param string $appId - * @param string $channel - * @param stdClass $payload - * @return bool - */ - public function publish(string $appId, string $channel, stdClass $payload): bool - { - $payload->appId = $appId; - $payload->serverId = $this->serverId; - - $this->publishClient->__call('publish', ["$appId:$channel", json_encode($payload)]); + DashboardLogger::log($appId, DashboardLogger::TYPE_REPLICATOR_UNSUBSCRIBED, [ + 'channel' => $channel, + 'serverId' => $this->getServerId(), + 'pubsub' => "{$appId}:{$channel}", + ]); return true; } @@ -216,7 +187,15 @@ public function publish(string $appId, string $channel, stdClass $payload): bool */ public function joinChannel(string $appId, string $channel, string $socketId, string $data) { - $this->publishClient->__call('hset', ["$appId:$channel", $socketId, $data]); + $this->publishClient->__call('hset', ["{$appId}:{$channel}", $socketId, $data]); + + DashboardLogger::log($appId, DashboardLogger::TYPE_REPLICATOR_JOINED_CHANNEL, [ + 'channel' => $channel, + 'serverId' => $this->getServerId(), + 'socketId' => $socketId, + 'data' => $data, + 'pubsub' => "{$appId}:{$channel}", + ]); } /** @@ -230,7 +209,14 @@ public function joinChannel(string $appId, string $channel, string $socketId, st */ public function leaveChannel(string $appId, string $channel, string $socketId) { - $this->publishClient->__call('hdel', ["$appId:$channel", $socketId]); + $this->publishClient->__call('hdel', ["{$appId}:{$channel}", $socketId]); + + DashboardLogger::log($appId, DashboardLogger::TYPE_REPLICATOR_LEFT_CHANNEL, [ + 'channel' => $channel, + 'serverId' => $this->getServerId(), + 'socketId' => $socketId, + 'pubsub' => "{$appId}:{$channel}", + ]); } /** @@ -242,7 +228,7 @@ public function leaveChannel(string $appId, string $channel, string $socketId) */ public function channelMembers(string $appId, string $channel): PromiseInterface { - return $this->publishClient->__call('hgetall', ["$appId:$channel"]) + return $this->publishClient->__call('hgetall', ["{$appId}:{$channel}"]) ->then(function ($members) { // The data is expected as objects, so we need to JSON decode return array_map(function ($user) { @@ -263,7 +249,7 @@ public function channelMemberCounts(string $appId, array $channelNames): Promise $this->publishClient->__call('multi', []); foreach ($channelNames as $channel) { - $this->publishClient->__call('hlen', ["$appId:$channel"]); + $this->publishClient->__call('hlen', ["{$appId}:{$channel}"]); } return $this->publishClient->__call('exec', []) @@ -272,6 +258,62 @@ public function channelMemberCounts(string $appId, array $channelNames): Promise }); } + /** + * Handle a message received from Redis on a specific channel. + * + * @param string $redisChannel + * @param string $payload + * @return void + */ + protected function onMessage(string $redisChannel, string $payload) + { + $payload = json_decode($payload); + + // Ignore messages sent by ourselves. + if (isset($payload->serverId) && $this->getServerId() === $payload->serverId) { + return; + } + + // Pull out the app ID. See RedisPusherBroadcaster + $appId = $payload->appId; + + // We need to put the channel name in the payload. + // We strip the app ID from the channel name, websocket clients + // expect the channel name to not include the app ID. + $payload->channel = Str::after($redisChannel, "{$appId}:"); + + $channelManager = app(ChannelManager::class); + + // Load the Channel instance to sync. + $channel = $channelManager->find($appId, $payload->channel); + + // If no channel is found, none of our connections want to + // receive this message, so we ignore it. + if (! $channel) { + return; + } + + $socket = $payload->socket ?? null; + $serverId = $payload->serverId ?? null; + + // Remove fields intended for internal use from the payload. + unset($payload->socket); + unset($payload->serverId); + unset($payload->appId); + + // Push the message out to connected websocket clients. + $channel->broadcastToEveryoneExcept($payload, $socket, $appId, false); + + DashboardLogger::log($appId, DashboardLogger::TYPE_REPLICATOR_MESSAGE_RECEIVED, [ + 'channel' => $channel->getChannelName(), + 'redisChannel' => $redisChannel, + 'serverId' => $this->getServer(), + 'incomingServerId' => $serverId, + 'incomingSocketId' => $socket, + 'payload' => $payload, + ]); + } + /** * Build the Redis connection URL from Laravel database config. * diff --git a/src/Statistics/Logger/NullStatisticsLogger.php b/src/Statistics/Logger/NullStatisticsLogger.php new file mode 100644 index 0000000000..885703e92b --- /dev/null +++ b/src/Statistics/Logger/NullStatisticsLogger.php @@ -0,0 +1,47 @@ +channelManager = $channelManager; + $this->browser = $browser; + } + + public function webSocketMessage(ConnectionInterface $connection) + { + // + } + + public function apiMessage($appId) + { + // + } + + public function connection(ConnectionInterface $connection) + { + // + } + + public function disconnection(ConnectionInterface $connection) + { + // + } + + public function save() + { + // + } +} diff --git a/src/WebSockets/Channels/Channel.php b/src/WebSockets/Channels/Channel.php index 8e301c113d..cd7e473aed 100644 --- a/src/WebSockets/Channels/Channel.php +++ b/src/WebSockets/Channels/Channel.php @@ -82,7 +82,10 @@ public function unsubscribe(ConnectionInterface $connection) $this->replicator->unsubscribe($connection->app->id, $this->channelName); if (! $this->hasConnections()) { - DashboardLogger::vacated($connection, $this->channelName); + DashboardLogger::log($connection->app->id, DashboardLogger::TYPE_VACATED, [ + 'socketId' => $connection->socketId, + 'channel' => $this->channelName, + ]); } } @@ -93,10 +96,15 @@ protected function saveConnection(ConnectionInterface $connection) $this->subscribedConnections[$connection->socketId] = $connection; if (! $hadConnectionsPreviously) { - DashboardLogger::occupied($connection, $this->channelName); + DashboardLogger::log($connection->app->id, DashboardLogger::TYPE_OCCUPIED, [ + 'channel' => $this->channelName, + ]); } - DashboardLogger::subscribed($connection, $this->channelName); + DashboardLogger::log($connection->app->id, DashboardLogger::TYPE_SUBSCRIBED, [ + 'socketId' => $connection->socketId, + 'channel' => $this->channelName, + ]); } public function broadcast($payload) diff --git a/src/WebSockets/Messages/PusherClientMessage.php b/src/WebSockets/Messages/PusherClientMessage.php index f7c4c4557a..1ef519cdc1 100644 --- a/src/WebSockets/Messages/PusherClientMessage.php +++ b/src/WebSockets/Messages/PusherClientMessage.php @@ -38,7 +38,12 @@ public function respond() return; } - DashboardLogger::clientMessage($this->connection, $this->payload); + DashboardLogger::log($this->connection->app->id, DashboardLogger::TYPE_WS_MESSAGE, [ + 'socketId' => $this->connection->socketId, + 'channel' => $this->payload->channel, + 'event' => $this->payload->event, + 'data' => $this->payload, + ]); $channel = $this->channelManager->find($this->connection->app->id, $this->payload->channel); diff --git a/src/WebSockets/WebSocketHandler.php b/src/WebSockets/WebSocketHandler.php index d2fdb6c10d..7820960b6b 100644 --- a/src/WebSockets/WebSocketHandler.php +++ b/src/WebSockets/WebSocketHandler.php @@ -48,7 +48,9 @@ public function onClose(ConnectionInterface $connection) { $this->channelManager->removeFromAllChannels($connection); - DashboardLogger::disconnection($connection); + DashboardLogger::log($connection->app->id, DashboardLogger::TYPE_DISCONNECTED, [ + 'socketId' => $connection->socketId, + ]); StatisticsLogger::disconnection($connection); } @@ -106,7 +108,13 @@ protected function establishConnection(ConnectionInterface $connection) ]), ])); - DashboardLogger::connection($connection); + /** @var \GuzzleHttp\Psr7\Request $request */ + $request = $connection->httpRequest; + + DashboardLogger::log($connection->app->id, DashboardLogger::TYPE_CONNECTED, [ + 'origin' => "{$request->getUri()->getScheme()}://{$request->getUri()->getHost()}", + 'socketId' => $connection->socketId, + ]); StatisticsLogger::connection($connection);