mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 13:10:37 -04:00
chore: merge main into next
This commit is contained in:
@@ -45,7 +45,28 @@ export function isExpandableLog(content, truncated) {
|
||||
return truncated || formatLogDetails(content) !== content;
|
||||
}
|
||||
|
||||
const LEVEL_FIELD = /\b(?:level|lvl|severity|loglevel)"?\s*[=:]\s*"?([a-z]+)\b/;
|
||||
// A level word at the start of the line, after timestamps: `2026-10-30t16:35:49z info msg`, `[debug] msg`.
|
||||
const LEADING_LEVEL = /^(?:\S*\d\S*\s+){0,3}[[(<]?([a-z]+)[\])>:]?(?:\s|$)/;
|
||||
const LEVEL_NAMES = {
|
||||
error: ['error', 'err', 'fatal', 'panic', 'critical', 'crit', 'alert', 'emerg', 'emergency'],
|
||||
warning: ['warn', 'warning', 'wrn'],
|
||||
debug: ['debug', 'dbg', 'trace', 'verbose'],
|
||||
info: ['info', 'inf', 'information', 'notice'],
|
||||
};
|
||||
|
||||
/**
|
||||
* Prefer the declared level (`level=debug`, `"level":"debug"`, a leading
|
||||
* `INFO`) over keywords, so `INFO ... 0 error events` or `level=debug err=EOF`
|
||||
* is not shown as an error.
|
||||
*/
|
||||
export function getLogLevel(content) {
|
||||
for (const pattern of [LEVEL_FIELD, LEADING_LEVEL]) {
|
||||
const declared = content.match(pattern)?.[1];
|
||||
for (const [level, names] of Object.entries(LEVEL_NAMES)) {
|
||||
if (names.includes(declared)) return level;
|
||||
}
|
||||
}
|
||||
if (/\b(error|err|failed|failure|exception|fatal|panic|critical)\b/.test(content)) return 'error';
|
||||
if (/\b(warn|warning|wrn|caution)\b/.test(content)) return 'warning';
|
||||
if (/\b(debug|dbg|trace|verbose)\b/.test(content)) return 'debug';
|
||||
|
||||
@@ -43,6 +43,14 @@ test('detects log levels by keyword', () => {
|
||||
assert.equal(getLogLevel('warning: disk'), 'warning');
|
||||
assert.equal(getLogLevel('trace id=1'), 'debug');
|
||||
assert.equal(getLogLevel('ready'), 'info');
|
||||
assert.equal(getLogLevel('time=2026-10-30t16:35:49z level=debug msg="read failed" err=eof'), 'debug');
|
||||
assert.equal(getLogLevel('{"level":"info","msg":"conn closed","err":"eof"}'), 'info');
|
||||
assert.equal(getLogLevel('level=error msg="db down"'), 'error');
|
||||
assert.equal(getLogLevel('{"level":50,"msg":"failed"}'), 'error');
|
||||
assert.equal(getLogLevel('2026-10-29t03:17:00.849341z info logs: pruned 840 requests, 0 error events'), 'info');
|
||||
assert.equal(getLogLevel('2026/10/30 16:35:49 [debug] read failed'), 'debug');
|
||||
assert.equal(getLogLevel('warn: retry failed'), 'warning');
|
||||
assert.equal(getLogLevel('connection failed'), 'error');
|
||||
});
|
||||
|
||||
test('filters by level and case-insensitive query', () => {
|
||||
|
||||
@@ -244,7 +244,8 @@ $appListboxOptions = array_merge(
|
||||
icon-name="unordered-list" />
|
||||
@else
|
||||
@php $maxAppRequests = max(1, (int) collect($topApps)->max('requests')); @endphp
|
||||
<div x-data="trafficTable()">
|
||||
{{-- Keyed so the lazy-load morph does not reuse the placeholder skeleton nodes. --}}
|
||||
<div wire:key="analytics-apps-table" x-data="trafficTable()">
|
||||
<div class="flex items-center gap-3 px-4 py-2 text-[11px] font-medium text-neutral-500 dark:text-fg-dim">
|
||||
@include('livewire.traffic._sort-header', ['column' => 'name', 'label' => 'Application / service', 'text' => true, 'class' => 'min-w-0 flex-1'])
|
||||
<span class="hidden w-16 shrink-0 sm:inline" aria-hidden="true"></span>
|
||||
|
||||
@@ -21,7 +21,8 @@
|
||||
<x-empty size="sm" title="No data"
|
||||
:description="'No '.strtolower($label).' data for the selected range.'" icon-name="network" />
|
||||
@else
|
||||
<div x-data="trafficTable()">
|
||||
{{-- Keyed so the lazy-load morph does not reuse the placeholder skeleton nodes. --}}
|
||||
<div wire:key="analytics-{{ $dimension }}-table" x-data="trafficTable()">
|
||||
<div class="flex items-center gap-3 px-4 py-2 text-[11px] font-medium text-neutral-500 dark:text-fg-dim">
|
||||
@include('livewire.traffic._sort-header', ['column' => 'name', 'label' => 'Name', 'text' => true, 'class' => 'min-w-0 flex-1'])
|
||||
<span class="hidden w-16 shrink-0 sm:inline" aria-hidden="true"></span>
|
||||
|
||||
@@ -16,7 +16,8 @@
|
||||
<x-empty size="sm" title="No host data" description="No hostnames were recorded for the selected range."
|
||||
icon-name="unordered-list" />
|
||||
@else
|
||||
<div x-data="trafficTable()">
|
||||
{{-- Keyed so the lazy-load morph does not reuse the placeholder skeleton nodes. --}}
|
||||
<div wire:key="{{ $keyPrefix }}-table" x-data="trafficTable()">
|
||||
<div class="flex items-center gap-3 px-4 py-2 text-[11px] font-medium text-neutral-500 dark:text-fg-dim">
|
||||
@include('livewire.traffic._sort-header', ['column' => 'name', 'label' => 'Host', 'text' => true, 'class' => 'min-w-0 flex-1'])
|
||||
<span class="hidden w-16 shrink-0 sm:inline" aria-hidden="true"></span>
|
||||
|
||||
@@ -20,7 +20,10 @@
|
||||
<x-empty size="sm" title="No path data" description="No requests were recorded for the selected range."
|
||||
icon-name="unordered-list" />
|
||||
@else
|
||||
<div x-data="trafficTable()">
|
||||
{{-- Keyed so the lazy-load morph inserts fresh nodes instead of reusing the
|
||||
placeholder skeleton; reused nodes keep their old Alpine scope and the
|
||||
pager and sort buttons stop working. --}}
|
||||
<div wire:key="{{ $keyPrefix }}-table" x-data="trafficTable()">
|
||||
<div class="flex items-center gap-3 px-4 py-2 text-[11px] font-medium text-neutral-500 dark:text-fg-dim">
|
||||
@include('livewire.traffic._sort-header', ['column' => 'name', 'label' => 'Path', 'text' => true, 'class' => 'min-w-0 flex-1'])
|
||||
<span class="hidden w-16 shrink-0 sm:inline" aria-hidden="true"></span>
|
||||
|
||||
Reference in New Issue
Block a user