diff --git a/argus-frontend/src/main/resources/public/css/style.css b/argus-frontend/src/main/resources/public/css/style.css index 081a54e..81320e3 100644 --- a/argus-frontend/src/main/resources/public/css/style.css +++ b/argus-frontend/src/main/resources/public/css/style.css @@ -493,7 +493,132 @@ main { word-break: break-all; } -/* Tabs */ +/* Feature Flags Status */ +.feature-flags { + display: flex; + align-items: center; + gap: 0.75rem; + padding: 0.5rem 1rem; + background-color: var(--bg-secondary); + border: 1px solid var(--border-color); + border-radius: 8px; + margin-bottom: 1rem; + flex-wrap: wrap; +} + +.feature-flags-label { + font-size: 0.75rem; + font-weight: 600; + color: var(--text-secondary); + text-transform: uppercase; + letter-spacing: 0.05em; + flex-shrink: 0; +} + +.feature-flags-list { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; +} + +.feature-badge { + display: inline-flex; + align-items: center; + gap: 0.375rem; + padding: 0.25rem 0.625rem; + border-radius: 12px; + font-size: 0.6875rem; + font-weight: 600; + text-transform: uppercase; + letter-spacing: 0.03em; +} + +.feature-badge.enabled { + background-color: rgba(63, 185, 80, 0.15); + color: var(--accent-green); + border: 1px solid rgba(63, 185, 80, 0.3); +} + +.feature-badge.disabled { + background-color: rgba(139, 148, 158, 0.1); + color: var(--text-secondary); + border: 1px solid var(--border-color); + opacity: 0.6; +} + +.feature-badge.loading { + background-color: var(--bg-tertiary); + color: var(--text-secondary); + border: 1px solid var(--border-color); +} + +.feature-badge .overhead-tag { + font-size: 0.5625rem; + padding: 0.0625rem 0.3125rem; + border-radius: 4px; + font-weight: 700; +} + +.feature-badge.enabled .overhead-tag.high { + background-color: rgba(248, 81, 73, 0.2); + color: var(--accent-red); +} + +.feature-badge.enabled .overhead-tag.medium { + background-color: rgba(210, 153, 34, 0.2); + color: var(--accent-orange); +} + +.feature-badge.enabled .overhead-tag.low { + background-color: rgba(63, 185, 80, 0.15); + color: var(--accent-green); +} + +.feature-badge.disabled .overhead-tag { + background-color: transparent; + color: var(--text-secondary); + opacity: 0.5; +} + +/* Main Tab Navigation (top-level) */ +.main-tab-nav { + display: flex; + gap: 0; + margin-bottom: 1.5rem; + border-bottom: 2px solid var(--border-color); +} + +.main-tab-btn { + background: transparent; + border: none; + color: var(--text-secondary); + padding: 0.75rem 2rem; + cursor: pointer; + font-size: 1rem; + font-weight: 600; + border-bottom: 3px solid transparent; + margin-bottom: -2px; + transition: color 0.2s, border-color 0.2s; +} + +.main-tab-btn:hover { + color: var(--text-primary); +} + +.main-tab-btn.active { + color: var(--accent-blue); + border-bottom-color: var(--accent-blue); +} + +.main-tab-content { + display: none; +} + +.main-tab-content.active { + display: block; +} + +/* Sub-tabs */ .tab-nav { display: flex; gap: 0; diff --git a/argus-frontend/src/main/resources/public/index.html b/argus-frontend/src/main/resources/public/index.html index e9a9d9f..de3c588 100644 --- a/argus-frontend/src/main/resources/public/index.html +++ b/argus-frontend/src/main/resources/public/index.html @@ -65,302 +65,325 @@

GC Overhead

- -
-
-
-
-

Events Rate

- events/sec over last 60s -
-
- -
+ +
+ Features: +
+ Loading... +
+
+ + +
+ + +
+ + +
+ + + + + +
+
+

Pinning Hotspots

+
-
-
-

Thread Duration Distribution

- lifetime of completed threads -
-
- -
+
+ Total Pinned: 0 + Unique Locations: 0
-
-
- - -
-
-

Memory & GC

-
- Total GC: 0 - Total Pause: 0ms - Avg Pause: 0ms - Max Pause: 0ms +
+
No pinning events detected yet
-
-
-
-
-

GC Pause Timeline

- pause duration over time -
-
- +
+ + +
+
+
+
+

Events Rate

+ events/sec over last 60s +
+
+ +
-
-
-
-

Heap Usage

- memory usage over time +
+
+

Active Threads

+ concurrent threads over last 60s +
+
+ +
-
- +
+
+

Thread Duration Distribution

+ lifetime of completed threads +
+
+ +
-
-
+
- -
-
-

CPU Utilization

-
- JVM: -% - System: -% - Peak JVM: -% + +
+
+

Recommendations

+
-
-
-
-
-

CPU Load

- JVM and system CPU over last 60s -
-
- -
+
+
No recommendations at this time
+
+ + +
+ +
- - -
-
-

Allocation & Metaspace

-
- Alloc Rate: - MB/s - Total Allocated: - MB - Metaspace: - MB - Classes: 0 + +
+
+

Active Threads

+ 0 threads
-
-
-
-
-

Allocation Rate

- memory allocation over time -
-
- -
+
+
No active threads
-
-
-

Metaspace Usage

- metaspace memory over time -
-
- +
+ + +
+
+

Virtual Thread Events

+
+ +
- -
- -
-
-

Profiling & Contention

-
- CPU Samples: 0 - Contention Events: 0 - Contention Time: 0ms -
-
-
-
-
-

Hot Methods

- top CPU consuming methods -
-
- -
-
-
-
-

Lock Contention

- top contention hotspots -
-
- + +
+
+ +
+ +
+ + +
+
+ + +
+ +
+ + to + + +
+
-
-
-
- -
-
-

Recommendations

- -
-
-
No recommendations at this time
-
-
+
+ +
+ +
+ + + + +
+
- -
-
-

Pinning Hotspots

- -
-
- Total Pinned: 0 - Unique Locations: 0 -
-
-
No pinning events detected yet
-
-
+ +
+ + + +
+
+ + + - - +
+
Waiting for virtual thread events...
+
+ - -
- -
- -
-
-

Active Threads

- 0 threads -
-
-
No active threads
-
-
- - -
-
-

Virtual Thread Events

-
- - + +
+ + +
+
+

Memory & GC

+
+ Total GC: 0 + Total Pause: 0ms + Avg Pause: 0ms + Max Pause: 0ms +
-
- - -
-
- -
- -
- - +
+
+
+

GC Pause Timeline

+ pause duration over time +
+
+
- - -
- -
- - to - - +
+
+

Heap Usage

+ memory usage over time +
+
+
- -
- -
- -
- - - - +
+ + +
+
+

CPU Utilization

+
+ JVM: -% + System: -% + Peak JVM: -% +
+
+
+
+
+

CPU Load

+ JVM and system CPU over last 60s +
+
+
- - -
- - - +
+
+ + +
+
+

Allocation & Metaspace

+
+ Alloc Rate: - MB/s + Total Allocated: - MB + Metaspace: - MB + Classes: 0
- - - +
+ + +
+
+

Profiling & Contention

+
+ CPU Samples: 0 + Contention Events: 0 + Contention Time: 0ms +
+
+
+
+
+

Hot Methods

+ top CPU consuming methods +
+
+ +
+
+
+
+

Lock Contention

+ top contention hotspots +
+
+ +
+
+
+
-
-
Waiting for virtual thread events...
-
- + diff --git a/argus-frontend/src/main/resources/public/js/app.js b/argus-frontend/src/main/resources/public/js/app.js index a64d76e..e538dad 100644 --- a/argus-frontend/src/main/resources/public/js/app.js +++ b/argus-frontend/src/main/resources/public/js/app.js @@ -191,6 +191,9 @@ function init() { // Setup event listeners setupEventListeners(); + // Fetch feature flags (once) + fetchConfig(); + // Initial data fetch fetchMetrics(); fetchPinningAnalysis(); @@ -219,7 +222,18 @@ function init() { } function setupEventListeners() { - // Tab switching + // Main tab switching (top-level: Virtual Threads / JVM Overview) + document.querySelectorAll('.main-tab-btn').forEach(btn => { + btn.addEventListener('click', () => { + const tabId = btn.dataset.mainTab; + document.querySelectorAll('.main-tab-btn').forEach(b => b.classList.remove('active')); + document.querySelectorAll('.main-tab-content').forEach(c => c.classList.remove('active')); + btn.classList.add('active'); + document.getElementById(`${tabId}-tab`).classList.add('active'); + }); + }); + + // Sub-tab switching (Thread View / Event History) elements.tabButtons.forEach(btn => { btn.addEventListener('click', () => { const tabId = btn.dataset.tab; @@ -794,5 +808,40 @@ function updateRecommendations(data) { }).join(''); } +async function fetchConfig() { + try { + const response = await fetch('/config'); + if (response.ok) { + const data = await response.json(); + renderFeatureFlags(data.features); + } + } catch (e) { + console.error('[Argus] Failed to fetch config:', e); + } +} + +function renderFeatureFlags(features) { + const container = document.getElementById('feature-flags-list'); + if (!container || !features) return; + + const featureNames = { + gc: 'GC', + cpu: 'CPU', + metaspace: 'Metaspace', + allocation: 'Allocation', + profiling: 'Profiling', + contention: 'Contention', + correlation: 'Correlation', + prometheus: 'Prometheus' + }; + + container.innerHTML = Object.entries(features).map(([key, info]) => { + const name = featureNames[key] || key; + const enabledClass = info.enabled ? 'enabled' : 'disabled'; + const overheadTag = `${info.overhead}`; + return `${name} ${overheadTag}`; + }).join(''); +} + // Start the application init(); diff --git a/argus-server/src/main/java/io/argus/server/ArgusServer.java b/argus-server/src/main/java/io/argus/server/ArgusServer.java index 2e330d4..24652a2 100644 --- a/argus-server/src/main/java/io/argus/server/ArgusServer.java +++ b/argus-server/src/main/java/io/argus/server/ArgusServer.java @@ -212,7 +212,7 @@ protected void initChannel(SocketChannel ch) { .addLast(new HttpObjectAggregator(65536)) .addLast(new WebSocketServerCompressionHandler()) .addLast(new ArgusChannelHandler( - clients, metrics, activeThreads, threadEvents, + config, clients, metrics, activeThreads, threadEvents, gcAnalyzer, cpuAnalyzer, allocationEventBuffer != null ? allocationAnalyzer : null, metaspaceEventBuffer != null ? metaspaceAnalyzer : null, diff --git a/argus-server/src/main/java/io/argus/server/handler/ArgusChannelHandler.java b/argus-server/src/main/java/io/argus/server/handler/ArgusChannelHandler.java index 6934895..27b1fdc 100644 --- a/argus-server/src/main/java/io/argus/server/handler/ArgusChannelHandler.java +++ b/argus-server/src/main/java/io/argus/server/handler/ArgusChannelHandler.java @@ -2,6 +2,7 @@ import java.util.Map; +import io.argus.core.config.AgentConfig; import io.argus.server.analysis.AllocationAnalyzer; import io.argus.server.analysis.ContentionAnalyzer; import io.argus.server.analysis.CorrelationAnalyzer; @@ -36,6 +37,7 @@ public final class ArgusChannelHandler extends SimpleChannelInboundHandler