Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[WIP]: Improve logs #2598

Draft
wants to merge 7 commits into
base: next
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions app/Livewire/Project/Shared/GetLogs.php
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
use App\Models\StandaloneRedis;
use Illuminate\Support\Facades\Process;
use Livewire\Component;
use Symfony\Component\Process\Exception\ProcessFailedException;

class GetLogs extends Component
{
Expand All @@ -40,6 +41,8 @@ class GetLogs extends Component

public int $numberOfLines = 100;

public ?bool $wrapLines = false;

public function mount()
{
if (! is_null($this->resource)) {
Expand Down Expand Up @@ -87,6 +90,36 @@ public function instantSave()
}
}
}
$this->wrapLines = $this->wrapLines;
}

public function downloadLogs()
{
if (! $this->server->isFunctional() || ! $this->container) {
return null;
}

$command = $this->server->isSwarm()
? "docker service logs -t {$this->container}"
: "docker logs -t {$this->container}";

if ($this->server->isNonRoot()) {
$command = parseCommandsByLineForSudo(collect($command), $this->server);
$command = $command[0];
}

$sshCommand = generateSshCommand($this->server, $command);

$logContent = '';

Process::run($sshCommand, function (string $type, string $output) use (&$logContent) {
$logContent .= $output;
});

return response()->streamDownload(function () use ($logContent) {
echo $logContent;
}, 'logs.txt');

}

public function getLogs($refresh = false)
Expand Down Expand Up @@ -151,6 +184,7 @@ public function getLogs($refresh = false)
}
}


public function render()
{
return view('livewire.project.shared.get-logs');
Expand Down
72 changes: 51 additions & 21 deletions resources/views/livewire/project/shared/get-logs.blade.php
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<div class="p-4 my-4 border dark:border-coolgray-200">
<div class="p-4 my-4 border dark:border-coolgray-200" x-data="{ wrapLines: @entangle('wrapLines') }">
<div x-init="$wire.getLogs" id="screen" x-data="{
fullscreen: false,
alwaysScroll: false,
Expand Down Expand Up @@ -31,6 +31,12 @@
clearInterval(this.intervalId);
const screen = document.getElementById('screen');
screen.scrollTop = 0;
},
toggleLine(event) {
const button = event.currentTarget;
const line = button.parentElement.querySelector('.log-line');
line.classList.toggle('truncate');
button.innerText = line.classList.contains('truncate') ? '+' : '-';
}
}">
<div class="flex items-center gap-2 ">
Expand All @@ -43,55 +49,79 @@
<div>({{ $pull_request }})</div>
@endif
@if ($streamLogs)
<x-loading wire:poll.2000ms='getLogs(true)' />
<x-loading wire:poll.2000ms='getLogs(true)'/>
@endif
</div>
<form wire:submit='getLogs(true)' class="flex items-end gap-2 ">
<div class="w-96">
<x-forms.input label="Only Show Number of Lines" placeholder="1000" required
id="numberOfLines"></x-forms.input>
id="numberOfLines"></x-forms.input>
</div>
<x-forms.button type="submit">Refresh</x-forms.button>
<x-forms.button wire:click="downloadLogs">Download Logs</x-forms.button>
<x-forms.checkbox instantSave label="Stream Logs" id="streamLogs"></x-forms.checkbox>
<x-forms.checkbox instantSave label="Include Timestamps" id="showTimeStamps"></x-forms.checkbox>
<x-forms.checkbox instantSave label="Wrap Lines" id="wrapLines"></x-forms.checkbox>
</form>
<div :class="fullscreen ? 'fullscreen' : 'relative w-full py-4 mx-auto'">
<div class="flex flex-col-reverse w-full px-4 py-2 overflow-y-auto bg-white dark:text-white dark:bg-coolgray-100 scrollbar dark:border-coolgray-300"
<div
class="flex flex-col-reverse w-full px-4 py-2 overflow-y-auto bg-white dark:text-white dark:bg-coolgray-100 scrollbar dark:border-coolgray-300"
:class="fullscreen ? '' : 'max-h-96 border border-solid rounded'">
<button title="Minimize" x-show="fullscreen" class="fixed top-4 right-4"
x-on:click="makeFullscreen"><svg class="icon" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
x-on:click="makeFullscreen">
<svg class="icon" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2" d="M6 14h4m0 0v4m0-4l-6 6m14-10h-4m0 0V6m0 4l6-6" />
</svg></button>
<button title="Go Top" x-show="fullscreen" class="fixed top-4 right-28" x-on:click="goTop"> <svg
stroke-width="2" d="M6 14h4m0 0v4m0-4l-6 6m14-10h-4m0 0V6m0 4l6-6"/>
</svg>
</button>
<button title="Go Top" x-show="fullscreen" class="fixed top-4 right-28" x-on:click="goTop">
<svg
class="icon" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2" d="M12 5v14m4-10l-4-4M8 9l4-4" />
</svg></button>
stroke-width="2" d="M12 5v14m4-10l-4-4M8 9l4-4"/>
</svg>
</button>
<button title="Follow Logs" x-show="fullscreen" :class="alwaysScroll ? 'dark:text-warning' : ''"
class="fixed top-4 right-16" x-on:click="toggleScroll"><svg class="icon" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
class="fixed top-4 right-16" x-on:click="toggleScroll">
<svg class="icon" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<path fill="none" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"
stroke-width="2" d="M12 5v14m4-4l-4 4m-4-4l4 4" />
</svg></button>
stroke-width="2" d="M12 5v14m4-4l-4 4m-4-4l4 4"/>
</svg>
</button>

<button title="Fullscreen" x-show="!fullscreen" class="absolute top-5 right-1"
x-on:click="makeFullscreen"><svg class="icon" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
x-on:click="makeFullscreen">
<svg class="icon" viewBox="0 0 24 24"
xmlns="http://www.w3.org/2000/svg">
<g fill="none">
<path
d="M24 0v24H0V0h24ZM12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035c-.01-.004-.019-.001-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427c-.002-.01-.009-.017-.017-.018Zm.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093c.012.004.023 0 .029-.008l.004-.014l-.034-.614c-.003-.012-.01-.02-.02-.022Zm-.715.002a.023.023 0 0 0-.027.006l-.006.014l-.034.614c0 .012.007.02.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01l-.184-.092Z" />
d="M24 0v24H0V0h24ZM12.593 23.258l-.011.002l-.071.035l-.02.004l-.014-.004l-.071-.035c-.01-.004-.019-.001-.024.005l-.004.01l-.017.428l.005.02l.01.013l.104.074l.015.004l.012-.004l.104-.074l.012-.016l.004-.017l-.017-.427c-.002-.01-.009-.017-.017-.018Zm.265-.113l-.013.002l-.185.093l-.01.01l-.003.011l.018.43l.005.012l.008.007l.201.093c.012.004.023 0 .029-.008l.004-.014l-.034-.614c-.003-.012-.01-.02-.02-.022Zm-.715.002a.023.023 0 0 0-.027.006l-.006.014l-.034.614c0 .012.007.02.017.024l.015-.002l.201-.093l.01-.008l.004-.011l.017-.43l-.003-.012l-.01-.01l-.184-.092Z"/>
<path fill="currentColor"
d="M9.793 12.793a1 1 0 0 1 1.497 1.32l-.083.094L6.414 19H9a1 1 0 0 1 .117 1.993L9 21H4a1 1 0 0 1-.993-.883L3 20v-5a1 1 0 0 1 1.993-.117L5 15v2.586l4.793-4.793ZM20 3a1 1 0 0 1 .993.883L21 4v5a1 1 0 0 1-1.993.117L19 9V6.414l-4.793 4.793a1 1 0 0 1-1.497-1.32l.083-.094L17.586 5H15a1 1 0 0 1-.117-1.993L15 3h5Z" />
d="M9.793 12.793a1 1 0 0 1 1.497 1.32l-.083.094L6.414 19H9a1 1 0 0 1 .117 1.993L9 21H4a1 1 0 0 1-.993-.883L3 20v-5a1 1 0 0 1 1.993-.117L5 15v2.586l4.793-4.793ZM20 3a1 1 0 0 1 .993.883L21 4v5a1 1 0 0 1-1.993.117L19 9V6.414l-4.793 4.793a1 1 0 0 1-1.497-1.32l.083-.094L17.586 5H15a1 1 0 0 1-.117-1.993L15 3h5Z"/>
</g>
</svg></button>
</svg>
</button>
@if ($outputs)
<pre id="logs" class="font-mono whitespace-pre-wrap">{{ $outputs }}</pre>
<div id="logs" class="font-mono">
@foreach (explode("\n", $outputs) as $line)
<div x-data="{ truncated: true, line: '{{ $line }}' }"
x-effect="updateLineVisibility"
class="log-container flex items-center h-full h-25">
<button x-ref="toggleButton" class="toggle-button mr-2 text-blue-500 h-full"
x-show="line.trim().length > 0 && wrapLines" x-on:click="truncated = !truncated"
x-text="truncated ? '+' : '-'" style="display: inline-block;"></button>
<span x-ref="logLine" :class="wrapLines && truncated ? 'truncate' : ''" class="log-line max-w-full"
x-bind:style="wrapLine ? 'white-space: nowrap;' : 'white-space: pre-wrap;'">{{ $line }}</span>
</div>
@endforeach
</div>
@else
<pre id="logs" class="font-mono whitespace-pre-wrap">Refresh to get the logs...</pre>
@endif
</div>
</div>
</div>
</div>