diff --git a/lucene/luke/src/java/org/apache/lucene/luke/app/desktop/components/LogsPanelProvider.java b/lucene/luke/src/java/org/apache/lucene/luke/app/desktop/components/LogsPanelProvider.java index 26bf9fb725c..4ddd2b2db7d 100644 --- a/lucene/luke/src/java/org/apache/lucene/luke/app/desktop/components/LogsPanelProvider.java +++ b/lucene/luke/src/java/org/apache/lucene/luke/app/desktop/components/LogsPanelProvider.java @@ -18,21 +18,19 @@ package org.apache.lucene.luke.app.desktop.components; import java.awt.*; +import java.awt.datatransfer.Clipboard; +import java.awt.datatransfer.StringSelection; import java.awt.event.HierarchyEvent; -import java.io.IOException; -import java.io.PrintWriter; -import java.io.StringWriter; -import java.time.ZoneId; -import java.time.format.DateTimeFormatter; import java.util.ArrayList; -import java.util.Locale; import java.util.Objects; import java.util.function.Function; import java.util.logging.Level; import java.util.stream.Collectors; import javax.swing.*; +import org.apache.lucene.luke.app.desktop.util.FontUtils; import org.apache.lucene.luke.app.desktop.util.MessageUtils; import org.apache.lucene.luke.util.CircularLogBufferHandler; +import org.apache.lucene.luke.util.LogRecordFormatter; import org.apache.lucene.luke.util.LoggerFactory; /** Provider of the Logs panel */ @@ -67,6 +65,18 @@ public final class LogsPanelProvider { var logTextArea = createLogPanel(logFilter); + JButton copyBtn = + new JButton( + FontUtils.elegantIconHtml("", MessageUtils.getLocalizedMessage("button.copy"))); + copyBtn.setMargin(new Insets(3, 3, 3, 3)); + copyBtn.addActionListener( + e -> { + Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); + StringSelection selection = new StringSelection(logTextArea.getText()); + clipboard.setContents(selection, null); + }); + header.add(copyBtn); + panel.add(header, BorderLayout.PAGE_START); panel.add(new JScrollPane(logTextArea), BorderLayout.CENTER); return panel; @@ -77,33 +87,6 @@ public final class LogsPanelProvider { JTextArea logTextArea = new JTextArea(); logTextArea.setEditable(false); - class LogRecordFormatter - implements Function { - @Override - public String apply(CircularLogBufferHandler.ImmutableLogRecord record) { - return String.format( - Locale.ROOT, - "%s [%s] %s: %s", - DateTimeFormatter.ofPattern("HH:mm:ss", Locale.ROOT) - .format(record.getInstant().atZone(ZoneId.systemDefault())), - record.getLevel(), - record.getLoggerName(), - record.getMessage() - + (record.getThrown() == null ? "" : "\n" + toString(record.getThrown()))); - } - - private String toString(Throwable t) { - try (StringWriter sw = new StringWriter(); - PrintWriter pw = new PrintWriter(sw)) { - t.printStackTrace(pw); - pw.flush(); - return sw.toString(); - } catch (IOException e) { - return "Could not dump stack trace: " + e.getMessage(); - } - } - } - // Hook into live data from the circular log buffer and update the initial state. Function formatter = new LogRecordFormatter(); @@ -118,7 +101,7 @@ public final class LogsPanelProvider { String logContent = clonedCopy.stream() - .filter(record -> record.getLevel().intValue() > level.intValue()) + .filter(record -> record.getLevel().intValue() >= level.intValue()) .map(formatter::apply) .collect(Collectors.joining("\n")); diff --git a/lucene/luke/src/java/org/apache/lucene/luke/app/desktop/components/dialog/menubar/CreateIndexDialogFactory.java b/lucene/luke/src/java/org/apache/lucene/luke/app/desktop/components/dialog/menubar/CreateIndexDialogFactory.java index 223fc4fabf3..747d5da43fc 100644 --- a/lucene/luke/src/java/org/apache/lucene/luke/app/desktop/components/dialog/menubar/CreateIndexDialogFactory.java +++ b/lucene/luke/src/java/org/apache/lucene/luke/app/desktop/components/dialog/menubar/CreateIndexDialogFactory.java @@ -339,7 +339,7 @@ public class CreateIndexDialogFactory implements DialogOpener.DialogFactory { } log.log(Level.SEVERE, "Cannot create index", ex); - String message = "See Logs tab or log file for more details."; + String message = "See Logs tab for more details."; JOptionPane.showMessageDialog( dialog, message, "Cannot create index", JOptionPane.ERROR_MESSAGE); } finally { diff --git a/lucene/luke/src/java/org/apache/lucene/luke/app/desktop/components/dialog/menubar/OpenIndexDialogFactory.java b/lucene/luke/src/java/org/apache/lucene/luke/app/desktop/components/dialog/menubar/OpenIndexDialogFactory.java index 30e3d0bde59..0db47ab39b9 100644 --- a/lucene/luke/src/java/org/apache/lucene/luke/app/desktop/components/dialog/menubar/OpenIndexDialogFactory.java +++ b/lucene/luke/src/java/org/apache/lucene/luke/app/desktop/components/dialog/menubar/OpenIndexDialogFactory.java @@ -337,7 +337,7 @@ public final class OpenIndexDialogFactory implements DialogOpener.DialogFactory closeDialog(); } catch (LukeException ex) { String message = - ex.getMessage() + System.lineSeparator() + "See Logs tab or log file for more details."; + ex.getMessage() + System.lineSeparator() + "See Logs tab for more details."; JOptionPane.showMessageDialog( dialog, message, "Invalid index path", JOptionPane.ERROR_MESSAGE); } catch (Throwable cause) { diff --git a/lucene/luke/src/java/org/apache/lucene/luke/util/CircularLogBufferHandler.java b/lucene/luke/src/java/org/apache/lucene/luke/util/CircularLogBufferHandler.java index e0ad6f7dcf3..7287cd5a8e0 100644 --- a/lucene/luke/src/java/org/apache/lucene/luke/util/CircularLogBufferHandler.java +++ b/lucene/luke/src/java/org/apache/lucene/luke/util/CircularLogBufferHandler.java @@ -106,9 +106,9 @@ public class CircularLogBufferHandler extends Handler { } /** @return Return a clone of the buffered records so far. */ - public Collection getLogRecords() { + public List getLogRecords() { synchronized (buffer) { - return new ArrayDeque<>(buffer); + return List.copyOf(buffer); } } } diff --git a/lucene/luke/src/java/org/apache/lucene/luke/util/LogRecordFormatter.java b/lucene/luke/src/java/org/apache/lucene/luke/util/LogRecordFormatter.java new file mode 100644 index 00000000000..8f788026b54 --- /dev/null +++ b/lucene/luke/src/java/org/apache/lucene/luke/util/LogRecordFormatter.java @@ -0,0 +1,53 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.lucene.luke.util; + +import java.io.IOException; +import java.io.PrintWriter; +import java.io.StringWriter; +import java.time.ZoneId; +import java.time.format.DateTimeFormatter; +import java.util.Locale; +import java.util.function.Function; + +public class LogRecordFormatter + implements Function { + @Override + public String apply(CircularLogBufferHandler.ImmutableLogRecord record) { + return String.format( + Locale.ROOT, + "%s [%s] %s: %s", + DateTimeFormatter.ofPattern("HH:mm:ss", Locale.ROOT) + .format(record.getInstant().atZone(ZoneId.systemDefault())), + record.getLevel(), + record.getLoggerName(), + record.getMessage() + + (record.getThrown() == null ? "" : "\n" + toString(record.getThrown()))); + } + + private String toString(Throwable t) { + try (StringWriter sw = new StringWriter(); + PrintWriter pw = new PrintWriter(sw)) { + t.printStackTrace(pw); + pw.flush(); + return sw.toString(); + } catch (IOException e) { + return "Could not dump stack trace: " + e.getMessage(); + } + } +}