packages feed

hslogger 1.0.5 → 1.0.6

raw patch · 11 files changed

+1192/−8 lines, 11 filesdep +old-localedep +timebinary-added

Dependencies added: old-locale, time

Files

+ contrib/java/build.xml view
@@ -0,0 +1,38 @@+<project name="hslogger4j" default="dist" basedir=".">+    <description>+        simple example build file+    </description>+  <!-- set global properties for this build -->+  <property name="src"   location="."/>+  <property name="build" location="build"/>+  <property name="dist"  location="dist"/>++  <target name="init">+    <!-- Create the time stamp -->+    <tstamp/>+    <!-- Create the build directory structure used by compile -->+    <mkdir dir="${build}"/>+  </target>++  <target name="compile" depends="init"+        description="compile the source " >+    <!-- Compile the java code from ${src} into ${build} -->+    <javac srcdir="${src}" destdir="${build}"/>+  </target>++  <target name="dist" depends="compile"+        description="generate the distribution" >+    <!-- Create the distribution directory -->+    <mkdir dir="${dist}"/>++    <!-- Put everything in ${build} into the MyProject-${DSTAMP}.jar file -->+    <jar jarfile="${dist}/hslogger4j-${DSTAMP}.jar" basedir="${build}"/>+  </target>++  <target name="clean"+        description="clean up" >+    <!-- Delete the ${build} and ${dist} directory trees -->+    <delete dir="${build}"/>+    <delete dir="${dist}"/>+  </target>+</project>
+ contrib/java/hslogger4j-plugins.xml view
@@ -0,0 +1,22 @@+<?xml version="1.0" encoding="UTF-8" ?>+<!DOCTYPE log4j:configuration >+<log4j:configuration xmlns:log4j="http://jakarta.apache.org/log4j/" debug="true">++  <plugin name="Hslogger4jSocketReceiver" +          class="org.apache.log4j.net.XMLSocketReceiver">+          <param name="decoder" value="org.haskell.hslogger4j.XMLDecoder"/>+    <param name="Port" value="4448"/>+    <level value="DEBUG"/>+  </plugin>++  <plugin name="Hslogger4jFileReader" +          class="org.haskell.hslogger4j.LogFileXMLReceiver">+    <param name="decoder" value="org.haskell.hslogger4j.XMLDecoder"/>+  </plugin>++  <root>+    <level value="debug"/>+  </root>++</log4j:configuration>+
+ contrib/java/hslogger4j.jar view

binary file changed (absent → 9129 bytes)

+ contrib/java/org/haskell/hslogger/HsloggerLevel.java view
@@ -0,0 +1,154 @@+/*+ * Copyright 1999,2004 The Apache Software Foundation.+ * + * Licensed 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.haskell.hslogger4j;++import java.util.ArrayList;+import java.util.List;++import org.apache.log4j.Level;++/**+ *  An extension of the Level class that provides support for java.util.logging + * Levels.+ *+ *  @author Scott Deboy <sdeboy@apache.org>+ *+ */++public class HsloggerLevel extends org.apache.log4j.Level {++  public final static int EMERGENCY_INT = 51000; // FATAL+  public final static int ALERT_INT     = 50000;+  public final static int CRITICAL_INT  = 41000;+  public final static int ERROR_INT     = 40000; // ERROR+  public final static int WARNING_INT   = 30000; // WARN+  public final static int NOTICE_INT    = 21000;+  public static final int INFO_INT      = 20000; // INFO+  public static final int DEBUG_INT     = 10000; // DEBUG+  +  public static final HsloggerLevel EMERGENCY = new HsloggerLevel(EMERGENCY_INT, "EMERGENCY", 0);+  public static final HsloggerLevel ALERT     = new HsloggerLevel(ALERT_INT    , "ALERT"    , 1);      +  public static final HsloggerLevel CRITICAL  = new HsloggerLevel(CRITICAL_INT , "CRITICAL" , 2);      +  public static final HsloggerLevel ERROR     = new HsloggerLevel(ERROR_INT    , "ERROR"    , 3);+  public static final HsloggerLevel WARNING   = new HsloggerLevel(WARNING_INT  , "WARNING"  , 4);+  public static final HsloggerLevel NOTICE    = new HsloggerLevel(NOTICE_INT   , "NOTICE"   , 5);+  public static final HsloggerLevel INFO      = new HsloggerLevel(INFO_INT     , "INFO"     , 6);+  public static final HsloggerLevel DEBUG     = new HsloggerLevel(DEBUG_INT    , "DEBUG"    , 7);++  protected HsloggerLevel(int level, String levelStr, int syslogEquivalent) {+    super(level, levelStr, syslogEquivalent);+  }++  /**+    Convert an integer passed as argument to a level. If the+    conversion fails, then this method returns the specified default.+  */+  public static HsloggerLevel toLevel(int val, HsloggerLevel defaultLevel) {+    switch (val) {+    case EMERGENCY_INT:+      return EMERGENCY;++    case ALERT_INT:+      return ALERT;++    case CRITICAL_INT:+      return CRITICAL;++    case ERROR_INT:+      return ERROR;++    case WARNING_INT:+      return WARNING;++    case NOTICE_INT:+      return NOTICE;+      +    case INFO_INT:+      return INFO;++    case DEBUG_INT:+      return DEBUG;++    default:+      return defaultLevel;+    }+  }++  public static Level toLevel(int val) {+    return toLevel(val, CRITICAL);+  }++  public static List getAllPossibleLevels() {+  	ArrayList list=new ArrayList();+  	list.add(DEBUG);+  	list.add(INFO);+  	list.add(NOTICE);+  	list.add(WARNING);+  	list.add(ERROR);+  	list.add(CRITICAL);+  	list.add(ALERT);+  	list.add(EMERGENCY);+  	return list;+  }++  public static Level toLevel(String s) {+  	return toLevel(s, Level.DEBUG);+  }+  +  public static Level toLevel(String sArg, Level defaultLevel) {+    if (sArg == null) {+      return defaultLevel;+    }++    String s = sArg.toUpperCase();++    if (s.equals("EMERGENCY")) {+      return EMERGENCY;+    }++    if (s.equals("ALERT")) {+      return ALERT;+    }++    if (s.equals("CRITICAL")) {+      return CRITICAL;+    }++    if (s.equals("ERROR")) {+      return ERROR;+    }++    if (s.equals("WARNING")) {+      return WARNING;+    }++    if (s.equals("NOTICE")) {+      return NOTICE;+    }++    if (s.equals("INFO")) {+      return INFO;+    }++    if (s.equals("DEBUG")) {+      return DEBUG;+    }+    return defaultLevel;+  }++}+
+ contrib/java/org/haskell/hslogger/LogFileXMLReceiver.java view
@@ -0,0 +1,303 @@+/*
+ * Copyright 1999,2004 The Apache Software Foundation. Licensed 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.haskell.hslogger4j;
+
+import java.io.BufferedReader;
+import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.io.Reader;
+import java.net.MalformedURLException;
+import java.net.URL;
+import java.util.Collection;
+import java.util.Iterator;
+
+import org.apache.log4j.xml.*;
+import org.apache.log4j.helpers.Constants;
+import org.apache.log4j.plugins.Receiver;
+import org.apache.log4j.rule.ExpressionRule;
+import org.apache.log4j.rule.Rule;
+import org.apache.log4j.spi.Decoder;
+import org.apache.log4j.spi.LoggingEvent;
+
+/**
+ * LogFileXMLReceiver will read an xml-formated log file and make the events in the log file
+ * available to the log4j framework.
+ * <p>
+ * This receiver supports log files created using log4j's XMLLayout, as well as java.util.logging
+ * XMLFormatter (via the org.apache.log4j.spi.Decoder interface).
+ * <p>
+ * By default, log4j's XMLLayout is supported (no need to specify a decoder in that case).
+ * <p>
+ * To configure this receiver to support java.util.logging's XMLFormatter, specify a 'decoder' param
+ * of org.apache.log4j.xml.UtilLoggingXMLDecoder.
+ * <p>
+ * Tailing -may- work, but not in all cases (try using a file:// URL). If a process has a log file
+ * open, the receiver may be able to read and tail the file. If the process closes the file and
+ * reopens the file, the receiver may not be able to continue tailing the file.
+ * <p>
+ * An expressionFilter may be specified. Only events passing the expression will be forwarded to the
+ * log4j framework.
+ * <p>
+ * Once the event has been "posted", it will be handled by the appenders currently configured in the
+ * LoggerRespository.
+ * 
+ * @author Scott Deboy <sdeboy@apache.org>
+ * @since 1.3
+ */
+
+public class LogFileXMLReceiver extends Receiver {
+    private String fileURL;
+    private Rule expressionRule;
+    private String filterExpression;
+    private String decoder = "org.apache.log4j.xml.XMLDecoder";
+    private boolean tailing = false;
+
+    private Decoder decoderInstance;
+    private Reader reader;
+    private static final String FILE_KEY = "file";
+    private String host;
+    private String path;
+    private boolean useCurrentThread;
+
+    /**
+     * Accessor
+     * 
+     * @return file URL
+     */
+    public String getFileURL() {
+        return fileURL;
+    }
+
+    /**
+     * Specify the URL of the XML-formatted file to process.
+     * 
+     * @param fileURL
+     */
+    public void setFileURL(String fileURL) {
+        this.fileURL = fileURL;
+    }
+
+    /**
+     * Accessor
+     * 
+     * @return
+     */
+    public String getDecoder() {
+        return decoder;
+    }
+
+    /**
+     * Specify the class name implementing org.apache.log4j.spi.Decoder that can process the file.
+     * 
+     * @param _decoder
+     */
+    public void setDecoder(String _decoder) {
+        decoder = _decoder;
+    }
+
+    /**
+     * Accessor
+     * 
+     * @return filter expression
+     */
+    public String getFilterExpression() {
+        return filterExpression;
+    }
+
+    /**
+     * Accessor
+     * 
+     * @return tailing flag
+     */
+    public boolean isTailing() {
+        return tailing;
+    }
+
+    /**
+     * Set the 'tailing' flag - may only work on file:// URLs and may stop tailing if the writing
+     * process closes the file and reopens.
+     * 
+     * @param tailing
+     */
+    public void setTailing(boolean tailing) {
+        this.tailing = tailing;
+    }
+
+    /**
+     * Set the filter expression that will cause only events which pass the filter to be forwarded
+     * to the log4j framework.
+     * 
+     * @param filterExpression
+     */
+    public void setFilterExpression(String filterExpression) {
+        this.filterExpression = filterExpression;
+    }
+
+    private boolean passesExpression(LoggingEvent event) {
+        if (event != null) {
+            if (expressionRule != null) {
+                return (expressionRule.evaluate(event));
+            }
+        }
+        return true;
+    }
+
+    public static void main(String[] args) {
+        /*
+         * LogFileXMLReceiver test = new LogFileXMLReceiver();
+         * test.setFileURL("file:///c:/samplelog.xml"); test.setFilterExpression("level >= TRACE");
+         * test.activateOptions();
+         */
+    }
+
+    /**
+     * Close the receiver, release any resources that are accessing the file.
+     */
+    public void shutdown() {
+        try {
+            if (reader != null) {
+                reader.close();
+                reader = null;
+            }
+        } catch (IOException ioe) {
+            ioe.printStackTrace();
+        }
+    }
+
+    /**
+     * Process the file
+     */
+    public void activateOptions() {
+        Runnable runnable = new Runnable() {
+            public void run() {
+                try {
+                    URL url = new URL(fileURL);
+                    host = url.getHost();
+                    if (host != null && host.equals("")) {
+                        host = FILE_KEY;
+                    }
+                    path = url.getPath();
+                } catch (MalformedURLException e1) {
+                    // TODO Auto-generated catch block
+                    e1.printStackTrace();
+                }
+
+                try {
+                    if (filterExpression != null) {
+                        expressionRule = ExpressionRule.getRule(filterExpression);
+                    }
+                } catch (Exception e) {
+                    getLogger().warn("Invalid filter expression: " + filterExpression, e);
+                }
+
+                Class c;
+                try {
+                    c = Class.forName(decoder);
+                    Object o = c.newInstance();
+                    if (o instanceof Decoder) {
+                        decoderInstance = (Decoder) o;
+                    }
+                } catch (ClassNotFoundException e) {
+                    // TODO Auto-generated catch block
+                    e.printStackTrace();
+                } catch (InstantiationException e) {
+                    // TODO Auto-generated catch block
+                    e.printStackTrace();
+                } catch (IllegalAccessException e) {
+                    // TODO Auto-generated catch block
+                    e.printStackTrace();
+                }
+
+                try {
+                    reader = new InputStreamReader(new URL(getFileURL()).openStream());
+                    process(reader);
+                } catch (FileNotFoundException fnfe) {
+                    getLogger().info("file not available");
+                } catch (IOException ioe) {
+                    getLogger().warn("unable to load file", ioe);
+                    return;
+                }
+            }
+        };
+        if (useCurrentThread) {
+            runnable.run();
+        } else {
+            Thread thread = new Thread(runnable, "LogFileXMLReceiver-" + getName());
+
+            thread.start();
+
+        }
+    }
+
+    private void process(Reader unbufferedReader) throws IOException {
+        BufferedReader bufferedReader = new BufferedReader(unbufferedReader);
+        char[] content = new char[10000];
+        getLogger().debug("processing starting: " + fileURL);
+        int length = 0;
+        do {
+            System.out.println("in do loop-about to process");
+            while ((length = bufferedReader.read(content)) > -1) {
+                processEvents(decoderInstance.decodeEvents(String.valueOf(content, 0, length)));
+            }
+            if (tailing) {
+                try {
+                    Thread.sleep(5000);
+                } catch (InterruptedException e) {
+                    // TODO Auto-generated catch block
+                    e.printStackTrace();
+                }
+            }
+        } while (tailing);
+        getLogger().debug("processing complete: " + fileURL);
+
+        shutdown();
+    }
+
+    private void processEvents(Collection c) {
+        if (c == null) {
+            return;
+        }
+
+        for (Iterator iter = c.iterator(); iter.hasNext();) {
+            LoggingEvent evt = (LoggingEvent) iter.next();
+            if (passesExpression(evt)) {
+                if (evt.getProperty(Constants.HOSTNAME_KEY) != null) {
+                    evt.setProperty(Constants.HOSTNAME_KEY, host);
+                }
+                if (evt.getProperty(Constants.APPLICATION_KEY) != null) {
+                    evt.setProperty(Constants.APPLICATION_KEY, path);
+                }
+                doPost(evt);
+            }
+        }
+    }
+
+    /**
+     * When true, this property uses the current Thread to perform the import, otherwise when false
+     * (the default), a new Thread is created and started to manage the import.
+     * 
+     * @return
+     */
+    public final boolean isUseCurrentThread() {
+        return useCurrentThread;
+    }
+
+    /**
+     * Sets whether the current Thread or a new Thread is created to perform the import, the default
+     * being false (new Thread created).
+     * 
+     * @param useCurrentThread
+     */
+    public final void setUseCurrentThread(boolean useCurrentThread) {
+        this.useCurrentThread = useCurrentThread;
+    }
+
+}
+ contrib/java/org/haskell/hslogger/XMLDecoder.java view
@@ -0,0 +1,410 @@+/*+ * Copyright 1999,2004 The Apache Software Foundation.+ * + * Licensed 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.haskell.hslogger4j;++import java.awt.Component;+import java.io.IOException;+import java.io.InputStreamReader;+import java.io.LineNumberReader;+import java.io.StringReader;+import java.net.URL;+import java.util.HashMap;+import java.util.Hashtable;+import java.util.Iterator;+import java.util.Map;+import java.util.Vector;++import javax.swing.ProgressMonitorInputStream;+import javax.xml.parsers.DocumentBuilder;+import javax.xml.parsers.DocumentBuilderFactory;+import javax.xml.parsers.ParserConfigurationException;++import org.apache.log4j.xml.*;+import org.apache.log4j.Level;+import org.apache.log4j.Logger;+import org.apache.log4j.spi.Decoder;+import org.apache.log4j.spi.LoggingEvent;+import org.apache.log4j.spi.ThrowableInformation;+import org.w3c.dom.Document;+import org.w3c.dom.Node;+import org.w3c.dom.NodeList;+import org.xml.sax.InputSource;+++/**+ * Decodes Logging Events in XML formated into elements that are used by+ * Chainsaw.+ *+ * This decoder can process a collection of log4j:event nodes ONLY + * (no XML declaration nor eventSet node)+ * + * NOTE:  Only a single LoggingEvent is returned from the decode method+ * even though the DTD supports multiple events nested in an eventSet.+ *+ * @author Scott Deboy <sdeboy@apache.org>+ * @author Paul Smith <psmith@apache.org>+ *+ */+public class XMLDecoder implements Decoder {+  private static final String BEGINPART =+    "<?xml version=\"1.0\" encoding=\"UTF-8\" ?><!DOCTYPE log4j:eventSet SYSTEM \"log4j.dtd\"><log4j:eventSet version=\"1.2\" xmlns:log4j=\"http://jakarta.apache.org/log4j/\">";+  private static final String ENDPART = "</log4j:eventSet>";+  private static final String RECORD_END = "</log4j:event>";+  private DocumentBuilderFactory dbf;+  private DocumentBuilder docBuilder;+  private Map additionalProperties = new HashMap();+  private String partialEvent;+  private Component owner = null;++  public XMLDecoder(Component owner) {+      this();+      this.owner = owner;+  }+  public XMLDecoder() {+    dbf = DocumentBuilderFactory.newInstance();+    dbf.setValidating(false);++    try {+      docBuilder = dbf.newDocumentBuilder();+      docBuilder.setErrorHandler(new SAXErrorHandler());+      docBuilder.setEntityResolver(new Log4jEntityResolver());+    } catch (ParserConfigurationException pce) {+      System.err.println("Unable to get document builder");+    }+  }++  /**+   * Sets an additionalProperty map, where each Key/Value pair is+   * automatically added to each LoggingEvent as it is decoded.+   *+   * This is useful, say, to include the source file name of the Logging events+   * @param additionalProperties+   */+  public void setAdditionalProperties(Map additionalProperties) {+    this.additionalProperties = additionalProperties;+  }++  /**+   * Converts the LoggingEvent data in XML string format into an actual+   * XML Document class instance.+   * @param data+   * @return dom document+   */+  private Document parse(String data) {+    if (docBuilder == null || data == null) {+      return null;+    }+    Document document = null;++    try {+      // we change the system ID to a valid URI so that Crimson won't+      // complain. Indeed, "log4j.dtd" alone is not a valid URI which+      // causes Crimson to barf. The Log4jEntityResolver only cares+      // about the "log4j.dtd" ending.+      //      buf.setLength(0);++      /**+       * resetting the length of the StringBuffer is dangerous, particularly+       * on some JDK 1.4 impls, there's a known Bug that causes a memory leak+       */+      StringBuffer buf = new StringBuffer(1024);++      buf.append(BEGINPART);+      buf.append(data);+      buf.append(ENDPART);++      InputSource inputSource =+        new InputSource(new StringReader(buf.toString()));+      inputSource.setSystemId("dummy://log4j.dtd");+      document = docBuilder.parse(inputSource);+    } catch (Exception e) {+      e.printStackTrace();+    }++    return document;+  }++  /**+   * Decodes a File into a Vector of LoggingEvents+   * @param url the url of a file containing events to decode+   * @return Vector of LoggingEvents+   * @throws IOException+   */+  public Vector decode(URL url) throws IOException {+    LineNumberReader reader = null;+    if (owner != null) {+      reader = new LineNumberReader(new InputStreamReader(new ProgressMonitorInputStream(owner, "Loading " + url , url.openStream())));+    } else {+      reader = new LineNumberReader(new InputStreamReader(url.openStream()));+    }++    Vector v = new Vector();++    String line = null;+	Vector events = null;+    try {+        while ((line = reader.readLine()) != null) {+            StringBuffer buffer = new StringBuffer(line);+            for (int i = 0;i<1000;i++) {+                buffer.append(reader.readLine()).append("\n");+            }+			events = decodeEvents(buffer.toString());+            if (events != null)+            	v.addAll(events);+        }+    } finally {+      partialEvent = null;+      try {+        if (reader != null) {+          reader.close();+        }+      } catch (Exception e) {+        e.printStackTrace();+      }+    }+    return v;+  }++  public Vector decodeEvents(String document) {+    if (document != null) {+      if (document.trim().equals("")) {+        return null;+      }+	  	String newDoc=null;+	  	String newPartialEvent=null;+	  	//separate the string into the last portion ending with </log4j:event> (which will+	  	//be processed) and the partial event which will be combined and processed in the next section+	+		//if the document does not contain a record end, append it to the partial event string+	  	if (document.lastIndexOf(RECORD_END) == -1) {+			partialEvent = partialEvent + document;+	  		return null;+	  	}+	+	  	if (document.lastIndexOf(RECORD_END) + RECORD_END.length() < document.length()) {+	      	newDoc = document.substring(0, document.lastIndexOf(RECORD_END) + RECORD_END.length());+			newPartialEvent = document.substring(document.lastIndexOf(RECORD_END) + RECORD_END.length());+	 	} else {+	  		newDoc = document;+	  	}+		if (partialEvent != null) {+			newDoc=partialEvent + newDoc;+		}	      		+	  	partialEvent=newPartialEvent;+	    Document doc = parse(newDoc);+	    if (doc == null) {+	      return null;+	    }+	    return decodeEvents(doc);+    }+    return null;+  }++  /**+   * Converts the string data into an XML Document, and then soaks out the+   * relevant bits to form a new LoggingEvent instance which can be used+   * by any Log4j element locally.+   * @param data+   * @return a single LoggingEvent+   */+  public LoggingEvent decode(String data) {+    Document document = parse(data);++    if (document == null) {+      return null;+    }++    Vector events = decodeEvents(document);++    if (events.size() > 0) {+      return (LoggingEvent) events.firstElement();+    }++    return null;+  }++  /**+   * Given a Document, converts the XML into a Vector of LoggingEvents+   * @param document+   * @return Vector of LoggingEvents+   */+  private Vector decodeEvents(Document document) {+    Vector events = new Vector();++    Logger logger = null;+    long timeStamp = 0L;+    String level = null;+    String threadName = null;+    Object message = null;+    String ndc = null;+    String[] exception = null;+    String className = null;+    String methodName = null;+    String fileName = null;+    String lineNumber = null;+    Hashtable properties = null;++    NodeList nl = document.getElementsByTagName("log4j:eventSet");+    Node eventSet = nl.item(0);++    NodeList eventList = eventSet.getChildNodes();++    for (int eventIndex = 0; eventIndex < eventList.getLength();+        eventIndex++) {+      Node eventNode = eventList.item(eventIndex);+      //ignore carriage returns in xml +	  if(eventNode.getNodeType() != Node.ELEMENT_NODE) {+	  	continue;+	  }+      logger =+        Logger.getLogger(+          eventNode.getAttributes().getNamedItem("logger").getNodeValue());+      timeStamp =+        Long.parseLong(+          eventNode.getAttributes().getNamedItem("timestamp").getNodeValue());+      level =eventNode.getAttributes().getNamedItem("level").getNodeValue();+      threadName =+        eventNode.getAttributes().getNamedItem("thread").getNodeValue();++      NodeList list = eventNode.getChildNodes();+      int listLength = list.getLength();++      for (int y = 0; y < listLength; y++) {+        String tagName = list.item(y).getNodeName();++        if (tagName.equalsIgnoreCase("log4j:message")) {+          message = getCData(list.item(y));+        }++        if (tagName.equalsIgnoreCase("log4j:NDC")) {+          ndc = getCData(list.item(y));+        }+        //still support receiving of MDC and convert to properties+        if (tagName.equalsIgnoreCase("log4j:MDC")) {+          properties = new Hashtable();+          NodeList propertyList = list.item(y).getChildNodes();+          int propertyLength = propertyList.getLength();++          for (int i = 0; i < propertyLength; i++) {+            String propertyTag = propertyList.item(i).getNodeName();++            if (propertyTag.equalsIgnoreCase("log4j:data")) {+              Node property = propertyList.item(i);+              String name =+                property.getAttributes().getNamedItem("name").getNodeValue();+              String value =+                property.getAttributes().getNamedItem("value").getNodeValue();+              properties.put(name, value);+            }+          }+        }++        if (tagName.equalsIgnoreCase("log4j:throwable")) {+          exception = new String[] { getCData(list.item(y)) };+        }++        if (tagName.equalsIgnoreCase("log4j:properties")) {+          if (properties == null) {+              properties = new Hashtable();+          }+          NodeList propertyList = list.item(y).getChildNodes();+          int propertyLength = propertyList.getLength();++          for (int i = 0; i < propertyLength; i++) {+            String propertyTag = propertyList.item(i).getNodeName();++            if (propertyTag.equalsIgnoreCase("log4j:data")) {+              Node property = propertyList.item(i);+              String name =+                property.getAttributes().getNamedItem("name").getNodeValue();+              String value =+                property.getAttributes().getNamedItem("value").getNodeValue();+              properties.put(name, value);+            }+          }+        }++        /**+         * We add all the additional properties to the properties+         * hashtable.  Don't override properties that already exist+         */+        if (additionalProperties.size() > 0) {+          if (properties == null) {+            properties = new Hashtable(additionalProperties);+          } else {+            Iterator i = additionalProperties.entrySet().iterator();+            while (i.hasNext()) {+              Map.Entry e = (Map.Entry) i.next();+              if (!(properties.containsKey(e.getKey()))) {+              	properties.put(e.getKey(), e.getValue());+              }+            }+          }+        }+      }+      Level levelImpl = HsloggerLevel.toLevel(level);+      +      if (exception == null) {+          exception = new String[]{""};+      }+      +      LoggingEvent loggingEvent = new LoggingEvent();+      loggingEvent.setLogger(logger);+      loggingEvent.setTimeStamp(timeStamp);+      loggingEvent.setLevel(levelImpl);+      loggingEvent.setThreadName(threadName);+      loggingEvent.setMessage(message);+      loggingEvent.setNDC(ndc);+      loggingEvent.setThrowableInformation(new ThrowableInformation(exception));+      loggingEvent.setProperties(properties);+      +      events.add(loggingEvent);+     +      logger = null;+      timeStamp = 0L;+      level = null;+      threadName = null;+      message = null;+      ndc = null;+      exception = null;+      className = null;+      methodName = null;+      fileName = null;+      lineNumber = null;+      properties = null;+    }++    return events;+  }++  private String getCData(Node n) {+    StringBuffer buf = new StringBuffer();+    NodeList nl = n.getChildNodes();++    for (int x = 0; x < nl.getLength(); x++) {+      Node innerNode = nl.item(x);++      if (+        (innerNode.getNodeType() == Node.TEXT_NODE)+          || (innerNode.getNodeType() == Node.CDATA_SECTION_NODE)) {+        buf.append(innerNode.getNodeValue());+      }+    }++    return buf.toString();+  }+}
hslogger.cabal view
@@ -1,5 +1,5 @@ Name: hslogger-Version: 1.0.5+Version: 1.0.6 License: LGPL Maintainer: John Goerzen <jgoerzen@complete.org> Author: John Goerzen@@ -17,7 +17,13 @@  or filter messages based on the priority and source.  hslogger also  has a syslog handler built in. Category: Interfaces-extra-source-files: COPYING+extra-source-files: COPYING,+    contrib/java/build.xml,+    contrib/java/hslogger4j.jar,+    contrib/java/hslogger4j-plugins.xml,+    contrib/java/org/haskell/hslogger/HsloggerLevel.java,+    contrib/java/org/haskell/hslogger/LogFileXMLReceiver.java,+    contrib/java/org/haskell/hslogger/XMLDecoder.java Cabal-Version: >= 1.2  flag small_base@@ -27,15 +33,17 @@     Exposed-Modules:        System.Log, System.Log.Handler,         System.Log.Handler.Simple, System.Log.Handler.Syslog,-        System.Log.Handler.Growl, System.Log.Logger +        System.Log.Handler.Growl, System.Log.Handler.Log4jXML,+        System.Log.Logger     Extensions: CPP, ExistentialQuantification     Build-Depends: network, mtl     if !os(windows)         Build-Depends: unix     if flag(small_base)-        build-depends: base >= 3, containers, directory, process+        build-depends: base >= 3, containers, directory, process,+                       time, old-locale     else-        build-depends: base < 3+        build-depends: base < 3, time     GHC-Options: -O2     Hs-Source-Dirs: src 
+ src/System/Log/Handler/Log4jXML.hs view
@@ -0,0 +1,242 @@+{- arch-tag: log4j XMLLayout log handler+Copyright (C) 2007-2008 John Goerzen <jgoerzen@complete.org>++This program is free software; you can redistribute it and/or modify+it under the terms of the GNU Lesser General Public License as published by+the Free Software Foundation; either version 2.1 of the License, or+(at your option) any later version.++This program is distributed in the hope that it will be useful,+but WITHOUT ANY WARRANTY; without even the implied warranty of+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the+GNU Lesser General Public License for more details.++You should have received a copy of the GNU Lesser General Public License+along with this program; if not, write to the Free Software+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA+-}++{- |+   Module     : System.Log.Handler.Log4jXML+   Copyright  : Copyright (C) 2007-2008 John Goerzen+   License    : GNU LGPL, version 2.1 or above++   Maintainer : bjorn.buckwalter@gmail.com+   Stability  : experimental+   Portability: GHC only?++log4j[1] XMLLayout log handlers.++Written by Bjorn Buckwalter, bjorn.buckwalter\@gmail.com+-}+++module System.Log.Handler.Log4jXML ( +    +    -- * Introduction++    {- | This module provides handlers for hslogger that are+    compatible with log4j's XMLLayout. In particular log messages+    created by the handlers can be published directly to the GUI-based+    log viewer Chainsaw v2[2].++    The set of log levels in hslogger is richer than the basic set+    of log4j levels. Two sets of handlers are provided with hslogger4j,+    one which produces logs with hslogger's levels and one which+    \"demotes\" them to the basic log4j levels. If full hslogger+    levels are used some Java installation (see below) is necessary+    to make Chainsaw aware of them.++    Usage of the handlers in hslogger4j is analoguous to usage of+    the 'System.Log.Handler.Simple.StreamHandler' and+    'System.Log.Handler.Simple.FileHandler' in "System.Log.Handler.Simple".+    The following handlers are provided: -}++    -- ** Handlers with hslogger levels +    log4jStreamHandler,+    log4jFileHandler,+    +    -- ** Handlers with log4j levels+    log4jStreamHandler',+    log4jFileHandler'+++    -- * Java install process++    {- | This is only necessary if you want to use the hslogger levels.++    Add @hslogger4j.jar@ from @contrib\/java@ to your classpath.+    To use you will also need to have the jars @log4j-1.3alpha-7.jar@+    and @log4j-xml-1.3alpha-7.jar@ that are distributed with Chainsaw+    on your classpath.++    (On Mac OS X I added all three jars to @~\/Library\/Java\/Extensions@.+    It seems that it is not sufficient that Chainsaw already includes+    its jars in the classpath when launching - perhaps the plugin+    classloader does not inherit Chainsaw's classpath. Adding the+    jars to @~\/.chainsaw\/plugins@ wouldn't work either.)++    If for whatever reason you have to rebuild the hslogger4j jar+    just run @ant@[3] in the @contrib\/java@ directory. The new jar+    will be created in the @contrib\/java\/dist@ directory. The Java+    source code is copyright The Apache Software Foundation and+    licensed under the Apache Licence version 2.0. -}+++    -- * Chainsaw setup++    {- | If you are only using the basic log4j levels just use+    Chainsaw's regular facilities to browse logs or listen for log+    messages (e.g. @XMLSocketReceiver@).++    If you want to use the hslogger levels the easiest way to set+    up Chainsaw is to load the plugins in @hslogger4j-plugins.xml@+    in @contrib\/java@ when launching Chainsaw. Two receivers will+    be defined, one that listens for logmessages and one for reading+    log files.  Edit the properties of those receivers as needed+    (e.g. @port@, @fileURL@) and restart them. You will also want+    to modify Chainsaw's formatting preferences to display levels+    as text instead of icons. -}+++    -- * Example usage++    {- | In the IO monad:++    > lh2 <- log4jFileHandler "log.xml" DEBUG+    > updateGlobalLogger rootLoggerName (addHandler lh2)++    > h  <- connectTo "localhost" (PortNumber 4448)+    > lh <- log4jStreamHandler h NOTICE+    > updateGlobalLogger rootLoggerName (addHandler lh)+    -}++    -- * References++    {- |+    (1) <http://logging.apache.org/log4j/>++    (2) <http://logging.apache.org/chainsaw/>++    (3) <http://ant.apache.org/>+    -}++    ) where++import Control.Concurrent (ThreadId, myThreadId)  -- myThreadId is GHC only!+import Control.Concurrent.MVar+import Data.List (isPrefixOf)+import System.IO+import System.Locale (defaultTimeLocale)+import Data.Time+import System.Log+import System.Log.Handler.Simple (GenericHandler (..))+++-- Handler that logs to a handle rendering message priorities according+-- to the supplied function.+log4jHandler :: (Priority -> String) -> Handle -> Priority -> IO (GenericHandler Handle)+log4jHandler showPrio h pri = do+    lock <- newMVar ()+    let mywritefunc hdl (prio, msg) loggername = withMVar lock (\_ -> do+        time   <- getCurrentTime+        thread <- myThreadId+        hPutStrLn hdl (show $ createMessage loggername time prio thread msg)+        hFlush hdl+        )+    return (GenericHandler { priority  = pri,+                             privData  = h,+                             writeFunc = mywritefunc,+                             closeFunc = \x -> return () })+    where+        -- Creates an XML element representing a log4j event/message.+        createMessage :: String -> UTCTime -> Priority -> ThreadId -> String -> XML+        createMessage logger time prio thread msg = Elem "log4j:event"+            [ ("logger"   , logger       )+            , ("timestamp", millis time  )+            , ("level"    , showPrio prio)+            , ("thread"   , show thread  )+            ]+            (Just $ Elem "log4j:message" [] (Just $ CDATA msg))+            where+                -- This is an ugly hack to get a unix epoch with milliseconds.+                -- The use of "take 3" causes the milliseconds to always be+                -- rounded downwards, which I suppose may be the expected+                -- behaviour for time.+                millis t = formatTime defaultTimeLocale "%s" t+                    ++ (take 3 $ formatTime defaultTimeLocale "%q" t)+++-- | Create a stream log handler that uses hslogger priorities.+log4jStreamHandler :: Handle -> Priority -> IO (GenericHandler Handle)+log4jStreamHandler = log4jHandler show++{- | Create a stream log handler that uses log4j levels (priorities). The+   priorities of messages are shoehorned into log4j levels as follows:++@+    DEBUG                  -> DEBUG+    INFO, NOTICE           -> INFO+    WARNING                -> WARN+    ERROR, CRITICAL, ALERT -> ERROR+    EMERGENCY              -> FATAL+@++   This is useful when the log will only be consumed by log4j tools and+   you don't want to go out of your way transforming the log or configuring+   the tools. -}+log4jStreamHandler' :: Handle -> Priority -> IO (GenericHandler Handle)+log4jStreamHandler' = log4jHandler show' where+    show' :: Priority -> String+    show' NOTICE    = "INFO"+    show' WARNING   = "WARN"+    show' CRITICAL  = "ERROR"+    show' ALERT     = "ERROR"+    show' EMERGENCY = "FATAL"+    show' p         = show p  -- Identical for DEBUG, INFO, ERROR.+++-- | Create a file log handler that uses hslogger priorities.+log4jFileHandler :: FilePath -> Priority -> IO (GenericHandler Handle)+log4jFileHandler fp pri = do+                          h <- openFile fp AppendMode+                          sh <- log4jStreamHandler h pri+                          return (sh{closeFunc = hClose})++{- | Create a file log handler that uses log4j levels (see+   'log4jStreamHandler'' for mappings). -}+log4jFileHandler' :: FilePath -> Priority -> IO (GenericHandler Handle)+log4jFileHandler' fp pri = do+                           h <- openFile fp AppendMode+                           sh <- log4jStreamHandler' h pri+                           return (sh{closeFunc = hClose})+++-- A type for building and showing XML elements. Could use a fancy XML+-- library but am reluctant to introduce dependencies.+data XML = Elem  String [(String, String)] (Maybe XML)+         | CDATA String++instance Show XML where+    show (CDATA s) = "<![CDATA[" ++ escapeCDATA s ++ "]]>" where+        escapeCDATA = replace "]]>" "]]&lt;"  -- The best we can do, I guess.+    show (Elem name attrs child) = "<" ++ name ++ showAttrs attrs ++ showChild child where+        showAttrs []         = ""+        showAttrs ((k,v):as) = " " ++ k ++ "=\"" ++ escapeAttr v ++ "\""+                             ++ showAttrs as+            where escapeAttr = replace "\"" "&quot;"+                             . replace "<" "&lt;"+                             . replace "&" "&amp;"+        showChild Nothing  = "/>"+        showChild (Just c) = ">" ++ show c ++ "</" ++ name ++ ">"+++-- Replaces instances of first list by second list in third list.+-- Definition blatantly stoled from jethr0's comment at+-- http://bluebones.net/2007/01/replace-in-haskell/. Can be swapped+-- with definition (or import) from MissingH.+replace :: Eq a => [a] -> [a] -> [a] -> [a]+replace _    _  [       ] = []+replace from to xs@(a:as) = if isPrefixOf from xs+    then to ++ drop (length from) xs else a : replace from to as+
src/System/Log/Handler/Simple.hs view
@@ -31,6 +31,7 @@ -}  module System.Log.Handler.Simple(streamHandler, fileHandler,+                                      GenericHandler (..),                                       verboseStreamHandler)     where @@ -38,6 +39,8 @@ import System.Log.Handler import System.IO import Control.Concurrent.MVar++{- | A helper data type. -}  data GenericHandler a = GenericHandler {priority :: Priority,                                         privData :: a,
src/System/Log/Handler/Syslog.hs view
@@ -96,6 +96,7 @@               | UUCP                    -- ^ UUCP messages               | CRON                    -- ^ Cron messages               | AUTHPRIV                -- ^ Private authentication messages+              | FTP                     -- ^ FTP messages               | LOCAL0                  -- ^ LOCAL0 through LOCAL7 are reserved for you to customize as you wish               | LOCAL1               | LOCAL2@@ -119,6 +120,7 @@                        UUCP -> 8                        CRON -> 9                        AUTHPRIV -> 10+                       FTP -> 11                        LOCAL0 -> 16                        LOCAL1 -> 17                        LOCAL2 -> 18
src/System/Log/Logger.hs view
@@ -180,6 +180,7 @@ import System.IO.Unsafe import Control.Concurrent.MVar import Data.List(map, isPrefixOf)+import Data.Maybe import qualified Data.Map as Map import qualified Control.Exception import Control.Monad.Error@@ -205,6 +206,7 @@  -- | The name of the root logger, which is always defined and present -- on the system.+rootLoggerName :: String rootLoggerName = ""  {- | Placeholders created when a new logger must be created.  This is used@@ -330,7 +332,7 @@          Nothing -> do                     -- Add logger(s).  Then call myself to retrieve it.                     let newlt = createLoggers (componentsOfName lname) lt-                    result <- Map.lookup lname newlt+                    let result = fromJust $ Map.lookup lname newlt                     return (newlt, result)     where createLoggers :: [String] -> LogTree -> LogTree           createLoggers [] lt = lt -- No names to add; return tree unmodified@@ -346,8 +348,8 @@           findmodellogger _ [] = error "findmodellogger: root logger does not exist?!"           findmodellogger lt (x:xs) =               case Map.lookup x lt of-                Left (_::String) -> findmodellogger lt xs-                Right logger -> logger {handlers = []}+                Nothing -> findmodellogger lt xs+                Just logger -> logger {handlers = []}  -- | Returns the root logger.