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 */ 017 package org.apache.logging.log4j.core.filter; 018 019 import java.lang.reflect.Field; 020 import java.util.Arrays; 021 import java.util.Comparator; 022 import java.util.regex.Matcher; 023 import java.util.regex.Pattern; 024 025 import org.apache.logging.log4j.Level; 026 import org.apache.logging.log4j.Marker; 027 import org.apache.logging.log4j.core.LogEvent; 028 import org.apache.logging.log4j.core.Logger; 029 import org.apache.logging.log4j.core.config.plugins.Plugin; 030 import org.apache.logging.log4j.core.config.plugins.PluginAttribute; 031 import org.apache.logging.log4j.core.config.plugins.PluginElement; 032 import org.apache.logging.log4j.core.config.plugins.PluginFactory; 033 import org.apache.logging.log4j.message.Message; 034 035 /** 036 * This filter returns the onMatch result if the message matches the regular expression. 037 * 038 * The "useRawMsg" attribute can be used to indicate whether the regular expression should be applied to the result of 039 * calling Message.getMessageFormat (true) or Message.getFormattedMessage() (false). The default is false. 040 * 041 */ 042 @Plugin(name = "RegexFilter", category = "Core", elementType = "filter", printObject = true) 043 public final class RegexFilter extends AbstractFilter { 044 045 private static final int DEFAULT_PATTERN_FLAGS = 0; 046 private final Pattern pattern; 047 private final boolean useRawMessage; 048 049 private RegexFilter(final boolean raw, final Pattern pattern, final Result onMatch, final Result onMismatch) { 050 super(onMatch, onMismatch); 051 this.pattern = pattern; 052 this.useRawMessage = raw; 053 } 054 055 @Override 056 public Result filter(final Logger logger, final Level level, final Marker marker, final String msg, 057 final Object... params) { 058 return filter(msg); 059 } 060 061 @Override 062 public Result filter(final Logger logger, final Level level, final Marker marker, final Object msg, 063 final Throwable t) { 064 if (msg == null) { 065 return onMismatch; 066 } 067 return filter(msg.toString()); 068 } 069 070 @Override 071 public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg, 072 final Throwable t) { 073 if (msg == null) { 074 return onMismatch; 075 } 076 final String text = useRawMessage ? msg.getFormat() : msg.getFormattedMessage(); 077 return filter(text); 078 } 079 080 @Override 081 public Result filter(final LogEvent event) { 082 final String text = useRawMessage ? event.getMessage().getFormat() : event.getMessage().getFormattedMessage(); 083 return filter(text); 084 } 085 086 private Result filter(final String msg) { 087 if (msg == null) { 088 return onMismatch; 089 } 090 final Matcher m = pattern.matcher(msg); 091 return m.matches() ? onMatch : onMismatch; 092 } 093 094 @Override 095 public String toString() { 096 final StringBuilder sb = new StringBuilder(); 097 sb.append("useRaw=").append(useRawMessage); 098 sb.append(", pattern=").append(pattern.toString()); 099 return sb.toString(); 100 } 101 102 /** 103 * Create a Filter that matches a regular expression. 104 * 105 * @param regex 106 * The regular expression to match. 107 * @param patternFlags 108 * An array of Stirngs where each String is a {@link Pattern#compile(String, int)} compilation flag. 109 * @param useRawMsg 110 * If true, the raw message will be used, otherwise the formatted message will be used. 111 * @param match 112 * The action to perform when a match occurs. 113 * @param mismatch 114 * The action to perform when a mismatch occurs. 115 * @return The RegexFilter. 116 * @throws IllegalAccessException 117 * @throws IllegalArgumentException 118 */ 119 @PluginFactory 120 public static RegexFilter createFilter( 121 //@formatter:off 122 @PluginAttribute("regex") final String regex, 123 @PluginElement("PatternFlags") final String[] patternFlags, 124 @PluginAttribute("useRawMsg") final Boolean useRawMsg, 125 @PluginAttribute("onMatch") final Result match, 126 @PluginAttribute("onMismatch") final Result mismatch) 127 //@formatter:on 128 throws IllegalArgumentException, IllegalAccessException { 129 if (regex == null) { 130 LOGGER.error("A regular expression must be provided for RegexFilter"); 131 return null; 132 } 133 return new RegexFilter(useRawMsg, Pattern.compile(regex, toPatternFlags(patternFlags)), match, mismatch); 134 } 135 136 private static int toPatternFlags(final String[] patternFlags) throws IllegalArgumentException, 137 IllegalAccessException { 138 if (patternFlags == null || patternFlags.length == 0) { 139 return DEFAULT_PATTERN_FLAGS; 140 } 141 final Field[] fields = Pattern.class.getDeclaredFields(); 142 final Comparator<Field> comparator = new Comparator<Field>() { 143 144 @Override 145 public int compare(final Field f1, final Field f2) { 146 return f1.getName().compareTo(f2.getName()); 147 } 148 }; 149 Arrays.sort(fields, comparator); 150 final String[] fieldNames = new String[fields.length]; 151 for (int i = 0; i < fields.length; i++) { 152 fieldNames[i] = fields[i].getName(); 153 } 154 int flags = DEFAULT_PATTERN_FLAGS; 155 for (final String test : patternFlags) { 156 final int index = Arrays.binarySearch(fieldNames, test); 157 if (index >= 0) { 158 final Field field = fields[index]; 159 flags |= field.getInt(Pattern.class); 160 } 161 } 162 return flags; 163 } 164 }