Added ability to create log file receiver configurations from log4j xml configuration fileappender entries
Parses the xml configuration and the patternLayout and date specifiers and creates LogFilePatternReceiver entries Project: http://git-wip-us.apache.org/repos/asf/logging-chainsaw/repo Commit: http://git-wip-us.apache.org/repos/asf/logging-chainsaw/commit/910dc454 Tree: http://git-wip-us.apache.org/repos/asf/logging-chainsaw/tree/910dc454 Diff: http://git-wip-us.apache.org/repos/asf/logging-chainsaw/diff/910dc454 Branch: refs/heads/master Commit: 910dc45429c1503257655860e762dfaae2c963fb Parents: e6f85a2 Author: Scott Deboy <[email protected]> Authored: Fri Nov 5 08:26:33 2010 +0000 Committer: Scott Deboy <[email protected]> Committed: Fri Nov 5 08:26:33 2010 +0000 ---------------------------------------------------------------------- .../chainsaw/LogFilePatternLayoutBuilder.java | 118 ++++++++++++++++++- .../java/org/apache/log4j/chainsaw/LogUI.java | 43 +++++++ .../chainsaw/ReceiverConfigurationPanel.java | 81 +++++++++++++ .../log4j/chainsaw/help/release-notes.html | 5 + 4 files changed, 246 insertions(+), 1 deletion(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/910dc454/src/main/java/org/apache/log4j/chainsaw/LogFilePatternLayoutBuilder.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/chainsaw/LogFilePatternLayoutBuilder.java b/src/main/java/org/apache/log4j/chainsaw/LogFilePatternLayoutBuilder.java index ccf1e7c..1249863 100644 --- a/src/main/java/org/apache/log4j/chainsaw/LogFilePatternLayoutBuilder.java +++ b/src/main/java/org/apache/log4j/chainsaw/LogFilePatternLayoutBuilder.java @@ -16,13 +16,23 @@ */ package org.apache.log4j.chainsaw; +import java.io.File; +import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; +import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; +import javax.xml.parsers.DocumentBuilder; +import javax.xml.parsers.DocumentBuilderFactory; +import javax.xml.parsers.ParserConfigurationException; + +import org.apache.log4j.FileAppender; import org.apache.log4j.helpers.OptionConverter; import org.apache.log4j.pattern.ClassNamePatternConverter; +import org.apache.log4j.pattern.DatePatternConverter; import org.apache.log4j.pattern.FileLocationPatternConverter; import org.apache.log4j.pattern.FullLocationPatternConverter; import org.apache.log4j.pattern.LevelPatternConverter; @@ -39,6 +49,14 @@ import org.apache.log4j.pattern.PropertiesPatternConverter; import org.apache.log4j.pattern.RelativeTimePatternConverter; import org.apache.log4j.pattern.SequenceNumberPatternConverter; import org.apache.log4j.pattern.ThreadPatternConverter; +import org.apache.log4j.xml.Log4jEntityResolver; +import org.apache.log4j.xml.SAXErrorHandler; +import org.w3c.dom.Document; +import org.w3c.dom.NamedNodeMap; +import org.w3c.dom.Node; +import org.w3c.dom.NodeList; +import org.xml.sax.InputSource; +import org.xml.sax.SAXException; public class LogFilePatternLayoutBuilder { @@ -52,11 +70,39 @@ public class LogFilePatternLayoutBuilder return getFormatFromConverters(converters); } + public static String getTimeStampFormat(String patternLayout) { + int basicIndex = patternLayout.indexOf("%d"); + if (basicIndex < 0) { + return null; + } + + int index = patternLayout.indexOf("%d{"); + //%d - default + if (index < 0) { + return "yyyy-MM-dd HH:mm:ss,SSS"; + } + + int length = patternLayout.substring(index).indexOf("}"); + String timestampFormat = patternLayout.substring(index + "%d{".length(), index + length); + if (timestampFormat.equals("ABSOLUTE")) { + return "HH:mm:ss,SSS"; + } + if (timestampFormat.equals("ISO8601")) { + return "yyyy-MM-dd HH:mm:ss,SSS"; + } + if (timestampFormat.equals("DATE")) { + return "dd MMM yyyy HH:mm:ss,SSS"; + } + return timestampFormat; + } + private static String getFormatFromConverters(List converters) { StringBuffer buffer = new StringBuffer(); for (Iterator iter = converters.iterator();iter.hasNext();) { LoggingEventPatternConverter converter = (LoggingEventPatternConverter)iter.next(); - if (converter instanceof MessagePatternConverter) { + if (converter instanceof DatePatternConverter) { + buffer.append("TIMESTAMP"); + } else if (converter instanceof MessagePatternConverter) { buffer.append("MESSAGE"); } else if (converter instanceof LoggerPatternConverter) { buffer.append("LOGGER"); @@ -98,4 +144,74 @@ public class LogFilePatternLayoutBuilder } return buffer.toString(); } + + public static Map getAppenderConfiguration(File file) throws IOException, ParserConfigurationException, SAXException { + InputStream stream = file.toURI().toURL().openStream(); + Map result = new HashMap(); + try { + InputSource src = new InputSource(stream); + src.setSystemId(file.toURI().toURL().toString()); + DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance(); + DocumentBuilder docBuilder = dbf.newDocumentBuilder(); + + docBuilder.setErrorHandler(new SAXErrorHandler()); + docBuilder.setEntityResolver(new Log4jEntityResolver()); + Document doc = docBuilder.parse(src); + NodeList appenders = doc.getElementsByTagName("appender"); + for (int i = 0; i < appenders.getLength(); i++) { + Node appender = appenders.item(i); + NamedNodeMap appenderAttributes = appender.getAttributes(); +// Class appenderClass = Class.forName(map.getNamedItem("class").getNodeValue()); + Node appenderClass = appenderAttributes.getNamedItem("class"); + try { + if (appenderAttributes.getNamedItem("name") != null && appenderClass != null && appenderClass.getNodeValue() != null) { + //load the class or try to support custom subclasses that we don't have available in the classpath but end in 'FileAppender' + if (appenderClass.getNodeValue().toLowerCase().endsWith("fileappender") || FileAppender.class.isAssignableFrom(Class.forName(appenderClass.getNodeValue()))) { + String appenderName = appenderAttributes.getNamedItem("name").getNodeValue(); + //subclass of FileAppender - add it + Map entry = new HashMap(); + NodeList appenderChildren = appender.getChildNodes(); + for (int j = 0; j < appenderChildren.getLength(); j++) { + Node appenderChild = appenderChildren.item(j); + if (appenderChild.getNodeName().equals("param") && appenderChild.hasAttributes()) { + Node fileNameNode = appenderChild.getAttributes().getNamedItem("name"); + if (fileNameNode != null && fileNameNode.getNodeValue().equals("file")) { + Node fileValueNode = appenderChild.getAttributes().getNamedItem("value"); + if (fileValueNode != null) { + entry.put("file", fileValueNode.getNodeValue()); + } + } + } + if (appenderChild.getNodeName().equals("layout") && appenderChild.hasAttributes()) { + NamedNodeMap layoutAttributes = appenderChild.getAttributes(); + Node layoutNode = layoutAttributes.getNamedItem("class"); + if (layoutNode != null && layoutNode.getNodeValue() != null && layoutNode.getNodeValue().equals("org.apache.log4j.PatternLayout")) { + NodeList layoutChildren = appenderChild.getChildNodes(); + for (int k = 0; k < layoutChildren.getLength(); k++) { + Node layoutChild = layoutChildren.item(k); + if (layoutChild.getNodeName().equals("param") && layoutChild.hasAttributes()) { + Node layoutName = layoutChild.getAttributes().getNamedItem("name"); + if (layoutName != null && layoutName.getNodeValue() != null && layoutName.getNodeValue().equals("ConversionPattern")) { + Node conversionValue = layoutChild.getAttributes().getNamedItem("value"); + if (conversionValue != null) { + entry.put("conversion", conversionValue.getNodeValue()); + } + } + } + } + } + } + } + result.put(appenderName, entry); + } + } + } catch (Exception e) { + e.printStackTrace(); + } + } + } finally { + stream.close(); + } + return result; + } } http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/910dc454/src/main/java/org/apache/log4j/chainsaw/LogUI.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/chainsaw/LogUI.java b/src/main/java/org/apache/log4j/chainsaw/LogUI.java index da000ca..e8bf684 100644 --- a/src/main/java/org/apache/log4j/chainsaw/LogUI.java +++ b/src/main/java/org/apache/log4j/chainsaw/LogUI.java @@ -84,6 +84,7 @@ import javax.swing.event.ChangeListener; import javax.swing.event.EventListenerList; import javax.swing.event.HyperlinkEvent; import javax.swing.event.HyperlinkListener; +import javax.xml.parsers.ParserConfigurationException; import org.apache.log4j.Appender; import org.apache.log4j.AppenderSkeleton; @@ -126,6 +127,7 @@ import org.apache.log4j.spi.LoggingEvent; import org.apache.log4j.spi.RepositorySelector; import org.apache.log4j.xml.DOMConfigurator; import org.apache.log4j.xml.XMLDecoder; +import org.xml.sax.SAXException; /** @@ -1459,6 +1461,47 @@ public class LogUI extends JFrame implements ChainsawViewer, SettingsListener { MessageCenter.getInstance().getLogger().info( "An error occurred creating your Receiver"); } + } else if (receiverConfigurationPanel.getModel().isLog4jConfig()) { + File log4jConfigFile = receiverConfigurationPanel.getModel().getLog4jConfigFile(); + if (log4jConfigFile != null) { + try { + Map entries = LogFilePatternLayoutBuilder.getAppenderConfiguration(log4jConfigFile); + for (Iterator iter = entries.entrySet().iterator();iter.hasNext();) { + try { + Map.Entry entry = (Map.Entry)iter.next(); + String name = (String) entry.getKey(); + Map values = (Map) entry.getValue(); + //values: conversion, file + String conversionPattern = values.get("conversion").toString(); + File file = new File(values.get("file").toString()); + URL fileURL = file.toURI().toURL(); + String timestampFormat = LogFilePatternLayoutBuilder.getTimeStampFormat(conversionPattern); + String receiverPattern = LogFilePatternLayoutBuilder.getLogFormatFromPatternLayout(conversionPattern); + VFSLogFilePatternReceiver fileReceiver = new VFSLogFilePatternReceiver(); + fileReceiver.setName(name); + fileReceiver.setAutoReconnect(true); + fileReceiver.setContainer(LogUI.this); + fileReceiver.setAppendNonMatches(true); + fileReceiver.setFileURL(fileURL.toURI().toString()); + fileReceiver.setTailing(true); + fileReceiver.setLogFormat(receiverPattern); + fileReceiver.setTimestampFormat(timestampFormat); + fileReceiver.setThreshold(Level.TRACE); + pluginRegistry.addPlugin(fileReceiver); + fileReceiver.activateOptions(); + receiversPanel.updateReceiverTreeInDispatchThread(); + } catch (URISyntaxException e1) { + e1.printStackTrace(); + } + } + } catch (IOException e1) { + e1.printStackTrace(); + } catch (ParserConfigurationException e1) { + e1.printStackTrace(); + } catch (SAXException e1) { + e1.printStackTrace(); + } + } } else if (receiverConfigurationPanel.getModel().isLoadConfig()) { configURL = receiverConfigurationPanel.getModel().getConfigToLoad(); } else if (receiverConfigurationPanel.getModel().isLogFileReceiverConfig()) { http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/910dc454/src/main/java/org/apache/log4j/chainsaw/ReceiverConfigurationPanel.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/apache/log4j/chainsaw/ReceiverConfigurationPanel.java b/src/main/java/org/apache/log4j/chainsaw/ReceiverConfigurationPanel.java index 1f94570..a6c3715 100644 --- a/src/main/java/org/apache/log4j/chainsaw/ReceiverConfigurationPanel.java +++ b/src/main/java/org/apache/log4j/chainsaw/ReceiverConfigurationPanel.java @@ -30,6 +30,7 @@ import java.awt.event.FocusEvent; import java.awt.event.FocusListener; import java.io.File; import java.net.MalformedURLException; +import java.net.URISyntaxException; import java.net.URL; import java.util.List; @@ -74,6 +75,10 @@ class ReceiverConfigurationPanel extends JPanel { private DefaultComboBoxModel networkReceiverClassNameComboBoxModel; private DefaultComboBoxModel networkReceiverPortComboBoxModel; + //log4j config receiver widgets + private JButton browseLog4jConfigButton; + private JTextField log4jConfigURLTextField; + //logfile receiver widgets private JButton browseLogFileButton; private JComboBox logFileFormatTypeComboBox; @@ -97,6 +102,7 @@ class ReceiverConfigurationPanel extends JPanel { private JButton cancelButton; //radiobutton widgets + private JRadioButton log4jConfigReceiverRadioButton; private JRadioButton logFileReceiverRadioButton; private JRadioButton networkReceiverRadioButton; private JRadioButton useExistingConfigurationRadioButton; @@ -106,6 +112,7 @@ class ReceiverConfigurationPanel extends JPanel { private final JPanel networkReceiverPanel = buildNetworkReceiverPanel(); private final JPanel logFileReceiverPanel = buildLogFileReceiverPanel(); + private final JPanel log4jConfigReceiverPanel = buildLog4jConfigReceiverPanel(); private final JPanel useExistingConfigurationPanel = buildUseExistingConfigurationPanel(); private final JPanel dontWarnAndOKPanel = buildDontWarnAndOKPanel(); private final JPanel bottomDescriptionPanel = buildBottomDescriptionPanel(); @@ -140,6 +147,14 @@ class ReceiverConfigurationPanel extends JPanel { c.gridx = 0; c.gridy = yPos++; c.fill = GridBagConstraints.HORIZONTAL; + log4jConfigReceiverRadioButton = new JRadioButton(" Use log4j.xml fileappender configuration "); + buttonGroup.add(log4jConfigReceiverRadioButton); + add(log4jConfigReceiverRadioButton, c); + + c = new GridBagConstraints(); + c.gridx = 0; + c.gridy = yPos++; + c.fill = GridBagConstraints.HORIZONTAL; networkReceiverRadioButton = new JRadioButton(" Receive events from the network "); buttonGroup.add(networkReceiverRadioButton); add(networkReceiverRadioButton, c); @@ -185,6 +200,7 @@ class ReceiverConfigurationPanel extends JPanel { }; logFileReceiverRadioButton.addActionListener(al); + log4jConfigReceiverRadioButton.addActionListener(al); networkReceiverRadioButton.addActionListener(al); useExistingConfigurationRadioButton.addActionListener(al); @@ -332,6 +348,52 @@ class ReceiverConfigurationPanel extends JPanel { return panel; } + private JPanel buildLog4jConfigReceiverPanel() { + log4jConfigURLTextField = new JTextField(); + browseLog4jConfigButton = new JButton(new AbstractAction(" Open File... ") { + public void actionPerformed(ActionEvent e) { + try { + + URL url = browseConfig(); + + if (url != null) { + log4jConfigURLTextField.setText(url.toExternalForm()); + } + } catch (Exception ex) { + logger.error( + "Error browsing for log4j config file", ex); + } + } + }); + + browseLog4jConfigButton.setToolTipText( + "Shows a File Open dialog to allow you to find a log4j configuration file"); + JPanel panel = new JPanel(new GridBagLayout()); + GridBagConstraints c = new GridBagConstraints(); + c.gridx = 0; + c.gridy = 0; + c.gridwidth = 2; + c.anchor = GridBagConstraints.LINE_START; + c.insets = new Insets(0, 0, 5, 0); + panel.add(browseLog4jConfigButton, c); + + c = new GridBagConstraints(); + c.gridx = 0; + c.gridy = 1; + c.insets = new Insets(0, 5, 0, 5); + panel.add(new JLabel(" log4j configuration file URL "), c); + + c = new GridBagConstraints(); + c.gridx = 1; + c.gridy = 1; + c.weightx = 0.5; + c.fill = GridBagConstraints.HORIZONTAL; + panel.add(log4jConfigURLTextField, c); + + + return panel; + } + private JPanel buildLogFileReceiverPanel() { JPanel panel = new JPanel(new GridBagLayout()); browseLogFileButton = new JButton(new AbstractAction(" Open File... ") { @@ -566,6 +628,9 @@ class ReceiverConfigurationPanel extends JPanel { if (component == logFileReceiverRadioButton) { lowerPanel.add(logFileReceiverPanel, BorderLayout.NORTH); } + if (component == log4jConfigReceiverRadioButton) { + lowerPanel.add(log4jConfigReceiverPanel, BorderLayout.NORTH); + } lowerPanel.revalidate(); lowerPanel.repaint(); } @@ -683,6 +748,10 @@ class ReceiverConfigurationPanel extends JPanel { return !cancelled && logFileReceiverRadioButton.isSelected(); } + boolean isLog4jConfig() { + return !cancelled && log4jConfigReceiverRadioButton.isSelected(); + } + URL getConfigToLoad() { try @@ -767,5 +836,17 @@ class ReceiverConfigurationPanel extends JPanel { public boolean isCancelled() { return cancelled; } + + public File getLog4jConfigFile() { + try { + URL newConfigurationURL = new URL(log4jConfigURLTextField.getText()); + return new File(newConfigurationURL.toURI()); + } catch (URISyntaxException e) { + e.printStackTrace(); + } catch (MalformedURLException e) { + e.printStackTrace(); + } + return null; + } } } http://git-wip-us.apache.org/repos/asf/logging-chainsaw/blob/910dc454/src/main/resources/org/apache/log4j/chainsaw/help/release-notes.html ---------------------------------------------------------------------- diff --git a/src/main/resources/org/apache/log4j/chainsaw/help/release-notes.html b/src/main/resources/org/apache/log4j/chainsaw/help/release-notes.html index 803563d..f267cea 100644 --- a/src/main/resources/org/apache/log4j/chainsaw/help/release-notes.html +++ b/src/main/resources/org/apache/log4j/chainsaw/help/release-notes.html @@ -10,6 +10,11 @@ <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>2.1</h1> +<h2>5 Nov 2010</h2> +<ul> +<li>Added ability to create log file receiver configurations from log4j xml configuration fileappender entries</li> +<li>Added ability to save the receiver configuration from the receiver modification panel</li> +</ul> <h2>4 Nov 2010</h2> <ul> <li>Added ability to save the receiver configuration defined through the initial receiver configuration panel with a user-specified file path and name</li>
