mwomack 2003/02/03 22:28:16 Added: . HOWTOBUILD.txt LICENSE.txt README.txt build.properties.sample build.xml src/java/org/apache/log4j/filter LevelMatchFilter.java LevelRangeMatchFilter.java MDCMatchFilter.java MatchFilterBase.java MessageMatchFilter.java NDCMatchFilter.java SetLocationInfoFilter.java src/java/org/apache/log4j/selector ContextClassLoaderSelector.java src/java/org/apache/log4j/servlet ConfigurationServlet.java CookieMDCFilter.java InitContextListener.java InitServlet.java Log: Initial check in for log4j-sandbox cvs repository. Contains filter, selector, and servlet packages. Revision Changes Path 1.1 jakarta-log4j-sandbox/HOWTOBUILD.txt Index: HOWTOBUILD.txt =================================================================== [placeholder that will describe the steps to set up the build environment and compile the code] 1.1 jakarta-log4j-sandbox/LICENSE.txt Index: LICENSE.txt =================================================================== /* * ============================================================================ * The Apache Software License, Version 1.1 * ============================================================================ * * Copyright (C) 1999 The Apache Software Foundation. All rights reserved. * * Redistribution and use in source and binary forms, with or without modifica- * tion, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * * 3. The end-user documentation included with the redistribution, if any, must * include the following acknowledgment: "This product includes software * developed by the Apache Software Foundation (http://www.apache.org/)." * Alternately, this acknowledgment may appear in the software itself, if * and wherever such third-party acknowledgments normally appear. * * 4. The names "log4j" and "Apache Software Foundation" must not be used to * endorse or promote products derived from this software without prior * written permission. For written permission, please contact * [EMAIL PROTECTED] * * 5. Products derived from this software may not be called "Apache", nor may * "Apache" appear in their name, without prior written permission of the * Apache Software Foundation. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE * APACHE SOFTWARE FOUNDATION OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- * DING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS * OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * This software consists of voluntary contributions made by many individuals * on behalf of the Apache Software Foundation. For more information on the * Apache Software Foundation, please see <http://www.apache.org/>. * */ 1.1 jakarta-log4j-sandbox/README.txt Index: README.txt =================================================================== Log4j sandbox is a collection of useful code and classes that extend or support the use of log4j. Many of the classes are ready for immediate use in your log4j environment. Some are considered "experimental" and should be used with that knowledge in mind. And others serve as useful examples or "springboards" for adding features to your own code. These classes are maintained and packaged separate from the core log4j release, but as classes "prove" themselves worthy and evolve, they may be moved into the core log4j release. If you have any suggestions, improvements, patches, or new submissions you would like to contribute to the log4j-sandbox, please post a message to the [EMAIL PROTECTED] email list. Please see the HOWTOBUILD.txt file for information about how to compile and build the log4j-sandbox project. Enjoy! 1.1 jakarta-log4j-sandbox/build.properties.sample Index: build.properties.sample =================================================================== # This property file is used by build.xml to located resource needed to build # the log4j-sandbox code. Duplicate this file and rename to "build.properties" # and locate it in the same directory as the build.xml file. Then modify the # properties below to reflect the locations of the resources in your # development environment. # log4j jar REQUIRED log4j.jar=./build-libs/log4j-1.2.7.jar # Servlet 2.3 OPTIONAL; required to build the org.apache.log4j.servlet package servlet.jar=./build-libs/servlet.jar # Checkstyle jar OPTIONAL; required to execute checkstyle target checkstyle.jar=d:/development/code-opensource/other/checkstyle-2.4/checkstyle-all-2.4.jar 1.1 jakarta-log4j-sandbox/build.xml Index: build.xml =================================================================== <!-- This file is an ANT build script. ANT is a Java based build tool. --> <!-- It is availale from http://ant.apache.org --> <!-- ================================================================= --> <!-- NOTE: all directories are relative to jakarta-log4j-sandbox/ --> <!-- the parent of build/ --> <!-- ================================================================= --> <project name="log4j" default="build" basedir="." > <!-- The build.properties file defines the parth to local jar files --> <property file="build.properties"/> <property name="version" value="0.1alpha"/> <!-- The base directory relative to which most targets are built --> <property name="base" value="."/> <!-- The directory where source files are stored. --> <property name="java.source.dir" value="./src/java/"/> <property name="build.home" value="./build"/> <!-- Destination for compiled files --> <property name="javac.dest" value="${build.home}/classes"/> <!-- Destination for generated jar files --> <property name="jar.dest" value="${build.home}/lib"/> <!-- Destination for documentation files --> <property name="docs.dest" value="${build.home}/docs"/> <!-- Destination for javadoc generated files --> <property name="javadoc.dest" value="${build.home}/docs/api"/> <!-- The jar file that the jar task will generate --> <property name="log4j-sandbox.jar" value="log4j-sandbox-${version}.jar"/> <!-- Construct compile classpath --> <path id="compile.classpath"> <pathelement location="${build.home}/classes"/> <pathelement location="${log4j.jar}"/> <pathelement location="${servlet.jar}"/> </path> <!-- ================================================================= --> <!-- Default target --> <!-- ================================================================= --> <target name="usage"> <echo> These are the targets supported by this ANT build scpript: build - compile all project files, if a certain library is missing, then the compilation of its dependents are skipped. javadoc - build project javadoc files jar - build log4j-core and log4j jar files </echo> </target> <target name="servlet23Check"> <available classname="javax.servlet.Filter" property="servlet23-present"> <classpath> <pathelement location="${servlet.jar}"/> </classpath> </available> </target> <target name="servlet" depends="servlet23Check" if="servlet23-present"> <echo message="Servlet 2.3 is present."/> </target> <target name="init"> <tstamp /> </target> <target name="build" depends="init, build.core, build.servlet"/> <target name="build.core" depends="init"> <mkdir dir="${javac.dest}" /> <javac srcdir="${java.source.dir}" destdir="${javac.dest}" includes="**/*.java" excludes="**/servlet/*.java" deprecation="${deprecation}" debug="on"> <classpath refid="compile.classpath"/> </javac> </target> <target name="build.servlet" depends="init, servlet" if="servlet23-present"> <javac deprecation="${deprecation}" srcdir="${java.source.dir}" destdir="${javac.dest}" includes="**/servlet/*.java"> <classpath refid="compile.classpath"/> </javac> </target> <!-- ================================================================= --> <!-- Remove all build generated files. --> <!-- ================================================================= --> <target name="clean" depends="init"> <delete dir="${build.home}" /> </target> <!-- ================================================================= --> <!-- Runs checkstyle. Available from http://checkstyle.sf.net --> <!-- ================================================================= --> <target name="checkstyle" depends="init"> <taskdef resource="checkstyletask.properties" classpath="${checkstyle.jar}"/> <property name="checkstyle.lcurly.method" value="nlow" /> <property name="checkstyle.lcurly.type" value="nlow" /> <property name="checkstyle.lcurly.other" value="nlow" /> <property name="checkstyle.maxmethodlen" value="500" /> <property name="checkstyle.maxconstructorlen" value="500" /> <property name="checkstyle.ignore.public.in.interface" value="true" /> <!-- by default checkstyle supports the Sun coding standard. --> <checkstyle> <fileset dir="src/java/org/apache/log4j/" includes="**/*.java"/> </checkstyle> </target> <!-- ================================================================= --> <!-- Actual work is done in the dependencies. --> <!-- ================================================================= --> <target name="jar" depends="log4j-sandbox.jar"> </target> <!-- ================================================================= --> <!-- Create log4j-sandbox.jar, excluding tests and other odds and ends. --> <!-- ================================================================= --> <target name="log4j-sandbox.jar" depends="build"> <mkdir dir="${jar.dest}"/> <delete> <fileset dir="${jar.dest}"> <include name="${log4j-sandbox.jar}"/> </fileset> </delete> <jar jarfile="${jar.dest}/${log4j-sandbox.jar}" basedir="${javac.dest}" includes="**/*.class"> <manifest> <attribute name="Manifest-version" value="1.0"/> <section name="org/apache/log4j/"> <attribute name="Implementation-Title" value="log4j-sandbox"/> <attribute name="Implementation-Version" value="${version}"/> <attribute name="Implementation-Vendor" value="Apache Software Foundation"/> </section> </manifest> </jar> </target> <!-- ================================================================= --> <!-- This target builds the javadoc files. --> <!-- ================================================================= --> <target name="javadoc" depends="init" unless="env.NO_JAVADOC"> <mkdir dir="${javadoc.dest}" /> <javadoc sourcepath="${java.source.dir}" destdir="${javadoc.dest}" packagenames="org.apache.log4j.filter, org.apache.log4j.selector, org.apache.log4j.servlet" version="true" protected="true" author="true" use="true" overview="${docs.dest}/overview.html" doctitle="log4j version ${version}<br>API Specification" windowtitle="Log4j-sandbox Version ${version}" header="<b>Log4j-sandbox ${version}</b>" bottom="Copyright 2000-2003 Apache Software Foundation."> <link href="http://java.sun.com/products/jdk/1.3/docs/api"/> <link href="http://java.sun.com/j2ee/sdk_1.3/techdocs/api/"/> <classpath refid="compile.classpath"/> </javadoc> </target> </project> 1.1 jakarta-log4j-sandbox/src/java/org/apache/log4j/filter/LevelMatchFilter.java Index: LevelMatchFilter.java =================================================================== /* * Copyright (C) The Apache Software Foundation. All rights reserved. * * This software is published under the terms of the Apache Software * License version 1.1, a copy of which has been included with this * distribution in the LICENSE.txt file. */ package org.apache.log4j.filter; import org.apache.log4j.Level; import org.apache.log4j.spi.LoggingEvent; import org.apache.log4j.helpers.OptionConverter; /** LevelMatchFilter is a very simple filter that matches a configured log level against the log level of a logging event. If they levels are the same, then the match() method returns true, else it returns false. <p>If a LevelMatchFilter is not configured with a level to match, then the canMatch() method will return false. <p>For more information about how the logging event will be passed to the appender for reporting, please see the {@link MatchFilterBase} class. @author Ceki Gülcü @author Mark Womack; @since 1.3 */ public class LevelMatchFilter extends MatchFilterBase { /** The level to match against. */ Level levelToMatch; /** Sets the level to match against. */ public void setLevelToMatch(String level) { levelToMatch = OptionConverter.toLevel(level, null); } /** Gets the level that will be matched against. */ public String getLevelToMatch() { return levelToMatch == null ? null : levelToMatch.toString(); } /** Overrides the implementation from the base class to return false if the levelToMatch has not been configured. */ protected boolean canMatch() { return (levelToMatch != null); } /** Returns true if the levelToMatch matches the level of the logging event. */ protected boolean match(LoggingEvent event) { return (levelToMatch.equals(event.getLevel())); } } 1.1 jakarta-log4j-sandbox/src/java/org/apache/log4j/filter/LevelRangeMatchFilter.java Index: LevelRangeMatchFilter.java =================================================================== /* * Copyright (C) The Apache Software Foundation. All rights reserved. * * This software is published under the terms of the Apache Software * License version 1.1, a copy of which has been included with this * distribution in the LICENSE.txt file. */ package org.apache.log4j.filter; import org.apache.log4j.Level; import org.apache.log4j.spi.LoggingEvent; /** LevelMatchFilter is a very simple filter based on level matching, which can be used to reject logging events with levels outside a certain range. If they levels are within the range, then the match() method returns true, else it returns false. <p>If <code>LevelMin</code> is not defined, then there is no minimum acceptable level (ie a level is never rejected for being too "low"/unimportant). If <code>LevelMax</code> is not defined, then there is no maximum acceptable level (ie a level is never rejected for being too "high"/important). <p>Refer to the {@link org.apache.log4j.AppenderSkeleton#setThreshold setThreshold} method available to <code>all</code> appenders extending {@link org.apache.log4j.AppenderSkeleton} for a more convenient way to filter out events by level. <p>For more information about how the logging event will be passed to the appender for reporting, please see the {@link MatchFilterBase} class. @author Simon Kitching @author based on code by Ceki Gülcü @author Mark Womack; @since 1.3 */ public class LevelRangeMatchFilter extends MatchFilterBase { /** Minimum level to match against. */ Level levelMin; /** Maximum level to match against. */ Level levelMax; /** Set the <code>LevelMax</code> option. */ public void setLevelMax(Level levelMax) { this.levelMax = levelMax; } /** Get the value of the <code>LevelMax</code> option. */ public Level getLevelMax() { return levelMax; } /** Set the <code>LevelMin</code> option. */ public void setLevelMin(Level levelMin) { this.levelMin = levelMin; } /** Get the value of the <code>LevelMin</code> option. */ public Level getLevelMin() { return levelMin; } /** Returns true if the the level of the logging event is in the configured range of <code>LevelMin</code> and <code>LevelMax</code>. */ protected boolean match(LoggingEvent event) { if(this.levelMin != null) { if (event.getLevel().isGreaterOrEqual(levelMin) == false) { // level of event is less than minimum return false; } } if(this.levelMax != null) { if (event.getLevel().toInt() > levelMax.toInt()) { // level of event is greater than maximum // Alas, there is no Level.isGreater method. and using // a combo of isGreaterOrEqual && !Equal seems worse than // checking the int values of the level objects.. return false; } } // return true match return true; } } 1.1 jakarta-log4j-sandbox/src/java/org/apache/log4j/filter/MDCMatchFilter.java Index: MDCMatchFilter.java =================================================================== /* * Copyright (C) The Apache Software Foundation. All rights reserved. * * This software is published under the terms of the Apache Software * License version 1.1, a copy of which has been included with this * distribution in the LICENSE.txt file. */ package org.apache.log4j.filter; import org.apache.log4j.MDC; import org.apache.log4j.spi.Filter; import org.apache.log4j.spi.LoggingEvent; /** The MDCMatchFilter matches a configured value against the value of a configured key in the MDC of a logging event. <p>The filter admits three options <b>KeyToMatch</b>, <b>ValueToMatch</b>, and <b>ExactMatch</b>. <p>The value of <b>KeyToMatch</b> property determines which key is used to match against in the MDC. The value of that key is used to test against the <b>ValueToMatch</b property. The <b>KeyToMatch</b> property must be set before this filter can function properly. <p>The value of <b>ValueToMatch</b> property determines the string value to match against. If <b>ExactMatch</b> is set to true, a match will occur only when <b>ValueToMatch</b> exactly matches the MDC value of the logging event. Otherwise, if the <b>ExactMatch</b> property is set to <code>false</code>, a match will occur if <b>ValueToMatch</b> is contained anywhere within the MDC value. The <b>ExactMatch</b> property is set to <code>false</code> by default. <p>Note that by default the value to match is set to <code>null</code> and will only match if the key is not contained or the value is null in the MDC. <p>For more information about how the logging event will be passed to the appender for reporting, please see the {@link MatchFilterBase} class. @author Mark Womack @since 1.3 */ public class MDCMatchFilter extends MatchFilterBase { /** The key to match in the MDC of the LoggingEvent. */ String keyToMatch; /** The value to match in the MDC value of the LoggingEvent. */ String valueToMatch; /** Do we look for an exact match or just a "contains" match? */ boolean exactMatch = false; /** Sets the key to match in the MDC of the LoggingEvent. */ public void setKeyToMatch(String key) { keyToMatch = key; } /** Gets the key to match in the MDC of the LoggingEvent. */ public String getKeyToMatch() { return keyToMatch; } /** Sets the value to match in the NDC value of the LoggingEvent. */ public void setValueToMatch(String value) { valueToMatch = value; } /** Gets the value to match in the NDC value of the LoggingEvent. */ public String getValueToMatch() { return valueToMatch; } /** Set to true if configured value must exactly match the MDC value of the LoggingEvent. Set to false if the configured value must only be contained in the MDC value of the LoggingEvent. Default is false. */ public void setExactMatch(boolean exact) { exactMatch = exact; } public boolean getExactMatch() { return exactMatch; } protected boolean canMatch() { return (keyToMatch != null); } /** If <b>ExactMatch</b> is set to true, returns true only when <b>ValueToMatch</b> exactly matches the MDC value of the logging event. If the <b>ExactMatch</b> property is set to <code>false</code>, returns true when <b>ValueToMatch</b> is contained anywhere within the MDC value. Otherwise, false is returned. */ protected boolean match(LoggingEvent event) { // get the mdc value for the key from the event // use the toString() value of the value object Object mdcObject = event.getMDC(keyToMatch); String mdcValue; if (mdcObject != null) { mdcValue = mdcObject.toString(); } else { mdcValue = null; } // check for a match if (mdcValue == null) { if (valueToMatch == null) { return true; } else { return false; } } else { if (valueToMatch != null) { if (exactMatch) { return mdcValue.equals(valueToMatch); } else { return (mdcValue.indexOf(valueToMatch) != -1); } } else { return false; } } } } 1.1 jakarta-log4j-sandbox/src/java/org/apache/log4j/filter/MatchFilterBase.java Index: MatchFilterBase.java =================================================================== /* * Copyright (C) The Apache Software Foundation. All rights reserved. * * This software is published under the terms of the Apache Software * License version 1.1, a copy of which has been included with this * distribution in the LICENSE.txt file. */ package org.apache.log4j.filter; import org.apache.log4j.spi.Filter; import org.apache.log4j.spi.LoggingEvent; import org.apache.log4j.helpers.LogLog; /** This is the abstract base class for many useful filters implemented in the log4j.filter package. It extends the base {@link Filter} class to allow a different return value for a match or a nomatch. What is specifically tested for matching is implemented in specific subclasses. <p>Properties matchReturnValue and noMatchReturnValue can be set programmitcally or from a configuration file. They set the value that will be returned when there is match or when there is not a match, respectively. By default matchReturnValue is set to Filter.ACCEPT and noMatchReturnValue is set to Filter.DENY. <p>In addition to being able to set the match and nomatch return values directly, one can instead use the chainPolicy property. Log4j allows for filters to be chained together, each filter deciding whether the logging event should be accepted, denied, or passed on to the next filter. However, for the event to passed to the next filter, one of the return values must be set to Filter.NEUTRAL. One can use chainPolicy to accomplish this, passing it one of the four valid policies: ACCEPT_ON_MATCH (match = Filter.ACCEPT/nomatch = Filter.NEUTRAL), DENY_ON_MATCH (match = Filter.DENY/nomatch = Filter.NEUTRAL), ACCEPT_ON_NOMATCH (match = Filter.NEUTRAL/nomatch = Filter.ACCEPT), and DENY_ON_NOMATCH (match = Filter.NEUTRAL/nomatch = Filter.DENY). Which policy is used can be set programmatically or from a configuration file. If more than one filter will be attached to a single appender, all but the last one should probably be configured via the chainPolicy property. <p>Subclasses are required to implement the match() method. The implementation should test for a match, returning true if there is a match and false if there is nomatch. Subclasses can also implement their own version of the canMatch() method, but should only do so if they will be unable to perform the match code due to misconfiguration. By default, canMatch() will always return true. <p>Developers are encouraged to extend this base class when implementing their own filters. For examples of how to use and extend this base class, please see the various filters implemented in the log4j.filters package. @author Mark Womack @since 1.3 */ public abstract class MatchFilterBase extends Filter { /** Chain policy constant = AcceptOnMatch. */ public static final String ACCEPT_ON_MATCH = "AcceptOnMatch"; /** Chain policy constant = DenyOnMatch. */ public static final String DENY_ON_MATCH = "DenyOnMatch"; /** Chain policy constant = AcceptOnNomatch. */ public static final String ACCEPT_ON_NOMATCH = "AcceptOnNomatch"; /** Chain policy constant = DenyOnNomatch. */ public static final String DENY_ON_NOMATCH = "DenyOnNomatch"; /** Chain policy constant = UnknownPolicy. */ public static final String UNKNOWN_POLICY = "UnknownPolicy"; /** The value that will be returned upon a successful match. */ protected int matchReturnValue = ACCEPT; /** The value that will be returned upon an unsuccessful match */ protected int noMatchReturnValue = DENY; /** Set the value to return upon a successful match. Valid string values are "ACCEPT", "DENY", and "NEUTRAL". */ public void setMatchReturnValue(String filterReturnValue) { if (filterReturnValue.equalsIgnoreCase("accept")) { matchReturnValue = ACCEPT; } else if (filterReturnValue.equalsIgnoreCase("deny")) { matchReturnValue = DENY; } else if (filterReturnValue.equalsIgnoreCase("neutral")) { matchReturnValue = NEUTRAL; } else { LogLog.error("invalid matchReturnValue: " + filterReturnValue); } } /** Gets the value that will be returned upon a successful match. */ public String getMatchReturnValue() { if (matchReturnValue == ACCEPT) return "accept"; else if (matchReturnValue == DENY) return "deny"; else if (matchReturnValue == NEUTRAL) return "neutral"; else return "unknown"; // this one should never happen } /** Set the value to return upon a successful match. Valid string values are "ACCEPT", "DENY", and "NEUTRAL". */ public void setNoMatchReturnValue(String filterReturnValue) { if (filterReturnValue.equalsIgnoreCase("accept")) { noMatchReturnValue = ACCEPT; } else if (filterReturnValue.equalsIgnoreCase("deny")) { noMatchReturnValue = DENY; } else if (filterReturnValue.equalsIgnoreCase("neutral")) { noMatchReturnValue = NEUTRAL; } else { LogLog.error("invalid noMatchReturnValue: " + filterReturnValue); } } /** Gets the value that will be returned upon an unsuccessful match. */ public String getNoMatchReturnValue() { if (noMatchReturnValue == ACCEPT) return "accept"; else if (noMatchReturnValue == DENY) return "deny"; else if (noMatchReturnValue == NEUTRAL) return "neutral"; else return "unknown"; // this one should never happen } /** Sets the match and nomatch return values based on a "policy" string. Valid values for the policy string are defined as constants for this class: ACCEPT_ON_MATCH, DENY_ON_MATCH, ACCEPT_ON_NOMATCH, DENY_ON_NOMATCH. */ public void setChainPolicy(String policyStr) { if (policyStr.equalsIgnoreCase(ACCEPT_ON_MATCH)) { matchReturnValue = ACCEPT; noMatchReturnValue = NEUTRAL; } else if (policyStr.equalsIgnoreCase(DENY_ON_MATCH)) { matchReturnValue = DENY; noMatchReturnValue = NEUTRAL; } else if (policyStr.equalsIgnoreCase(ACCEPT_ON_NOMATCH)) { matchReturnValue = NEUTRAL; noMatchReturnValue = ACCEPT; } else if (policyStr.equalsIgnoreCase(DENY_ON_NOMATCH)) { matchReturnValue = NEUTRAL; noMatchReturnValue = DENY; } else { LogLog.error("invalid chainPolicy: " + policyStr); } } /** Gets the chain policy string value that matches the current settings of matchReturnValue and noMatchReturn value. If the current values do not match a known policy setting, then the value of UNKNOWN_PLOCY is returned. Valid return values for the policy string are defined as constants for this class: ACCEPT_ON_MATCH, DENY_ON_MATCH, ACCEPT_ON_NOMATCH, DENY_ON_NOMATCH, and UNKNOWN_POLICY. */ public String getChainPolicy() { if (matchReturnValue == ACCEPT && noMatchReturnValue == NEUTRAL) { return ACCEPT_ON_MATCH; } else if (matchReturnValue == DENY && noMatchReturnValue == NEUTRAL) { return DENY_ON_MATCH; } else if (matchReturnValue == NEUTRAL && noMatchReturnValue == ACCEPT) { return ACCEPT_ON_NOMATCH; } else if (matchReturnValue == NEUTRAL && noMatchReturnValue == DENY) { return DENY_ON_NOMATCH; } else { return UNKNOWN_POLICY; } } /** Implementation that calls the canMatch() and match() methods of subclasses. If a match test can be performed (canMatch() returned true), then either the configured matchReturnValue or noMatchReturnValue will be returned. If no match test can be performed (canMatch() returned false), then Filter.NEUTRAL is returned. */ public int decide(LoggingEvent event) { if (canMatch()) { if (match(event)) { return matchReturnValue; } else { return noMatchReturnValue; } } else return NEUTRAL; } /** Subclasses can override this method with their own version if it is possible that no match test can/should be performed due to a misconfiguration. This method should return true if a match test can be performed, and false if it cannot be performed. The default version always returns true. */ protected boolean canMatch() { return true; } /** Subclasses must implement this method to perform the specific match test that they require. This method should return true if a match is made, and false if no match is made. */ abstract protected boolean match(LoggingEvent event); } 1.1 jakarta-log4j-sandbox/src/java/org/apache/log4j/filter/MessageMatchFilter.java Index: MessageMatchFilter.java =================================================================== /* * Copyright (C) The Apache Software Foundation. All rights reserved. * * This software is published under the terms of the Apache Software * License version 1.1, a copy of which has been included with this * distribution in the LICENSE.txt file. */ package org.apache.log4j.filter; import org.apache.log4j.Level; import org.apache.log4j.spi.LoggingEvent; import org.apache.log4j.helpers.OptionConverter; /** MessageMatchFilter is a very simple filter that matches a configured value against the message value of a logging event. <p>The filter admits two options <b>MessageToMatch</b> and <b>ExactMatch</b>. <p>As the name indicates, the value of <b>MessageToMatch</b> property determines the string value to match. If <b>ExactMatch</b> is set to true, a match will occur only when <b>MessageToMatch</b> exactly matches the message value of the logging event. Otherwise, if the <b>ExactMatch</b> property is set to <code>false</code>, a match will occur when <b>MessageToMatch</b> is contained anywhere within the message value. The <b>ExactMatch</b> property is set to <code>false</code> by default. <p>Note that by default <b>MessageToMatch</b> is set to <code>null</code> and will only match a null message. <p>For more information about how the logging event will be passed to the appender for reporting, please see the {@link MatchFilterBase} class. @author Mark Womack; @since 1.3 */ public class MessageMatchFilter extends MatchFilterBase { /** The message match against. */ String messageToMatch; /** Do we look for an exact match or just a "contains" match? */ boolean exactMatch = false; /** Sets the string to match against the logging event message. */ public void setMessageToMatch(String _message) { messageToMatch = _message; } public String getMessageToMatch() { return messageToMatch; } /** Set to true if configured value must exactly match the message value of the LoggingEvent. Set to false if the configured value must only be contained in the message value of the LoggingEvent. Default is false. */ public void setExactMatch(boolean exact) { exactMatch = exact; } public boolean getExactMatch() { return exactMatch; } /** If <b>ExactMatch</b> is set to true, returns true only when <b>MessageToMatch</b> exactly matches the message value of the logging event. If the <b>ExactMatch</b> property is set to <code>false</code>, returns true when <b>MessageToMatch</b> is contained anywhere within the message value. Otherwise, false is returned. */ protected boolean match(LoggingEvent event) { String msg = event.getRenderedMessage(); if (msg == null) { return (messageToMatch == null); } else { if (messageToMatch != null) { if (exactMatch) { return messageToMatch.equals(msg); } else { return (msg.indexOf(messageToMatch) != -1); } } } return false; } } 1.1 jakarta-log4j-sandbox/src/java/org/apache/log4j/filter/NDCMatchFilter.java Index: NDCMatchFilter.java =================================================================== /* * Copyright (C) The Apache Software Foundation. All rights reserved. * * This software is published under the terms of the Apache Software * License version 1.1, a copy of which has been included with this * distribution in the LICENSE.txt file. */ package org.apache.log4j.filter; import org.apache.log4j.NDC; import org.apache.log4j.spi.Filter; import org.apache.log4j.spi.LoggingEvent; import org.apache.log4j.helpers.OptionConverter; /** The NDCMatchFilter matches a configured value against the NDC value of a logging event. <p>The filter admits two options <b>ValueToMatch</b> and <b>ExactMatch</b>. <p>As the name indicates, the value of <b>ValueToMatch</b> property determines the string value to match. If <b>ExactMatch</b> is set to true, a match will occur only when <b>ValueToMatch</b> exactly matches the NDC value of the logging event. Otherwise, if the <b>ExactMatch</b> property is set to <code>false</code>, a match will occur when <b>ValueToMatch</b> is contained anywhere within the NDC value. The <b>ExactMatch</b> property is set to <code>false</code> by default. <p>Note that by default <b>ValueToMatch</b> is set to <code>null</code> and will only match an empty NDC stack. <p>For more information about how the logging event will be passed to the appender for reporting, please see the {@link MatchFilterBase} class. @author Mark Womack @since 1.3 */ public class NDCMatchFilter extends MatchFilterBase { /** The value to match in the NDC value of the LoggingEvent. */ String valueToMatch; /** Do we look for an exact match or just a "contains" match? */ boolean exactMatch = false; /** Sets the value to match in the NDC value of the LoggingEvent. */ public void setValueToMatch(String value) { valueToMatch = value; } /** Gets the value to match in the NDC value of the LoggingEvent. */ public String getValueToMatch() { return valueToMatch; } /** Set to true if configured value must exactly match the NDC value of the LoggingEvent. Set to false if the configured value must only be contained in the NDC value of the LoggingEvent. Default is false. */ public void setExactMatch(boolean exact) { exactMatch = exact; } public boolean getExactMatch() { return exactMatch; } /** If <b>ExactMatch</b> is set to true, returns true only when <b>ValueToMatch</b> exactly matches the NDC value of the logging event. If the <b>ExactMatch</b> property is set to <code>false</code>, returns true when <b>ValueToMatch</b> is contained anywhere within the NDC value. Otherwise, false is returned. */ protected boolean match(LoggingEvent event) { // get the ndc value for the event String eventNDC = event.getNDC(); // check for a match // if the NDC stack is empty if (eventNDC == null) { // return true if are we matching a null if (valueToMatch == null) { return true; // else return false } else { return false; } } else { // try to match the configured non-null value if (valueToMatch != null) { if (exactMatch) { return eventNDC.equals(valueToMatch); } else { return (eventNDC.indexOf(valueToMatch) != -1); } // else the value to match is null, so return false } else { return false; } } } } 1.1 jakarta-log4j-sandbox/src/java/org/apache/log4j/filter/SetLocationInfoFilter.java Index: SetLocationInfoFilter.java =================================================================== /* * Copyright (C) The Apache Software Foundation. All rights reserved. * * This software is published under the terms of the Apache Software * License version 1.1, a copy of which has been included with this * distribution in the LICENSE.txt file. */ package org.apache.log4j.filter; import org.apache.log4j.spi.Filter; import org.apache.log4j.spi.LoggingEvent; /** SetLocationInfoFilter is pass through filter that simply calls {@link LoggingEvent#getLocationInformation} method of every LoggingEvent that is sent to it. After calling the method, it returns {@link Filter#NEUTRAL} to send the event to the next filter. <p><bold>Use of this filter is probably not typical.</bold> Its primary purpose is to increase throughput performance for appenders like SocketAppender and SocketHubAppender that send logging events to remote clients. These appenders have the option to set the location info for every event appended to them so that the client can see where the event was logged in the code. However, resolving the location info for every event can be costly performance-wise, and will reduce the number of events per second that can be appended. This can affect performance in the application that is logging the event. Chances are that one does not want the location info for every event, but rather for a smaller set of events that are of interest. <p>SetLocationInfoFilter can be placed at the end of a filter chain configured for an appender. After the event has been filtered through the chain, it will pass through the SetLocationInfoFilter, thus setting the location info for just that event (this assumes that the location info setting of the appender has been set to false). Using subclasses of the MatchFilterBase class, one can configure the filter chain to accept all events sent to the appender, while only setting the location info for a select set of events. Please see the examples for information on how to do this. <p>Please review the available filters in the org.apache.log4j.filters package. Most of these subclass the MatchFilterBase class and are easily configurable for use in log4j filter chains. <p>(Note that any log4j filter can be used in an appender filter chain, but it needs to support the return of the {@link Filter#NEUTRAL} value from its decide method. MatchFilterBase subclasses simply expose this functionality directly as part of their configuration.) @author Mark Womack @since 1.3 */ public class SetLocationInfoFilter extends Filter { /** Sets the LocationInfo for the event and returns {@link Filter#NEUTRAL} to pass the event to the next filter. */ public int decide(LoggingEvent event) { event.getLocationInformation(); return Filter.NEUTRAL; } } 1.1 jakarta-log4j-sandbox/src/java/org/apache/log4j/selector/ContextClassLoaderSelector.java Index: ContextClassLoaderSelector.java =================================================================== /* * Copyright (C) The Apache Software Foundation. All rights reserved. * * This software is published under the terms of the Apache Software * License version 1.1, a copy of which has been included with this * distribution in the LICENSE.txt file. */ package org.apache.log4j.selector; import org.apache.log4j.spi.RepositorySelector; import org.apache.log4j.spi.LoggerRepository; import org.apache.log4j.spi.RootCategory; import org.apache.log4j.Hierarchy; import org.apache.log4j.Level; import org.apache.log4j.LogManager; import java.util.Collections; import java.util.Map; import java.util.WeakHashMap; /** * @author Jacob Kjome */ public class ContextClassLoaderSelector implements RepositorySelector { // key: current thread's ContextClassLoader, // value: Hierarchy instance final private static Map hierMap = Collections.synchronizedMap(new WeakHashMap()); final private static ContextClassLoaderSelector singleton = new ContextClassLoaderSelector(); private static boolean initialized = false; private ContextClassLoaderSelector() {} public LoggerRepository getLoggerRepository() { ClassLoader cl = Thread.currentThread().getContextClassLoader(); Hierarchy hierarchy = (Hierarchy) hierMap.get(cl); if(hierarchy == null) { hierarchy = new Hierarchy(new RootCategory((Level) Level.DEBUG)); hierMap.put(cl, hierarchy); } return hierarchy; } /** * The Container should initialize the logger repository for each * webapp upon startup or reload. In this case, it is controllable * via each webapp. */ public static void doIdempotentInitialization() { if(!initialized) { try { Object guard = new Object(); LogManager.setRepositorySelector(singleton, guard); initialized = true; } catch (IllegalArgumentException iae) { //either ignore the exception or log the fact that the setting of this //custom repository selector failed because another had been set previously // and maybe we should set "initialized" to "true" in here so this exception doesn't // occur again in this class } } } } 1.1 jakarta-log4j-sandbox/src/java/org/apache/log4j/servlet/ConfigurationServlet.java Index: ConfigurationServlet.java =================================================================== /* * Copyright (C) The Apache Software Foundation. All rights reserved. * * This software is published under the terms of the Apache Software * License version 1.1, a copy of which has been included with this * distribution in the LICENSE.txt file. */ package org.apache.log4j.servlet; import java.io.IOException; import java.io.PrintWriter; import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Enumeration; import java.util.Iterator; import java.util.List; import javax.servlet.ServletException; import javax.servlet.SingleThreadModel; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.apache.log4j.Logger; import org.apache.log4j.LogManager; import org.apache.log4j.Level; /** * A servlet used to dynamically adjust package logging levels * while an application is running. NOTE: This servlet is * only aware of pre-configured packages and packages that contain objects * that have logged at least one message since application startup. * * @author <a href="mailto:[EMAIL PROTECTED]">Luther E. Birdzell</a> * @since 1.3 */ public class ConfigurationServlet extends HttpServlet implements SingleThreadModel { /** * The response content type: text/html */ public static final String CONTENT_TYPE = "text/html"; /** * The root appender. */ public static final String ROOT = "Root"; /** * The name of the class / package. */ public static final String CLASS = "CLASS"; /** * The logging level. */ public static final String PRIORITY = "PRIORITY"; /** * Print the status of all current <code>Logger</code>s and * an option to change their respective logging levels. * * @param request a <code>HttpServletRequest</code> value * @param response a <code>HttpServletResponse</code> value * @exception ServletException if an error occurs * @exception IOException if an error occurs */ public void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { response.setContentType(CONTENT_TYPE); PrintWriter out = response.getWriter(); List loggers = getSortedLoggers(); Logger logger = null; String loggerName = null; int loggerNum = 0; // print title and header out.println("<html><head><title>Log4J Control Console</title></head>" + "<body><H3>Log4J Control Console</H3>"); out.println("<A href=\"" + request.getRequestURI() + "\">Refresh</A><HR>"); out.println("<table width=\"50%\" border=\"1\">"); out.println("<tr BGCOLOR=\"#5991A6\">"); out.println("<td><FONT COLOR=\"BLACK\" FACE=\"Helvetica\"><B>Class</B></FONT></td>"); out.println("<td><FONT COLOR=\"BLACK\" FACE=\"Helvetica\"><B>Priority</B></FONT></td>"); out.println("</tr>"); // print the root Logger displayLogger(out, Logger.getRootLogger(), loggerNum++, request); // print the rest of the loggers Iterator ii = loggers.iterator(); while( ii.hasNext() ) { displayLogger(out, (Logger) ii.next(), loggerNum++, request); } out.println("</table>"); out.println("<FONT SIZE=\"-3\" COLOR=\"BLACK\" FACE=\"Helvetica\">* "+ "Inherits Priority From Parent.</FONT><BR>"); out.println("<A href=\"" + request.getRequestURI() + "\">Refresh</A><HR>"); // print set options out.println("<FORM action=\"" + request.getRequestURI() + "\" method=\"post\">"); out.println("<FONT SIZE=\"+2\" COLOR=\"BLACK\" FACE=\"Helvetica\"><U>"+ "Set Log4J Option</U><BR><BR></FONT>"); out.println("<FONT COLOR=\"BLACK\" FACE=\"Helvetica\">"); out.println("<table width=\"50%\" border=\"1\">"); out.println("<tr BGCOLOR=\"#5991A6\">"); out.println("<td><FONT COLOR=\"BLACK\" "+ "FACE=\"Helvetica\"><B>Class Name:</B></FONT></td>"); out.println("<td><SELECT name=\"CLASS\">"); out.println("<OPTION VALUE=\"" + ROOT + "\">" + ROOT + "</OPTION>"); ii = loggers.iterator(); while( ii.hasNext() ) { logger = (Logger) ii.next(); loggerName = (logger.getName().equals("") ? "Root" : logger.getName()); out.println("<OPTION VALUE=\"" + loggerName + "\">" + loggerName + "</OPTION>"); } out.println("</SELECT><BR></td></tr>"); // print logging levels out.println("<tr BGCOLOR=\"#5991A6\"><td><FONT COLOR=\"BLACK\" "+ "FACE=\"Helvetica\"><B>Priority:</B></FONT></td>"); out.println("<td><SELECT name=\"PRIORITY\">"); out.println("<OPTION VALUE=\"" + Level.OFF + "\">" + Level.OFF + "</OPTION>"); out.println("<OPTION VALUE=\"" + Level.FATAL + "\">" + Level.FATAL + "</OPTION>"); out.println("<OPTION VALUE=\"" + Level.ERROR + "\">" + Level.ERROR + "</OPTION>"); out.println("<OPTION VALUE=\"" + Level.WARN + "\">" + Level.WARN + "</OPTION>"); out.println("<OPTION VALUE=\"" + Level.INFO + "\">" + Level.INFO + "</OPTION>"); out.println("<OPTION VALUE=\"" + Level.DEBUG + "\">" + Level.DEBUG + "</OPTION>"); out.println("<OPTION VALUE=\"" + Level.ALL + "\">" + Level.ALL + "</OPTION>"); out.println("</SELECT><BR></td></tr>"); out.println("</table></FONT>"); out.println("<input type=\"submit\" name=\"Submit\" value=\"Set Option\"></FONT>"); out.println("</FORM>"); out.println("</body></html>"); out.flush(); out.close(); } /** * Change a <code>Logger</code>'s level, then call <code>doGet</code> * to refresh the page. * * @param request a <code>HttpServletRequest</code> value * @param response a <code>HttpServletResponse</code> value * @exception ServletException if an error occurs * @exception java.io.IOException if an error occurs */ public void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { String className = (String) request.getParameter(CLASS); String priority = (String) request.getParameter(PRIORITY); if (className != null) { setClass(className, priority); } doGet(request, response); } // print a Logger and its current level private void displayLogger(PrintWriter out, Logger logger, int row, HttpServletRequest request) { String color = null; String loggerName = (logger.getName().equals("") ? ROOT : logger.getName()); color = (row%2 == 1) ? "#E1E1E1" : "#FBFBFB"; out.println("<tr BGCOLOR=\"" + color + "\">"); out.println("<td><FONT SIZE=\"-2\" COLOR=\"BLACK\" FACE=\"Helvetica\">" + loggerName + "</FONT></td>"); out.println("<td><FONT SIZE=\"-2\" COLOR=\"BLACK\" FACE=\"Helvetica\">" + ( (logger.getLevel() == null) ? logger.getEffectiveLevel().toString() + "*" : logger.getLevel().toString()) + "</FONT></td>"); out.println("</tr>"); } // set a logger's level private synchronized String setClass(String className, String level) { Logger logger = null; String message = null; try { logger = (className.equals(ROOT)) ? logger.getRootLogger() : logger.getLogger(className); logger.setLevel(Level.toLevel(level)); } catch (Exception e) { System.out.println("ERROR Setting LOG4J Logger:" + e); } return "Message Set For " + (logger.getName().equals("") ? ROOT : logger.getName()); } // get a sorted list of all current loggers private List getSortedLoggers() { Logger logger = null; Enumeration enum = LogManager.getCurrentLoggers(); Comparator comp = new LoggerComparator(); ArrayList list = new ArrayList(); // Add all current loggers to the list while(enum.hasMoreElements()) { list.add(enum.nextElement()); } // sort the loggers Collections.sort(list, comp); return list; } /** * Compare the names of two <code>Logger</code>s. Used * for sorting. */ private class LoggerComparator implements Comparator { /** * Compare the names of two <code>Logger</code>s. * * @param o1 an <code>Object</code> value * @param o2 an <code>Object</code> value * @return an <code>int</code> value */ public int compare(Object o1, Object o2) { Logger logger1 = (Logger) o1; Logger logger2 = (Logger) o2;; String logger1Name = null; String logger2Name = null; if ( logger1 != null ) logger1Name = (logger1.getName().equals("") ? ROOT : logger1.getName()); if ( logger2 != null ) logger2Name = (logger2.getName().equals("") ? ROOT : logger2.getName()); return logger1Name.compareTo(logger2Name); } /** * Return <code>true</code> if the <code>Object</code> is a * <code>LoggerComparator</code> instance. * * * @param o an <code>Object</code> value * @return a <code>boolean</code> value */ public boolean equals(Object o) { if (o instanceof LoggerComparator) return true; else return false; } } }//EOF 1.1 jakarta-log4j-sandbox/src/java/org/apache/log4j/servlet/CookieMDCFilter.java Index: CookieMDCFilter.java =================================================================== /* * Copyright (C) The Apache Software Foundation. All rights reserved. * * This software is published under the terms of the Apache Software * License version 1.1, a copy of which has been included with this * distribution in the LICENSE.txt file. */ package org.apache.log4j.servlet; import java.util.HashMap; import java.util.Iterator; import java.util.StringTokenizer; import java.io.IOException; import javax.servlet.Filter; import javax.servlet.FilterChain; import javax.servlet.FilterConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; import org.apache.log4j.Logger; import org.apache.log4j.MDC; /** A useful Servlet 2.3 compatible filter which will search for a predefined set of cookies in the request and place their values into the log4j MDC. The key used in the MDC is the name of the cookie. The value placed in the MDC is the value of the cookie in the request. @author Mark Womack <[EMAIL PROTECTED]> @since 1.3 */ // An example of the web.xml configuration: // // <!-- looks for cookies named "JSESSIONID" and "USERID" --> // <filter> // <filter-name>trace-cookie-context-filter</filter-name> // <filter-class> // org.apache.log4j.servlet.CookieMDCFilter // </filter-class> // <init-param> // <param-name>cookie-list</param-name> // <param-value> // JSESSIONID // USERID // </param-value> // </init-param> // </filter> // // <!-- any jsp will have this filter execute first --> // <filter-mapping> // <filter-name>trace-cookie-context-filter</filter-name> // <url-pattern>*/*.jsp</url-pattern> // </filter-mapping> // // An example of the log4j xml configuration using PatternLayout: // // <appender name="stdout" class="org.apache.log4j.ConsoleAppender"> // <layout class="org.apache.log4j.PatternLayout"> // <param name="ConversionPattern" // value="%d{ABSOLUTE} %-5p %X{JSESSIONID} %X{USERID} %c{1} : %m%n"/> // </layout> // </appender> public class CookieMDCFilter implements Filter { private static final Logger _TRACE = Logger.getLogger(CookieMDCFilter.class); /** The set of cookies names we want to look for. */ private HashMap cookieMap; /** Uses filter init parameter to initialize a list of cookies that will be retrieved from the request and placed into the log4j MDC context. */ public void init(FilterConfig filterConfig) throws ServletException { // check for existense of init param String cookieParam = filterConfig.getInitParameter("cookie-list"); if (cookieParam == null || cookieParam.length() == 0) return; // parse the list of cookies StringTokenizer tokenizer = new StringTokenizer(cookieParam); while (tokenizer.hasMoreTokens()) { if (cookieMap == null) { cookieMap = new HashMap(); } cookieMap.put(tokenizer.nextToken(), null); } // report the configuration if (cookieMap != null && _TRACE.isDebugEnabled()) { Iterator iter = cookieMap.keySet().iterator(); while (iter.hasNext()) { _TRACE.debug("configured to search for cookie with name " + iter.next()); } } } /** Search the request cookies for the cookies whose value we want to stuff into the log4j MDC. */ public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { // if this filter configured and request has cookies, search if (cookieMap != null) { Cookie[] cookies = ((HttpServletRequest)request).getCookies(); if (cookies != null) { for (int x = 0; x < cookies.length; x++) { // if request has a cookie we are lookig for, put its // value into the MDC String name = cookies[x].getName(); if (cookieMap.containsKey(cookies[x].getName())) { MDC.put(name, cookies[x].getValue()); if (_TRACE.isDebugEnabled()) _TRACE.debug("put into MDC cookie with name " + name + " and value " + cookies[x].getValue()); } else { if (_TRACE.isDebugEnabled()) _TRACE.debug("ignoring cookie with name " + name); } } } } // pass control to the next filter chain.doFilter(request, response); } /** Not used. */ public void destroy() { // do nothing } } 1.1 jakarta-log4j-sandbox/src/java/org/apache/log4j/servlet/InitContextListener.java Index: InitContextListener.java =================================================================== /* * Copyright (C) The Apache Software Foundation. All rights reserved. * * This software is published under the terms of the Apache Software * License version 1.1, a copy of which has been included with this * distribution in the LICENSE.txt file. */ package org.apache.log4j.servlet; import javax.servlet.ServletContext; import javax.servlet.ServletContextListener; import javax.servlet.ServletContextEvent; import java.io.File; import java.net.MalformedURLException; import java.net.URL; import java.util.Properties; import org.apache.log4j.helpers.LogLog; import org.apache.log4j.PropertyConfigurator; import org.apache.log4j.xml.DOMConfigurator; import org.apache.log4j.selector.ContextClassLoaderSelector; /** * A servlet context listener for initializing and shutting down Log4j. See * <a href="http://jakarta.apache.org/log4j/docs/documentation.html">Log4j documentation</a> * for how to use Log4j. * <p> * This is a <code>ServletContextListener</code> as defined by the servlet 2.3 * specification. It gets called immediately before full application startup * and immediately before full application shutdown. Unlike servlets, which * may be destroyed at the will of the container at any time during the * application lifecycle, a servlet context listener is guaranteed to be * called exactly twice within the application's lifecycle. As such, we can * use it to initialize things once at application startup and clean things * up at application shutdown.</p> * <p> * Initialization is described below in the discussion of the various parameters * available for configuring this context listener. In the case of shutdown we are * concerned with cleaning up loggers and appenders within the * <code>Hierarchy</code> that the current application is using for * logging. If we didn't do this, there is a chance that, for instance, file * appenders won't have given up handles to files they are logging to which * would leave them in a locked state until the current JVM is shut down. This * would entail a full shutdown of the application server in order to release * locks on log files. Using this servlet context listener ensures that locks * will be released without requiring a full server shutdown.</p> * </p> * <p> * The following needs to be added to the webapp's web.xml file to configure this listener: * <blockquote> * <pre> * <context-param> * <!-- relative path to config file within current webapp --> * <param-name>log4j-config</param-name> * <param-value>WEB-INF/log4j.xml</param-value> * </context-param> * <context-param> * <!-- config file re-reading specified in milliseconds... * Note that if the webapp is served directly from the * .war file, configureAndWatch() cannot be used because * it requires a system file path. In that case, this * param will be ignored. Set to 0 or don't specify this * param to do a normal configure(). --> * <param-name>log4j-cron</param-name> * <param-value>5000</param-value> * </context-param> * <!-- Below is an optional param for use with a File Appender. * Specifies a path to be read from a log4j xml * config file as a system property. The property name is * dynamically generated and takes on the following pattern: * [webapp name].log.home * If the app has a path of "/Barracuda", the system * variable name would be "Barracuda.log.home". So, * the FileAppender in log4j.xml would contain a param which looks like: * <param name="File" value="${Barracuda.log.home}/arbitraryLogFileName.log" /> * If the "log4j-log-home" context param is not specified, the * path associated with the generated system variable defaults to * the WEB-INF/logs directory of the current webapp which is created * if it doesn't exist... unless the webapp is running directly * from a .war file. In the latter case, this context param * *must* be specified if using a FileAppender. * Note that, if specified, the value is treated as an absolute * system path which is not relative to the webapp. --> * <!-- <context-param> * <param-name>log4j-log-home</param-name> * <param-value>/usr/local/logs/tomcat</param-value> * </context-param> --> * * <listener> * <listener-class> * org.apache.log4j.servlet.InitContextListener * </listener-class> * </listener> * </pre> * </blockquote> * </p> * <h4>Below is some more information on each of the configuration properties</h4> * <p> * <dl> * <dt><code>log4j-config</code></dt> * <dd> * The <code>log4j-config</code> init parameter specifies the location of the * Log4j configuration file relative to the current webapp. * If the <code>log4j-config</code> init parameter is omitted, this class * will just let Log4j configure itself since, upon first use of Log4j, if it * has not yet been configured, it will search for a config file named log4j.xml * or log4j.properties in the classpath. If it can't find one, it falls back to using the * <code>BasicConfigurator.configure()</code> to initialize Log4j. * </dd> * <dt><code>log4j-cron</code></dt> * <dd> * The <code>log4j-cron</code> init parameter specifies the number of milliseconds * to wait in between reads of the config file using <code>configureAndWatch()</code>. * If omitted, given a value of 0, or given a value that is other than something that * which can be converted to a Java long value a normal <code>configure()</code> is used. * </dd> * <dt><code>log4j-log-home</code></dt> * <dd> * The <code>log4j-log-home</code> init parameter is optional. It specifies a * custom path to a directory meant to contain log files for the current webapp * when using a <code>FileAppender</code>. If not specified, it will default to * using the location WEB-INF/logs to contain log files. If the directory doesn't * exist, it is created. A system parameter is then created in the following format: * <blockquote> * <code>[webapp name].log.home</code> * </blockquote> * This can be referenced in an xml config file (not sure if it works for a properties * config file?) in the following fashion for a webapp with the context path "/Barracuda": * <blockquote> * <code><param name="File" value="${Barracuda.log.home}/main.log" /></code> * </blockquote> * In this case, we are running in the "Barracuda" context and the "main.log" file * will get created in whatever directory path is specified by the system property * "Barracuda.log.home". * <p> * <strong>Note</strong> that if the webapp is being run directly from a .war file, the automatic creation * of the WEB-INF/logs directory and [webapp name].log.home system property will *not* be * performed. In this case, you would have to provide a custom directory path for the * this to work. Also note that <code>configureAndWatch()</code> will not be used in the case * that the webapp is running directly from a .war file. <code>configure()</code> will be used * instead. * </p> * </dd> * </dl> * * @author Jacob Kjome <[EMAIL PROTECTED]> * @since 1.3 */ public class InitContextListener implements ServletContextListener { // store the time at which the current application became fully initialized public static long applicationInitialized = 0L; private final static String PARAM_LOG4J_CONFIG_PATH = "log4j-config"; private final static String PARAM_LOG4J_WATCH_INTERVAL = "log4j-cron"; private final static String PARAM_LOG4J_LOG_HOME = "log4j-log-home"; private final static String DEFAULT_LOG_HOME = "WEB-INF" + File.separator + "logs"; /** * Application Startup Event */ public void contextInitialized(ServletContextEvent sce) { applicationInitialized = System.currentTimeMillis(); ServletContext context = sce.getServletContext(); initializeLog4j(context); } /** * Application Shutdown Event */ public void contextDestroyed(ServletContextEvent sce) { ServletContext context = sce.getServletContext(); cleanupLog4j(context); } /** * Log4j specific cleanup. Shuts down all loggers and appenders and * removes the hierarchy associated with the current classloader. */ private void cleanupLog4j(ServletContext context) { //shutdown this webapp's logger repository context.log("Cleaning up Log4j resources for context: " + context.getServletContextName() + "..."); context.log("Shutting down all loggers and appenders..."); org.apache.log4j.LogManager.shutdown(); context.log("Log4j cleaned up."); } /** * Log4j specific initialization. Shuts down all loggers and appenders and * removes the hierarchy associated with the current classloader. */ private void initializeLog4j(ServletContext context) { String configPath = context.getInitParameter(PARAM_LOG4J_CONFIG_PATH); // if the log4j-config parameter is not set, then no point in trying if (configPath!=null) { if (configPath.startsWith("/")) configPath = (configPath.length() > 1) ? configPath.substring(1) : ""; // if the configPath is an empty string, then no point in trying if (configPath.length() >= 1) { // set up log path System property String logHome = context.getInitParameter(PARAM_LOG4J_LOG_HOME); if (logHome!=null) { // set up custom log path system property setFileAppenderSystemProperty(logHome, context); } boolean isXMLConfigFile = (configPath.endsWith(".xml")) ? true : false; String contextPath = context.getRealPath("/"); if (contextPath!=null) { // The webapp is deployed directly off the filesystem, // not from a .war file so we *can* do File IO. // This means we can use configureAndWatch() to re-read // the the config file at defined intervals. // Now let's check if the given configPath actually exists. if (logHome==null) { // no log path specified in web.xml. Setting to default logHome = contextPath+DEFAULT_LOG_HOME; setFileAppenderSystemProperty(logHome, context); } String systemConfigPath = configPath.replace('/', File.separatorChar); File log4jFile = new File(contextPath+systemConfigPath); if (log4jFile.canRead()) { log4jFile = null; String timerInterval = context.getInitParameter(PARAM_LOG4J_WATCH_INTERVAL); long timerIntervalVal = 0L; if (timerInterval!=null) { try { timerIntervalVal = Integer.valueOf(timerInterval).longValue(); } catch (NumberFormatException nfe) {} } initLoggerRepository(); context.log("Configuring Log4j from File: "+contextPath+systemConfigPath); if (timerIntervalVal > 0) { context.log("Configuring Log4j with watch interval: "+timerIntervalVal+"ms"); if (isXMLConfigFile) { DOMConfigurator.configureAndWatch(contextPath+systemConfigPath, timerIntervalVal); } else { PropertyConfigurator.configureAndWatch(contextPath+systemConfigPath, timerIntervalVal); } } else { if (isXMLConfigFile) { DOMConfigurator.configure(contextPath+systemConfigPath); } else { PropertyConfigurator.configure(contextPath+systemConfigPath); } } } else { // The given configPath does not exist. So, let's just let Log4j look for the // default files (log4j.properties or log4j.xml) on its own. displayConfigNotFoundMessage(); } //end log4jFile.canRead() check } else { // The webapp is deployed from a .war file, not directly // off the file system so we *cannot* do File IO. // Note that we *won't* be able to use configureAndWatch() here // because that requires an absolute system file path. // Now let's check if the given configPath actually exists. URL log4jURL = null; try { log4jURL = context.getResource("/"+configPath); } catch (MalformedURLException murle) {} if (log4jURL!=null) { initLoggerRepository(); context.log("Configuring Log4j from URL at path: /"+configPath); if (isXMLConfigFile) { try { DOMConfigurator.configure(log4jURL); } //catch (javax.xml.parsers.FactoryConfigurationError fce) {} catch (Exception e) { //report errors to server logs LogLog.error(e.getMessage()); } } else { Properties log4jProps = new Properties(); try { log4jProps.load(log4jURL.openStream()); PropertyConfigurator.configure(log4jProps); } //catch (java.io.IOException ioe) {} catch (Exception e) { //report errors to server logs LogLog.error(e.getMessage()); } } } else { // The given configPath does not exist. So, let's just let Log4j look for the // default files (log4j.properties or log4j.xml) on its own. displayConfigNotFoundMessage(); } //end log4jURL null check } //end contextPath null check } else { LogLog.error("Zero length Log4j config file path given."); displayConfigNotFoundMessage(); } //end configPath length check } else { LogLog.error("Missing log4j-config servlet parameter missing."); displayConfigNotFoundMessage(); } //end configPath null check } private void displayConfigNotFoundMessage() { LogLog.warn("No Log4j configuration file found at given path. Falling back to Log4j auto-configuration."); } private void setFileAppenderSystemProperty(String logHome, ServletContext context) { File logHomeDir = new File(logHome); if (logHomeDir.exists() || logHomeDir.mkdirs()) { String tempdir = ""+context.getAttribute("javax.servlet.context.tempdir"); int lastSlash = tempdir.lastIndexOf(File.separator); if ((tempdir.length()-1) > lastSlash) { String logHomePropertyName = tempdir.substring(lastSlash+1) + ".log.home"; context.log("Setting system property [ " + logHomePropertyName + " ] to [ " + logHome + " ]"); System.setProperty(logHomePropertyName, logHome); } } } private void initLoggerRepository() { ContextClassLoaderSelector.doIdempotentInitialization(); } } 1.1 jakarta-log4j-sandbox/src/java/org/apache/log4j/servlet/InitServlet.java Index: InitServlet.java =================================================================== /* * Copyright (C) The Apache Software Foundation. All rights reserved. * * This software is published under the terms of the Apache Software * License version 1.1, a copy of which has been included with this * distribution in the LICENSE.txt file. */ package org.apache.log4j.servlet; import javax.servlet.ServletConfig; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.net.URL; import java.util.Properties; import org.apache.log4j.helpers.LogLog; import org.apache.log4j.PropertyConfigurator; import org.apache.log4j.xml.DOMConfigurator; import org.apache.log4j.selector.ContextClassLoaderSelector; /** * A servlet for initializing Log4j. See * <a href="http://jakarta.apache.org/log4j/docs/documentation.html">Log4j documentation</a> * for how to use Log4j. * <p> * <strong>Note:</strong> This log4j initialization servlet should be used *only* when * running under a container which doesn't support servlet-2.3. A far better choice for * servlet-2.3 configuration exists in {@link Log4jApplicationWatch}. Use it instead of * this class for initialization. If you really need to use this class, read on...</p> * <p> * This servlet is never called by a client, but should be called during * web application initialization, i.e. when the servlet engine starts. The * following code should be inserted in the web.xml file for the web * application: * <p> * <pre> * <servlet> * <servlet-name>log4j-init</servlet-name> * <servlet-class>org.apache.log4j.servlet.InitServlet</servlet-class> * <init-param> * <param-name>log4j-config</param-name> * <param-value>WEB-INF/log4j.xml</param-value> * </init-param> * <init-param> * <param-name>log4j-cron</param-name> * <param-value>5000</param-value> * </init-param> * <init-param> * <param-name>log4j-log-home</param-name> * <param-value>/usr/local/logs/tomcat</param-value> * </init-param> * <load-on-startup>1</load-on-startup> * </servlet> * </pre> * </p> * <p> * See {@link Log4jApplicationWatch} for detailed information about these parameters. * </p> * * @author Jacob Kjome <[EMAIL PROTECTED]> * @since 1.3 */ public class InitServlet extends HttpServlet { private final static String PARAM_LOG4J_CONFIG_PATH = "log4j-config"; private final static String PARAM_LOG4J_WATCH_INTERVAL = "log4j-cron"; private final static String PARAM_LOG4J_LOG_HOME = "log4j-log-home"; private final static String DEFAULT_LOG_HOME = "WEB-INF" + File.separator + "logs"; private static Boolean CONFIGURED = Boolean.FALSE; public void init() throws ServletException { if (CONFIGURED.equals(Boolean.FALSE)) { String configPath = getInitParameter(PARAM_LOG4J_CONFIG_PATH); // if the log4j-config parameter is not set, then no point in trying if (configPath!=null) { if (configPath.startsWith("/")) configPath = (configPath.length() > 1) ? configPath.substring(1) : ""; // if the configPath is an empty string, then no point in trying if (configPath.length() >= 1) { // set up log path System property String logHome = getInitParameter(PARAM_LOG4J_LOG_HOME); if (logHome!=null) { // set up custom log path system property setFileAppenderSystemProperty(logHome, this); } boolean isXMLConfigFile = (configPath.endsWith(".xml")) ? true : false; String contextPath = getServletContext().getRealPath("/"); if (contextPath!=null) { // The webapp is deployed directly off the filesystem, // not from a .war file so we *can* do File IO. // This means we can use configureAndWatch() to re-read // the the config file at defined intervals. // Now let's check if the given configPath actually exists. if (logHome==null) { // no log path specified in web.xml. Setting to default logHome = contextPath+DEFAULT_LOG_HOME; setFileAppenderSystemProperty(logHome, this); } String systemConfigPath = configPath.replace('/', File.separatorChar); File log4jFile = new File(contextPath+systemConfigPath); if (log4jFile.canRead()) { log4jFile = null; String timerInterval = getInitParameter(PARAM_LOG4J_WATCH_INTERVAL); long timerIntervalVal = 0L; if (timerInterval!=null) { try { timerIntervalVal = Integer.valueOf(timerInterval).longValue(); } catch (NumberFormatException nfe) {} } synchronized (CONFIGURED) { if (CONFIGURED.equals(Boolean.FALSE)) { initLoggerRepository(); log("Configuring Log4j from File: "+contextPath+systemConfigPath); if (timerIntervalVal > 0) { log("Configuring Log4j with watch interval: "+timerIntervalVal+"ms"); if (isXMLConfigFile) { DOMConfigurator.configureAndWatch(contextPath+systemConfigPath, timerIntervalVal); } else { PropertyConfigurator.configureAndWatch(contextPath+systemConfigPath, timerIntervalVal); } } else { if (isXMLConfigFile) { DOMConfigurator.configure(contextPath+systemConfigPath); } else { PropertyConfigurator.configure(contextPath+systemConfigPath); } } CONFIGURED = Boolean.TRUE; } //end CONFIGURED check } //end syncronized block } else { // The given configPath does not exist. So, let's just let Log4j look for the // default files (log4j.properties or log4j.xml) on its own. displayConfigNotFoundMessage(); } //end log4jFile.canRead() check } else { // The webapp is deployed from a .war file, not directly // off the file system so we *cannot* do File IO. // Note that we *won't* be able to use configureAndWatch() here // because that requires an absolute system file path. // Now let's check if the given configPath actually exists. URL log4jURL = null; try { log4jURL = getServletContext().getResource("/"+configPath); } catch (MalformedURLException murle) {} if (log4jURL!=null) { synchronized (CONFIGURED) { if (CONFIGURED.equals(Boolean.FALSE)) { initLoggerRepository(); log("Configuring Log4j from URL at path: /"+configPath); if (isXMLConfigFile) { try { DOMConfigurator.configure(log4jURL); CONFIGURED = Boolean.TRUE; } //catch (javax.xml.parsers.FactoryConfigurationError fce) {} catch (Exception e) { //report errors to server logs LogLog.error(e.getMessage()); } } else { Properties log4jProps = new Properties(); try { log4jProps.load(log4jURL.openStream()); PropertyConfigurator.configure(log4jProps); CONFIGURED = Boolean.TRUE; } //catch (IOException ioe) {} catch (Exception e) { //report errors to server logs LogLog.error(e.getMessage()); } } } //end CONFIGURED check } //end syncronized block } else { // The given configPath does not exist. So, let's just let Log4j look for the // default files (log4j.properties or log4j.xml) on its own. displayConfigNotFoundMessage(); } //end log4jURL null check } //end contextPath null check } else { LogLog.error("Zero length Log4j config file path given."); displayConfigNotFoundMessage(); } //end configPath length check } else { LogLog.error("Missing log4j-config servlet parameter missing."); displayConfigNotFoundMessage(); } //end configPath null check } //end CONFIGURED check } //end init() method private void displayConfigNotFoundMessage() { LogLog.warn("No Log4j configuration file found at given path. Falling back to Log4j auto-configuration."); } private void setFileAppenderSystemProperty(String logHome, ServletConfig config) { File logHomeDir = new File(logHome); if (logHomeDir.exists() || logHomeDir.mkdirs()) { ServletContext context = config.getServletContext(); String tempdir = ""+context.getAttribute("javax.servlet.context.tempdir"); int lastSlash = tempdir.lastIndexOf(File.separator); if ((tempdir.length()-1) > lastSlash) { String logHomePropertyName = tempdir.substring(lastSlash+1) + ".log.home"; context.log("Setting system property [ " + logHomePropertyName + " ] to [ " + logHome + " ]"); System.setProperty(logHomePropertyName, logHome); } } } private void initLoggerRepository() { ContextClassLoaderSelector.doIdempotentInitialization(); } /** * Throws a ServletException. */ public void doGet(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { throw new ServletException("Servlet only used for Log4j initialization"); } public void doPost(HttpServletRequest req, HttpServletResponse res) throws ServletException, IOException { doGet(req, res); } }
--------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]