Skip to content

Commit 5519247

Browse files
authored
Add colorized log levels to runtime logs with toggle (#7502)
2 parents 558a885 + 863fe79 commit 5519247

File tree

2 files changed

+61
-10
lines changed

2 files changed

+61
-10
lines changed

resources/views/livewire/project/application/deployment/show.blade.php

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<livewire:project.application.heading :application="$application" />
88
<div x-data="{
99
fullscreen: false,
10-
alwaysScroll: false,
10+
alwaysScroll: {{ $isKeepAliveOn ? 'true' : 'false' }},
1111
intervalId: null,
1212
showTimestamps: true,
1313
searchQuery: '',
@@ -28,7 +28,7 @@
2828
const logsContainer = document.getElementById('logsContainer');
2929
if (logsContainer) {
3030
this.isScrolling = true;
31-
logsContainer.scrollTop = 0;
31+
logsContainer.scrollTop = logsContainer.scrollHeight;
3232
setTimeout(() => { this.isScrolling = false; }, 50);
3333
}
3434
}, 100);
@@ -40,9 +40,9 @@
4040
handleScroll(event) {
4141
if (!this.alwaysScroll || this.isScrolling) return;
4242
const el = event.target;
43-
// With flex-col-reverse, scrollTop is 0 at visual top and goes negative when scrolling down
44-
const isAtTop = Math.abs(el.scrollTop) < 50;
45-
if (!isAtTop) {
43+
// Check if user scrolled away from the bottom
44+
const distanceFromBottom = el.scrollHeight - el.scrollTop - el.clientHeight;
45+
if (distanceFromBottom > 50) {
4646
this.alwaysScroll = false;
4747
clearInterval(this.intervalId);
4848
this.intervalId = null;
@@ -144,6 +144,17 @@
144144
this.$nextTick(() => { this.renderTrigger++; });
145145
});
146146
});
147+
// Start auto-scroll if deployment is in progress
148+
if (this.alwaysScroll) {
149+
this.intervalId = setInterval(() => {
150+
const logsContainer = document.getElementById('logsContainer');
151+
if (logsContainer) {
152+
this.isScrolling = true;
153+
logsContainer.scrollTop = logsContainer.scrollHeight;
154+
setTimeout(() => { this.isScrolling = false; }, 50);
155+
}
156+
}, 100);
157+
}
147158
}
148159
}">
149160
<livewire:project.application.deployment-navbar
@@ -214,7 +225,7 @@ class="p-1 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-
214225
d="M12 12.75c1.148 0 2.278.08 3.383.237 1.037.146 1.866.966 1.866 2.013 0 3.728-2.35 6.75-5.25 6.75S6.75 18.728 6.75 15c0-1.046.83-1.867 1.866-2.013A24.204 24.204 0 0 1 12 12.75Zm0 0c2.883 0 5.647.508 8.207 1.44a23.91 23.91 0 0 1-1.152 6.06M12 12.75c-2.883 0-5.647.508-8.208 1.44.125 2.104.52 4.136 1.153 6.06M12 12.75a2.25 2.25 0 0 0 2.248-2.354M12 12.75a2.25 2.25 0 0 1-2.248-2.354M12 8.25c.995 0 1.971-.08 2.922-.236.403-.066.74-.358.795-.762a3.778 3.778 0 0 0-.399-2.25M12 8.25c-.995 0-1.97-.08-2.922-.236-.402-.066-.74-.358-.795-.762a3.734 3.734 0 0 1 .4-2.253M12 8.25a2.25 2.25 0 0 0-2.248 2.146M12 8.25a2.25 2.25 0 0 1 2.248 2.146M8.683 5a6.032 6.032 0 0 1-1.155-1.002c.07-.63.27-1.222.574-1.747m.581 2.749A3.75 3.75 0 0 1 15.318 5m0 0c.427-.283.815-.62 1.155-.999a4.471 4.471 0 0 0-.575-1.752M4.921 6a24.048 24.048 0 0 0-.392 3.314c1.668.546 3.416.914 5.223 1.082M19.08 6c.205 1.08.337 2.187.392 3.314a23.882 23.882 0 0 1-5.223 1.082" />
215226
</svg>
216227
</button>
217-
<button title="Follow Logs" x-show="fullscreen" :class="alwaysScroll ? '!text-warning' : ''"
228+
<button title="Follow Logs" :class="alwaysScroll ? '!text-warning' : ''"
218229
x-on:click="toggleScroll"
219230
class="p-1 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200">
220231
<svg class="w-4 h-4" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
@@ -259,7 +270,7 @@ class="text-gray-500 dark:text-gray-400 py-2">
259270
<div data-log-line data-log-content="{{ htmlspecialchars($searchableContent) }}"
260271
x-bind:class="{ 'hidden': !matchesSearch($el.dataset.logContent) }" @class([
261272
'mt-2' => isset($line['command']) && $line['command'],
262-
'flex gap-2 dark:hover:bg-coolgray-500 hover:bg-gray-100',
273+
'flex gap-2',
263274
])>
264275
<span x-show="showTimestamps"
265276
class="shrink-0 text-gray-500">{{ $line['timestamp'] }}</span>

resources/views/livewire/project/shared/get-logs.blade.php

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
fullscreen: false,
77
alwaysScroll: false,
88
intervalId: null,
9+
colorLogs: localStorage.getItem('coolify-color-logs') === 'true',
910
searchQuery: '',
1011
renderTrigger: 0,
1112
containerName: '{{ $container ?? "logs" }}',
@@ -44,6 +45,30 @@
4445
this.intervalId = null;
4546
}
4647
},
48+
toggleColorLogs() {
49+
this.colorLogs = !this.colorLogs;
50+
localStorage.setItem('coolify-color-logs', this.colorLogs);
51+
},
52+
getLogLevel(text) {
53+
const lowerText = text.toLowerCase();
54+
// Error detection (highest priority)
55+
if (/\b(error|err|failed|failure|exception|fatal|panic|critical)\b/.test(lowerText)) {
56+
return 'error';
57+
}
58+
// Warning detection
59+
if (/\b(warn|warning|wrn|caution)\b/.test(lowerText)) {
60+
return 'warning';
61+
}
62+
// Debug detection
63+
if (/\b(debug|dbg|trace|verbose)\b/.test(lowerText)) {
64+
return 'debug';
65+
}
66+
// Info detection
67+
if (/\b(info|inf|notice)\b/.test(lowerText)) {
68+
return 'info';
69+
}
70+
return null;
71+
},
4772
matchesSearch(line) {
4873
if (!this.searchQuery.trim()) return true;
4974
return line.toLowerCase().includes(this.searchQuery.toLowerCase());
@@ -174,7 +199,7 @@ class="flex items-center justify-between gap-2 px-4 py-2 border-b dark:border-co
174199
class="absolute left-2 top-1/2 -translate-y-1/2 text-xs text-gray-400 pointer-events-none">Lines:</span>
175200
<input type="number" wire:model="numberOfLines" placeholder="100" min="1"
176201
title="Number of Lines" {{ $streamLogs ? 'readonly' : '' }}
177-
class="input input-sm w-24 pl-11 text-center dark:bg-coolgray-300" />
202+
class="input input-sm w-32 pl-11 text-center dark:bg-coolgray-300" />
178203
</form>
179204
<span x-show="searchQuery.trim()" x-text="getMatchCount() + ' matches'"
180205
class="text-xs text-gray-500 dark:text-gray-400 whitespace-nowrap"></span>
@@ -221,6 +246,15 @@ class="p-1 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-
221246
d="M12 6v6h4.5m4.5 0a9 9 0 1 1-18 0 9 9 0 0 1 18 0Z" />
222247
</svg>
223248
</button>
249+
<button title="Toggle Log Colors" x-on:click="toggleColorLogs"
250+
:class="colorLogs ? '!text-warning' : ''"
251+
class="p-1 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200">
252+
<svg class="w-4 h-4" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg" fill="none"
253+
stroke="currentColor" stroke-width="1.5">
254+
<path stroke-linecap="round" stroke-linejoin="round"
255+
d="M9.53 16.122a3 3 0 0 0-5.78 1.128 2.25 2.25 0 0 1-2.4 2.245 4.5 4.5 0 0 0 8.4-2.245c0-.399-.078-.78-.22-1.128Zm0 0a15.998 15.998 0 0 0 3.388-1.62m-5.043-.025a15.994 15.994 0 0 1 1.622-3.395m3.42 3.42a15.995 15.995 0 0 0 4.764-4.648l3.876-5.814a1.151 1.151 0 0 0-1.597-1.597L14.146 6.32a15.996 15.996 0 0 0-4.649 4.763m3.42 3.42a6.776 6.776 0 0 0-3.42-3.42" />
256+
</svg>
257+
</button>
224258
<button wire:click="toggleStreamLogs"
225259
title="{{ $streamLogs ? 'Stop Streaming' : 'Stream Logs' }}"
226260
class="p-1 text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200 {{ $streamLogs ? '!text-warning' : '' }}">
@@ -303,8 +337,14 @@ class="text-gray-500 dark:text-gray-400 py-2">
303337
304338
@endphp
305339
<div data-log-line data-log-content="{{ $line }}"
306-
x-bind:class="{ 'hidden': !matchesSearch($el.dataset.logContent) }"
307-
class="flex gap-2 hover:bg-gray-100 dark:hover:bg-coolgray-500">
340+
x-bind:class="{
341+
'hidden': !matchesSearch($el.dataset.logContent),
342+
'bg-red-500/10 dark:bg-red-500/15': colorLogs && getLogLevel($el.dataset.logContent) === 'error',
343+
'bg-yellow-500/10 dark:bg-yellow-500/15': colorLogs && getLogLevel($el.dataset.logContent) === 'warning',
344+
'bg-purple-500/10 dark:bg-purple-500/15': colorLogs && getLogLevel($el.dataset.logContent) === 'debug',
345+
'bg-blue-500/10 dark:bg-blue-500/15': colorLogs && getLogLevel($el.dataset.logContent) === 'info',
346+
}"
347+
class="flex gap-2">
308348
@if ($timestamp && $showTimeStamps)
309349
<span class="shrink-0 text-gray-500">{{ $timestamp }}</span>
310350
@endif

0 commit comments

Comments
 (0)