001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017package org.apache.logging.log4j.core.filter; 018 019import java.lang.reflect.Field; 020import java.util.Arrays; 021import java.util.Comparator; 022import java.util.regex.Matcher; 023import java.util.regex.Pattern; 024 025import org.apache.logging.log4j.Level; 026import org.apache.logging.log4j.Marker; 027import org.apache.logging.log4j.core.Filter; 028import org.apache.logging.log4j.core.LogEvent; 029import org.apache.logging.log4j.core.Logger; 030import org.apache.logging.log4j.core.config.Node; 031import org.apache.logging.log4j.core.config.plugins.Plugin; 032import org.apache.logging.log4j.core.config.plugins.PluginAttribute; 033import org.apache.logging.log4j.core.config.plugins.PluginElement; 034import org.apache.logging.log4j.core.config.plugins.PluginFactory; 035import org.apache.logging.log4j.message.Message; 036 037/** 038 * This filter returns the onMatch result if the message matches the regular expression. 039 * 040 * The "useRawMsg" attribute can be used to indicate whether the regular expression should be applied to the result of 041 * calling Message.getMessageFormat (true) or Message.getFormattedMessage() (false). The default is false. 042 * 043 */ 044@Plugin(name = "RegexFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true) 045public final class RegexFilter extends AbstractFilter { 046 047 private static final int DEFAULT_PATTERN_FLAGS = 0; 048 private final Pattern pattern; 049 private final boolean useRawMessage; 050 051 private RegexFilter(final boolean raw, final Pattern pattern, final Result onMatch, final Result onMismatch) { 052 super(onMatch, onMismatch); 053 this.pattern = pattern; 054 this.useRawMessage = raw; 055 } 056 057 @Override 058 public Result filter(final Logger logger, final Level level, final Marker marker, final String msg, 059 final Object... params) { 060 return filter(msg); 061 } 062 063 @Override 064 public Result filter(final Logger logger, final Level level, final Marker marker, final Object msg, 065 final Throwable t) { 066 if (msg == null) { 067 return onMismatch; 068 } 069 return filter(msg.toString()); 070 } 071 072 @Override 073 public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg, 074 final Throwable t) { 075 if (msg == null) { 076 return onMismatch; 077 } 078 final String text = useRawMessage ? msg.getFormat() : msg.getFormattedMessage(); 079 return filter(text); 080 } 081 082 @Override 083 public Result filter(final LogEvent event) { 084 final String text = useRawMessage ? event.getMessage().getFormat() : event.getMessage().getFormattedMessage(); 085 return filter(text); 086 } 087 088 private Result filter(final String msg) { 089 if (msg == null) { 090 return onMismatch; 091 } 092 final Matcher m = pattern.matcher(msg); 093 return m.matches() ? onMatch : onMismatch; 094 } 095 096 @Override 097 public String toString() { 098 final StringBuilder sb = new StringBuilder(); 099 sb.append("useRaw=").append(useRawMessage); 100 sb.append(", pattern=").append(pattern.toString()); 101 return sb.toString(); 102 } 103 104 /** 105 * Creates a Filter that matches a regular expression. 106 * 107 * @param regex 108 * The regular expression to match. 109 * @param patternFlags 110 * An array of Stirngs where each String is a {@link Pattern#compile(String, int)} compilation flag. 111 * @param useRawMsg 112 * If true, the raw message will be used, otherwise the formatted message will be used. 113 * @param match 114 * The action to perform when a match occurs. 115 * @param mismatch 116 * The action to perform when a mismatch occurs. 117 * @return The RegexFilter. 118 * @throws IllegalAccessException 119 * @throws IllegalArgumentException 120 */ 121 // TODO Consider refactoring to use AbstractFilter.AbstractFilterBuilder 122 @PluginFactory 123 public static RegexFilter createFilter( 124 //@formatter:off 125 @PluginAttribute("regex") final String regex, 126 @PluginElement("PatternFlags") final String[] patternFlags, 127 @PluginAttribute("useRawMsg") final Boolean useRawMsg, 128 @PluginAttribute("onMatch") final Result match, 129 @PluginAttribute("onMismatch") final Result mismatch) 130 //@formatter:on 131 throws IllegalArgumentException, IllegalAccessException { 132 if (regex == null) { 133 LOGGER.error("A regular expression must be provided for RegexFilter"); 134 return null; 135 } 136 return new RegexFilter(useRawMsg, Pattern.compile(regex, toPatternFlags(patternFlags)), match, mismatch); 137 } 138 139 private static int toPatternFlags(final String[] patternFlags) throws IllegalArgumentException, 140 IllegalAccessException { 141 if (patternFlags == null || patternFlags.length == 0) { 142 return DEFAULT_PATTERN_FLAGS; 143 } 144 final Field[] fields = Pattern.class.getDeclaredFields(); 145 final Comparator<Field> comparator = new Comparator<Field>() { 146 147 @Override 148 public int compare(final Field f1, final Field f2) { 149 return f1.getName().compareTo(f2.getName()); 150 } 151 }; 152 Arrays.sort(fields, comparator); 153 final String[] fieldNames = new String[fields.length]; 154 for (int i = 0; i < fields.length; i++) { 155 fieldNames[i] = fields[i].getName(); 156 } 157 int flags = DEFAULT_PATTERN_FLAGS; 158 for (final String test : patternFlags) { 159 final int index = Arrays.binarySearch(fieldNames, test); 160 if (index >= 0) { 161 final Field field = fields[index]; 162 flags |= field.getInt(Pattern.class); 163 } 164 } 165 return flags; 166 } 167}