diff --git a/client/src/main/java/ctbrec/ui/tabs/logging/LoggingTab.java b/client/src/main/java/ctbrec/ui/tabs/logging/LoggingTab.java index dc3a4c01..b6482b1a 100644 --- a/client/src/main/java/ctbrec/ui/tabs/logging/LoggingTab.java +++ b/client/src/main/java/ctbrec/ui/tabs/logging/LoggingTab.java @@ -7,8 +7,13 @@ import java.util.Collections; import java.util.LinkedList; import java.util.stream.Collectors; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + import com.google.common.eventbus.Subscribe; +import ch.qos.logback.classic.LoggerContext; +import ch.qos.logback.classic.encoder.PatternLayoutEncoder; import ch.qos.logback.classic.spi.IThrowableProxy; import ch.qos.logback.classic.spi.LoggingEvent; import ch.qos.logback.classic.spi.StackTraceElementProxy; @@ -19,9 +24,16 @@ import javafx.beans.property.SimpleStringProperty; import javafx.collections.FXCollections; import javafx.collections.ObservableList; import javafx.geometry.Insets; +import javafx.scene.control.ContextMenu; +import javafx.scene.control.MenuItem; +import javafx.scene.control.SelectionMode; import javafx.scene.control.Tab; import javafx.scene.control.TableColumn; import javafx.scene.control.TableView; +import javafx.scene.input.Clipboard; +import javafx.scene.input.ClipboardContent; +import javafx.scene.input.ContextMenuEvent; +import javafx.scene.input.MouseEvent; import javafx.scene.layout.BorderPane; public class LoggingTab extends Tab { @@ -33,6 +45,7 @@ public class LoggingTab extends Tab { private ObservableList filteredEvents = FXCollections.observableArrayList(); private DateTimeFormatter timeFormatter = DateTimeFormatter.ISO_LOCAL_DATE_TIME; private volatile boolean tabClosed = false; + private ContextMenu popup; private Object eventBustSubscriber = new Object() { @Subscribe public void publishLoggingEevent(LoggingEvent event) { @@ -75,6 +88,14 @@ public class LoggingTab extends Tab { }); table.getColumns().add(time); + TableColumn location = createTableColumn("Location", 250, idx++); + location.setCellValueFactory(cdf -> { + StackTraceElement loc = cdf.getValue().getCallerData()[0]; + String l = loc.getFileName() + ":" + loc.getLineNumber(); + return new SimpleStringProperty(l); + }); + table.getColumns().add(location); + TableColumn msg = createTableColumn("Message", 2000, idx++); msg.setCellValueFactory(cdf -> new SimpleStringProperty(createLogMessage(cdf.getValue()))); table.getColumns().add(msg); @@ -93,6 +114,59 @@ public class LoggingTab extends Tab { filter.setPromptText("Search"); filter.textProperty().addListener( (observableValue, oldValue, newValue) -> filter()); + + table.getSelectionModel().setSelectionMode(SelectionMode.MULTIPLE); + table.addEventHandler(ContextMenuEvent.CONTEXT_MENU_REQUESTED, event -> { + popup = createContextMenu(); + if (popup != null) { + popup.show(table, event.getScreenX(), event.getScreenY()); + } + event.consume(); + }); + table.addEventHandler(MouseEvent.MOUSE_PRESSED, event -> { + if (popup != null) { + popup.hide(); + } + }); + } + + private ContextMenu createContextMenu() { + final ObservableList selectedEvents = table.getSelectionModel().getSelectedItems(); + if (selectedEvents.isEmpty()) { + return null; + } + MenuItem copy = new MenuItem("Copy"); + copy.setOnAction(e -> { + Platform.runLater(() -> { + String formattedMessages = getFormattedMessages(selectedEvents); + final Clipboard clipboard = Clipboard.getSystemClipboard(); + final ClipboardContent content = new ClipboardContent(); + content.putString(formattedMessages); + clipboard.setContent(content); + }); + }); + + ContextMenu menu = new ContextMenu(copy); + return menu; + } + + private String getFormattedMessages(ObservableList selectedEvents) { + StringBuilder sb = new StringBuilder(); + + ch.qos.logback.classic.Logger rootLogger = (ch.qos.logback.classic.Logger)LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME); + LoggerContext loggerContext = rootLogger.getLoggerContext(); + loggerContext.reset(); + + PatternLayoutEncoder encoder = new PatternLayoutEncoder(); + encoder.setContext(loggerContext); + encoder.setPattern("%date %level [%thread] %logger{10} [%file:%line] %msg%n"); + encoder.start(); + + for (LoggingEvent evt : selectedEvents) { + byte[] encode = encoder.encode(evt); + sb.append(new String(encode)); + } + return sb.toString(); } private String createLogMessage(LoggingEvent evt) {