feat(logs): Add loading indicator to download all logs buttons (#7847)

This commit is contained in:
Andras Bacsai authored and GitHub committed 2026-01-02 12:05:14 +01:00
commit 006e787e2a
5 files changed
+143 -28

No files matched your search

+39 -6
View File
@@ -23,6 +23,8 @@ class GetLogs extends Component
{
public const MAX_LOG_LINES = 50000;
public const MAX_DOWNLOAD_SIZE_BYTES = 50 * 1024 * 1024; // 50MB
public string $outputs = '';
public string $errors = '';
@@ -164,10 +166,12 @@ class GetLogs extends Component
}
// Collect new logs into temporary variable first to prevent flickering
// (avoids clearing output before new data is ready)
$newOutputs = '';
Process::run($sshCommand, function (string $type, string $output) use (&$newOutputs) {
$newOutputs .= removeAnsiColors($output);
// Use array accumulation + implode for O(n) instead of O(n²) string concatenation
$logChunks = [];
Process::timeout(config('constants.ssh.command_timeout'))->run($sshCommand, function (string $type, string $output) use (&$logChunks) {
$logChunks[] = removeAnsiColors($output);
});
$newOutputs = implode('', $logChunks);
if ($this->showTimeStamps) {
$newOutputs = str($newOutputs)->split('/\n/')->sort(function ($a, $b) {
@@ -215,11 +219,40 @@ class GetLogs extends Component
$sshCommand = SshMultiplexingHelper::generateSshCommand($this->server, $command);
$allLogs = '';
Process::run($sshCommand, function (string $type, string $output) use (&$allLogs) {
$allLogs .= removeAnsiColors($output);
// Use array accumulation + implode for O(n) instead of O(n²) string concatenation
// Enforce 50MB size limit to prevent memory exhaustion from large logs
$logChunks = [];
$accumulatedBytes = 0;
$truncated = false;
Process::timeout(config('constants.ssh.command_timeout'))->run($sshCommand, function (string $type, string $output) use (&$logChunks, &$accumulatedBytes, &$truncated) {
if ($truncated) {
return;
}
$output = removeAnsiColors($output);
$outputBytes = strlen($output);
if ($accumulatedBytes + $outputBytes > self::MAX_DOWNLOAD_SIZE_BYTES) {
$remaining = self::MAX_DOWNLOAD_SIZE_BYTES - $accumulatedBytes;
if ($remaining > 0) {
$logChunks[] = substr($output, 0, $remaining);
}
$truncated = true;
return;
}
$logChunks[] = $output;
$accumulatedBytes += $outputBytes;
});
$allLogs = implode('', $logChunks);
if ($truncated) {
$allLogs .= "\n\n[... Output truncated at 50MB limit ...]";
}
if ($this->showTimeStamps) {
$allLogs = str($allLogs)->split('/\n/')->sort(function ($a, $b) {
$a = explode(' ', $a);