View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements. See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache license, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License. You may obtain a copy of the License at
8    *
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the license for the specific language governing permissions and
15   * limitations under the license.
16   */
17  package org.apache.logging.log4j.core.filter;
18  
19  import java.util.ArrayList;
20  import java.util.HashMap;
21  import java.util.List;
22  import java.util.Map;
23  
24  import org.apache.logging.log4j.Level;
25  import org.apache.logging.log4j.Marker;
26  import org.apache.logging.log4j.core.Filter;
27  import org.apache.logging.log4j.core.LogEvent;
28  import org.apache.logging.log4j.core.Logger;
29  import org.apache.logging.log4j.core.config.Node;
30  import org.apache.logging.log4j.core.config.plugins.Plugin;
31  import org.apache.logging.log4j.core.config.plugins.PluginAttribute;
32  import org.apache.logging.log4j.core.config.plugins.PluginElement;
33  import org.apache.logging.log4j.core.config.plugins.PluginFactory;
34  import org.apache.logging.log4j.core.util.KeyValuePair;
35  import org.apache.logging.log4j.message.Message;
36  import org.apache.logging.log4j.message.StructuredDataMessage;
37  
38  /**
39   * Filter based on data in a StructuredDataMessage.
40   */
41  @Plugin(name = "StructuredDataFilter", category = Node.CATEGORY, elementType = Filter.ELEMENT_TYPE, printObject = true)
42  public final class StructuredDataFilter extends MapFilter {
43  
44      private static final long serialVersionUID = 1L;
45  
46      private StructuredDataFilter(final Map<String, List<String>> map, final boolean oper, final Result onMatch,
47                                   final Result onMismatch) {
48          super(map, oper, onMatch, onMismatch);
49      }
50  
51      @Override
52      public Result filter(final Logger logger, final Level level, final Marker marker, final Message msg,
53                           final Throwable t) {
54          if (msg instanceof StructuredDataMessage) {
55              return filter((StructuredDataMessage) msg);
56          }
57          return Result.NEUTRAL;
58      }
59  
60      @Override
61      public Result filter(final LogEvent event) {
62          final Message msg = event.getMessage();
63          if (msg instanceof StructuredDataMessage) {
64              return filter((StructuredDataMessage) msg);
65          }
66          return super.filter(event);
67      }
68  
69      protected Result filter(final StructuredDataMessage message) {
70          boolean match = false;
71          for (final Map.Entry<String, List<String>> entry : getMap().entrySet()) {
72              final String toMatch = getValue(message, entry.getKey());
73              if (toMatch != null) {
74                  match = entry.getValue().contains(toMatch);
75              } else {
76                  match = false;
77              }
78              if ((!isAnd() && match) || (isAnd() && !match)) {
79                  break;
80              }
81          }
82          return match ? onMatch : onMismatch;
83      }
84  
85      private String getValue(final StructuredDataMessage data, final String key) {
86          if (key.equalsIgnoreCase("id")) {
87              return data.getId().toString();
88          } else if (key.equalsIgnoreCase("id.name")) {
89              return data.getId().getName();
90          } else if (key.equalsIgnoreCase("type")) {
91              return data.getType();
92          } else if (key.equalsIgnoreCase("message")) {
93              return data.getFormattedMessage();
94          } else {
95              return data.getData().get(key);
96          }
97      }
98  
99      /**
100      * Create the StructuredDataFilter.
101      * @param pairs Key and value pairs.
102      * @param oper The operator to perform. If not "or" the operation will be an "and".
103      * @param match The action to perform on a match.
104      * @param mismatch The action to perform on a mismatch.
105      * @return The StructuredDataFilter.
106      */
107     @PluginFactory
108     public static StructuredDataFilter createFilter(
109             @PluginElement("Pairs") final KeyValuePair[] pairs,
110             @PluginAttribute("operator") final String oper,
111             @PluginAttribute("onMatch") final Result match,
112             @PluginAttribute("onMismatch") final Result mismatch) {
113         if (pairs == null || pairs.length == 0) {
114             LOGGER.error("keys and values must be specified for the StructuredDataFilter");
115             return null;
116         }
117         final Map<String, List<String>> map = new HashMap<String, List<String>>();
118         for (final KeyValuePair pair : pairs) {
119             final String key = pair.getKey();
120             if (key == null) {
121                 LOGGER.error("A null key is not valid in MapFilter");
122                 continue;
123             }
124             final String value = pair.getValue();
125             if (value == null) {
126                 LOGGER.error("A null value for key " + key + " is not allowed in MapFilter");
127                 continue;
128             }
129             List<String> list = map.get(pair.getKey());
130             if (list != null) {
131                 list.add(value);
132             } else {
133                 list = new ArrayList<String>();
134                 list.add(value);
135                 map.put(pair.getKey(), list);
136             }
137         }
138         if (map.isEmpty()) {
139             LOGGER.error("StructuredDataFilter is not configured with any valid key value pairs");
140             return null;
141         }
142         final boolean isAnd = oper == null || !oper.equalsIgnoreCase("or");
143         return new StructuredDataFilter(map, isAnd, match, mismatch);
144     }
145 }