Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

make the log output faster, and add colors too #832

Merged
merged 6 commits into from
May 31, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@
import javafx.scene.control.Control;
import javafx.scene.control.DatePicker;
import javafx.scene.control.Label;
import javafx.scene.control.ListView;
import javafx.scene.control.MenuBar;
import javafx.scene.control.RadioButton;
import javafx.scene.control.SelectionMode;
Expand Down Expand Up @@ -126,7 +127,7 @@ public class GuiConfigController implements Initializable {
private boolean guiIsBusy;

@FXML
private TextArea textAreaStatus;
private ListView<Label> textAreaStatus;
@FXML
private TextArea textAreaHelp;
@FXML
Expand Down
124 changes: 114 additions & 10 deletions src/main/java/network/brightspots/rcv/Logger.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
* Captures all INFO level logging for the execution of a session.
* "session" could span multiple tabulations in GUI mode.
*
* GUI handler (INFO) -> textArea
* GUI handler (INFO) -> listView
* Displays INFO level logging in GUI for user feedback in GUI mode.
*
* Default handler -> console
Expand All @@ -32,19 +32,34 @@

package network.brightspots.rcv;

import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.logging.FileHandler;
import java.util.logging.Formatter;
import java.util.logging.Handler;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import javafx.application.Platform;
import javafx.scene.control.TextArea;
import javafx.collections.FXCollections;
import javafx.collections.ObservableList;
import javafx.geometry.Insets;
import javafx.scene.control.ContextMenu;
import javafx.scene.control.Label;
import javafx.scene.control.ListCell;
import javafx.scene.control.ListView;
import javafx.scene.control.MenuItem;
import javafx.scene.effect.BlendMode;
import javafx.scene.input.Clipboard;
import javafx.scene.input.ClipboardContent;
import javafx.scene.layout.Background;
import javafx.scene.layout.BackgroundFill;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;

class Logger {

Expand All @@ -61,6 +76,7 @@ class Logger {
private static java.util.logging.Logger logger;
private static java.util.logging.FileHandler tabulationHandler;
private static String tabulationLogPattern;
private static final List<Label> labelsQueue = new ArrayList<>();

static void setup() {
logger = java.util.logging.Logger.getLogger("");
Expand Down Expand Up @@ -156,23 +172,111 @@ private static void log(Level level, String message, Object... obj) {
}

// add logging to the provided text area for display to user in the GUI
static void addGuiLogging(TextArea textArea) {
static void addGuiLogging(ListView<Label> listView) {
ObservableList<Label> logMessages = FXCollections.observableArrayList();
listView.setItems(logMessages);

// Set cell factory to reduce vertical gap
listView.setCellFactory(param -> new ListCell<Label>() {
@Override
protected void updateItem(Label item, boolean empty) {
// Sets zero padding and updates the cell colors
super.updateItem(item, empty);
if (empty || item == null) {
setGraphic(null);
setText(null);
setStyle(null);
} else {
// Reset the widths to allow word wrap
item.setMinWidth(listView.getWidth() - 50);
item.setMaxWidth(listView.getWidth() - 50);
item.setPrefWidth(listView.getWidth() - 50);

// Fix the label padding
setPadding(new Insets(0, 0, 0, 3));

// First remove any existing style, which can either be overridden
// (if it needs a custom background) or can remain as the default.
setStyle(null);

// Set the entire background color to the label's background
// This changes the background from being a text highlight to taking up the whole row
Background bg = item.getBackground();
if (bg != null) {
List<BackgroundFill> fills = item.getBackground().getFills();
if (!fills.isEmpty()) {
Paint bgColor = fills.get(0).getFill();
String hexColor = bgColor.toString().replace("0x", "#");
setStyle("-fx-background-color: " + hexColor);
}
}

// Change the look when selected -- lighten it up a bit
// while maintaining the warning/severe color
if (isSelected()) {
setBlendMode(BlendMode.SCREEN);
} else {
setBlendMode(BlendMode.SRC_OVER);
}

setGraphic(item);
}
}
});

java.util.logging.Handler guiHandler =
new Handler() {
@Override
public void publish(LogRecord record) {
if (isLoggable(record)) {
String msg = getFormatter().format(record);
// if we are executing on the GUI thread we can post immediately (e.g. button clicks)
// otherwise schedule the text update to run on the GUI thread
if (Platform.isFxApplicationThread()) {
textArea.appendText(msg);
} else {
Platform.runLater(() -> textArea.appendText(msg));
Label logLabel = new Label(msg);
logLabel.setPadding(new Insets(0, 0, 0, 3));
logLabel.setWrapText(true);

// Set background color based on log level
if (record.getLevel() == Level.SEVERE) {
logLabel.setBackground(Background.fill(Color.DARKRED));
} else if (record.getLevel() == Level.WARNING) {
logLabel.setBackground(Background.fill(Color.SIENNA));
}

// On Right Click, user can copy text
ContextMenu contextMenu = new ContextMenu();
MenuItem copyMenuItem = new MenuItem("Copy");
copyMenuItem.setOnAction(event -> copyToClipboard(logLabel));
contextMenu.getItems().add(copyMenuItem);
logLabel.setContextMenu(contextMenu);

// Rather than adding to the list too many times in a row,
// we add to a queue and schedule an occasional update to the UI.
// This prevents the UI from lagging when there are many log messages.
synchronized (labelsQueue) {
labelsQueue.add(logLabel);

// The first item in the queue is the only one that needs to trigger the update.
if (labelsQueue.size() == 1) {
Platform.runLater(this::addFromMainThread);
}
}
}
}

private void addFromMainThread() {
synchronized (labelsQueue) {
logMessages.addAll(labelsQueue);
labelsQueue.clear();
}
listView.scrollTo(logMessages.size() - 1);
}

private void copyToClipboard(Label logLabel) {
Clipboard clipboard = Clipboard.getSystemClipboard();
ClipboardContent content = new ClipboardContent();
content.putString(logLabel.getText());
clipboard.setContent(content);
}

@Override
public void flush() {}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -696,13 +696,12 @@
styleClass="help-text-area" wrapText="true" BorderPane.alignment="TOP_LEFT"/>
</right>
<bottom>
<TextArea id="textStatus" fx:id="textAreaStatus" editable="false" prefHeight="200.0"
prefWidth="200.0" styleClass="console-text-area" wrapText="true"
BorderPane.alignment="TOP_LEFT">
<ListView id="textStatus" fx:id="textAreaStatus" editable="false" prefHeight="200.0"
prefWidth="200.0" styleClass="console-text-area" BorderPane.alignment="TOP_LEFT">
<BorderPane.margin>
<Insets bottom="4.0" left="4.0" right="4.0" top="4.0"/>
</BorderPane.margin>
</TextArea>
</ListView>
</bottom>
</BorderPane>
</ScrollPane>
Loading