You are viewing a plain text version of this content. The canonical link for it is here.
Posted to general@logging.apache.org by sd...@apache.org on 2010/03/28 01:25:54 UTC

svn commit: r928311 - in /logging/chainsaw/trunk/src/main: java/org/apache/log4j/chainsaw/LogPanel.java java/org/apache/log4j/chainsaw/color/RuleColorizer.java resources/org/apache/log4j/chainsaw/help/release-notes.html

Author: sdeboy
Date: Sun Mar 28 00:25:54 2010
New Revision: 928311

URL: http://svn.apache.org/viewvc?rev=928311&view=rev
Log:
New feature: clickable warning/error/fatal thumbnail bar

New clickable thumbnail bar next to logpanel scroll bar provides a visual indicator of events with warning level as well as error/fatal.

Clicking in the bar will jump to the closest event.

Colors in the thumbnail bar will sync with the default warning and error/fatal color rules (LEVEL == WARNING) and (level == FATAL || level == ERROR) respectively.

Modified:
    logging/chainsaw/trunk/src/main/java/org/apache/log4j/chainsaw/LogPanel.java
    logging/chainsaw/trunk/src/main/java/org/apache/log4j/chainsaw/color/RuleColorizer.java
    logging/chainsaw/trunk/src/main/resources/org/apache/log4j/chainsaw/help/release-notes.html

Modified: logging/chainsaw/trunk/src/main/java/org/apache/log4j/chainsaw/LogPanel.java
URL: http://svn.apache.org/viewvc/logging/chainsaw/trunk/src/main/java/org/apache/log4j/chainsaw/LogPanel.java?rev=928311&r1=928310&r2=928311&view=diff
==============================================================================
--- logging/chainsaw/trunk/src/main/java/org/apache/log4j/chainsaw/LogPanel.java (original)
+++ logging/chainsaw/trunk/src/main/java/org/apache/log4j/chainsaw/LogPanel.java Sun Mar 28 00:25:54 2010
@@ -25,6 +25,7 @@ import java.awt.Dimension;
 import java.awt.FlowLayout;
 import java.awt.Font;
 import java.awt.FontMetrics;
+import java.awt.Graphics;
 import java.awt.Point;
 import java.awt.Toolkit;
 import java.awt.event.ActionEvent;
@@ -124,6 +125,7 @@ import javax.swing.table.TableColumn;
 import javax.swing.table.TableColumnModel;
 import javax.swing.text.Document;
 
+import org.apache.log4j.Level;
 import org.apache.log4j.LogManager;
 import org.apache.log4j.Logger;
 import org.apache.log4j.PatternLayout;
@@ -143,6 +145,7 @@ import org.apache.log4j.chainsaw.prefs.S
 import org.apache.log4j.chainsaw.prefs.SettingsManager;
 import org.apache.log4j.chainsaw.xstream.TableColumnConverter;
 import org.apache.log4j.helpers.Constants;
+import org.apache.log4j.rule.ColorRule;
 import org.apache.log4j.rule.ExpressionRule;
 import org.apache.log4j.rule.Rule;
 import org.apache.log4j.spi.LoggingEvent;
@@ -1072,6 +1075,13 @@ public class LogPanel extends DockablePa
     final JScrollPane eventsPane = new JScrollPane(table);
 
     eventsAndStatusPanel.add(eventsPane, BorderLayout.CENTER);
+    JPanel rightPanel = new JPanel();
+    rightPanel.setLayout(new BoxLayout(rightPanel, BoxLayout.Y_AXIS));
+    rightPanel.add(Box.createVerticalStrut(table.getTableHeader().getMinimumSize().height));
+    JPanel thumbNailPanel = new EventMatchThumbnail();
+    thumbNailPanel.setPreferredSize(new Dimension(10, -1));
+    rightPanel.add(thumbNailPanel);
+    eventsAndStatusPanel.add(rightPanel, BorderLayout.EAST);
 
     final JPanel statusLabelPanel = new JPanel();
     statusLabelPanel.setLayout(new BorderLayout());
@@ -3040,6 +3050,253 @@ public class LogPanel extends DockablePa
         }
     }
 
+    private class EventMatchThumbnail extends JPanel {
+        private List errors= new ArrayList();
+        private List warnings = new ArrayList();
+
+        private Color WARNING_COLOR = colorizer.getDefaultWarnColor();
+        private Color ERROR_OR_FATAL_COLOR = colorizer.getDefaultErrorOrFatalColor();
+
+        private final int eventHeight = 8;
+
+        public EventMatchThumbnail() {
+            configureColors();
+            colorizer.addPropertyChangeListener(new PropertyChangeListener()
+            {
+                public void propertyChange(PropertyChangeEvent evt)
+                {
+                    configureColors();
+                }
+            });
+            addMouseListener(new MouseAdapter(){
+                public void mouseClicked(MouseEvent e)
+                {
+                    int rowCount = table.getRowCount();
+                    int height = getHeight();
+                    int clickLocationY = e.getPoint().y;
+                    float ratio = (float)clickLocationY / height;
+                    int rowToSelect = (int)(rowCount * ratio);
+                    EventWrapper event = getClosestRow(rowToSelect);
+                    if (event != null) {
+                        setSelectedEvent(event.rowNum + 1);
+                    }
+                }
+            });
+
+            tableModel.addTableModelListener(new TableModelListener(){
+                public void tableChanged(TableModelEvent e) {
+                    int firstRow = e.getFirstRow();
+                    int lastRow = e.getLastRow();
+                    if (lastRow == Integer.MAX_VALUE) {
+                        lastRow = table.getRowCount() -1; //zero-indexed rows
+                    }
+                    if (firstRow < 0 || lastRow < 0) {
+                        return;
+                    }
+                    List displayedEvents = tableModel.getFilteredEvents();
+                    if (e.getType() == TableModelEvent.INSERT) {
+//                        System.out.println("insert - current warnings: " + warnings.size() + ", errors: " + errors.size() + ", first row: " + firstRow + ", last row: " + lastRow);
+                        for (int i=firstRow;i<lastRow;i++) {
+                            LoggingEvent event = (LoggingEvent)displayedEvents.get(i);
+                            if (isWarningEvent(event)) {
+                                warnings.add(new EventWrapper(i, event));
+//                                System.out.println("added warning: " + i + " - " + event.getLevel());
+                            }
+                            if (isErrorOrFatalEvent(event)) {
+                                errors.add(new EventWrapper(i, event));
+//                                System.out.println("added error: " + i + " - " + event.getLevel());
+                            }
+                        }
+//                        System.out.println("insert- new warnings: " + warnings + ", errors: " + errors);
+
+                        //run evaluation on rows & add to list
+                    } else if (e.getType() == TableModelEvent.DELETE) {
+                        //find each eventwrapper with an id in the deleted range and remove it...
+//                        System.out.println("delete- current warnings: " + warnings.size() + ", errors: " + errors.size() + ", first row: " + firstRow + ", last row: " + lastRow + ", displayed event count: " + displayedEvents.size() );
+                        for (Iterator iter = warnings.iterator();iter.hasNext();) {
+                            EventWrapper wrapper = (EventWrapper)iter.next();
+                            if ((wrapper.rowNum >= firstRow) && (wrapper.rowNum <= lastRow)) {
+//                                System.out.println("deleting warning: " + wrapper);
+                                iter.remove();
+                            }
+                        }
+                        for (Iterator iter = errors.iterator();iter.hasNext();) {
+                            EventWrapper wrapper = (EventWrapper)iter.next();
+                            if ((wrapper.rowNum >= firstRow) && (wrapper.rowNum <= lastRow)) {
+//                                System.out.println("deleting error: " + wrapper);
+                                iter.remove();
+                            }
+                        }
+//                        System.out.println("delete- new warnings: " + warnings.size() + ", errors: " + errors.size());
+
+                        //remove any matching rows
+                    } else if (e.getType() == TableModelEvent.UPDATE) {
+//                        System.out.println("update - about to delete old warnings in range: " + firstRow + " to " + lastRow + ", current warnings: " + warnings.size() + ", errors: " + errors.size());
+                        //find each eventwrapper with an id in the deleted range and remove it...
+                        for (Iterator iter = warnings.iterator();iter.hasNext();) {
+                            EventWrapper wrapper = (EventWrapper)iter.next();
+                            if ((wrapper.rowNum >= firstRow) && (wrapper.rowNum <= lastRow)) {
+//                                System.out.println("update - deleting warning: " + wrapper);
+                                iter.remove();
+                            }
+                        }
+                        for (Iterator iter = errors.iterator();iter.hasNext();) {
+                            EventWrapper wrapper = (EventWrapper)iter.next();
+                            if ((wrapper.rowNum >= firstRow) && (wrapper.rowNum <= lastRow)) {
+//                                System.out.println("update - deleting error: " + wrapper);
+                                iter.remove();
+                            }
+                        }
+//                        System.out.println("update - after deleting old warnings in range: " + firstRow + " to " + lastRow + ", new warnings: " + warnings.size() + ", errors: " + errors.size());
+                        //NOTE: for update, we need to do i<= lastRow
+                        for (int i=firstRow;i<=lastRow;i++) {
+                            LoggingEvent event = (LoggingEvent)displayedEvents.get(i);
+                            if (isWarningEvent(event)) {
+//                                System.out.println("update - adding warning: " + i + ", event: " + event.getMessage());
+                                warnings.add(new EventWrapper(i, event));
+                            }
+                            if (isErrorOrFatalEvent(event)) {
+//                                System.out.println("update - adding error: " + i + ", event: " + event.getMessage());
+                                errors.add(new EventWrapper(i, event));
+                            }
+                        }
+//                        System.out.println("update - new warnings: " + warningRows.size() + ", errors: " + errorRows.size());
+                    }
+                    repaint();
+                }
+
+                private boolean isWarningEvent(LoggingEvent e) {
+                    return e.getLevel().equals(Level.WARN);
+                }
+
+                private boolean isErrorOrFatalEvent(LoggingEvent e) {
+                    return e.getLevel().equals(Level.ERROR) || e.getLevel().equals(Level.FATAL);
+                }
+            });
+        }
+
+        private EventWrapper getClosestRow(int rowToSelect)
+        {
+            //now we need to find the closest row
+            EventWrapper closestRow = null;
+            for (Iterator iter = errors.iterator();iter.hasNext();) {
+                EventWrapper event = (EventWrapper) iter.next();
+                int backDelta = rowToSelect - event.rowNum;
+                int forwardDelta = event.rowNum - rowToSelect;
+//                        System.out.println("errors - rowToSelect: " + rowToSelect + ", rowNum: " + event.rowNum + ", backDelta: " + backDelta + ", forwardDelta: " + forwardDelta);
+                if ((closestRow == null) || ((backDelta * -1) <= (rowToSelect - closestRow.rowNum)) || (forwardDelta <= (closestRow.rowNum - rowToSelect))) {
+//                            System.out.println("errors - setting closestRow to : " + event.rowNum);
+                    closestRow = event;
+                }
+            }
+            for (Iterator iter = warnings.iterator();iter.hasNext();) {
+                EventWrapper event = (EventWrapper) iter.next();
+                int backDelta = rowToSelect - event.rowNum;
+                int forwardDelta = event.rowNum - rowToSelect;
+//                        System.out.println("warnings - rowToSelect: " + rowToSelect + ", rowNum: " + event.rowNum + ", backDelta: " + backDelta + ", forwardDelta: " + forwardDelta);
+                if ((closestRow == null) || ((backDelta * -1) <= (rowToSelect - closestRow.rowNum)) || (forwardDelta <= (closestRow.rowNum - rowToSelect))) {
+//                            System.out.println("warnings - setting closestRow to : " + event.rowNum);
+                    closestRow = event;
+                }
+            }
+            return closestRow;
+        }
+
+        private void configureColors() {
+            List colorRules = colorizer.getCurrentRules();
+            for (Iterator iter = colorRules.iterator();iter.hasNext();) {
+                ColorRule rule = (ColorRule)iter.next();
+                String expression = rule.getExpression().toLowerCase().trim();
+                if (expression.equalsIgnoreCase(colorizer.getDefaultWarnExpression())) {
+                    WARNING_COLOR = rule.getBackgroundColor();
+                }
+                if (expression.equalsIgnoreCase(colorizer.getDefaultErrorOrFatalExpression())) {
+                    ERROR_OR_FATAL_COLOR = rule.getBackgroundColor();
+                }
+            }
+            repaint();
+        }
+
+        public void paintComponent(Graphics g)
+        {
+            super.paintComponent(g);
+
+            int rowCount = table.getRowCount();
+            if (rowCount == 0) {
+                return;
+            }
+            int componentHeight = getHeight();
+
+            for (Iterator iter = warnings.iterator();iter.hasNext();) {
+                EventWrapper wrapper = (EventWrapper)iter.next();
+                float ratio = (wrapper.rowNum / (float)rowCount);
+//                System.out.println("warning - ratio: " + ratio + ", component height: " + componentHeight);
+                int verticalLocation = (int) (componentHeight * ratio);
+                drawEvent(WARNING_COLOR, verticalLocation, eventHeight, g);
+//                System.out.println("painting warning - rownum: " + wrapper.rowNum + ", location: " + verticalLocation + ", height: " + eventHeight + ", component height: " + componentHeight + ", row count: " + rowCount);
+            }
+
+            for (Iterator iter = errors.iterator();iter.hasNext();) {
+                EventWrapper wrapper = (EventWrapper)iter.next();
+                float ratio = (wrapper.rowNum / (float)rowCount);
+//                System.out.println("error - ratio: " + ratio + ", component height: " + componentHeight);
+                int verticalLocation = (int) (componentHeight * ratio);
+                drawEvent(ERROR_OR_FATAL_COLOR, verticalLocation, eventHeight, g);
+//                System.out.println("painting error - rownum: " + wrapper.rowNum + ", location: " + verticalLocation + ", height: " + eventHeight + ", component height: " + componentHeight + ", row count: " + rowCount);
+            }
+        }
+
+        private void drawEvent(Color newColor, int verticalLocation, int eventHeight, Graphics g) {
+//            System.out.println("painting: - color: " + newColor + ", verticalLocation: " + verticalLocation + ", eventHeight: " + eventHeight);
+            Color oldColor = g.getColor();
+            g.setColor(newColor);
+            g.fillRect(0, verticalLocation, getWidth(), eventHeight);
+            g.setColor(newColor.darker());
+            g.drawRect(0, verticalLocation, getWidth(), eventHeight);
+            g.setColor(oldColor);
+        }
+
+        class EventWrapper {
+            int rowNum;
+            LoggingEvent loggingEvent;
+            public EventWrapper(int rowNum, LoggingEvent loggingEvent) {
+                this.rowNum = rowNum;
+                this.loggingEvent = loggingEvent;
+            }
+
+            public String toString()
+            {
+                return "event - rownum: " + rowNum + ", level: " + loggingEvent.getLevel();
+            }
+
+            public boolean equals(Object o)
+            {
+                if (this == o)
+                {
+                    return true;
+                }
+                if (o == null || getClass() != o.getClass())
+                {
+                    return false;
+                }
+
+                EventWrapper that = (EventWrapper) o;
+
+                if (loggingEvent != null ? !loggingEvent.equals(that.loggingEvent) : that.loggingEvent != null)
+                {
+                    return false;
+                }
+
+                return true;
+            }
+
+            public int hashCode()
+            {
+                return loggingEvent != null ? loggingEvent.hashCode() : 0;
+            }
+        }
+    }
+
     static class AutoFilterComboBox extends JComboBox {
         private boolean bypassFiltering;
         private List allEntries = new ArrayList();

Modified: logging/chainsaw/trunk/src/main/java/org/apache/log4j/chainsaw/color/RuleColorizer.java
URL: http://svn.apache.org/viewvc/logging/chainsaw/trunk/src/main/java/org/apache/log4j/chainsaw/color/RuleColorizer.java?rev=928311&r1=928310&r2=928311&view=diff
==============================================================================
--- logging/chainsaw/trunk/src/main/java/org/apache/log4j/chainsaw/color/RuleColorizer.java (original)
+++ logging/chainsaw/trunk/src/main/java/org/apache/log4j/chainsaw/color/RuleColorizer.java Sun Mar 28 00:25:54 2010
@@ -39,7 +39,7 @@ import org.apache.log4j.spi.LoggingEvent
  * @author Scott Deboy <sd...@apache.org>
  */
 public class RuleColorizer implements Colorizer {
-  private static final String DEFAULT_NAME = "Default";
+  public static final String DEFAULT_NAME = "Default";
   private Map rules;
   private final PropertyChangeSupport colorChangeSupport =
     new PropertyChangeSupport(this);
@@ -51,19 +51,23 @@ public class RuleColorizer implements Co
   private final Color FIND_BACKGROUND = new Color(40, 40, 40);
   private final Color LOGGER_FOREGROUND = Color.white;
   private final Color LOGGER_BACKGROUND = new Color(40, 40, 40);
-  
+  private final Color WARN_DEFAULT_COLOR = new Color(255, 255, 153);
+  private final Color ERROR_OR_FATAL_DEFAULT_COLOR = new Color(255, 153, 153);
+  private final String DEFAULT_ERROR_FATAL_EXPRESSION = "level == FATAL || level == ERROR";
+  private final String DEFAULT_WARN_EXPRESSION = "level == WARN";
+
   public RuleColorizer() {
     List rulesList = new ArrayList();
 
-    String expression = "level == FATAL || level == ERROR";
-    rulesList.add(
+      String expression = DEFAULT_ERROR_FATAL_EXPRESSION;
+      rulesList.add(
       new ColorRule(
-        expression, ExpressionRule.getRule(expression), new Color(255, 153, 153),
+        expression, ExpressionRule.getRule(expression), ERROR_OR_FATAL_DEFAULT_COLOR,
         Color.black));
-    expression = "level == WARN";
-    rulesList.add(
+      expression = DEFAULT_WARN_EXPRESSION;
+      rulesList.add(
       new ColorRule(
-        expression, ExpressionRule.getRule(expression), new Color(255, 255, 153),
+        expression, ExpressionRule.getRule(expression), WARN_DEFAULT_COLOR,
         Color.black));
 
       expression = "prop.log4j.marker exists";
@@ -75,6 +79,22 @@ public class RuleColorizer implements Co
     defaultRules.put(DEFAULT_NAME, rulesList);
     setRules(defaultRules);
   }
+
+  public String getDefaultWarnExpression() {
+      return DEFAULT_WARN_EXPRESSION;
+  }
+
+  public String getDefaultErrorOrFatalExpression() {
+      return DEFAULT_ERROR_FATAL_EXPRESSION;
+  }
+    
+  public Color getDefaultWarnColor() {
+      return WARN_DEFAULT_COLOR;
+  }
+
+  public Color getDefaultErrorOrFatalColor() {
+      return ERROR_OR_FATAL_DEFAULT_COLOR;
+  }
   
   public void setLoggerRule(Rule loggerRule) {
     this.loggerRule = loggerRule;
@@ -95,6 +115,10 @@ public class RuleColorizer implements Co
     return rules;
   }
 
+  public List getCurrentRules() {
+    return (List) rules.get(currentRuleSet);
+  }
+
   public void addRules(Map newRules) {
     Iterator iter = newRules.entrySet().iterator();
 

Modified: logging/chainsaw/trunk/src/main/resources/org/apache/log4j/chainsaw/help/release-notes.html
URL: http://svn.apache.org/viewvc/logging/chainsaw/trunk/src/main/resources/org/apache/log4j/chainsaw/help/release-notes.html?rev=928311&r1=928310&r2=928311&view=diff
==============================================================================
--- logging/chainsaw/trunk/src/main/resources/org/apache/log4j/chainsaw/help/release-notes.html (original)
+++ logging/chainsaw/trunk/src/main/resources/org/apache/log4j/chainsaw/help/release-notes.html Sun Mar 28 00:25:54 2010
@@ -10,6 +10,14 @@
 <b>NOTE:</b> The mechanism and format used to persist settings in Chainsaw is subject to change.  If you are experiencing problems displaying events in Chainsaw, please delete everything in the $user.dir/.chainsaw directory and restart Chainsaw.
 <br>
 <h1>1.99.99</h1>
+<h2>27 Mar 2010</h2>
+<ul>
+<li>
+New clickable thumbnail bar next to logpanel scroll bar provides a visual indicator of events with warning level as well as error/fatal.
+Clicking in the bar will jump to the closest event.
+Colors in the thumbnail bar will sync with the default warning and error/fatal color rules (LEVEL == WARNING) and (level == FATAL || level == ERROR) respectively.
+</li>
+</ul>
 <h2>23 Mar 2010</h2>
 <ul>
 <li>