Skip to content

Commit fba1b14

Browse files
committed
feat(demo): add collapsible log panel with vertical tab
Implements expandable log panel as vertical side tab on right edge: - LogEntry record with Level (INFO/DEBUG/WARN/ERROR) and Category (SYS/CACHE/RAG/LLM/PDF) - EventLogger service with thread-safe CopyOnWriteArrayList, MAX_ENTRIES=500 - LogPanel with vertical 'LOG' tab + count badge, horizontal slide-out to 400px - Dark theme with monospace font, color-coded log levels - Logs: queries, cache hits/misses, PDF loading, retrieval timing, Redis connection - Click tab to toggle expanded/collapsed state
1 parent 0e2eebd commit fba1b14

File tree

5 files changed

+597
-14
lines changed

5 files changed

+597
-14
lines changed
Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
package com.redis.vl.demo.rag.model;
2+
3+
import java.time.Instant;
4+
import java.time.ZoneId;
5+
import java.time.format.DateTimeFormatter;
6+
7+
/**
8+
* Represents a log entry in the application event log.
9+
*
10+
* @param timestamp When the event occurred
11+
* @param level Log level (INFO, DEBUG, WARN, ERROR)
12+
* @param category Event category (SYSTEM, CACHE, RETRIEVAL, LLM)
13+
* @param message Human-readable message
14+
*/
15+
public record LogEntry(Instant timestamp, Level level, Category category, String message) {
16+
17+
private static final DateTimeFormatter TIME_FORMAT =
18+
DateTimeFormatter.ofPattern("HH:mm:ss.SSS").withZone(ZoneId.systemDefault());
19+
20+
public enum Level {
21+
INFO,
22+
DEBUG,
23+
WARN,
24+
ERROR
25+
}
26+
27+
public enum Category {
28+
SYSTEM("SYS"),
29+
CACHE("CACHE"),
30+
RETRIEVAL("RAG"),
31+
LLM("LLM"),
32+
PDF("PDF");
33+
34+
private final String shortName;
35+
36+
Category(String shortName) {
37+
this.shortName = shortName;
38+
}
39+
40+
public String getShortName() {
41+
return shortName;
42+
}
43+
}
44+
45+
/**
46+
* Creates an INFO level log entry.
47+
*
48+
* @param category Event category
49+
* @param message Log message
50+
* @return New log entry
51+
*/
52+
public static LogEntry info(Category category, String message) {
53+
return new LogEntry(Instant.now(), Level.INFO, category, message);
54+
}
55+
56+
/**
57+
* Creates a DEBUG level log entry.
58+
*
59+
* @param category Event category
60+
* @param message Log message
61+
* @return New log entry
62+
*/
63+
public static LogEntry debug(Category category, String message) {
64+
return new LogEntry(Instant.now(), Level.DEBUG, category, message);
65+
}
66+
67+
/**
68+
* Creates a WARN level log entry.
69+
*
70+
* @param category Event category
71+
* @param message Log message
72+
* @return New log entry
73+
*/
74+
public static LogEntry warn(Category category, String message) {
75+
return new LogEntry(Instant.now(), Level.WARN, category, message);
76+
}
77+
78+
/**
79+
* Creates an ERROR level log entry.
80+
*
81+
* @param category Event category
82+
* @param message Log message
83+
* @return New log entry
84+
*/
85+
public static LogEntry error(Category category, String message) {
86+
return new LogEntry(Instant.now(), Level.ERROR, category, message);
87+
}
88+
89+
/**
90+
* Formats the timestamp for display.
91+
*
92+
* @return Formatted time string
93+
*/
94+
public String formattedTime() {
95+
return TIME_FORMAT.format(timestamp);
96+
}
97+
98+
/**
99+
* Formats the full log entry for display.
100+
*
101+
* @return Formatted log string
102+
*/
103+
public String formatted() {
104+
return String.format("[%s] [%s] [%s] %s", formattedTime(), level, category.getShortName(), message);
105+
}
106+
}
Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
package com.redis.vl.demo.rag.service;
2+
3+
import com.redis.vl.demo.rag.model.LogEntry;
4+
import com.redis.vl.demo.rag.model.LogEntry.Category;
5+
import java.util.ArrayList;
6+
import java.util.List;
7+
import java.util.concurrent.CopyOnWriteArrayList;
8+
import java.util.function.Consumer;
9+
10+
/**
11+
* Centralized event logger for the RAG demo application.
12+
*
13+
* <p>Collects application events and notifies registered listeners.
14+
* Thread-safe for use from multiple threads (UI thread, background executor).
15+
*/
16+
public class EventLogger {
17+
18+
private static final int MAX_ENTRIES = 500;
19+
20+
private final List<LogEntry> entries = new CopyOnWriteArrayList<>();
21+
private final List<Consumer<LogEntry>> listeners = new CopyOnWriteArrayList<>();
22+
23+
/**
24+
* Logs an INFO event.
25+
*
26+
* @param category Event category
27+
* @param message Log message
28+
*/
29+
public void info(Category category, String message) {
30+
log(LogEntry.info(category, message));
31+
}
32+
33+
/**
34+
* Logs a DEBUG event.
35+
*
36+
* @param category Event category
37+
* @param message Log message
38+
*/
39+
public void debug(Category category, String message) {
40+
log(LogEntry.debug(category, message));
41+
}
42+
43+
/**
44+
* Logs a WARN event.
45+
*
46+
* @param category Event category
47+
* @param message Log message
48+
*/
49+
public void warn(Category category, String message) {
50+
log(LogEntry.warn(category, message));
51+
}
52+
53+
/**
54+
* Logs an ERROR event.
55+
*
56+
* @param category Event category
57+
* @param message Log message
58+
*/
59+
public void error(Category category, String message) {
60+
log(LogEntry.error(category, message));
61+
}
62+
63+
/**
64+
* Logs an entry and notifies all listeners.
65+
*
66+
* @param entry Log entry to add
67+
*/
68+
public void log(LogEntry entry) {
69+
entries.add(entry);
70+
71+
// Trim old entries if we exceed max
72+
while (entries.size() > MAX_ENTRIES) {
73+
entries.remove(0);
74+
}
75+
76+
// Notify listeners
77+
for (Consumer<LogEntry> listener : listeners) {
78+
try {
79+
listener.accept(entry);
80+
} catch (Exception e) {
81+
// Don't let listener errors break logging
82+
System.err.println("EventLogger listener error: " + e.getMessage());
83+
}
84+
}
85+
86+
// Also print to console for debugging
87+
System.out.println(entry.formatted());
88+
}
89+
90+
/**
91+
* Adds a listener to receive new log entries.
92+
*
93+
* @param listener Callback for new entries
94+
*/
95+
public void addListener(Consumer<LogEntry> listener) {
96+
listeners.add(listener);
97+
}
98+
99+
/**
100+
* Removes a listener.
101+
*
102+
* @param listener Listener to remove
103+
*/
104+
public void removeListener(Consumer<LogEntry> listener) {
105+
listeners.remove(listener);
106+
}
107+
108+
/**
109+
* Gets all log entries.
110+
*
111+
* @return Copy of all entries
112+
*/
113+
public List<LogEntry> getEntries() {
114+
return new ArrayList<>(entries);
115+
}
116+
117+
/**
118+
* Clears all log entries.
119+
*/
120+
public void clear() {
121+
entries.clear();
122+
}
123+
}

0 commit comments

Comments
 (0)