Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

A Java advanced text logging pane for large output

Mathematica comes with a simple java program that allows to inspect the communication between front end and the kernel. It's called LinkSnooper and in general it works quite nice. It looks like this

enter image description here

I think I can improve the behavior and usability of the program to some extend, but to do this, I need to reimplement some parts. One fundamental piece that I need is a text pane, which has the following properties:

  • it can receive a lot of data and it probably should use a fast ring-buffer so that the very first log-lines are removed when the data grows too much. Another possibility is that it automatically starts to write data to disk and possibly reloads it when the user scrolls up to see the first entries
  • it should be able to handle colored text. I plan to use a simple highlighter (the log-data is actually real Mathematica syntax) on each arriving line to make reading more easy
  • it doesn't need to be writable. It's OK if the text pane is read-only.

Question: Does something like this already exist? Currently, LinkSnooper uses a JTextArea underneath and before I start do write my own version, I wanted to ask whether someone has already done this.

Edit:

What I planned to do was to use some Logger framework because it seems natural to me that those libraries should be able to handle a lot of data. Additionally, they often provide interfaces to format the messages and you can define different handlers that can take care of different messages. What I was hoping for was that someone already has combined this with a neatly working text window that can handle large output.

like image 224
halirutan Avatar asked Dec 11 '14 03:12

halirutan


1 Answers

As Simon has pointed out I would suggest using JavaFX for this task.

If you "just" need to display large amounts of log data without advanced highlighting (sub-string range highlighting), ListView is the component for you.

It uses a virtualized layout container, so only the cells that are in the visible area of the viewport are actually rendered. This allows for lazy loading, cell recycling etc. The ListView uses an ObservableList as its DataStructure. Similar to EMF EList, the ObservableListautomatically notifies the ListView on changes in its contained data.

There are several factory methods to create an ObservableList via FXCollections even allowing to wrap an existing List (e.g. RingBuffer).

If you need the advanced highlighting, RichTextFX is probably the solution to go for as it allows detailed styling of its contained text. RichTextFX uses a virtualized layout, too.


Edit #2

Tom has written about this in his blog: http://tomsondev.bestsolution.at/2014/12/27/displaying-and-editing-large-styled-texts/


Edit #1 ListView example

JavaFX does a very good job at separating the model from the view, so we try not to mix this up and need to create two things:

  1. A data class (model)
  2. A Cell renderer for that data class (view).

First the data class:

public class LogData {

    private final String logMessage;
    private List<String> highlightedFragments = null;

    public LogData(String pLogMessage) {
        logMessage = pLogMessage;
    }

    public String getLogMessage() {
        return logMessage;
    }

    public List<String> getHighlightedFragments() {
        if (highlightedFragments == null) {
            doHighlight();
        }
        return highlightedFragments;
    }

    private void doHighlight() {
        List<String> highlightedParts = Collections.emptyList(); // TODO lexer
        highlightedFragments = highlightedParts;
    }
}

The interesting part is, that the highlighting is done on demand not on initialization. Or in other words: The lexer only performs its work, when the cell renderer requests the data.

Now the Cell renderer:

ListView<LogData> listView = new ListView<>();
listView.setCellFactory(cb -> new LogDataCell(){});

public class LogDataCell extends ListCell<LogData>
{
    @Override
    protected void updateItem(LogData item, boolean empty) {
        super.updateItem(item, empty);

        if(empty || item == null) {
            setText(null);
            setGraphic(null);
        }
        else {
            List<String> fragments = item.getHighlightedFragments();
            if(fragments == null || fragments.isEmpty()) {
                setText(item.getLogMessage());
                setGraphic(null);
            }
            else {
                TextFlow textFlow = null; //TODO
                setText(null);
                setGraphic(textFlow);
            }
        }
    }
}

This is not a fully working example, there are several TODOs left, but hopefully you get the idea.

If you want to add search highlighting, I described a similar approach for the TableView control element here: JavaFX Table with highlighted text (Labels) with poor performance

like image 75
eckig Avatar answered Oct 25 '22 00:10

eckig