class LogDialog extends JDialog
{
    private static LogDialog instance;
    private final JLabel logLabel;
    private final JTextPane textPane;
    private final JScrollPane scrollPane;
    private int fatalCount = 0;
    private int errorCount = 0;
    private int warnCount = 0;
    private int otherCount = 0;
    private int exceptionCount = 0;

    private LogDialog(Frame owner, JLabel logLabel)
    {
        super(owner);
        this.logLabel = logLabel;
        
        textPane = new JTextPane();
        scrollPane = new JScrollPane(textPane);
        getContentPane().add(scrollPane);
        
        this.pack();
    }

    public static void init(Frame owner, JLabel logLabel)
    {
        instance = new LogDialog(owner, logLabel);
    }

    public static LogDialog instance()
    {
        return instance;
    }

    public void log(String name, String level, Object o, Throwable throwable)
    {<FILL_FUNCTION_BODY>}
    
    private void updateStatusBar()
    {
        List<String> infos = new ArrayList<>();

        if (exceptionCount > 0)
        {
            infos.add(exceptionCount + " exception" + (errorCount > 1 ? "s" : ""));
        }

        if (fatalCount > 0)
        {
            infos.add(errorCount + " error" + (errorCount > 1 ? "s" : ""));
        }

        if (errorCount > 0)
        {
            infos.add(errorCount + " error" + (errorCount > 1 ? "s" : ""));
        }

        if (warnCount > 0)
        {
            infos.add(warnCount + " warning" + (warnCount > 1 ? "s" : ""));
        }

        if (otherCount > 0)
        {
            infos.add(otherCount + " message" + (otherCount > 1 ? "s" : ""));
        }
        
        String info = String.join(", ", infos);
        
        logLabel.setText(info);
    }
    
    public void clear()
    {
        fatalCount = 0;
        errorCount = 0;
        warnCount = 0;
        otherCount = 0;
        exceptionCount = 0;
        textPane.setText("");
        logLabel.setText("");
    }

    // these two just to avoid the "overridable method call in constructor" warning

    @Override
    public final Container getContentPane()
    {
        return super.getContentPane();
    }

    @Override
    public final void pack()
    {
        super.pack();
    }
}

        StyledDocument doc = textPane.getStyledDocument();
        
        String levelText;
        SimpleAttributeSet levelStyle = new SimpleAttributeSet();
        switch (level)
        {
            case "FATAL":
                levelText = "Fatal";
                StyleConstants.setForeground(levelStyle, Color.WHITE);
                StyleConstants.setBackground(levelStyle, Color.BLACK);
                fatalCount++;
                break;
            case "ERROR":
                levelText = "Error";
                StyleConstants.setForeground(levelStyle, new Color(0xFF291F));
                StyleConstants.setBackground(levelStyle, new Color(0xFFF0F0));
                errorCount++;
                break;
            case "WARN":
                levelText = "Warning";
                StyleConstants.setForeground(levelStyle, new Color(0x614201));
                StyleConstants.setBackground(levelStyle, new Color(0xFFFCE5));
                warnCount++;
                break;
            case "INFO":
                levelText = "Info";
                StyleConstants.setForeground(levelStyle, new Color(0x203261));
                StyleConstants.setBackground(levelStyle, new Color(0xE2E8FF));
                otherCount++;
                break;
            case "DEBUG":
                levelText = "Debug";
                StyleConstants.setForeground(levelStyle, new Color(0x32612E));
                StyleConstants.setBackground(levelStyle, new Color(0xF4FFEC));
                otherCount++;
                break;
            case "TRACE":
                levelText = "Trace";
                StyleConstants.setForeground(levelStyle, new Color(0x64438D));
                StyleConstants.setBackground(levelStyle, new Color(0xFEF3FF));
                otherCount++;
                break;
            default:
                throw new Error(level);
        }

        SimpleAttributeSet nameStyle = new SimpleAttributeSet();
        StyleConstants.setForeground(nameStyle, new Color(0x6A6A6A));

        String shortName = name.substring(name.lastIndexOf('.') + 1);
        String message = o == null ? "(null)" : o.toString();
        
        if (throwable != null)
        {
            StringWriter sw = new StringWriter();
            throwable.printStackTrace(new PrintWriter(sw));
            message += "\n    " + sw;
            exceptionCount++;
        }
        
        try
        {
            doc.insertString(doc.getLength(), " " + levelText + " ", levelStyle);
            doc.insertString(doc.getLength(), " [" + shortName + "]", nameStyle);
            doc.insertString(doc.getLength(), " " + message + "\n", null);
        }
        catch (BadLocationException e)
        {
            throw new Error(e);
        }
        textPane.setCaretPosition(doc.getLength());

        // update status bar with new counts
        updateStatusBar();
    
<methods>public void <init>() ,public void <init>(java.awt.Frame) ,public void <init>(java.awt.Dialog) ,public void <init>(java.awt.Window) ,public void <init>(java.awt.Frame, boolean) ,public void <init>(java.awt.Frame, java.lang.String) ,public void <init>(java.awt.Dialog, boolean) ,public void <init>(java.awt.Dialog, java.lang.String) ,public void <init>(java.awt.Window, java.awt.Dialog.ModalityType) ,public void <init>(java.awt.Window, java.lang.String) ,public void <init>(java.awt.Frame, java.lang.String, boolean) ,public void <init>(java.awt.Dialog, java.lang.String, boolean) ,public void <init>(java.awt.Window, java.lang.String, java.awt.Dialog.ModalityType) ,public void <init>(java.awt.Frame, java.lang.String, boolean, java.awt.GraphicsConfiguration) ,public void <init>(java.awt.Dialog, java.lang.String, boolean, java.awt.GraphicsConfiguration) ,public void <init>(java.awt.Window, java.lang.String, java.awt.Dialog.ModalityType, java.awt.GraphicsConfiguration) ,public javax.accessibility.AccessibleContext getAccessibleContext() ,public java.awt.Container getContentPane() ,public int getDefaultCloseOperation() ,public java.awt.Component getGlassPane() ,public java.awt.Graphics getGraphics() ,public javax.swing.JMenuBar getJMenuBar() ,public javax.swing.JLayeredPane getLayeredPane() ,public javax.swing.JRootPane getRootPane() ,public javax.swing.TransferHandler getTransferHandler() ,public static boolean isDefaultLookAndFeelDecorated() ,public void remove(java.awt.Component) ,public void repaint(long, int, int, int, int) ,public void setContentPane(java.awt.Container) ,public void setDefaultCloseOperation(int) ,public static void setDefaultLookAndFeelDecorated(boolean) ,public void setGlassPane(java.awt.Component) ,public void setJMenuBar(javax.swing.JMenuBar) ,public void setLayeredPane(javax.swing.JLayeredPane) ,public void setLayout(java.awt.LayoutManager) ,public void setTransferHandler(javax.swing.TransferHandler) ,public void update(java.awt.Graphics) <variables>protected javax.accessibility.AccessibleContext accessibleContext,private int defaultCloseOperation,private static final java.lang.Object defaultLookAndFeelDecoratedKey,protected javax.swing.JRootPane rootPane,protected boolean rootPaneCheckingEnabled,private javax.swing.TransferHandler transferHandler