From 8d8f8e89883aaec0586c8544940f418850a178b9 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Mon, 26 Oct 2026 17:59:06 +0100 Subject: [PATCH] feat(analytics): compact traffic metrics and scale latency units --- bootstrap/helpers/shared.php | 21 ++++++++++++++++--- resources/views/livewire/analytics.blade.php | 6 +++--- .../dashboard/traffic-analytics.blade.php | 4 ++-- .../project/application/analytics.blade.php | 6 +++--- .../livewire/project/shared/metrics.blade.php | 8 ++++--- .../livewire/traffic/_paths-list.blade.php | 2 +- tests/Unit/CompactNumberTest.php | 12 +++++++++++ tests/Unit/FormatLatencyTest.php | 11 ++++++++++ 8 files changed, 55 insertions(+), 15 deletions(-) create mode 100644 tests/Unit/CompactNumberTest.php create mode 100644 tests/Unit/FormatLatencyTest.php diff --git a/bootstrap/helpers/shared.php b/bootstrap/helpers/shared.php index 2ed69ed773..0ca4cfe922 100644 --- a/bootstrap/helpers/shared.php +++ b/bootstrap/helpers/shared.php @@ -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 diff --git a/resources/views/livewire/analytics.blade.php b/resources/views/livewire/analytics.blade.php index 4af35df784..6b41fcc983 100644 --- a/resources/views/livewire/analytics.blade.php +++ b/resources/views/livewire/analytics.blade.php @@ -139,7 +139,7 @@ $appListboxOptions = array_merge(
Requests - {{ number_format($overview['requests'] ?? 0) }} + {{ compactNumber($overview['requests'] ?? 0) }}
@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 - {{ number_format($overview['uniqueVisitors'] ?? 0) }} + {{ compactNumber($overview['uniqueVisitors'] ?? 0) }}
@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 - {{ number_format($overview['latencyP95'] ?? 0, 1) }} ms + {{ formatLatency($overview['latencyP95'] ?? 0) }}
@include('livewire.traffic._sparkline', [ 'id' => $chartId.'-spark-latency', diff --git a/resources/views/livewire/dashboard/traffic-analytics.blade.php b/resources/views/livewire/dashboard/traffic-analytics.blade.php index 3cf7ec4b5c..17fa85b076 100644 --- a/resources/views/livewire/dashboard/traffic-analytics.blade.php +++ b/resources/views/livewire/dashboard/traffic-analytics.blade.php @@ -56,7 +56,7 @@ $spark = 'refreshChartData-'.$chartId.'-status'; Requests - {{ number_format($overview['requests'] ?? 0) }} + {{ compactNumber($overview['requests'] ?? 0) }}
@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 - {{ number_format($overview['uniqueVisitors'] ?? 0) }} + {{ compactNumber($overview['uniqueVisitors'] ?? 0) }}
@include('livewire.traffic._sparkline', [ 'id' => $chartId.'-spark-visitors', diff --git a/resources/views/livewire/project/application/analytics.blade.php b/resources/views/livewire/project/application/analytics.blade.php index d034b66cc3..8cfeb299bb 100644 --- a/resources/views/livewire/project/application/analytics.blade.php +++ b/resources/views/livewire/project/application/analytics.blade.php @@ -62,7 +62,7 @@ $approxBadge = fn (string $tooltip) => '
Requests - {{ number_format($overview['requests'] ?? 0) }} + {{ compactNumber($overview['requests'] ?? 0) }}
@include('livewire.traffic._sparkline', [ 'id' => $chartId.'-spark-requests', @@ -80,7 +80,7 @@ $approxBadge = fn (string $tooltip) => '{{ number_format($overview['uniqueVisitors'] ?? 0) }} + {{ compactNumber($overview['uniqueVisitors'] ?? 0) }}
@include('livewire.traffic._sparkline', [ 'id' => $chartId.'-spark-visitors', @@ -124,7 +124,7 @@ $approxBadge = fn (string $tooltip) => '{{ number_format($overview['latencyP95'] ?? 0, 1) }} ms + {{ formatLatency($overview['latencyP95'] ?? 0) }}
@include('livewire.traffic._sparkline', [ 'id' => $chartId.'-spark-latency', diff --git a/resources/views/livewire/project/shared/metrics.blade.php b/resources/views/livewire/project/shared/metrics.blade.php index 4b78b12a6e..341c66c68c 100644 --- a/resources/views/livewire/project/shared/metrics.blade.php +++ b/resources/views/livewire/project/shared/metrics.blade.php @@ -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, }]); }); })(); diff --git a/resources/views/livewire/traffic/_paths-list.blade.php b/resources/views/livewire/traffic/_paths-list.blade.php index 0287c84996..d997c033ca 100644 --- a/resources/views/livewire/traffic/_paths-list.blade.php +++ b/resources/views/livewire/traffic/_paths-list.blade.php @@ -81,7 +81,7 @@ title="Combined 4xx and 5xx response rate">{{ $errorRate }}% + title="p95 latency: {{ number_format($p95, 1) }} ms">{{ formatLatency($p95) }}
@endforeach
diff --git a/tests/Unit/CompactNumberTest.php b/tests/Unit/CompactNumberTest.php new file mode 100644 index 0000000000..09ac1ff7ca --- /dev/null +++ b/tests/Unit/CompactNumberTest.php @@ -0,0 +1,12 @@ +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'], +]); diff --git a/tests/Unit/FormatLatencyTest.php b/tests/Unit/FormatLatencyTest.php new file mode 100644 index 0000000000..f992bfe9db --- /dev/null +++ b/tests/Unit/FormatLatencyTest.php @@ -0,0 +1,11 @@ +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'], +]);