This is an automated email from the ASF dual-hosted git repository.
rmiddleton pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/logging-chainsaw.git
The following commit(s) were added to refs/heads/master by this push:
new 0cd2b89 Copied the relevant log4j1 extras into chainsaw (#10)
0cd2b89 is described below
commit 0cd2b8996ed332c5e451c5e98500e706e1665ac4
Author: Robert Middleton <[email protected]>
AuthorDate: Fri Dec 31 14:50:56 2021 -0500
Copied the relevant log4j1 extras into chainsaw (#10)
---
pom.xml | 5 -
.../java/org/apache/log4j/rule/AbstractRule.java | 80 +++++
src/main/java/org/apache/log4j/rule/AndRule.java | 127 +++++++
src/main/java/org/apache/log4j/rule/ColorRule.java | 127 +++++++
.../java/org/apache/log4j/rule/EqualsRule.java | 119 +++++++
.../java/org/apache/log4j/rule/ExistsRule.java | 105 ++++++
.../java/org/apache/log4j/rule/ExpressionRule.java | 181 ++++++++++
.../java/org/apache/log4j/rule/InFixToPostFix.java | 379 +++++++++++++++++++++
.../java/org/apache/log4j/rule/InequalityRule.java | 162 +++++++++
.../org/apache/log4j/rule/LevelEqualsRule.java | 149 ++++++++
.../org/apache/log4j/rule/LevelInequalityRule.java | 270 +++++++++++++++
src/main/java/org/apache/log4j/rule/LikeRule.java | 164 +++++++++
.../java/org/apache/log4j/rule/NotEqualsRule.java | 121 +++++++
.../org/apache/log4j/rule/NotLevelEqualsRule.java | 149 ++++++++
src/main/java/org/apache/log4j/rule/NotRule.java | 104 ++++++
src/main/java/org/apache/log4j/rule/OrRule.java | 127 +++++++
.../apache/log4j/rule/PartialTextMatchRule.java | 112 ++++++
src/main/java/org/apache/log4j/rule/Rule.java | 64 ++++
.../java/org/apache/log4j/rule/RuleFactory.java | 187 ++++++++++
.../org/apache/log4j/rule/TimestampEqualsRule.java | 121 +++++++
.../apache/log4j/rule/TimestampInequalityRule.java | 142 ++++++++
.../log4j/spi/LoggingEventFieldResolver.java | 275 +++++++++++++++
22 files changed, 3265 insertions(+), 5 deletions(-)
diff --git a/pom.xml b/pom.xml
index c7b5fbf..93ff082 100644
--- a/pom.xml
+++ b/pom.xml
@@ -365,11 +365,6 @@
<dependencies>
<dependency>
<groupId>log4j</groupId>
- <artifactId>apache-log4j-extras</artifactId>
- <version>1.1</version>
- </dependency>
- <dependency>
- <groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.16</version>
</dependency>
diff --git a/src/main/java/org/apache/log4j/rule/AbstractRule.java
b/src/main/java/org/apache/log4j/rule/AbstractRule.java
new file mode 100644
index 0000000..f74bf50
--- /dev/null
+++ b/src/main/java/org/apache/log4j/rule/AbstractRule.java
@@ -0,0 +1,80 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.rule;
+
+import java.beans.PropertyChangeEvent;
+import java.beans.PropertyChangeListener;
+import java.beans.PropertyChangeSupport;
+import java.io.Serializable;
+
+
+/**
+ * An abstract Rule class that provides the PropertyChange support plumbing.
+ *
+ * @author Paul Smith ([email protected])
+ * @author Scott Deboy ([email protected])
+ */
+public abstract class AbstractRule implements Rule, Serializable {
+ /**
+ * Serialization id.
+ */
+ static final long serialVersionUID = -2844288145563025172L;
+
+ /**
+ * PropertySupport instance.
+ */
+ private PropertyChangeSupport propertySupport =
+ new PropertyChangeSupport(this);
+
+ /**
+ * Add property change listener.
+ * @param l listener.
+ */
+ public void addPropertyChangeListener(final PropertyChangeListener l) {
+ propertySupport.addPropertyChangeListener(l);
+ }
+
+ /**
+ * Remove property change listener.
+ * @param l listener.
+ */
+ public void removePropertyChangeListener(final PropertyChangeListener l) {
+ propertySupport.removePropertyChangeListener(l);
+ }
+
+ /**
+ * Send property change notification to attached listeners.
+ * @param propertyName property name.
+ * @param oldVal old value.
+ * @param newVal new value.
+ */
+ protected void firePropertyChange(
+ final String propertyName,
+ final Object oldVal,
+ final Object newVal) {
+ propertySupport.firePropertyChange(propertyName, oldVal, newVal);
+ }
+
+ /**
+ * Send property change notification to attached listeners.
+ * @param evt property change event.
+ */
+ public void firePropertyChange(final PropertyChangeEvent evt) {
+ propertySupport.firePropertyChange(evt);
+ }
+}
diff --git a/src/main/java/org/apache/log4j/rule/AndRule.java
b/src/main/java/org/apache/log4j/rule/AndRule.java
new file mode 100644
index 0000000..2334cb6
--- /dev/null
+++ b/src/main/java/org/apache/log4j/rule/AndRule.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.rule;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+
+
+/**
+ * A Rule class implementing a logical 'and'.
+ *
+ * @author Scott Deboy ([email protected])
+ */
+public class AndRule extends AbstractRule {
+ /**
+ * First rule.
+ */
+ private final Rule firstRule;
+ /**
+ * Second rule.
+ */
+ private final Rule secondRule;
+ /**
+ * Serialization id.
+ */
+ static final long serialVersionUID = -8233444426923854651L;
+
+ /**
+ * Create new instance.
+ * @param first first rule.
+ * @param second second rule.
+ */
+ private AndRule(final Rule first, final Rule second) {
+ super();
+ this.firstRule = first;
+ this.secondRule = second;
+ }
+
+ /**
+ * Create rule from top two elements of stack.
+ * @param stack stack of rules.
+ * @return Rule that evaluates true only if both rules are true.
+ */
+ public static Rule getRule(final Stack stack) {
+ if (stack.size() < 2) {
+ throw new IllegalArgumentException(
+ "Invalid AND rule - expected two rules but received "
+ + stack.size());
+ }
+ Object o2 = stack.pop();
+ Object o1 = stack.pop();
+ if ((o2 instanceof Rule) && (o1 instanceof Rule)) {
+ Rule p2 = (Rule) o2;
+ Rule p1 = (Rule) o1;
+ return new AndRule(p1, p2);
+ }
+ throw new IllegalArgumentException("Invalid AND rule: " + o2 + "..." + o1);
+ }
+
+ /**
+ * Get rule.
+ * @param firstParam first rule.
+ * @param secondParam second rule.
+ * @return Rule that evaluates true only if both rules are true.
+ */
+ public static Rule getRule(final Rule firstParam, final Rule secondParam) {
+ return new AndRule(firstParam, secondParam);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean evaluate(final LoggingEvent event, Map matches) {
+ if (matches == null) {
+ return firstRule.evaluate(event, null) &&
secondRule.evaluate(event, null);
+ }
+ Map tempMatches1 = new HashMap();
+ Map tempMatches2 = new HashMap();
+ boolean result = firstRule.evaluate(event, tempMatches1) &&
secondRule.evaluate(event, tempMatches2);
+ if (result) {
+ for (Iterator iter =
tempMatches1.entrySet().iterator();iter.hasNext();) {
+ Map.Entry entry = (Map.Entry)iter.next();
+ Object key = entry.getKey();
+ Set value = (Set)entry.getValue();
+ Set mainSet = (Set) matches.get(key);
+ if (mainSet == null) {
+ mainSet = new HashSet();
+ matches.put(key, mainSet);
+ }
+ mainSet.addAll(value);
+ }
+ for (Iterator iter =
tempMatches2.entrySet().iterator();iter.hasNext();) {
+ Map.Entry entry = (Map.Entry)iter.next();
+ Object key = entry.getKey();
+ Set value = (Set)entry.getValue();
+ Set mainSet = (Set) matches.get(key);
+ if (mainSet == null) {
+ mainSet = new HashSet();
+ matches.put(key, mainSet);
+ }
+ mainSet.addAll(value);
+ }
+ }
+ return result;
+ }
+}
diff --git a/src/main/java/org/apache/log4j/rule/ColorRule.java
b/src/main/java/org/apache/log4j/rule/ColorRule.java
new file mode 100644
index 0000000..c3f7465
--- /dev/null
+++ b/src/main/java/org/apache/log4j/rule/ColorRule.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.rule;
+
+import java.awt.Color;
+import java.io.Serializable;
+import java.util.Map;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+
+/**
+ * A Rule class which also holds a color.
+ *
+ * @author Scott Deboy ([email protected])
+ */
+public class ColorRule extends AbstractRule implements Serializable {
+ /**
+ * Serialization id.
+ */
+ static final long serialVersionUID = -794434783372847773L;
+
+ /**
+ * Wrapped rule.
+ */
+ private final Rule rule;
+ /**
+ * Foreground color.
+ */
+ private final Color foregroundColor;
+ /**
+ * Background color.
+ */
+ private final Color backgroundColor;
+ /**
+ * Expression.
+ */
+ private final String expression;
+
+ /**
+ * Create new instance.
+ * @param expression expression.
+ * @param rule rule.
+ * @param backgroundColor background color.
+ * @param foregroundColor foreground color.
+ */
+ public ColorRule(final String expression,
+ final Rule rule,
+ final Color backgroundColor,
+ final Color foregroundColor) {
+ super();
+ this.expression = expression;
+ this.rule = rule;
+ this.backgroundColor = backgroundColor;
+ this.foregroundColor = foregroundColor;
+ }
+
+ /**
+ * Get rule.
+ * @return underlying rule.
+ */
+ public Rule getRule() {
+ return rule;
+ }
+
+ /**
+ * Get foreground color.
+ * @return foreground color.
+ */
+ public Color getForegroundColor() {
+ return foregroundColor;
+ }
+
+ /**
+ * Get background color.
+ * @return background color.
+ */
+ public Color getBackgroundColor() {
+ return backgroundColor;
+ }
+
+ /**
+ * Get expression.
+ * @return expression.
+ */
+ public String getExpression() {
+ return expression;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean evaluate(final LoggingEvent event, Map matches) {
+ //no need for color rules to build matches
+ return (rule != null && rule.evaluate(event, null));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String toString() {
+ StringBuffer buf = new StringBuffer("color rule - expression: ");
+ buf.append(expression);
+ buf.append(", rule: ");
+ buf.append(rule);
+ buf.append(" bg: ");
+ buf.append(backgroundColor);
+ buf.append(" fg: ");
+ buf.append(foregroundColor);
+ return buf.toString();
+ }
+}
diff --git a/src/main/java/org/apache/log4j/rule/EqualsRule.java
b/src/main/java/org/apache/log4j/rule/EqualsRule.java
new file mode 100644
index 0000000..298768d
--- /dev/null
+++ b/src/main/java/org/apache/log4j/rule/EqualsRule.java
@@ -0,0 +1,119 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.rule;
+
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.LoggingEventFieldResolver;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+
+
+/**
+ * A Rule class which returns the result of
+ * performing equals against two strings.
+ *
+ * @author Scott Deboy ([email protected])
+ */
+public class EqualsRule extends AbstractRule {
+ /**
+ * Serialization ID.
+ */
+ static final long serialVersionUID = 1712851553477517245L;
+ /**
+ * Resolver.
+ */
+ private static final LoggingEventFieldResolver RESOLVER =
+ LoggingEventFieldResolver.getInstance();
+ /**
+ * Value.
+ */
+ private final String value;
+ /**
+ * Field.
+ */
+ private final String field;
+
+ /**
+ * Create new instance.
+ * @param field field
+ * @param value value
+ */
+ private EqualsRule(final String field, final String value) {
+ super();
+ if (!RESOLVER.isField(field)) {
+ throw new IllegalArgumentException(
+ "Invalid EQUALS rule - " + field + " is not a supported field");
+ }
+
+ this.field = field;
+ this.value = value;
+ }
+
+ /**
+ * Create new instance from top two elements of stack.
+ * @param stack stack
+ * @return new instance
+ */
+ public static Rule getRule(final Stack stack) {
+ if (stack.size() < 2) {
+ throw new IllegalArgumentException(
+ "Invalid EQUALS rule - expected two parameters but received "
+ + stack.size());
+ }
+
+ String p2 = stack.pop().toString();
+ String p1 = stack.pop().toString();
+
+ return getRule(p1, p2);
+ }
+
+ /**
+ * Create new instance.
+ * @param p1 field, special treatment for level and timestamp.
+ * @param p2 value
+ * @return new instance
+ */
+ public static Rule getRule(final String p1, final String p2) {
+ if (p1.equalsIgnoreCase(LoggingEventFieldResolver.LEVEL_FIELD)) {
+ return LevelEqualsRule.getRule(p2);
+ } else if (p1.equalsIgnoreCase(LoggingEventFieldResolver.TIMESTAMP_FIELD))
{
+ return TimestampEqualsRule.getRule(p2);
+ } else {
+ return new EqualsRule(p1, p2);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public boolean evaluate(final LoggingEvent event, Map matches) {
+ Object p2 = RESOLVER.getValue(field, event);
+
+ boolean result = (p2 != null) && p2.toString().equals(value);
+ if (result && matches != null) {
+ Set entries = (Set) matches.get(field.toUpperCase());
+ if (entries == null) {
+ entries = new HashSet();
+ matches.put(field.toUpperCase(), entries);
+ }
+ entries.add(value);
+ }
+ return result;
+ }
+}
diff --git a/src/main/java/org/apache/log4j/rule/ExistsRule.java
b/src/main/java/org/apache/log4j/rule/ExistsRule.java
new file mode 100644
index 0000000..5dbc6c5
--- /dev/null
+++ b/src/main/java/org/apache/log4j/rule/ExistsRule.java
@@ -0,0 +1,105 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.rule;
+
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.LoggingEventFieldResolver;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+
+
+/**
+ * A Rule class implementing a not null (and not empty string) check.
+ *
+ * @author Scott Deboy ([email protected])
+ */
+public class ExistsRule extends AbstractRule {
+ /**
+ * Serialization id.
+ */
+ static final long serialVersionUID = -5386265224649967464L;
+ /**
+ * field resolver.
+ */
+ private static final LoggingEventFieldResolver RESOLVER =
+ LoggingEventFieldResolver.getInstance();
+ /**
+ * field name.
+ */
+ private final String field;
+
+ /**
+ * Create new instance.
+ * @param fld field name.
+ */
+ private ExistsRule(final String fld) {
+ super();
+ if (!RESOLVER.isField(fld)) {
+ throw new IllegalArgumentException(
+ "Invalid EXISTS rule - " + fld + " is not a supported field");
+ }
+
+ this.field = fld;
+ }
+
+ /**
+ * Get an instance of ExistsRule.
+ * @param field field.
+ * @return instance of ExistsRule.
+ */
+ public static Rule getRule(final String field) {
+ return new ExistsRule(field);
+ }
+
+ /**
+ * Create an instance of ExistsRule using the
+ * top name on the stack.
+ * @param stack stack
+ * @return instance of ExistsRule.
+ */
+ public static Rule getRule(final Stack stack) {
+ if (stack.size() < 1) {
+ throw new IllegalArgumentException(
+ "Invalid EXISTS rule - expected one parameter but received "
+ + stack.size());
+ }
+
+ return new ExistsRule(stack.pop().toString());
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean evaluate(final LoggingEvent event, Map matches) {
+ Object p2 = RESOLVER.getValue(field, event);
+
+ boolean result = !((p2 == null) || (p2.toString().equals("")));
+ if (result && matches != null) {
+ Set entries = (Set) matches.get(field.toUpperCase());
+ if (entries == null) {
+ entries = new HashSet();
+ matches.put(field.toUpperCase(), entries);
+ }
+ entries.add(p2);
+ }
+ return result;
+ }
+}
diff --git a/src/main/java/org/apache/log4j/rule/ExpressionRule.java
b/src/main/java/org/apache/log4j/rule/ExpressionRule.java
new file mode 100644
index 0000000..875e3b9
--- /dev/null
+++ b/src/main/java/org/apache/log4j/rule/ExpressionRule.java
@@ -0,0 +1,181 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.rule;
+
+import java.util.Map;
+import java.util.Stack;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+
+/**
+ * A Rule class supporting both infix and postfix expressions,
+ * accepting any rule which
+ * is supported by the <code>RuleFactory</code>.
+ *
+ * NOTE: parsing is supported through the use of
+ * <code>StringTokenizer</code>, which
+ * implies two limitations:
+ * 1: all tokens in the expression must be separated by spaces,
+ * including parenthesis
+ * 2: operands which contain spaces MUST be wrapped in single quotes.
+ * For example, the expression:
+ * msg == 'some msg'
+ * is a valid expression.
+ * 3: To group expressions, use parentheses.
+ * For example, the expression:
+ * level >= INFO || ( msg == 'some msg' || logger == 'test' )
+ * is a valid expression.
+ * See org.apache.log4j.rule.InFixToPostFix for a
+ * description of supported operators.
+ * See org.apache.log4j.spi.LoggingEventFieldResolver for field keywords.
+ *
+ * @author Scott Deboy ([email protected])
+ */
+public class ExpressionRule extends AbstractRule {
+ /**
+ * Serialization ID.
+ */
+ static final long serialVersionUID = 5809121703146893729L;
+ /**
+ * Converter.
+ */
+ private static final InFixToPostFix CONVERTER = new InFixToPostFix();
+ /**
+ * Compiler.
+ */
+ private static final PostFixExpressionCompiler COMPILER =
+ new PostFixExpressionCompiler();
+ /**
+ * Rule.
+ */
+ private final Rule rule;
+
+ /**
+ * Create new instance.
+ * @param r rule
+ */
+ private ExpressionRule(final Rule r) {
+ super();
+ this.rule = r;
+ }
+
+ /**
+ * Get rule.
+ * @param expression expression.
+ * @return rule.
+ */
+ public static Rule getRule(final String expression) {
+ return getRule(expression, false);
+ }
+
+ /**
+ * Get rule.
+ * @param expression expression.
+ * @param isPostFix If post-fix.
+ * @return rule
+ */
+ public static Rule getRule(final String expression,
+ final boolean isPostFix) {
+ String postFix = expression;
+ if (!isPostFix) {
+ postFix = CONVERTER.convert(expression);
+ }
+
+ return new ExpressionRule(COMPILER.compileExpression(postFix));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean evaluate(final LoggingEvent event, Map matches) {
+ return rule.evaluate(event, matches);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String toString() {
+ return rule.toString();
+ }
+
+ /**
+ * Evaluate a boolean postfix expression.
+ *
+ */
+ static final class PostFixExpressionCompiler {
+ /**
+ * Compile expression.
+ * @param expression expression.
+ * @return rule.
+ */
+ public Rule compileExpression(final String expression) {
+ RuleFactory factory = RuleFactory.getInstance();
+
+ Stack stack = new Stack();
+ InFixToPostFix.CustomTokenizer tokenizer = new
InFixToPostFix.CustomTokenizer(expression);
+
+ while (tokenizer.hasMoreTokens()) {
+ //examine each token
+ String token = tokenizer.nextToken();
+ if (token.startsWith("'") || token.startsWith("\"")) {
+ String quoteChar = token.substring(0, 1);
+ token = token.substring(1);
+ while (!token.endsWith(quoteChar) &&
tokenizer.hasMoreTokens()) {
+ token = token + " " + tokenizer.nextToken();
+ }
+ if (token.length() > 0) {
+ token = token.substring(0, token.length() - 1);
+ }
+ } else {
+ //if a symbol is found, pop 2 off the stack,
+ // evaluate and push the result
+ if (factory.isRule(token)) {
+ Rule r = factory.getRule(token, stack);
+ stack.push(r);
+ //null out the token so we don't try to push it below
+ token = null;
+ }
+ }
+ //variables or constants are pushed onto the stack
+ if (token != null && token.length() > 0) {
+ stack.push(token);
+ }
+ }
+
+ if ((stack.size() == 1) && (!(stack.peek() instanceof Rule))) {
+ //while this may be an attempt at creating an expression,
+ //for ease of use, convert this single entry to a partial-text
+ //match on the MSG field
+ Object o = stack.pop();
+ stack.push("MSG");
+ stack.push(o);
+ return factory.getRule("~=", stack);
+ }
+
+ //stack should contain a single rule if the expression is valid
+ if ((stack.size() != 1) || (!(stack.peek() instanceof Rule))) {
+ throw new IllegalArgumentException("invalid expression: " +
expression);
+ } else {
+ return (Rule) stack.pop();
+ }
+ }
+ }
+}
+
+
diff --git a/src/main/java/org/apache/log4j/rule/InFixToPostFix.java
b/src/main/java/org/apache/log4j/rule/InFixToPostFix.java
new file mode 100644
index 0000000..765574a
--- /dev/null
+++ b/src/main/java/org/apache/log4j/rule/InFixToPostFix.java
@@ -0,0 +1,379 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.rule;
+
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.Stack;
+import java.util.Vector;
+
+import org.apache.log4j.spi.LoggingEventFieldResolver;
+
+/**
+ * A helper class which converts infix expressions to postfix expressions
+ * Currently grouping is supported, as well as all of the
+ * Rules supported by <code>RuleFactory</code>
+ *
+ * Supports grouping via parens, mult-word operands using single or double
quotes,
+ * and these operators:
+ *
+ * ! NOT operator
+ * != NOT EQUALS operator
+ * == EQUALS operator
+ * ~= CASE-INSENSITIVE equals operator
+ * || OR operator
+ * && AND operator
+ * like REGEXP operator
+ * exists NOT NULL operator
+ * < LESS THAN operator
+ * > GREATER THAN operator
+ * <= LESS THAN EQUALS operator
+ * >= GREATER THAN EQUALS operator
+ *
+ * @author Scott Deboy ([email protected])
+ */
+
+public class InFixToPostFix {
+ /**
+ * Precedence map.
+ */
+ private static final Map precedenceMap = new HashMap();
+ /**
+ * Operators.
+ */
+ private static final List operators = new Vector();
+
+
+ static {
+ //order multi-char operators before single-char operators (will use this
order during parsing)
+ operators.add("<=");
+ operators.add(">=");
+ operators.add("!=");
+ operators.add("==");
+ operators.add("~=");
+ operators.add("||");
+ operators.add("&&");
+ operators.add("like");
+ operators.add("exists");
+ operators.add("!");
+ operators.add("<");
+ operators.add(">");
+
+ //boolean precedence
+ precedenceMap.put("<", new Integer(3));
+ precedenceMap.put(">", new Integer(3));
+ precedenceMap.put("<=", new Integer(3));
+ precedenceMap.put(">=", new Integer(3));
+
+ precedenceMap.put("!", new Integer(3));
+ precedenceMap.put("!=", new Integer(3));
+ precedenceMap.put("==", new Integer(3));
+ precedenceMap.put("~=", new Integer(3));
+ precedenceMap.put("like", new Integer(3));
+ precedenceMap.put("exists", new Integer(3));
+
+ precedenceMap.put("||", new Integer(2));
+ precedenceMap.put("&&", new Integer(2));
+ }
+ /**
+ * Convert in-fix expression to post-fix.
+ * @param expression in-fix expression.
+ * @return post-fix expression.
+ */
+ public String convert(final String expression) {
+ return infixToPostFix(new CustomTokenizer(expression));
+ }
+
+ /**
+ * Evaluates whether symbol is operand.
+ * @param s symbol.
+ * @return true if operand.
+ */
+ public static boolean isOperand(final String s) {
+ String symbol = s.toLowerCase(Locale.ENGLISH);
+ return (!operators.contains(symbol));
+ }
+
+ /**
+ * Determines whether one symbol precedes another.
+ * @param s1 symbol 1
+ * @param s2 symbol 2
+ * @return true if symbol 1 precedes symbol 2
+ */
+ boolean precedes(final String s1, final String s2) {
+ String symbol1 = s1.toLowerCase(Locale.ENGLISH);
+ String symbol2 = s2.toLowerCase(Locale.ENGLISH);
+
+ if (!precedenceMap.keySet().contains(symbol1)) {
+ return false;
+ }
+
+ if (!precedenceMap.keySet().contains(symbol2)) {
+ return false;
+ }
+
+ int index1 = ((Integer) precedenceMap.get(symbol1)).intValue();
+ int index2 = ((Integer) precedenceMap.get(symbol2)).intValue();
+
+ boolean precedesResult = (index1 < index2);
+
+ return precedesResult;
+ }
+
+ /**
+ * convert in-fix expression to post-fix.
+ * @param tokenizer tokenizer.
+ * @return post-fix expression.
+ */
+ String infixToPostFix(final CustomTokenizer tokenizer) {
+ final String space = " ";
+ StringBuffer postfix = new StringBuffer();
+
+ Stack stack = new Stack();
+
+ while (tokenizer.hasMoreTokens()) {
+ String token = tokenizer.nextToken();
+
+ boolean inText = (token.startsWith("'") && (!token.endsWith("'"))) ||
(token.startsWith("\"") && (!token.endsWith("\"")));
+ String quoteChar = token.substring(0, 1);
+ if (inText) {
+ while (inText && tokenizer.hasMoreTokens()) {
+ token = token + " " + tokenizer.nextToken();
+ inText = !(token.endsWith(quoteChar));
+ }
+ }
+
+ if ("(".equals(token)) {
+ //recurse
+ postfix.append(infixToPostFix(tokenizer));
+ postfix.append(space);
+ } else if (")".equals(token)) {
+ //exit recursion level
+ while (stack.size() > 0) {
+ postfix.append(stack.pop().toString());
+ postfix.append(space);
+ }
+
+ return postfix.toString();
+ } else if (isOperand(token)) {
+ postfix.append(token);
+ postfix.append(space);
+ } else {
+ //operator..
+ //peek the stack..if the top element has a lower precedence than token
+ //(peeked + has lower precedence than token *),
+ // push token onto the stack
+ //otherwise, pop top element off stack and add to postfix string
+ //in a loop until lower precedence or empty..then push token
+ if (stack.size() > 0) {
+
+ String peek = stack.peek().toString();
+
+ if (precedes(peek, token)) {
+ stack.push(token);
+ } else {
+ boolean bypass = false;
+
+ do {
+ if (
+ (stack.size() > 0)
+ && !precedes(stack.peek().toString(), token)) {
+ postfix.append(stack.pop().toString());
+ postfix.append(space);
+ } else {
+ bypass = true;
+ }
+ } while (!bypass);
+
+ stack.push(token);
+ }
+ } else {
+ stack.push(token);
+ }
+ }
+ }
+
+ while (stack.size() > 0) {
+ postfix.append(stack.pop().toString());
+ postfix.append(space);
+ }
+
+ return postfix.toString();
+ }
+
+ public static class CustomTokenizer {
+ private LinkedList linkedList = new LinkedList();
+
+ public CustomTokenizer(String input) {
+ parseInput(input, linkedList);
+ }
+
+ public void parseInput(String input, LinkedList linkedList) {
+ /*
+ Operators:
+ !
+ !=
+ ==
+ ~=
+ ||
+ &&
+ like
+ exists
+ <
+ <=
+ >
+ >=
+ */
+ //for operators, handle multi-char matches before single-char matches
+ //order does not matter for keywords
+ List keywords = LoggingEventFieldResolver.KEYWORD_LIST;
+ //remove PROP. from the keyword list...it is a keyword in that list, but
is handled separately here from the other keywords since its name is not fixed
+ keywords.remove("PROP.");
+ int pos = 0;
+ while (pos < input.length()) {
+ if (nextValueIs(input, pos, "'") || nextValueIs(input, pos, "\"")) {
+ pos = handleQuotedString(input, pos, linkedList);
+ }
+ if (nextValueIs(input, pos, "PROP.")) {
+ pos = handleProperty(input, pos, linkedList);
+ }
+ boolean operatorFound = false;
+ for (Iterator iter = operators.iterator();iter.hasNext();) {
+ String operator = (String)iter.next();
+ if (nextValueIs(input, pos, operator)) {
+ operatorFound = true;
+ pos = handle(pos, linkedList, operator);
+ }
+ }
+ boolean keywordFound = false;
+ for (Iterator iter = keywords.iterator();iter.hasNext();) {
+ String keyword = (String)iter.next();
+ if (nextValueIs(input, pos, keyword)) {
+ keywordFound = true;
+ pos = handle(pos, linkedList, keyword);
+ }
+ }
+ if (operatorFound || keywordFound) {
+ continue;
+ }
+ if (nextValueIs(input, pos, ")")) {
+ pos = handle(pos, linkedList, ")");
+ } else if (nextValueIs(input, pos, "(")) {
+ pos = handle(pos, linkedList, "(");
+ } else if (nextValueIs(input, pos, " ")) {
+ pos++;
+ } else {
+ pos = handleText(input, pos, linkedList);
+ }
+ }
+ }
+
+ private boolean nextValueIs(String input, int pos, String value) {
+ return (input.length() >= (pos + value.length())) &&
(input.substring(pos, pos + value.length()).equalsIgnoreCase(value));
+ }
+
+ private int handle(int pos, LinkedList linkedList, String value) {
+ linkedList.add(value);
+ return pos + value.length();
+ }
+
+ private int handleQuotedString(String input, int pos, LinkedList
linkedList) {
+ String quoteChar = input.substring(pos, pos + 1);
+ int nextSingleQuotePos = input.indexOf(quoteChar, pos + 1);
+ if (nextSingleQuotePos < 0) {
+ throw new IllegalArgumentException("Missing an end quote");
+ }
+ String result = input.substring(pos, nextSingleQuotePos + 1);
+ linkedList.add(result);
+ return nextSingleQuotePos + 1;
+ }
+
+ private int handleText(String input, int pos, LinkedList linkedList) {
+ StringBuffer text = new StringBuffer("");
+ int newPos = pos;
+ while (newPos < input.length()) {
+ if (nextValueIs(input, newPos, " ")) {
+ linkedList.add(text);
+ return newPos;
+ }
+ if (nextValueIs(input, newPos, "(")) {
+ linkedList.add(text);
+ return newPos;
+ }
+ if (nextValueIs(input, newPos, ")")) {
+ linkedList.add(text);
+ return newPos;
+ }
+ for (Iterator iter = operators.iterator();iter.hasNext();) {
+ String operator = (String)iter.next();
+ if (nextValueIs(input, newPos, operator)) {
+ linkedList.add(text);
+ return newPos;
+ }
+ }
+ text.append(input.substring(newPos, ++newPos));
+ }
+ //don't add empty text entry (may be at end)
+ if (!text.toString().trim().equals("")) {
+ linkedList.add(text);
+ }
+ return newPos;
+ }
+
+ private int handleProperty(String input, int pos, LinkedList linkedList) {
+ int propertyPos = pos + "PROP.".length();
+ StringBuffer propertyName = new StringBuffer("PROP.");
+ while (propertyPos < input.length()) {
+ if (nextValueIs(input, propertyPos, " ")) {
+ linkedList.add(propertyName);
+ return propertyPos;
+ }
+ if (nextValueIs(input, propertyPos, "(")) {
+ linkedList.add(propertyName);
+ return propertyPos;
+ }
+ if (nextValueIs(input, propertyPos, ")")) {
+ linkedList.add(propertyName);
+ return propertyPos;
+ }
+ for (Iterator iter = operators.iterator();iter.hasNext();) {
+ String operator = (String)iter.next();
+ if (nextValueIs(input, propertyPos, operator)) {
+ linkedList.add(propertyName);
+ return propertyPos;
+ }
+ }
+ propertyName.append(input.substring(propertyPos, ++propertyPos));
+ }
+ linkedList.add(propertyName);
+ return propertyPos;
+ }
+
+ public boolean hasMoreTokens() {
+ return linkedList.size() > 0;
+ }
+
+ public String nextToken() {
+ return linkedList.remove().toString();
+ }
+ }
+}
diff --git a/src/main/java/org/apache/log4j/rule/InequalityRule.java
b/src/main/java/org/apache/log4j/rule/InequalityRule.java
new file mode 100644
index 0000000..8395b75
--- /dev/null
+++ b/src/main/java/org/apache/log4j/rule/InequalityRule.java
@@ -0,0 +1,162 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.rule;
+
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.LoggingEventFieldResolver;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+
+
+/**
+ * A Rule class implementing inequality evaluation.
+ * expects to be able to convert two values to longs.
+ * If a specific inequality evaluation class has been provided
+ * for the event field, the appropriate rule is returned.
+ * (For example, if the expression is Level < DEBUG,
+ * a LevelInequalityRule is returned).
+ *
+ * @author Scott Deboy ([email protected])
+ */
+public class InequalityRule extends AbstractRule {
+ /**
+ * Serialization ID.
+ */
+ static final long serialVersionUID = -5592986598528885122L;
+ /**
+ * field RESOLVER.
+ */
+ private static final LoggingEventFieldResolver RESOLVER =
+ LoggingEventFieldResolver.getInstance();
+ /**
+ * Field name.
+ */
+ private final String field;
+ /**
+ * Comparison value.
+ */
+ private final String value;
+ /**
+ * Inequality symbol.
+ */
+ private final String inequalitySymbol;
+
+ /**
+ * Create new instance.
+ * @param inequalitySymbol inequality symbol.
+ * @param field field
+ * @param value comparison value.
+ */
+ private InequalityRule(
+ final String inequalitySymbol,
+ final String field,
+ final String value) {
+ super();
+ this.inequalitySymbol = inequalitySymbol;
+ if (!RESOLVER.isField(field)) {
+ throw new IllegalArgumentException("Invalid " + inequalitySymbol
+ + " rule - " + field + " is not a supported field");
+ }
+
+ this.field = field;
+ this.value = value;
+ }
+
+ /**
+ * Create new instance from top two elements on stack.
+ * @param inequalitySymbol inequality symbol.
+ * @param stack stack.
+ * @return rule.
+ */
+ public static Rule getRule(final String inequalitySymbol,
+ final Stack stack) {
+ if (stack.size() < 2) {
+ throw new IllegalArgumentException("Invalid " + inequalitySymbol
+ + " rule - expected two parameters but received "
+ + stack.size());
+ }
+
+ String p2 = stack.pop().toString();
+ String p1 = stack.pop().toString();
+ return getRule(inequalitySymbol, p1, p2);
+ }
+
+ /**
+ * Create new instance from top two elements on stack.
+ * @param inequalitySymbol inequality symbol.
+ * @param field field.
+ * @param value comparison value.
+ * @return rule.
+ */
+ public static Rule getRule(final String inequalitySymbol,
+ final String field,
+ final String value) {
+ if (field.equalsIgnoreCase(LoggingEventFieldResolver.LEVEL_FIELD)) {
+ //push the value back on the stack and
+ // allow the level-specific rule pop values
+ return LevelInequalityRule.getRule(inequalitySymbol, value);
+ } else if (
+ field.equalsIgnoreCase(LoggingEventFieldResolver.TIMESTAMP_FIELD))
{
+ return TimestampInequalityRule.getRule(inequalitySymbol, value);
+ } else {
+ return new InequalityRule(inequalitySymbol, field, value);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public boolean evaluate(final LoggingEvent event, Map matches) {
+ long first = 0;
+ try {
+ first = new Long(RESOLVER.getValue(field,
event).toString()).longValue();
+ } catch (NumberFormatException nfe) {
+ return false;
+ }
+
+ long second = 0;
+
+ try {
+ second = new Long(value).longValue();
+ } catch (NumberFormatException nfe) {
+ return false;
+ }
+
+ boolean result = false;
+
+ if ("<".equals(inequalitySymbol)) {
+ result = first < second;
+ } else if (">".equals(inequalitySymbol)) {
+ result = first > second;
+ } else if ("<=".equals(inequalitySymbol)) {
+ result = first <= second;
+ } else if (">=".equals(inequalitySymbol)) {
+ result = first >= second;
+ }
+ if (result && matches != null) {
+ Set entries = (Set) matches.get(field.toUpperCase());
+ if (entries == null) {
+ entries = new HashSet();
+ matches.put(field.toUpperCase(), entries);
+ }
+ entries.add(String.valueOf(first));
+ }
+ return result;
+ }
+}
diff --git a/src/main/java/org/apache/log4j/rule/LevelEqualsRule.java
b/src/main/java/org/apache/log4j/rule/LevelEqualsRule.java
new file mode 100644
index 0000000..68219bf
--- /dev/null
+++ b/src/main/java/org/apache/log4j/rule/LevelEqualsRule.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.rule;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.helpers.UtilLoggingLevel;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.LoggingEventFieldResolver;
+
+/**
+ * A Rule class implementing equals against two levels.
+ *
+ * @author Scott Deboy ([email protected])
+ */
+public class LevelEqualsRule extends AbstractRule {
+ /**
+ * Serialization ID.
+ */
+ static final long serialVersionUID = -3638386582899583994L;
+
+ /**
+ * Level.
+ */
+ private transient Level level;
+
+ /**
+ * List of levels.
+ */
+ private static List levelList = new LinkedList();
+
+ static {
+ populateLevels();
+ }
+
+ /**
+ * Create new instance.
+ * @param level level.
+ */
+ private LevelEqualsRule(final Level level) {
+ super();
+ this.level = level;
+ }
+
+ /**
+ * Populate list of levels.
+ */
+ private static void populateLevels() {
+ levelList = new LinkedList();
+
+ levelList.add(Level.FATAL.toString());
+ levelList.add(Level.ERROR.toString());
+ levelList.add(Level.WARN.toString());
+ levelList.add(Level.INFO.toString());
+ levelList.add(Level.DEBUG.toString());
+ Level trace = Level.toLevel(5000, null);
+ if (trace != null) {
+ levelList.add(trace.toString());
+ }
+ }
+
+ /**
+ * Create new rule.
+ * @param value name of level.
+ * @return instance of LevelEqualsRule.
+ */
+ public static Rule getRule(final String value) {
+ Level thisLevel;
+ if (levelList.contains(value.toUpperCase())) {
+ thisLevel = Level.toLevel(value.toUpperCase());
+ } else {
+ thisLevel = UtilLoggingLevel.toLevel(value.toUpperCase());
+ }
+
+ return new LevelEqualsRule(thisLevel);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean evaluate(final LoggingEvent event, Map matches) {
+ //both util.logging and log4j contain 'info' - use the int values
instead of equality
+ //info level set to the same value for both levels
+ Level eventLevel = event.getLevel();
+ boolean result = (level.toInt() == eventLevel.toInt());
+ if (result && matches != null) {
+ Set entries = (Set)
matches.get(LoggingEventFieldResolver.LEVEL_FIELD);
+ if (entries == null) {
+ entries = new HashSet();
+ matches.put(LoggingEventFieldResolver.LEVEL_FIELD, entries);
+ }
+ entries.add(eventLevel);
+ }
+ return result;
+ }
+
+ /**
+ * Deserialize the state of the object.
+ *
+ * @param in object input stream.
+ *
+ * @throws IOException if error in reading stream for deserialization.
+ */
+ private void readObject(final java.io.ObjectInputStream in)
+ throws IOException {
+ populateLevels();
+ boolean isUtilLogging = in.readBoolean();
+ int levelInt = in.readInt();
+ if (isUtilLogging) {
+ level = UtilLoggingLevel.toLevel(levelInt);
+ } else {
+ level = Level.toLevel(levelInt);
+ }
+ }
+
+ /**
+ * Serialize the state of the object.
+ *
+ * @param out object output stream.
+ *
+ * @throws IOException if error in writing stream during serialization.
+ */
+ private void writeObject(final java.io.ObjectOutputStream out)
+ throws IOException {
+ out.writeBoolean(level instanceof UtilLoggingLevel);
+ out.writeInt(level.toInt());
+ }
+}
diff --git a/src/main/java/org/apache/log4j/rule/LevelInequalityRule.java
b/src/main/java/org/apache/log4j/rule/LevelInequalityRule.java
new file mode 100644
index 0000000..1662a17
--- /dev/null
+++ b/src/main/java/org/apache/log4j/rule/LevelInequalityRule.java
@@ -0,0 +1,270 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.rule;
+
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.helpers.UtilLoggingLevel;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.LoggingEventFieldResolver;
+
+/**
+ * A Rule class implementing inequality evaluation for Levels (log4j and
+ * util.logging) using the toInt method.
+ *
+ * @author Scott Deboy ([email protected])
+ */
+public class LevelInequalityRule {
+ /**
+ * Level list.
+ */
+ private static List levelList;
+ /**
+ * List equivalents of java.util.logging levels.
+ */
+ private static List utilLoggingLevelList;
+
+
+ static {
+ populateLevels();
+ }
+
+ /**
+ * Create new instance.
+ */
+ private LevelInequalityRule() {
+ super();
+ }
+
+ /**
+ * Populate list of levels.
+ */
+ private static void populateLevels() {
+ levelList = new LinkedList();
+
+ levelList.add(Level.FATAL.toString());
+ levelList.add(Level.ERROR.toString());
+ levelList.add(Level.WARN.toString());
+ levelList.add(Level.INFO.toString());
+ levelList.add(Level.DEBUG.toString());
+ Level trace = Level.toLevel(5000, null);
+ if (trace != null) {
+ levelList.add(trace.toString());
+ }
+
+ utilLoggingLevelList = new LinkedList();
+
+ utilLoggingLevelList.add(UtilLoggingLevel.SEVERE.toString());
+ utilLoggingLevelList.add(UtilLoggingLevel.WARNING.toString());
+ utilLoggingLevelList.add(UtilLoggingLevel.INFO.toString());
+ utilLoggingLevelList.add(UtilLoggingLevel.CONFIG.toString());
+ utilLoggingLevelList.add(UtilLoggingLevel.FINE.toString());
+ utilLoggingLevelList.add(UtilLoggingLevel.FINER.toString());
+ utilLoggingLevelList.add(UtilLoggingLevel.FINEST.toString());
+
+ }
+
+ /**
+ * Create new rule.
+ * @param inequalitySymbol inequality symbol.
+ * @param value Symbolic name of comparison level.
+ * @return instance of AbstractRule.
+ */
+ public static Rule getRule(final String inequalitySymbol,
+ final String value) {
+
+ Level thisLevel;
+
+ //if valid util.logging levels are used against events
+ // with log4j levels, the
+ //DEBUG level is used and an illegalargumentexception won't be
generated
+
+ //an illegalargumentexception is only generated
+ // if the user types a level name
+ //that doesn't exist as either a log4j or util.logging level name
+ if (levelList.contains(value.toUpperCase())) {
+ thisLevel = Level.toLevel(value.toUpperCase());
+ } else if (utilLoggingLevelList.contains(value.toUpperCase())) {
+ thisLevel = UtilLoggingLevel.toLevel(value.toUpperCase());
+ } else {
+ throw new IllegalArgumentException(
+ "Invalid level inequality rule - " + value
+ + " is not a supported level");
+ }
+
+ if ("<".equals(inequalitySymbol)) {
+ return new LessThanRule(thisLevel);
+ }
+ if (">".equals(inequalitySymbol)) {
+ return new GreaterThanRule(thisLevel);
+ }
+ if ("<=".equals(inequalitySymbol)) {
+ return new LessThanEqualsRule(thisLevel);
+ }
+ if (">=".equals(inequalitySymbol)) {
+ return new GreaterThanEqualsRule(thisLevel);
+ }
+
+ return null;
+ }
+
+ /**
+ * Rule returning true if event level less than specified level.
+ */
+ private static final class LessThanRule extends AbstractRule {
+ /**
+ * Comparison level.
+ */
+ private final int newLevelInt;
+
+ /**
+ * Create new instance.
+ * @param level comparison level.
+ */
+ public LessThanRule(final Level level) {
+ super();
+ newLevelInt = level.toInt();
+ }
+
+ /** {@inheritDoc} */
+ public boolean evaluate(final LoggingEvent event, Map matches) {
+ Level eventLevel = event.getLevel();
+ boolean result = (eventLevel.toInt() < newLevelInt);
+ if (result && matches != null) {
+ Set entries = (Set)
matches.get(LoggingEventFieldResolver.LEVEL_FIELD);
+ if (entries == null) {
+ entries = new HashSet();
+ matches.put(LoggingEventFieldResolver.LEVEL_FIELD,
entries);
+ }
+ entries.add(eventLevel);
+ }
+ return result;
+ }
+ }
+
+ /**
+ * Rule returning true if event level greater than specified level.
+ */
+ private static final class GreaterThanRule extends AbstractRule {
+ /**
+ * Comparison level.
+ */
+ private final int newLevelInt;
+
+ /**
+ * Create new instance.
+ * @param level comparison level.
+ */
+ public GreaterThanRule(final Level level) {
+ super();
+ newLevelInt = level.toInt();
+ }
+
+ /** {@inheritDoc} */
+ public boolean evaluate(final LoggingEvent event, Map matches) {
+ Level eventLevel = event.getLevel();
+ boolean result = (eventLevel.toInt() > newLevelInt);
+ if (result && matches != null) {
+ Set entries = (Set)
matches.get(LoggingEventFieldResolver.LEVEL_FIELD);
+ if (entries == null) {
+ entries = new HashSet();
+ matches.put(LoggingEventFieldResolver.LEVEL_FIELD,
entries);
+ }
+ entries.add(eventLevel);
+ }
+ return result;
+ }
+ }
+
+ /**
+ * Rule returning true if event level greater than
+ * or equal to specified level.
+ */
+ private static final class GreaterThanEqualsRule extends AbstractRule {
+ /**
+ * Comparison level.
+ */
+ private final int newLevelInt;
+
+ /**
+ * Create new instance.
+ * @param level comparison level.
+ */
+ public GreaterThanEqualsRule(final Level level) {
+ super();
+ newLevelInt = level.toInt();
+ }
+
+ /** {@inheritDoc} */
+ public boolean evaluate(final LoggingEvent event, Map matches) {
+ Level eventLevel = event.getLevel();
+ boolean result = eventLevel.toInt() >= newLevelInt;
+ if (result && matches != null) {
+ Set entries = (Set)
matches.get(LoggingEventFieldResolver.LEVEL_FIELD);
+ if (entries == null) {
+ entries = new HashSet();
+ matches.put(LoggingEventFieldResolver.LEVEL_FIELD,
entries);
+ }
+ entries.add(eventLevel);
+ }
+ return result;
+ }
+ }
+
+ /**
+ * Rule returning true if event level less than or
+ * equal to specified level.
+ */
+
+ private static final class LessThanEqualsRule extends AbstractRule {
+ /**
+ * Comparison level.
+ */
+ private final int newLevelInt;
+
+ /**
+ * Create new instance.
+ * @param level comparison level.
+ */
+ public LessThanEqualsRule(final Level level) {
+ super();
+ newLevelInt = level.toInt();
+ }
+
+ /** {@inheritDoc} */
+ public boolean evaluate(final LoggingEvent event, Map matches) {
+ Level eventLevel = event.getLevel();
+ boolean result = eventLevel.toInt() <= newLevelInt;
+ if (result && matches != null) {
+ Set entries = (Set)
matches.get(LoggingEventFieldResolver.LEVEL_FIELD);
+ if (entries == null) {
+ entries = new HashSet();
+ matches.put(LoggingEventFieldResolver.LEVEL_FIELD,
entries);
+ }
+ entries.add(eventLevel);
+ }
+
+ return result;
+ }
+ }
+}
diff --git a/src/main/java/org/apache/log4j/rule/LikeRule.java
b/src/main/java/org/apache/log4j/rule/LikeRule.java
new file mode 100644
index 0000000..ffc7a69
--- /dev/null
+++ b/src/main/java/org/apache/log4j/rule/LikeRule.java
@@ -0,0 +1,164 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.rule;
+
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.LoggingEventFieldResolver;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.util.regex.PatternSyntaxException;
+
+/**
+ * A Rule class supporting java.util.regex regular expression syntax.
+ *
+ * @author Scott Deboy ([email protected])
+ */
+public class LikeRule extends AbstractRule {
+ /**
+ * Serialization ID.
+ */
+ static final long serialVersionUID = -3375458885595683156L;
+
+ /**
+ * Resolver.
+ */
+ private static final LoggingEventFieldResolver RESOLVER =
+ LoggingEventFieldResolver.getInstance();
+ /**
+ * Pattern.
+ */
+ private transient Pattern pattern;
+ /**
+ * Regular expression matcher.
+ */
+ private transient Matcher matcher = null;
+ /**
+ * Field.
+ */
+ private transient String field;
+
+ /**
+ * Create new instance.
+ * @param field field
+ * @param pattern pattern
+ */
+ private LikeRule(final String field, final Pattern pattern) {
+ super();
+ if (!RESOLVER.isField(field)) {
+ throw new IllegalArgumentException(
+ "Invalid LIKE rule - " + field + " is not a supported field");
+ }
+
+ this.field = field;
+ this.pattern = pattern;
+ }
+
+ /**
+ * Create new instance from top two elements of stack.
+ * @param stack stack
+ * @return new instance
+ */
+ public static Rule getRule(final Stack stack) {
+ if (stack.size() < 2) {
+ throw new IllegalArgumentException(
+ "Invalid LIKE rule - expected two parameters but received "
+ + stack.size());
+ }
+
+ String p2 = stack.pop().toString();
+ String p1 = stack.pop().toString();
+ return getRule(p1, p2);
+ }
+
+ /**
+ * Create new instance.
+ * @param field field
+ * @param pattern pattern
+ * @return new instance
+ */
+ public static Rule getRule(final String field, final String pattern) {
+ try {
+ return new LikeRule(field, Pattern.compile(pattern,
Pattern.CASE_INSENSITIVE));
+ } catch (PatternSyntaxException e) {
+ throw new IllegalArgumentException(
+ "Invalid LIKE rule - " + e.getMessage());
+ }
+ }
+
+ /** {@inheritDoc} */
+ public boolean evaluate(final LoggingEvent event, Map matches) {
+ //no need to figure out what part of the string matched, just set the
entire string as a match
+ Object input = RESOLVER.getValue(field, event);
+ if((input != null) && (pattern != null)) {
+ if (matcher == null) {
+ matcher = pattern.matcher(input.toString());
+ } else {
+ matcher.reset(input.toString());
+ }
+ boolean result = matcher.matches();
+ if (result && matches != null) {
+ Set entries = (Set) matches.get(field.toUpperCase());
+ if (entries == null) {
+ entries = new HashSet();
+ matches.put(field.toUpperCase(), entries);
+ }
+ entries.add(input);
+ }
+ return result;
+ }
+ return false;
+ }
+
+ /**
+ * Deserialize the state of the object.
+ *
+ * @param in object input stream
+ *
+ * @throws IOException if IOException during deserialization
+ * @throws ClassNotFoundException if class not found.
+ */
+ private void readObject(final java.io.ObjectInputStream in)
+ throws IOException, ClassNotFoundException {
+ try {
+ field = (String) in.readObject();
+ String patternString = (String) in.readObject();
+ pattern = Pattern.compile(patternString, Pattern.CASE_INSENSITIVE);
+ } catch (PatternSyntaxException e) {
+ throw new IOException("Invalid LIKE rule - " + e.getMessage());
+ }
+ }
+
+ /**
+ * Serialize the state of the object.
+ *
+ * @param out object output stream
+ *
+ * @throws IOException if IOException during serialization
+ */
+ private void writeObject(final java.io.ObjectOutputStream out)
+ throws IOException {
+ out.writeObject(field);
+ out.writeObject(pattern.pattern());
+ }
+}
diff --git a/src/main/java/org/apache/log4j/rule/NotEqualsRule.java
b/src/main/java/org/apache/log4j/rule/NotEqualsRule.java
new file mode 100644
index 0000000..aea2792
--- /dev/null
+++ b/src/main/java/org/apache/log4j/rule/NotEqualsRule.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.rule;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.LoggingEventFieldResolver;
+
+
+/**
+ * A Rule class implementing not equals against two strings.
+ *
+ * @author Scott Deboy ([email protected])
+ */
+public class NotEqualsRule extends AbstractRule {
+ /**
+ * Serialization ID.
+ */
+ static final long serialVersionUID = -1135478467213793211L;
+ /**
+ * Resolver.
+ */
+ private static final LoggingEventFieldResolver RESOLVER =
+ LoggingEventFieldResolver.getInstance();
+ /**
+ * Field.
+ */
+ private final String field;
+ /**
+ * Value.
+ */
+ private final String value;
+
+ /**
+ * Create new instance.
+ * @param field field
+ * @param value value
+ */
+ private NotEqualsRule(final String field, final String value) {
+ super();
+ if (!RESOLVER.isField(field)) {
+ throw new IllegalArgumentException(
+ "Invalid NOT EQUALS rule - " + field + " is not a supported field");
+ }
+
+ this.field = field;
+ this.value = value;
+ }
+
+ /**
+ * Get new instance.
+ * @param field field
+ * @param value value
+ * @return new instance.
+ */
+ public static Rule getRule(final String field, final String value) {
+ if (field.equalsIgnoreCase(LoggingEventFieldResolver.LEVEL_FIELD)) {
+ return NotLevelEqualsRule.getRule(value);
+ } else {
+ return new NotEqualsRule(field, value);
+ }
+ }
+
+ /**
+ * Get new instance from top two elements of stack.
+ * @param stack stack.
+ * @return new instance.
+ */
+ public static Rule getRule(final Stack stack) {
+ if (stack.size() < 2) {
+ throw new IllegalArgumentException(
+ "Invalid NOT EQUALS rule - expected two parameters but received "
+ + stack.size());
+ }
+
+ String p2 = stack.pop().toString();
+ String p1 = stack.pop().toString();
+
+ if (p1.equalsIgnoreCase(LoggingEventFieldResolver.LEVEL_FIELD)) {
+ return NotLevelEqualsRule.getRule(p2);
+ } else {
+ return new NotEqualsRule(p1, p2);
+ }
+ }
+
+ /** {@inheritDoc} */
+ public boolean evaluate(final LoggingEvent event, Map matches) {
+ Object p2 = RESOLVER.getValue(field, event);
+
+ boolean result = (p2 != null) && !(p2.toString().equals(value));
+ if (result && matches != null) {
+ //not equals - add the text that isn't equal (p2)
+ Set entries = (Set) matches.get(field.toUpperCase());
+ if (entries == null) {
+ entries = new HashSet();
+ matches.put(field.toUpperCase(), entries);
+ }
+ entries.add(value);
+ }
+ return result;
+ }
+}
diff --git a/src/main/java/org/apache/log4j/rule/NotLevelEqualsRule.java
b/src/main/java/org/apache/log4j/rule/NotLevelEqualsRule.java
new file mode 100644
index 0000000..336b767
--- /dev/null
+++ b/src/main/java/org/apache/log4j/rule/NotLevelEqualsRule.java
@@ -0,0 +1,149 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.rule;
+
+import java.io.IOException;
+import java.util.HashSet;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.log4j.Level;
+import org.apache.log4j.helpers.UtilLoggingLevel;
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.LoggingEventFieldResolver;
+
+/**
+ * A Rule class implementing not equals against two levels.
+ *
+ * @author Scott Deboy ([email protected])
+ */
+public class NotLevelEqualsRule extends AbstractRule {
+ /**
+ * Serialization ID.
+ */
+ static final long serialVersionUID = -3638386582899583994L;
+
+ /**
+ * Level.
+ */
+ private transient Level level;
+
+ /**
+ * List of levels.
+ */
+ private static List levelList = new LinkedList();
+
+ static {
+ populateLevels();
+ }
+
+ /**
+ * Create new instance.
+ * @param level level.
+ */
+ private NotLevelEqualsRule(final Level level) {
+ super();
+ this.level = level;
+ }
+
+ /**
+ * Populate list of levels.
+ */
+ private static void populateLevels() {
+ levelList = new LinkedList();
+
+ levelList.add(Level.FATAL.toString());
+ levelList.add(Level.ERROR.toString());
+ levelList.add(Level.WARN.toString());
+ levelList.add(Level.INFO.toString());
+ levelList.add(Level.DEBUG.toString());
+ Level trace = Level.toLevel(5000, null);
+ if (trace != null) {
+ levelList.add(trace.toString());
+ }
+ }
+
+ /**
+ * Create new rule.
+ * @param value name of level.
+ * @return instance of NotLevelEqualsRule.
+ */
+ public static Rule getRule(final String value) {
+ Level thisLevel;
+ if (levelList.contains(value.toUpperCase())) {
+ thisLevel = Level.toLevel(value.toUpperCase());
+ } else {
+ thisLevel = UtilLoggingLevel.toLevel(value.toUpperCase());
+ }
+
+ return new NotLevelEqualsRule(thisLevel);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean evaluate(final LoggingEvent event, Map matches) {
+ //both util.logging and log4j contain 'info' - use the int values
instead of equality
+ //info level set to the same value for both levels
+ Level eventLevel = event.getLevel();
+ boolean result = level.toInt() != eventLevel.toInt();
+ if (result && matches != null) {
+ Set entries = (Set)
matches.get(LoggingEventFieldResolver.LEVEL_FIELD);
+ if (entries == null) {
+ entries = new HashSet();
+ matches.put(LoggingEventFieldResolver.LEVEL_FIELD, entries);
+ }
+ entries.add(eventLevel);
+ }
+ return result;
+ }
+
+ /**
+ * Deserialize the state of the object.
+ *
+ * @param in object input stream.
+ *
+ * @throws IOException if error in reading stream for deserialization.
+ */
+ private void readObject(final java.io.ObjectInputStream in)
+ throws IOException {
+ populateLevels();
+ boolean isUtilLogging = in.readBoolean();
+ int levelInt = in.readInt();
+ if (isUtilLogging) {
+ level = UtilLoggingLevel.toLevel(levelInt);
+ } else {
+ level = Level.toLevel(levelInt);
+ }
+ }
+
+ /**
+ * Serialize the state of the object.
+ *
+ * @param out object output stream.
+ *
+ * @throws IOException if error in writing stream during serialization.
+ */
+ private void writeObject(final java.io.ObjectOutputStream out)
+ throws IOException {
+ out.writeBoolean(level instanceof UtilLoggingLevel);
+ out.writeInt(level.toInt());
+ }
+}
diff --git a/src/main/java/org/apache/log4j/rule/NotRule.java
b/src/main/java/org/apache/log4j/rule/NotRule.java
new file mode 100644
index 0000000..5e264e3
--- /dev/null
+++ b/src/main/java/org/apache/log4j/rule/NotRule.java
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.rule;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+
+/**
+ * A Rule class implementing logical not.
+ *
+ * @author Scott Deboy ([email protected])
+ */
+public class NotRule extends AbstractRule {
+ /**
+ * Serialization ID.
+ */
+ static final long serialVersionUID = -6827159473117969306L;
+ /**
+ * Enclosed rule.
+ */
+ private final Rule rule;
+
+ /**
+ * Create new instance.
+ * @param rule enclosed rule.
+ */
+ private NotRule(final Rule rule) {
+ super();
+ this.rule = rule;
+ }
+
+ /**
+ * Create new instance.
+ * @param rule enclosed rule.
+ * @return new rule.
+ */
+ public static Rule getRule(final Rule rule) {
+ return new NotRule(rule);
+ }
+
+ /**
+ * Create new instance from top element of stack.
+ * @param stack stack
+ * @return new rule.
+ */
+ public static Rule getRule(final Stack stack) {
+ if (stack.size() < 1) {
+ throw new IllegalArgumentException(
+ "Invalid NOT rule - expected one rule but received "
+ + stack.size());
+ }
+ Object o1 = stack.pop();
+ if (o1 instanceof Rule) {
+ Rule p1 = (Rule) o1;
+ return new NotRule(p1);
+ }
+ throw new IllegalArgumentException(
+ "Invalid NOT rule: - expected rule but received " + o1);
+ }
+
+ /** {@inheritDoc} */
+ public boolean evaluate(final LoggingEvent event, Map matches) {
+ if (matches == null) {
+ return !(rule.evaluate(event, null));
+ }
+ Map tempMatches = new HashMap();
+ boolean result = !(rule.evaluate(event, tempMatches));
+ if (result) {
+ for (Iterator iter =
tempMatches.entrySet().iterator();iter.hasNext();) {
+ Map.Entry entry = (Map.Entry)iter.next();
+ Object key = entry.getKey();
+ Set value = (Set)entry.getValue();
+ Set mainSet = (Set) matches.get(key);
+ if (mainSet == null) {
+ mainSet = new HashSet();
+ matches.put(key, mainSet);
+ }
+ mainSet.addAll(value);
+ }
+ }
+ return result;
+ }
+}
diff --git a/src/main/java/org/apache/log4j/rule/OrRule.java
b/src/main/java/org/apache/log4j/rule/OrRule.java
new file mode 100644
index 0000000..ecfa46f
--- /dev/null
+++ b/src/main/java/org/apache/log4j/rule/OrRule.java
@@ -0,0 +1,127 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.rule;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+
+/**
+ * A Rule class implementing logical or.
+ *
+ * @author Scott Deboy ([email protected])
+ */
+public class OrRule extends AbstractRule {
+ /**
+ * Serialization ID.
+ */
+ static final long serialVersionUID = 2088765995061413165L;
+ /**
+ * rule 1.
+ */
+ private final Rule rule1;
+ /**
+ * Rule 2.
+ */
+ private final Rule rule2;
+
+ /**
+ * Create new instance.
+ * @param firstParam first rule
+ * @param secondParam second rule
+ */
+ private OrRule(final Rule firstParam, final Rule secondParam) {
+ super();
+ this.rule1 = firstParam;
+ this.rule2 = secondParam;
+ }
+
+ /**
+ * Create new instance.
+ * @param firstParam first rule
+ * @param secondParam second rule
+ * @return new instance
+ */
+ public static Rule getRule(final Rule firstParam, final Rule secondParam) {
+ return new OrRule(firstParam, secondParam);
+ }
+
+ /**
+ * Create new instance from top two elements of stack.
+ * @param stack stack
+ * @return new instance
+ */
+ public static Rule getRule(final Stack stack) {
+ if (stack.size() < 2) {
+ throw new IllegalArgumentException(
+ "Invalid OR rule - expected two rules but received "
+ + stack.size());
+ }
+ Object o2 = stack.pop();
+ Object o1 = stack.pop();
+ if ((o2 instanceof Rule) && (o1 instanceof Rule)) {
+ Rule p2 = (Rule) o2;
+ Rule p1 = (Rule) o1;
+ return new OrRule(p1, p2);
+ }
+ throw new IllegalArgumentException("Invalid OR rule: " + o2 + "..." +
o1);
+ }
+
+ /** {@inheritDoc} */
+ public boolean evaluate(final LoggingEvent event, Map matches) {
+ if (matches == null) {
+ return (rule1.evaluate(event, null) || rule2.evaluate(event, null));
+ }
+ Map tempMatches1 = new HashMap();
+ Map tempMatches2 = new HashMap();
+ //not short-circuiting because we want to build the matches list
+ boolean result1 = rule1.evaluate(event, tempMatches1);
+ boolean result2 = rule2.evaluate(event, tempMatches2);
+ boolean result = result1 || result2;
+ if (result) {
+ for (Iterator iter =
tempMatches1.entrySet().iterator();iter.hasNext();) {
+ Map.Entry entry = (Map.Entry)iter.next();
+ Object key = entry.getKey();
+ Set value = (Set)entry.getValue();
+ Set mainSet = (Set) matches.get(key);
+ if (mainSet == null) {
+ mainSet = new HashSet();
+ matches.put(key, mainSet);
+ }
+ mainSet.addAll(value);
+ }
+ for (Iterator iter =
tempMatches2.entrySet().iterator();iter.hasNext();) {
+ Map.Entry entry = (Map.Entry)iter.next();
+ Object key = entry.getKey();
+ Set value = (Set)entry.getValue();
+ Set mainSet = (Set) matches.get(key);
+ if (mainSet == null) {
+ mainSet = new HashSet();
+ matches.put(key, mainSet);
+ }
+ mainSet.addAll(value);
+ }
+ }
+ return result;
+ }
+}
diff --git a/src/main/java/org/apache/log4j/rule/PartialTextMatchRule.java
b/src/main/java/org/apache/log4j/rule/PartialTextMatchRule.java
new file mode 100644
index 0000000..da56258
--- /dev/null
+++ b/src/main/java/org/apache/log4j/rule/PartialTextMatchRule.java
@@ -0,0 +1,112 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.rule;
+
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.LoggingEventFieldResolver;
+
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+import java.util.Stack;
+
+
+/**
+ * A Rule class implementing case-insensitive
+ * partial-text matches against two strings.
+ *
+ * @author Scott Deboy ([email protected])
+ */
+public class PartialTextMatchRule extends AbstractRule {
+ /**
+ * Serialization ID.
+ */
+ static final long serialVersionUID = 6963284773637727558L;
+ /**
+ * Resolver.
+ */
+ private static final LoggingEventFieldResolver RESOLVER =
+ LoggingEventFieldResolver.getInstance();
+ /**
+ * Field.
+ */
+ private final String field;
+ /**
+ * Value.
+ */
+ private final String value;
+
+ /**
+ * Create new instance.
+ * @param field field
+ * @param value value
+ */
+ private PartialTextMatchRule(final String field, final String value) {
+ super();
+ if (!RESOLVER.isField(field)) {
+ throw new IllegalArgumentException(
+ "Invalid partial text rule - " + field + " is not a supported field");
+ }
+
+ this.field = field;
+ this.value = value;
+ }
+
+ /**
+ * Create new instance.
+ * @param field field
+ * @param value value
+ * @return new instance
+ */
+ public static Rule getRule(final String field, final String value) {
+ return new PartialTextMatchRule(field, value);
+ }
+
+ /**
+ * Create new instance from top two elements of stack.
+ * @param stack stack
+ * @return new instance
+ */
+ public static Rule getRule(final Stack stack) {
+ if (stack.size() < 2) {
+ throw new IllegalArgumentException(
+ "invalid partial text rule - expected two parameters but received "
+ + stack.size());
+ }
+
+ String p2 = stack.pop().toString();
+ String p1 = stack.pop().toString();
+
+ return new PartialTextMatchRule(p1, p2);
+ }
+
+ /** {@inheritDoc} */
+ public boolean evaluate(final LoggingEvent event, Map matches) {
+ Object p2 = RESOLVER.getValue(field, event);
+ boolean result = ((p2 != null) && (value != null) &&
(p2.toString().toLowerCase().indexOf(value.toLowerCase()) > -1));
+ if (result && matches != null) {
+ Set entries = (Set) matches.get(field.toUpperCase());
+ if (entries == null) {
+ entries = new HashSet();
+ matches.put(field.toUpperCase(), entries);
+ }
+ entries.add(value);
+ }
+ return result;
+ }
+}
diff --git a/src/main/java/org/apache/log4j/rule/Rule.java
b/src/main/java/org/apache/log4j/rule/Rule.java
new file mode 100644
index 0000000..b7c3ee8
--- /dev/null
+++ b/src/main/java/org/apache/log4j/rule/Rule.java
@@ -0,0 +1,64 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.rule;
+
+import org.apache.log4j.spi.LoggingEvent;
+
+import java.beans.PropertyChangeListener;
+import java.util.Map;
+
+
+/**
+ * A Rule evaluates to true of false given a LoggingEvent object, and can
notify
+ * listeners when the underlying implementation of this Rule has it's
+ * criteria changed by using the standard PropertyChangeListener
infrastructure.
+ *
+ * @author Paul Smith ([email protected])
+ * @author Scott Deboy ([email protected])
+ */
+public interface Rule {
+ /**
+ * Returns true if this implementation of the rule accepts the LoggingEvent,
+ * or false if not.
+ *
+ * <p>What True/False means can be client-specific.
+ *
+ * @param e LoggingEvent this instance will evaluate
+ * @param matches a Map of event field keys to Sets of matching strings (may
be null) which will be
+ * updated during execution of this method to include field and string
matches based on the rule
+ * evaluation results
+ * @return true if this Rule instance accepts the event, otherwise false.
+ */
+ boolean evaluate(LoggingEvent e, Map matches);
+
+ /**
+ * Adds a PropertyChangeListener to this instance, which is notified when
+ * underlying Rule information has changed.
+ * (there are no specific property name events).
+ * @param listener listener
+ */
+ void addPropertyChangeListener(PropertyChangeListener listener);
+
+ /**
+ * Removes a known PropertyChangeListener from this Rule.
+ * @param listener listener
+ */
+ void removePropertyChangeListener(PropertyChangeListener listener);
+}
diff --git a/src/main/java/org/apache/log4j/rule/RuleFactory.java
b/src/main/java/org/apache/log4j/rule/RuleFactory.java
new file mode 100644
index 0000000..afc6668
--- /dev/null
+++ b/src/main/java/org/apache/log4j/rule/RuleFactory.java
@@ -0,0 +1,187 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.rule;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.Locale;
+import java.util.Stack;
+
+/**
+ * A Factory class which, given a string representation of the rule,
+ * and a context stack, will
+ * return a Rule ready for evaluation against events.
+ * If an operator is requested that isn't supported,
+ * an IllegalArgumentException is thrown.
+ *
+ * @author Scott Deboy ([email protected])
+ */
+public final class RuleFactory {
+ /**
+ * Singleton instance.
+ */
+ private static final RuleFactory FACTORY = new RuleFactory();
+ /**
+ * Rules.
+ */
+ private static final Collection RULES = new LinkedList();
+ /**
+ * AND operator literal.
+ */
+ private static final String AND_RULE = "&&";
+ /**
+ * OR operator literal.
+ */
+ private static final String OR_RULE = "||";
+ /**
+ * NOT operator literal.
+ */
+ private static final String NOT_RULE = "!";
+ /**
+ * Inequality operator literal.
+ */
+ private static final String NOT_EQUALS_RULE = "!=";
+ /**
+ * Equality operator literal.
+ */
+ private static final String EQUALS_RULE = "==";
+ /**
+ * Partial match operator literal.
+ */
+ private static final String PARTIAL_TEXT_MATCH_RULE = "~=";
+ /**
+ * Like operator literal.
+ */
+ private static final String LIKE_RULE = "like";
+ /**
+ * Exists operator literal.
+ */
+ private static final String EXISTS_RULE = "exists";
+ /**
+ * Less than operator literal.
+ */
+ private static final String LESS_THAN_RULE = "<";
+ /**
+ * Greater than operator literal.
+ */
+ private static final String GREATER_THAN_RULE = ">";
+ /**
+ * Less than or equal operator literal.
+ */
+ private static final String LESS_THAN_EQUALS_RULE = "<=";
+ /**
+ * Greater than or equal operator literal.
+ */
+ private static final String GREATER_THAN_EQUALS_RULE = ">=";
+
+ static {
+ RULES.add(AND_RULE);
+ RULES.add(OR_RULE);
+ RULES.add(NOT_RULE);
+ RULES.add(NOT_EQUALS_RULE);
+ RULES.add(EQUALS_RULE);
+ RULES.add(PARTIAL_TEXT_MATCH_RULE);
+ RULES.add(LIKE_RULE);
+ RULES.add(EXISTS_RULE);
+ RULES.add(LESS_THAN_RULE);
+ RULES.add(GREATER_THAN_RULE);
+ RULES.add(LESS_THAN_EQUALS_RULE);
+ RULES.add(GREATER_THAN_EQUALS_RULE);
+ }
+
+ /**
+ * Create instance.
+ */
+ private RuleFactory() {
+ super();
+ }
+
+ /**
+ * Get instance.
+ * @return rule factory instance.
+ */
+ public static RuleFactory getInstance() {
+ return FACTORY;
+ }
+
+ /**
+ * Determine if specified string is a known operator.
+ * @param symbol string
+ * @return true if string is a known operator
+ */
+ public boolean isRule(final String symbol) {
+ return ((symbol != null) &&
(RULES.contains(symbol.toLowerCase(Locale.ENGLISH))));
+ }
+
+ /**
+ * Create rule from applying operator to stack.
+ * @param symbol symbol
+ * @param stack stack
+ * @return new instance
+ */
+ public Rule getRule(final String symbol, final Stack stack) {
+ if (AND_RULE.equals(symbol)) {
+ return AndRule.getRule(stack);
+ }
+
+ if (OR_RULE.equals(symbol)) {
+ return OrRule.getRule(stack);
+ }
+
+ if (NOT_RULE.equals(symbol)) {
+ return NotRule.getRule(stack);
+ }
+
+ if (NOT_EQUALS_RULE.equals(symbol)) {
+ return NotEqualsRule.getRule(stack);
+ }
+
+ if (EQUALS_RULE.equals(symbol)) {
+ return EqualsRule.getRule(stack);
+ }
+
+ if (PARTIAL_TEXT_MATCH_RULE.equals(symbol)) {
+ return PartialTextMatchRule.getRule(stack);
+ }
+
+ if (RULES.contains(LIKE_RULE) && LIKE_RULE.equalsIgnoreCase(symbol)) {
+ return LikeRule.getRule(stack);
+ }
+
+ if (EXISTS_RULE.equalsIgnoreCase(symbol)) {
+ return ExistsRule.getRule(stack);
+ }
+
+ if (LESS_THAN_RULE.equals(symbol)) {
+ return InequalityRule.getRule(LESS_THAN_RULE, stack);
+ }
+
+ if (GREATER_THAN_RULE.equals(symbol)) {
+ return InequalityRule.getRule(GREATER_THAN_RULE, stack);
+ }
+
+ if (LESS_THAN_EQUALS_RULE.equals(symbol)) {
+ return InequalityRule.getRule(LESS_THAN_EQUALS_RULE, stack);
+ }
+
+ if (GREATER_THAN_EQUALS_RULE.equals(symbol)) {
+ return InequalityRule.getRule(GREATER_THAN_EQUALS_RULE, stack);
+ }
+ throw new IllegalArgumentException("Invalid rule: " + symbol);
+ }
+}
diff --git a/src/main/java/org/apache/log4j/rule/TimestampEqualsRule.java
b/src/main/java/org/apache/log4j/rule/TimestampEqualsRule.java
new file mode 100644
index 0000000..f43f36f
--- /dev/null
+++ b/src/main/java/org/apache/log4j/rule/TimestampEqualsRule.java
@@ -0,0 +1,121 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.rule;
+
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.LoggingEventFieldResolver;
+
+/**
+ * A Rule class implementing equality evaluation for timestamps.
+ *
+ * @author Scott Deboy ([email protected])
+ */
+public class TimestampEqualsRule extends AbstractRule {
+ /**
+ * Serialization ID.
+ */
+ static final long serialVersionUID = 1639079557187790321L;
+ /**
+ * Resolver.
+ */
+ private static final LoggingEventFieldResolver RESOLVER =
+ LoggingEventFieldResolver.getInstance();
+ /**
+ * Date format.
+ */
+ private static final DateFormat DATE_FORMAT =
+ new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
+
+ /**
+ * time stamp.
+ */
+ private long timeStamp;
+
+ /**
+ * Create new instance.
+ * @param value string representation of date.
+ */
+ private TimestampEqualsRule(final String value) {
+ super();
+ //expects value to be a timestamp value represented as a long
+ try {
+ timeStamp = DATE_FORMAT.parse(value).getTime();
+ } catch (ParseException pe) {
+ throw new IllegalArgumentException("Could not parse date: " + value);
+ }
+ }
+
+ /**
+ * Create new instance.
+ * @param value string representation of date.
+ * @return new instance
+ */
+ public static Rule getRule(final String value) {
+ return new TimestampEqualsRule(value);
+ }
+
+ /** {@inheritDoc} */
+ public boolean evaluate(final LoggingEvent event, Map matches) {
+ String eventTimeStampString =
RESOLVER.getValue(LoggingEventFieldResolver.TIMESTAMP_FIELD, event).toString();
+ long eventTimeStamp = Long.parseLong(eventTimeStampString) / 1000 * 1000;
+ boolean result = (eventTimeStamp == timeStamp);
+ if (result && matches != null) {
+ Set entries = (Set)
matches.get(LoggingEventFieldResolver.TIMESTAMP_FIELD);
+ if (entries == null) {
+ entries = new HashSet();
+ matches.put(LoggingEventFieldResolver.TIMESTAMP_FIELD, entries);
+ }
+ entries.add(eventTimeStampString);
+ }
+ return result;
+ }
+
+ /**
+ * Deserialize the state of the object.
+ *
+ * @param in object input stream
+ *
+ * @throws IOException if IO error during deserialization
+ * @throws ClassNotFoundException if class not found during
+ * deserialization
+ */
+ private void readObject(final java.io.ObjectInputStream in)
+ throws IOException, ClassNotFoundException {
+ timeStamp = in.readLong();
+ }
+
+ /**
+ * Serialize the state of the object.
+ *
+ * @param out object output stream
+ *
+ * @throws IOException if IO error during serialization
+ */
+ private void writeObject(final java.io.ObjectOutputStream out)
+ throws IOException {
+ out.writeLong(timeStamp);
+ }
+}
diff --git a/src/main/java/org/apache/log4j/rule/TimestampInequalityRule.java
b/src/main/java/org/apache/log4j/rule/TimestampInequalityRule.java
new file mode 100644
index 0000000..44c5776
--- /dev/null
+++ b/src/main/java/org/apache/log4j/rule/TimestampInequalityRule.java
@@ -0,0 +1,142 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.rule;
+
+import java.io.IOException;
+import java.text.DateFormat;
+import java.text.ParseException;
+import java.text.SimpleDateFormat;
+import java.util.HashSet;
+import java.util.Map;
+import java.util.Set;
+
+import org.apache.log4j.spi.LoggingEvent;
+import org.apache.log4j.spi.LoggingEventFieldResolver;
+
+/**
+ * A Rule class implementing inequality evaluation for timestamps.
+ *
+ * @author Scott Deboy ([email protected])
+ */
+public class TimestampInequalityRule extends AbstractRule {
+ /**
+ * Serialization ID.
+ */
+ static final long serialVersionUID = -4642641663914789241L;
+ /**
+ * Resolver.
+ */
+ private static final LoggingEventFieldResolver RESOLVER =
+ LoggingEventFieldResolver.getInstance();
+ /**
+ * Date format.
+ */
+ private static final DateFormat DATE_FORMAT =
+ new SimpleDateFormat("yyyy/MM/dd HH:mm:ss");
+ /**
+ * Inequality symbol.
+ */
+ private transient String inequalitySymbol;
+ /**
+ * Timestamp.
+ */
+ private long timeStamp;
+
+ /**
+ * Create new instance.
+ * @param inequalitySymbol inequality symbol.
+ * @param value string representation of date.
+ */
+ private TimestampInequalityRule(
+ final String inequalitySymbol, final String value) {
+ super();
+ this.inequalitySymbol = inequalitySymbol;
+ try {
+ timeStamp = DATE_FORMAT.parse(value).getTime();
+ } catch (ParseException pe) {
+ throw new IllegalArgumentException("Could not parse date: " + value);
+ }
+ }
+
+ /**
+ * Create new instance.
+ * @param inequalitySymbol inequality symbol
+ * @param value string representation of date
+ * @return new instance
+ */
+ public static Rule getRule(final String inequalitySymbol,
+ final String value) {
+ return new TimestampInequalityRule(inequalitySymbol, value);
+ }
+
+ /** {@inheritDoc} */
+ public boolean evaluate(final LoggingEvent event, Map matches) {
+ String eventTimeStampString =
RESOLVER.getValue(LoggingEventFieldResolver.TIMESTAMP_FIELD, event).toString();
+ long eventTimeStamp = Long.parseLong(
+ eventTimeStampString) / 1000 * 1000;
+ boolean result = false;
+ long first = eventTimeStamp;
+ long second = timeStamp;
+
+ if ("<".equals(inequalitySymbol)) {
+ result = first < second;
+ } else if (">".equals(inequalitySymbol)) {
+ result = first > second;
+ } else if ("<=".equals(inequalitySymbol)) {
+ result = first <= second;
+ } else if (">=".equals(inequalitySymbol)) {
+ result = first >= second;
+ }
+ if (result && matches != null) {
+ Set entries = (Set)
matches.get(LoggingEventFieldResolver.TIMESTAMP_FIELD);
+ if (entries == null) {
+ entries = new HashSet();
+ matches.put(LoggingEventFieldResolver.TIMESTAMP_FIELD, entries);
+ }
+ entries.add(eventTimeStampString);
+ }
+ return result;
+ }
+
+ /**
+ * Deserialize the state of the object.
+ *
+ * @param in object input stream
+ *
+ * @throws IOException if IO error during deserialization
+ * @throws ClassNotFoundException if class not found
+ */
+ private void readObject(final java.io.ObjectInputStream in)
+ throws IOException, ClassNotFoundException {
+ inequalitySymbol = (String) in.readObject();
+ timeStamp = in.readLong();
+ }
+
+ /**
+ * Serialize the state of the object.
+ *
+ * @param out object output stream
+ *
+ * @throws IOException if IO error during serialization
+ */
+ private void writeObject(final java.io.ObjectOutputStream out)
+ throws IOException {
+ out.writeObject(inequalitySymbol);
+ out.writeLong(timeStamp);
+ }
+}
diff --git a/src/main/java/org/apache/log4j/spi/LoggingEventFieldResolver.java
b/src/main/java/org/apache/log4j/spi/LoggingEventFieldResolver.java
new file mode 100644
index 0000000..f1f4ab0
--- /dev/null
+++ b/src/main/java/org/apache/log4j/spi/LoggingEventFieldResolver.java
@@ -0,0 +1,275 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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.spi;
+
+import java.util.ArrayList;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.Locale;
+
+import org.apache.log4j.rule.InFixToPostFix;
+
+
+/**
+ * A singleton helper utility which accepts a field name
+ * and a LoggingEvent and returns the value of that field.
+ *
+ * This class defines a grammar used in creation of an expression-based Rule.
+ *
+ * The only available method is
+ * Object getField(String fieldName, LoggingEvent event).
+ *
+ * Here is a description of the mapping of field names in the grammar
+ * to fields on the logging event. While the getField method returns an
Object,
+ * the individual types returned per field are described here:
+ *
+ * Field Name Field value (String representation Return type
+ * LOGGER category name (logger) String
+ * LEVEL level Level
+ * CLASS locationInformation's class name String
+ * FILE locationInformation's file name String
+ * LINE locationInformation's line number String
+ * METHOD locationInformation's method name String
+ * MSG message Object
+ * NDC NDC String
+ * EXCEPTION throwable string representation ThrowableInformation
+ * TIMESTAMP timestamp Long
+ * THREAD thread String
+ * PROP.keyName entry in the Property hashtable String
+ * mapped to the key [keyName]
+
+ * NOTE: the values for the 'keyName' portion of the MDC and PROP mappings
must
+ * be an exact match to the key in the hashTable (case sensitive).
+ *
+ * If the passed-in field is null or doesn't match an entry
+ * in the above-described mapping, an exception is thrown.
+ *
+ * @author Scott Deboy ([email protected])
+ * @author Paul Smith ([email protected])
+ *
+ */
+public final class LoggingEventFieldResolver {
+ /**
+ * Keyword list.
+ */
+ public static final List KEYWORD_LIST = new ArrayList();
+ /**
+ * LOGGER string literal.
+ */
+ public static final String LOGGER_FIELD = "LOGGER";
+ /**
+ * LEVEL string literal.
+ */
+ public static final String LEVEL_FIELD = "LEVEL";
+ /**
+ * CLASS string literal.
+ */
+ public static final String CLASS_FIELD = "CLASS";
+ /**
+ * FILE string literal.
+ */
+ public static final String FILE_FIELD = "FILE";
+ /**
+ * LINE string literal.
+ */
+ public static final String LINE_FIELD = "LINE";
+ /**
+ * METHOD string literal.
+ */
+ public static final String METHOD_FIELD = "METHOD";
+ /**
+ * MSG string literal.
+ */
+ public static final String MSG_FIELD = "MSG";
+ /**
+ * NDC string literal.
+ */
+ public static final String NDC_FIELD = "NDC";
+ /**
+ * EXCEPTION string literal.
+ */
+ public static final String EXCEPTION_FIELD = "EXCEPTION";
+ /**
+ * TIMESTAMP string literal.
+ */
+ public static final String TIMESTAMP_FIELD = "TIMESTAMP";
+ /**
+ * THREAD string literal.
+ */
+ public static final String THREAD_FIELD = "THREAD";
+ /**
+ * PROP. string literal.
+ */
+ public static final String PROP_FIELD = "PROP.";
+ /**
+ * empty string literal.
+ */
+ public static final String EMPTY_STRING = "";
+ /**
+ * LOGGER string literal.
+ */
+ private static final LoggingEventFieldResolver RESOLVER =
+ new LoggingEventFieldResolver();
+
+ /**
+ * Create new instance.
+ */
+ private LoggingEventFieldResolver() {
+ super();
+ KEYWORD_LIST.add(LOGGER_FIELD);
+ KEYWORD_LIST.add(LEVEL_FIELD);
+ KEYWORD_LIST.add(CLASS_FIELD);
+ KEYWORD_LIST.add(FILE_FIELD);
+ KEYWORD_LIST.add(LINE_FIELD);
+ KEYWORD_LIST.add(METHOD_FIELD);
+ KEYWORD_LIST.add(MSG_FIELD);
+ KEYWORD_LIST.add(NDC_FIELD);
+ KEYWORD_LIST.add(EXCEPTION_FIELD);
+ KEYWORD_LIST.add(TIMESTAMP_FIELD);
+ KEYWORD_LIST.add(THREAD_FIELD);
+ KEYWORD_LIST.add(PROP_FIELD);
+ }
+
+ /**
+ * Apply fields.
+ * @param replaceText replacement text.
+ * @param event logging event.
+ * @return evaluted expression
+ */
+ public String applyFields(final String replaceText,
+ final LoggingEvent event) {
+ if (replaceText == null) {
+ return null;
+ }
+ InFixToPostFix.CustomTokenizer tokenizer = new
InFixToPostFix.CustomTokenizer(replaceText);
+ StringBuffer result = new StringBuffer();
+ boolean found = false;
+
+ while (tokenizer.hasMoreTokens()) {
+ String token = tokenizer.nextToken();
+ if (isField(token) ||
token.toUpperCase(Locale.US).startsWith(PROP_FIELD)) {
+ result.append(getValue(token, event).toString());
+ found = true;
+ } else {
+ result.append(token);
+ }
+ }
+ if (found) {
+ return result.toString();
+ }
+ return null;
+ }
+
+ /**
+ * Get singleton instance.
+ * @return singleton instance
+ */
+ public static LoggingEventFieldResolver getInstance() {
+ return RESOLVER;
+ }
+
+ /**
+ * Determines if specified string is a recognized field.
+ * @param fieldName field name
+ * @return true if recognized field.
+ */
+ public boolean isField(final String fieldName) {
+ if (fieldName != null) {
+ return (KEYWORD_LIST.contains(
+ fieldName.toUpperCase(Locale.US))
+ || fieldName.toUpperCase().startsWith(PROP_FIELD));
+ }
+ return false;
+ }
+
+ /**
+ * Get value of field.
+ * @param fieldName field
+ * @param event event
+ * @return value of field
+ */
+ public Object getValue(final String fieldName,
+ final LoggingEvent event) {
+ String upperField = fieldName.toUpperCase(Locale.US);
+ if (LOGGER_FIELD.equals(upperField)) {
+ return event.getLoggerName();
+ } else if (LEVEL_FIELD.equals(upperField)) {
+ return event.getLevel();
+ } else if (MSG_FIELD.equals(upperField)) {
+ return event.getMessage();
+ } else if (NDC_FIELD.equals(upperField)) {
+ String ndcValue = event.getNDC();
+ return ((ndcValue == null) ? EMPTY_STRING : ndcValue);
+ } else if (EXCEPTION_FIELD.equals(upperField)) {
+ String[] throwableRep = event.getThrowableStrRep();
+ if (throwableRep == null) {
+ return EMPTY_STRING;
+ } else {
+ return getExceptionMessage(throwableRep);
+ }
+ } else if (TIMESTAMP_FIELD.equals(upperField)) {
+ return new Long(event.timeStamp);
+ } else if (THREAD_FIELD.equals(upperField)) {
+ return event.getThreadName();
+ } else if (upperField.startsWith(PROP_FIELD)) {
+ //note: need to use actual fieldname since case matters
+ Object propValue = event.getMDC(fieldName.substring(5));
+ if (propValue == null) {
+ //case-specific match didn't work, try case insensitive match
+ String lowerPropKey = fieldName.substring(5).toLowerCase();
+ Set entrySet = event.getProperties().entrySet();
+ for (Iterator iter = entrySet.iterator();iter.hasNext();) {
+ Map.Entry thisEntry = (Map.Entry) iter.next();
+ if
(thisEntry.getKey().toString().equalsIgnoreCase(lowerPropKey)) {
+ propValue = thisEntry.getValue();
+ }
+ }
+ }
+ return ((propValue == null) ? EMPTY_STRING : propValue.toString());
+ } else {
+ LocationInfo info = event.getLocationInformation();
+ if (CLASS_FIELD.equals(upperField)) {
+ return ((info == null) ? EMPTY_STRING : info.getClassName());
+ } else if (FILE_FIELD.equals(upperField)) {
+ return ((info == null) ? EMPTY_STRING : info.getFileName());
+ } else if (LINE_FIELD.equals(upperField)) {
+ return ((info == null) ? EMPTY_STRING : info.getLineNumber());
+ } else if (METHOD_FIELD.equals(upperField)) {
+ return ((info == null) ? EMPTY_STRING : info.getMethodName());
+ }
+ }
+
+ //there wasn't a match, so throw a runtime exception
+ throw new IllegalArgumentException("Unsupported field name: " + fieldName);
+ }
+
+ /**
+ * Get message from throwable representation.
+ * @param exception exception
+ * @return message
+ */
+ private static String getExceptionMessage(final String[] exception) {
+ StringBuffer buff = new StringBuffer();
+ for (int i = 0; i < exception.length; i++) {
+ buff.append(exception[i]);
+ }
+ return buff.toString();
+ }
+}