You are viewing a plain text version of this content. The canonical link for it is here.
Posted to log4j-dev@logging.apache.org by ce...@apache.org on 2005/01/13 17:12:26 UTC

cvs commit: logging-log4j/tests/src/java/org/apache/log4j/joran/action TouchAction.java

ceki        2005/01/13 08:12:26

  Modified:    src/java/org/apache/log4j/joran/spi Interpreter.java
               src/java/org/apache/log4j/joran/action AppenderAction.java
                        Action.java
               tests/src/java/org/apache/log4j/joran InterpreterTest.java
               tests    build.xml
               tests/src/java/org/apache/log4j/joran/action
                        TouchAction.java
  Added:       src/java/org/apache/log4j/joran/spi ActionException.java
               tests/src/java/org/apache/log4j/joran
                        SkippingInInterpreterTest.java
               tests/input/joran badEnd1.xml badEnd2.xml
  Removed:     tests/input/joran exception2.xml
  Log:
  - By thowing an ActionExcetion an Action can now signal the joran Interpreter to skip processing children or following siblings.
  - Added corresponding test cases
  
  Revision  Changes    Path
  1.3       +41 -10    logging-log4j/src/java/org/apache/log4j/joran/spi/Interpreter.java
  
  Index: Interpreter.java
  ===================================================================
  RCS file: /home/cvs/logging-log4j/src/java/org/apache/log4j/joran/spi/Interpreter.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- Interpreter.java	12 Jan 2005 18:04:36 -0000	1.2
  +++ Interpreter.java	13 Jan 2005 16:12:25 -0000	1.3
  @@ -17,7 +17,6 @@
   package org.apache.log4j.joran.spi;
   
   import org.apache.log4j.LogManager;
  -import org.apache.log4j.helpers.LogLog;
   import org.apache.log4j.joran.action.Action;
   import org.apache.log4j.joran.action.ImplicitAction;
   import org.apache.log4j.spi.Component;
  @@ -124,11 +123,6 @@
       //LogLog.debug("in startElement <" + tagName + ">");
   
       pattern.push(tagName);
  -
  -    if(skip != null) {
  -      System.out.println("Skipping nested <"+tagName+"> element.");
  -    }
  -
       
       List applicableActionList = getApplicableActionList(pattern, atts);
   
  @@ -149,9 +143,14 @@
     public void endElement(String namespaceURI, String localName, String qName) {
       List applicableActionList = (List) actionListStack.pop();
   
  -    if(skip != null && skip.equals(pattern)) {
  -      System.out.println("Setting skipping to null");
  -      skip = null;
  +    if(skip != null) {
  +      //System.err.println("In End, pattern is "+pattern+", skip pattern "+skip);
  +      if(skip.equals(pattern)) {
  +        getLogger().info("Skipping discontinued. Normall processing will continue with the following element.");
  +        skip = null;
  +      } else {
  +        getLogger().info("Skipping invoking end() method for <{}>.", localName);
  +      }
       } else if (applicableActionList != EMPTY_LIST) {
         callEndAction(applicableActionList, getTagName(localName, qName));
       }
  @@ -224,6 +223,11 @@
         return;
       }
   
  +    if(skip != null) {
  +      getLogger().debug("Skipping invoking end() method for <{}>.", pattern);
  +      return;
  +    }
  +    
       Iterator i = applicableActionList.iterator();
   
       while (i.hasNext()) {
  @@ -233,9 +237,22 @@
         // exceptions
         try {
           action.begin(ec, tagName, atts);
  +      } catch( ActionException ae) {
  +        switch(ae.getSkipCode()) {
  +        case ActionException.SKIP_CHILDREN:
  +          skip = (Pattern) pattern.clone();
  +          break;
  +        case ActionException.SKIP_SIBLINGS:
  +          skip = (Pattern) pattern.clone();
  +          // pretend the exception came from one level up. This will cause
  +          // all children and following siblings elements to be skipped
  +          skip.pop();
  +          break;
  +        }
  +        getLogger().info("Skip pattern set to: "+skip);
         } catch (Exception e) {
           skip = (Pattern) pattern.clone();
  -        System.out.println("Skip pattern set to "+skip);
  +        getLogger().info("Skip pattern set to: "+skip);
           ec.addError(new ErrorItem("Exception in Action for tag <"+tagName+">", e));
         }
       }
  @@ -255,8 +272,22 @@
         // any eventual exceptions
         try {
           action.end(ec, tagName);
  +      } catch( ActionException ae) {
  +        switch(ae.getSkipCode()) {
  +        case ActionException.SKIP_CHILDREN:
  +          // after end() is called there can't be any children
  +          break;
  +        case ActionException.SKIP_SIBLINGS:
  +          skip = (Pattern) pattern.clone();
  +          skip.pop();
  +          break;
  +        }
  +        getLogger().info("Skip pattern set to: "+skip);
         } catch(Exception e) {
           ec.addError(new ErrorItem("Exception in Action for tag <"+tagName+">", e));
  +        skip = (Pattern) pattern.clone();
  +        skip.pop(); // induce the siblings to be skipped
  +        getLogger().info("Skip pattern set to <{}>.", skip);
         }
       }
     }
  
  
  
  1.1                  logging-log4j/src/java/org/apache/log4j/joran/spi/ActionException.java
  
  Index: ActionException.java
  ===================================================================
  /*
   * 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.apache.log4j.joran.spi;
  
  
  /**
   * By throwing an exception an action can signal the Interpreter to skip
   * processing, all the nested elements nested within the element throwing the
   * exception or skip all following sibling elements in the document.
   *
   * @author <a href="http://www.qos.ch/log4j/">Ceki Gulcu</a>
   */
  public class ActionException extends Exception {
    /**
     * SKIP_CHILDREN signals the {@link Interpreter} to skip processing all the
     * nested elements contained within the element causing this ActionException.
     */
    public static final int SKIP_CHILDREN = 1;
  
    /**
     * SKIP_SIBLINGS signals the {@link Interpreter} to skip processing all the
     * children of this element as well as all the siblings of this elements,
     * including any children they may have.
     */
    public static final int SKIP_SIBLINGS = 2;
    final Throwable rootCause;
    final int skipCode;
  
    public ActionException(final int skipCode) {
      this(skipCode, null);
    }
  
    public ActionException(final int skipCode, final Throwable rootCause) {
      this.skipCode = skipCode;
      this.rootCause = rootCause;
    }
  
    public Throwable getCause() {
      return rootCause;
    }
  
    public int getSkipCode() {
      return skipCode;
    }
  }
  
  
  
  1.20      +8 -2      logging-log4j/src/java/org/apache/log4j/joran/action/AppenderAction.java
  
  Index: AppenderAction.java
  ===================================================================
  RCS file: /home/cvs/logging-log4j/src/java/org/apache/log4j/joran/action/AppenderAction.java,v
  retrieving revision 1.19
  retrieving revision 1.20
  diff -u -r1.19 -r1.20
  --- AppenderAction.java	12 Jan 2005 18:04:37 -0000	1.19
  +++ AppenderAction.java	13 Jan 2005 16:12:26 -0000	1.20
  @@ -20,6 +20,7 @@
   import org.apache.log4j.Appender;
   import org.apache.log4j.helpers.Option;
   import org.apache.log4j.helpers.OptionConverter;
  +import org.apache.log4j.joran.spi.ActionException;
   import org.apache.log4j.joran.spi.ExecutionContext;
   import org.apache.log4j.spi.ErrorItem;
   import org.apache.log4j.spi.LoggerRepository;
  @@ -40,9 +41,13 @@
      * The appender thus generated is placed in the ExecutionContext appender bag.
      */
     public void begin(
  -    ExecutionContext ec, String localName, Attributes attributes) {
  +    ExecutionContext ec, String localName, Attributes attributes) throws ActionException {
       String className = attributes.getValue(CLASS_ATTRIBUTE);
   
  +    // We are just beginning, reset variables
  +    appender = null;
  +    inError = false;
  +    
       try {
         getLogger().debug("About to instantiate appender of type [{}]", className);
   
  @@ -72,12 +77,13 @@
   
         getLogger().debug("Pushing appender on to the object stack.");
         ec.pushObject(appender);
  -    } catch (Throwable oops) {
  +    } catch (Exception oops) {
         inError = true;
         getLogger().error(
           "Could not create an Appender. Reported error follows.", oops);
         ec.addError(
           new ErrorItem("Could not create appender of type " + className + "]."));
  +      throw new ActionException(ActionException.SKIP_CHILDREN, oops);
       }
     }
   
  
  
  
  1.2       +3 -2      logging-log4j/src/java/org/apache/log4j/joran/action/Action.java
  
  Index: Action.java
  ===================================================================
  RCS file: /home/cvs/logging-log4j/src/java/org/apache/log4j/joran/action/Action.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- Action.java	12 Jan 2005 15:04:18 -0000	1.1
  +++ Action.java	13 Jan 2005 16:12:26 -0000	1.2
  @@ -16,6 +16,7 @@
   
   package org.apache.log4j.joran.action;
   
  +import org.apache.log4j.joran.spi.ActionException;
   import org.apache.log4j.joran.spi.ExecutionContext;
   import org.apache.log4j.joran.spi.Interpreter;
   import org.apache.log4j.spi.ComponentBase;
  @@ -53,9 +54,9 @@
      * the returned value is 'false', then child elements are ignored.
      */
     public abstract void begin(
  -    ExecutionContext ec, String name, Attributes attributes);
  +    ExecutionContext ec, String name, Attributes attributes) throws ActionException ;
   
  -  public abstract void end(ExecutionContext ec, String name);
  +  public abstract void end(ExecutionContext ec, String name) throws ActionException;
   
     public String toString() {
       return this.getClass().getName();
  
  
  
  1.2       +2 -52     logging-log4j/tests/src/java/org/apache/log4j/joran/InterpreterTest.java
  
  Index: InterpreterTest.java
  ===================================================================
  RCS file: /home/cvs/logging-log4j/tests/src/java/org/apache/log4j/joran/InterpreterTest.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- InterpreterTest.java	12 Jan 2005 18:04:35 -0000	1.1
  +++ InterpreterTest.java	13 Jan 2005 16:12:26 -0000	1.2
  @@ -38,10 +38,7 @@
   import org.apache.log4j.joran.action.ActionConst;
   import org.apache.log4j.joran.action.AppenderAction;
   import org.apache.log4j.joran.action.AppenderRefAction;
  -import org.apache.log4j.joran.action.BadBeginAction;
  -import org.apache.log4j.joran.action.BadEndAction;
   import org.apache.log4j.joran.action.ConversionRuleAction;
  -import org.apache.log4j.joran.action.HelloAction;
   import org.apache.log4j.joran.action.LayoutAction;
   import org.apache.log4j.joran.action.LevelAction;
   import org.apache.log4j.joran.action.LoggerAction;
  @@ -49,7 +46,6 @@
   import org.apache.log4j.joran.action.ParamAction;
   import org.apache.log4j.joran.action.RootLoggerAction;
   import org.apache.log4j.joran.action.StackCounterAction;
  -import org.apache.log4j.joran.action.TouchAction;
   import org.apache.log4j.joran.spi.ExecutionContext;
   import org.apache.log4j.joran.spi.Interpreter;
   import org.apache.log4j.joran.spi.Pattern;
  @@ -63,7 +59,6 @@
   import org.xml.sax.SAXParseException;
   
   import java.util.HashMap;
  -import java.util.List;
   import java.util.Map;
   import java.util.Stack;
   
  @@ -389,52 +384,7 @@
       assertEquals("Hello John Doe.", str);
     }
    
  -  public void testException1() throws Exception {
  -    logger.debug("Starting testException1");
  -  
  -    RuleStore rs = new SimpleRuleStore();
  -    rs.addRule(new Pattern("test"), new NOPAction());
  -    rs.addRule(new Pattern("test/badBegin"), new BadBeginAction());
  -    rs.addRule(new Pattern("test/badBegin/touch"), new TouchAction());
  -    rs.addRule(new Pattern("test/hello"), new HelloAction());
  -
  -    Interpreter jp = new Interpreter(rs);
  -    ExecutionContext ec = jp.getExecutionContext();
  -    Map omap = ec.getObjectMap();
  -
  -    SAXParser saxParser = createParser();
  -    saxParser.parse("file:input/joran/exception1.xml", jp);
  -    List el = jp.getExecutionContext().getErrorList();
  -    for(int i = 0; i < el.size(); i++) {
  -      ((ErrorItem) el.get(i)).dump(); 
  -    }
  -    String str = (String) ec.getObjectMap().get("hello");
  -    assertEquals("Hello John Doe.", str);
  -  }
  -
  -  public void testException2() throws Exception {
  -    logger.debug("Starting testException2");
  -  
  -    RuleStore rs = new SimpleRuleStore();
  -    rs.addRule(new Pattern("test"), new NOPAction());
  -    rs.addRule(new Pattern("test/badEnd"), new BadEndAction());
  -    rs.addRule(new Pattern("test/hello"), new HelloAction());
  -
  -    Interpreter jp = new Interpreter(rs);
  -    ExecutionContext ec = jp.getExecutionContext();
  -    Map omap = ec.getObjectMap();
  -
  -    SAXParser saxParser = createParser();
  -    saxParser.parse("file:input/joran/exception2.xml", jp);
  -    List el = jp.getExecutionContext().getErrorList();
  -    for(int i = 0; i < el.size(); i++) {
  -      ((ErrorItem) el.get(i)).dump(); 
  -    }
  -    String str = (String) ec.getObjectMap().get("hello");
  -    assertEquals("Hello John Doe.", str);
  -  }
  -
  -  public static Test suite() {
  +  public static Test RUNALLsuite() {
       TestSuite suite = new TestSuite();
        //suite.addTest(new InterpreterTest("testIllFormedXML"));
       //suite.addTest(new InterpreterTest("testBasicLoop"));
  @@ -442,7 +392,7 @@
       //suite.addTest(new InterpreterTest("testParsing2"));
       //suite.addTest(new InterpreterTest("testParsing3"))
       suite.addTest(new InterpreterTest("testException1"));
  -    //suite.addTest(new InterpreterTest("testException2"));
  +    suite.addTest(new InterpreterTest("testException2"));
       return suite;
     }
   
  
  
  
  1.1                  logging-log4j/tests/src/java/org/apache/log4j/joran/SkippingInInterpreterTest.java
  
  Index: SkippingInInterpreterTest.java
  ===================================================================
  /*
   * 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.apache.log4j.joran;
  
  import junit.framework.Test;
  import junit.framework.TestCase;
  import junit.framework.TestSuite;
  
  import org.apache.log4j.ConsoleAppender;
  import org.apache.log4j.LogManager;
  import org.apache.log4j.Logger;
  import org.apache.log4j.PatternLayout;
  import org.apache.log4j.joran.action.BadBeginAction;
  import org.apache.log4j.joran.action.BadEndAction;
  import org.apache.log4j.joran.action.HelloAction;
  import org.apache.log4j.joran.action.TouchAction;
  import org.apache.log4j.joran.spi.ExecutionContext;
  import org.apache.log4j.joran.spi.Interpreter;
  import org.apache.log4j.joran.spi.Pattern;
  import org.apache.log4j.joran.spi.RuleStore;
  import org.apache.log4j.joran.spi.SimpleRuleStore;
  import org.apache.log4j.spi.ErrorItem;
  
  import java.util.List;
  import java.util.Map;
  
  import javax.xml.parsers.SAXParser;
  import javax.xml.parsers.SAXParserFactory;
  
  
  /**
   * Test the way Interpreter skips elements in case of exceptions thrown by
   * Actions.
   * 
   * @author Ceki Gulcu
   */
  public class SkippingInInterpreterTest extends TestCase {
    static final Logger logger = Logger.getLogger(SkippingInInterpreterTest.class);
  
    public SkippingInInterpreterTest(String name) {
      super(name);
    }
  
    /*
     * @see TestCase#setUp()
     */
    protected void setUp() throws Exception {
      super.setUp();
  
      Logger root = Logger.getRootLogger();
      root.addAppender(
        new ConsoleAppender(new PatternLayout("%r %5p [%t] %c - %m%n")));
      
    }
  
    /*
     * @see TestCase#tearDown()
     */
    protected void tearDown() throws Exception {
      super.tearDown();
      LogManager.shutdown();
    }
  
    SAXParser createParser() throws Exception {
      SAXParserFactory spf = SAXParserFactory.newInstance();
      return spf.newSAXParser();
    }
     
    public void testChildrenSkipping() throws Exception {
      logger.debug("Starting testException1");
    
      RuleStore rs = new SimpleRuleStore();
      rs.addRule(new Pattern("test"), new NOPAction());
      rs.addRule(new Pattern("test/badBegin"), new BadBeginAction());
      rs.addRule(new Pattern("test/badBegin/touch"), new TouchAction());
      rs.addRule(new Pattern("test/hello"), new HelloAction());
  
      Interpreter jp = new Interpreter(rs);
      ExecutionContext ec = jp.getExecutionContext();
      Map omap = ec.getObjectMap();
  
      SAXParser saxParser = createParser();
      saxParser.parse("file:input/joran/exception1.xml", jp);
      List el = jp.getExecutionContext().getErrorList();
      for(int i = 0; i < el.size(); i++) {
        ((ErrorItem) el.get(i)).dump(); 
      }
      String str = (String) ec.getObjectMap().get("hello");
      assertEquals("Hello John Doe.", str);
      
      Integer i = (Integer) ec.getObjectMap().get(TouchAction.KEY);
      assertNull(i);
    }
  
    public void testSkipSiblings() throws Exception {
      logger.debug("Starting testException2");
    
      RuleStore rs = new SimpleRuleStore();
      rs.addRule(new Pattern("test"), new NOPAction());
      rs.addRule(new Pattern("test/badEnd"), new BadEndAction());
      rs.addRule(new Pattern("test/badEnd/touch"), new TouchAction());
      rs.addRule(new Pattern("test/hello"), new HelloAction());
  
      Interpreter jp = new Interpreter(rs);
      ExecutionContext ec = jp.getExecutionContext();
      Map omap = ec.getObjectMap();
  
      SAXParser saxParser = createParser();
      saxParser.parse("file:input/joran/badEnd1.xml", jp);
      String str = (String) ec.getObjectMap().get("hello");
      assertNull(str);
      Integer i = (Integer) ec.getObjectMap().get(TouchAction.KEY);
      assertEquals(2, i.intValue());
    }
  
    public void testSkipSiblings2() throws Exception {
      logger.debug("Starting testException2");
    
      RuleStore rs = new SimpleRuleStore();
      rs.addRule(new Pattern("test"), new NOPAction());
      rs.addRule(new Pattern("test/badEnd"), new BadEndAction());
      rs.addRule(new Pattern("test/badEnd/touch"), new TouchAction());
      rs.addRule(new Pattern("test/hello"), new HelloAction());
  
      Interpreter jp = new Interpreter(rs);
      ExecutionContext ec = jp.getExecutionContext();
      Map omap = ec.getObjectMap();
  
      SAXParser saxParser = createParser();
      saxParser.parse("file:input/joran/badEnd2.xml", jp);
   
      String str = (String) ec.getObjectMap().get("hello");
      assertEquals("Hello John Doe.", str);
      Integer i = (Integer) ec.getObjectMap().get(TouchAction.KEY);
      assertNull(i);
    }
    
    public static Test RUNALLsuite() {
      TestSuite suite = new TestSuite();
      suite.addTest(new SkippingInInterpreterTest("testChildrenSkipping"));
      suite.addTest(new SkippingInInterpreterTest("testNoSkipping"));
      return suite;
    }
  
  }
  
  
  
  1.1                  logging-log4j/tests/input/joran/badEnd1.xml
  
  Index: badEnd1.xml
  ===================================================================
  <?xml version="1.0" encoding="UTF-8" ?>
  <!DOCTYPE test>
  
  <test>
  
    <!-- this action throws an exception in the Action.end method -->
    <badEnd>
      <touch/>
      <touch/>
    </badEnd>  
    
  
    <hello name="John Doe"></hello>
    
  </test>
  
  
  1.1                  logging-log4j/tests/input/joran/badEnd2.xml
  
  Index: badEnd2.xml
  ===================================================================
  <?xml version="1.0" encoding="UTF-8" ?>
  <!DOCTYPE test>
  
  <test>
  
    <isolate>
      <!-- badEnd throws an exception in the Action.end method -->
      <badEnd>
        <touch/>
        <touch/>
      </badEnd>  
    </isolate>
  
    <hello name="John Doe"></hello>
    
  </test>
  
  
  1.93      +13 -2     logging-log4j/tests/build.xml
  
  Index: build.xml
  ===================================================================
  RCS file: /home/cvs/logging-log4j/tests/build.xml,v
  retrieving revision 1.92
  retrieving revision 1.93
  diff -u -r1.92 -r1.93
  --- build.xml	12 Jan 2005 18:04:41 -0000	1.92
  +++ build.xml	13 Jan 2005 16:12:26 -0000	1.93
  @@ -215,7 +215,10 @@
     <!-- ================================================================= -->
     <!-- Joran unit tests                                                 -->
     <!-- ================================================================= -->
  -  <target name="Joran" depends="Pattern, SimpleStore, Interpreter, 
  +  <target name="Joran" depends="Pattern, 
  +  	                            SimpleStore, 
  +  	                            Interpreter, 
  +  	                            SkipInInterpreter,
                                   JoranConfigurator"/>
   
   
  @@ -580,7 +583,15 @@
         <test name="org.apache.log4j.joran.InterpreterTest" />
       </junit>
     </target>
  -  
  +
  +  <target name="SkipInInterpreter" depends="check, build, cleanOutputDir">
  +	<junit printsummary="yes" fork="yes" haltonfailure="yes">
  +	  <classpath refid="tests.classpath"/>
  +      <formatter type="plain" usefile="false" />
  +	  <test name="org.apache.log4j.joran.SkippingInInterpreterTest" />
  +    </junit>
  +   </target>
  +		
     <target name="JoranConfigurator" depends="check, build, cleanOutputDir">
       <junit printsummary="yes" fork="yes" haltonfailure="yes">
         <classpath refid="tests.classpath"/>
  
  
  
  1.2       +9 -2      logging-log4j/tests/src/java/org/apache/log4j/joran/action/TouchAction.java
  
  Index: TouchAction.java
  ===================================================================
  RCS file: /home/cvs/logging-log4j/tests/src/java/org/apache/log4j/joran/action/TouchAction.java,v
  retrieving revision 1.1
  retrieving revision 1.2
  diff -u -r1.1 -r1.2
  --- TouchAction.java	12 Jan 2005 18:04:36 -0000	1.1
  +++ TouchAction.java	13 Jan 2005 16:12:26 -0000	1.2
  @@ -24,7 +24,8 @@
   
   public class TouchAction extends Action {
   
  -
  +  public static String KEY = "touched";
  +  
     public TouchAction() {
     }
     /**
  @@ -32,7 +33,13 @@
      *
      */
     public void begin(ExecutionContext ec, String name, Attributes attributes) {
  -    ec.getObjectMap().put("touch", "x");
  +    
  +    Integer i = (Integer) ec.getObjectMap().get(KEY);
  +    if(i == null) {
  +      ec.getObjectMap().put(KEY, new Integer(1));
  +    } else {
  +      ec.getObjectMap().put(KEY, new Integer(i.intValue()+1));
  +    }
     }
   
     /**
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: log4j-dev-unsubscribe@logging.apache.org
For additional commands, e-mail: log4j-dev-help@logging.apache.org