mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 13:10:37 -04:00
feat(analytics): compact traffic metrics and scale latency units
This commit is contained in:
@@ -5064,8 +5064,8 @@ function formatBytes(?int $bytes, int $precision = 2): string
|
||||
}
|
||||
|
||||
/**
|
||||
* Compact human-readable count (e.g. 26_360 -> "26.36k", 1_200_000 -> "1.2M").
|
||||
* Trailing zeros are trimmed so round values read cleanly ("1k", not "1.00k").
|
||||
* Compact human-readable count (e.g. 26_360 -> "26.36K", 1_200_000 -> "1.2M").
|
||||
* Trailing zeros are trimmed so round values read cleanly ("1K", not "1.00K").
|
||||
* Used for the dense metric columns in the traffic-analytics lists.
|
||||
*/
|
||||
function compactNumber(?int $n): string
|
||||
@@ -5079,12 +5079,27 @@ function compactNumber(?int $n): string
|
||||
[$divisor, $suffix] = match (true) {
|
||||
$n >= 1_000_000_000 => [1_000_000_000, 'B'],
|
||||
$n >= 1_000_000 => [1_000_000, 'M'],
|
||||
default => [1000, 'k'],
|
||||
default => [1000, 'K'],
|
||||
};
|
||||
|
||||
return rtrim(rtrim(number_format($n / $divisor, 2, '.', ''), '0'), '.').$suffix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compact response time for the traffic-analytics latency figures
|
||||
* (e.g. 85.4 -> "85.4 ms", 2_450 -> "2.5 s", 312_000 -> "5.2 min").
|
||||
*/
|
||||
function formatLatency(float|int|null $ms): string
|
||||
{
|
||||
$ms = (float) $ms;
|
||||
|
||||
return match (true) {
|
||||
$ms >= 60_000 => number_format($ms / 60_000, 1).' min',
|
||||
$ms >= 1000 => number_format($ms / 1000, 1).' s',
|
||||
default => number_format($ms, 1).' ms',
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Validates that a file path is safely within the /tmp/ directory.
|
||||
* Protects against unsafe parent directory paths by resolving the real path
|
||||
|
||||
@@ -139,7 +139,7 @@ $appListboxOptions = array_merge(
|
||||
<div class="grid grid-cols-2 gap-px overflow-hidden rounded-lg bg-neutral-200 sm:grid-cols-3 lg:grid-cols-5 dark:bg-white/[0.07]">
|
||||
<div class="flex flex-col bg-[var(--coollabs-base)] px-4 py-3">
|
||||
<span class="text-[11px] font-medium tracking-wide text-neutral-500 uppercase dark:text-fg-dim">Requests</span>
|
||||
<span class="mt-1 text-xl font-semibold text-black tabular-nums dark:text-fg">{{ number_format($overview['requests'] ?? 0) }}</span>
|
||||
<span class="mt-1 text-xl font-semibold text-black tabular-nums dark:text-fg" title="{{ number_format($overview['requests'] ?? 0) }} requests">{{ compactNumber($overview['requests'] ?? 0) }}</span>
|
||||
<div class="mt-auto pt-3">
|
||||
@include('livewire.traffic._sparkline', [
|
||||
'id' => $chartId.'-spark-requests',
|
||||
@@ -157,7 +157,7 @@ $appListboxOptions = array_merge(
|
||||
{!! $approxBadge('Summed across servers or services; visitors seen on more than one may be double-counted.') !!}
|
||||
@endif
|
||||
</span>
|
||||
<span class="mt-1 text-xl font-semibold text-black tabular-nums dark:text-fg">{{ number_format($overview['uniqueVisitors'] ?? 0) }}</span>
|
||||
<span class="mt-1 text-xl font-semibold text-black tabular-nums dark:text-fg" title="{{ number_format($overview['uniqueVisitors'] ?? 0) }} unique visitors">{{ compactNumber($overview['uniqueVisitors'] ?? 0) }}</span>
|
||||
<div class="mt-auto pt-3">
|
||||
@include('livewire.traffic._sparkline', [
|
||||
'id' => $chartId.'-spark-visitors',
|
||||
@@ -201,7 +201,7 @@ $appListboxOptions = array_merge(
|
||||
{!! $approxBadge('Highest p95 latency across servers or services; not a true merged percentile.') !!}
|
||||
@endif
|
||||
</span>
|
||||
<span class="mt-1 text-xl font-semibold text-black tabular-nums dark:text-fg">{{ number_format($overview['latencyP95'] ?? 0, 1) }} ms</span>
|
||||
<span class="mt-1 text-xl font-semibold text-black tabular-nums dark:text-fg">{{ formatLatency($overview['latencyP95'] ?? 0) }}</span>
|
||||
<div class="mt-auto pt-3">
|
||||
@include('livewire.traffic._sparkline', [
|
||||
'id' => $chartId.'-spark-latency',
|
||||
|
||||
@@ -56,7 +56,7 @@ $spark = 'refreshChartData-'.$chartId.'-status';
|
||||
<a href="{{ route('analytics') }}" {{ wireNavigate() }}
|
||||
class="group flex flex-col bg-white px-4 py-3 transition-colors hover:bg-neutral-50 dark:bg-[color-mix(in_srgb,var(--color-app)_95%,white)] dark:hover:bg-[color-mix(in_srgb,var(--color-app)_93%,white)]">
|
||||
<span class="text-[11px] font-medium tracking-wide text-neutral-500 uppercase dark:text-fg-dim">Requests</span>
|
||||
<span class="mt-1 text-xl font-semibold text-black tabular-nums dark:text-fg">{{ number_format($overview['requests'] ?? 0) }}</span>
|
||||
<span class="mt-1 text-xl font-semibold text-black tabular-nums dark:text-fg" title="{{ number_format($overview['requests'] ?? 0) }} requests">{{ compactNumber($overview['requests'] ?? 0) }}</span>
|
||||
<div class="mt-auto pt-3">
|
||||
@include('livewire.traffic._sparkline', [
|
||||
'id' => $chartId.'-spark-requests',
|
||||
@@ -75,7 +75,7 @@ $spark = 'refreshChartData-'.$chartId.'-status';
|
||||
{!! $approxBadge('Summed across '.$servers->count().' servers; visitors seen on multiple servers may be double-counted.') !!}
|
||||
@endif
|
||||
</span>
|
||||
<span class="mt-1 text-xl font-semibold text-black tabular-nums dark:text-fg">{{ number_format($overview['uniqueVisitors'] ?? 0) }}</span>
|
||||
<span class="mt-1 text-xl font-semibold text-black tabular-nums dark:text-fg" title="{{ number_format($overview['uniqueVisitors'] ?? 0) }} unique visitors">{{ compactNumber($overview['uniqueVisitors'] ?? 0) }}</span>
|
||||
<div class="mt-auto pt-3">
|
||||
@include('livewire.traffic._sparkline', [
|
||||
'id' => $chartId.'-spark-visitors',
|
||||
|
||||
@@ -62,7 +62,7 @@ $approxBadge = fn (string $tooltip) => '<span title="'.e($tooltip).'" class="ml-
|
||||
<div class="grid grid-cols-2 gap-px overflow-hidden rounded-lg bg-neutral-200 sm:grid-cols-3 lg:grid-cols-5 dark:bg-white/[0.07]">
|
||||
<div class="flex flex-col bg-[var(--coollabs-base)] px-4 py-3">
|
||||
<span class="text-[11px] font-medium tracking-wide text-neutral-500 uppercase dark:text-fg-dim">Requests</span>
|
||||
<span class="mt-1 text-xl font-semibold text-black tabular-nums dark:text-fg">{{ number_format($overview['requests'] ?? 0) }}</span>
|
||||
<span class="mt-1 text-xl font-semibold text-black tabular-nums dark:text-fg" title="{{ number_format($overview['requests'] ?? 0) }} requests">{{ compactNumber($overview['requests'] ?? 0) }}</span>
|
||||
<div class="mt-auto pt-3">
|
||||
@include('livewire.traffic._sparkline', [
|
||||
'id' => $chartId.'-spark-requests',
|
||||
@@ -80,7 +80,7 @@ $approxBadge = fn (string $tooltip) => '<span title="'.e($tooltip).'" class="ml-
|
||||
{!! $approxBadge('Summed across services; visitors of more than one service may be double-counted.') !!}
|
||||
@endif
|
||||
</span>
|
||||
<span class="mt-1 text-xl font-semibold text-black tabular-nums dark:text-fg">{{ number_format($overview['uniqueVisitors'] ?? 0) }}</span>
|
||||
<span class="mt-1 text-xl font-semibold text-black tabular-nums dark:text-fg" title="{{ number_format($overview['uniqueVisitors'] ?? 0) }} unique visitors">{{ compactNumber($overview['uniqueVisitors'] ?? 0) }}</span>
|
||||
<div class="mt-auto pt-3">
|
||||
@include('livewire.traffic._sparkline', [
|
||||
'id' => $chartId.'-spark-visitors',
|
||||
@@ -124,7 +124,7 @@ $approxBadge = fn (string $tooltip) => '<span title="'.e($tooltip).'" class="ml-
|
||||
{!! $approxBadge('Highest p95 latency across services; not a true merged percentile.') !!}
|
||||
@endif
|
||||
</span>
|
||||
<span class="mt-1 text-xl font-semibold text-black tabular-nums dark:text-fg">{{ number_format($overview['latencyP95'] ?? 0, 1) }} ms</span>
|
||||
<span class="mt-1 text-xl font-semibold text-black tabular-nums dark:text-fg">{{ formatLatency($overview['latencyP95'] ?? 0) }}</span>
|
||||
<div class="mt-auto pt-3">
|
||||
@include('livewire.traffic._sparkline', [
|
||||
'id' => $chartId.'-spark-latency',
|
||||
|
||||
@@ -235,6 +235,8 @@
|
||||
(() => {
|
||||
checkTheme();
|
||||
|
||||
const formatMemory = value => value >= 1024 ? `${Number((value / 1024).toFixed(1))} GB` : `${Math.round(value)} MB`;
|
||||
|
||||
const memoryChart = new ApexCharts(document.getElementById('{!! $chartId !!}-memory'), {
|
||||
chart: {
|
||||
height: 240,
|
||||
@@ -294,7 +296,7 @@
|
||||
style: {
|
||||
colors: textColor,
|
||||
},
|
||||
formatter: value => `${Math.round(value)} MB`,
|
||||
formatter: formatMemory,
|
||||
},
|
||||
},
|
||||
noData: {
|
||||
@@ -354,7 +356,7 @@
|
||||
style: {
|
||||
colors: textColor,
|
||||
},
|
||||
formatter: value => `${Math.round(value)} MB`,
|
||||
formatter: formatMemory,
|
||||
},
|
||||
},
|
||||
noData: {
|
||||
@@ -368,7 +370,7 @@
|
||||
name: 'Memory',
|
||||
color: ramColor,
|
||||
data: chartData[0].seriesData,
|
||||
formatter: value => `${Math.round(value)} MB`,
|
||||
formatter: formatMemory,
|
||||
}]);
|
||||
});
|
||||
})();
|
||||
|
||||
@@ -81,7 +81,7 @@
|
||||
title="Combined 4xx and 5xx response rate">{{ $errorRate }}%</span>
|
||||
<span class="hidden w-16 shrink-0 text-right text-[11px] tabular-nums text-neutral-400 sm:inline dark:text-fg-faint">{{ formatBytes($bytesOut) }}</span>
|
||||
<span class="hidden w-16 shrink-0 text-right text-[11px] tabular-nums text-neutral-400 md:inline dark:text-fg-faint"
|
||||
title="p95 latency">{{ number_format($p95, 1) }} ms</span>
|
||||
title="p95 latency: {{ number_format($p95, 1) }} ms">{{ formatLatency($p95) }}</span>
|
||||
</div>
|
||||
@endforeach
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
it('formats counts with K, M, and B suffixes', function (?int $input, string $expected) {
|
||||
expect(compactNumber($input))->toBe($expected);
|
||||
})->with([
|
||||
'null' => [null, '0'],
|
||||
'below a thousand' => [999, '999'],
|
||||
'round thousand' => [1000, '1K'],
|
||||
'thousands' => [26_360, '26.36K'],
|
||||
'millions' => [1_200_000, '1.2M'],
|
||||
'billions' => [3_456_789_012, '3.46B'],
|
||||
]);
|
||||
@@ -0,0 +1,11 @@
|
||||
<?php
|
||||
|
||||
it('formats latency in ms, s, or min', function (float|int|null $input, string $expected) {
|
||||
expect(formatLatency($input))->toBe($expected);
|
||||
})->with([
|
||||
'null' => [null, '0.0 ms'],
|
||||
'milliseconds' => [85.44, '85.4 ms'],
|
||||
'just below a second' => [999.9, '999.9 ms'],
|
||||
'seconds' => [2_450, '2.5 s'],
|
||||
'minutes' => [312_000, '5.2 min'],
|
||||
]);
|
||||
Reference in New Issue
Block a user