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.layout;
18  
19  import org.apache.logging.log4j.Level;
20  import org.apache.logging.log4j.Logger;
21  import org.apache.logging.log4j.Marker;
22  import org.apache.logging.log4j.core.LogEvent;
23  import org.apache.logging.log4j.core.config.Configuration;
24  import org.apache.logging.log4j.core.config.Node;
25  import org.apache.logging.log4j.core.config.plugins.Plugin;
26  import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
27  import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
28  import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
29  import org.apache.logging.log4j.core.config.plugins.PluginElement;
30  import org.apache.logging.log4j.core.impl.LocationAware;
31  import org.apache.logging.log4j.core.pattern.PatternFormatter;
32  import org.apache.logging.log4j.core.pattern.PatternParser;
33  import org.apache.logging.log4j.status.StatusLogger;
34  
35  import java.util.HashMap;
36  import java.util.List;
37  import java.util.Map;
38  
39  /**
40   * Selects the pattern to use based on the Level in the LogEvent.
41   */
42  @Plugin(name = "LevelPatternSelector", category = Node.CATEGORY, elementType = PatternSelector.ELEMENT_TYPE, printObject = true)
43  public class LevelPatternSelector implements PatternSelector, LocationAware {
44  
45      /**
46       * Custom MarkerPatternSelector builder. Use the {@link LevelPatternSelector#newBuilder() builder factory method} to create this.
47       */
48      public static class Builder implements org.apache.logging.log4j.core.util.Builder<LevelPatternSelector> {
49  
50          @PluginElement("PatternMatch")
51          private PatternMatch[] properties;
52  
53          @PluginBuilderAttribute("defaultPattern")
54          private String defaultPattern;
55  
56          @PluginBuilderAttribute(value = "alwaysWriteExceptions")
57          private boolean alwaysWriteExceptions = true;
58  
59          @PluginBuilderAttribute(value = "disableAnsi")
60          private boolean disableAnsi;
61  
62          @PluginBuilderAttribute(value = "noConsoleNoAnsi")
63          private boolean noConsoleNoAnsi;
64  
65          @PluginConfiguration
66          private Configuration configuration;
67  
68          @Override
69          public LevelPatternSelector build() {
70              if (defaultPattern == null) {
71                  defaultPattern = PatternLayout.DEFAULT_CONVERSION_PATTERN;
72              }
73              if (properties == null || properties.length == 0) {
74                  LOGGER.warn("No marker patterns were provided with PatternMatch");
75                  return null;
76              }
77              return new LevelPatternSelector(properties, defaultPattern, alwaysWriteExceptions, disableAnsi,
78                      noConsoleNoAnsi, configuration);
79          }
80  
81          public Builder setProperties(final PatternMatch[] properties) {
82              this.properties = properties;
83              return this;
84          }
85  
86          public Builder setDefaultPattern(final String defaultPattern) {
87              this.defaultPattern = defaultPattern;
88              return this;
89          }
90  
91          public Builder setAlwaysWriteExceptions(final boolean alwaysWriteExceptions) {
92              this.alwaysWriteExceptions = alwaysWriteExceptions;
93              return this;
94          }
95  
96          public Builder setDisableAnsi(final boolean disableAnsi) {
97              this.disableAnsi = disableAnsi;
98              return this;
99          }
100 
101         public Builder setNoConsoleNoAnsi(final boolean noConsoleNoAnsi) {
102             this.noConsoleNoAnsi = noConsoleNoAnsi;
103             return this;
104         }
105 
106         public Builder setConfiguration(final Configuration configuration) {
107             this.configuration = configuration;
108             return this;
109         }
110 
111     }
112 
113     private final Map<String, PatternFormatter[]> formatterMap = new HashMap<>();
114 
115     private final Map<String, String> patternMap = new HashMap<>();
116 
117     private final PatternFormatter[] defaultFormatters;
118 
119     private final String defaultPattern;
120 
121     private static Logger LOGGER = StatusLogger.getLogger();
122 
123     private final boolean requiresLocation;
124 
125     /**
126      * @deprecated Use {@link #newBuilder()} instead. This will be private in a future version.
127      */
128     @Deprecated
129     public LevelPatternSelector(final PatternMatch[] properties, final String defaultPattern,
130                                  final boolean alwaysWriteExceptions, final boolean noConsoleNoAnsi,
131                                  final Configuration config) {
132         this(properties, defaultPattern, alwaysWriteExceptions, false, noConsoleNoAnsi, config);
133     }
134 
135     private LevelPatternSelector(final PatternMatch[] properties, final String defaultPattern,
136                                  final boolean alwaysWriteExceptions, final boolean disableAnsi,
137                                  final boolean noConsoleNoAnsi, final Configuration config) {
138         boolean needsLocation = false;
139         final PatternParser parser = PatternLayout.createPatternParser(config);
140         for (final PatternMatch property : properties) {
141             try {
142                 final List<PatternFormatter> list = parser.parse(property.getPattern(), alwaysWriteExceptions,
143                         disableAnsi, noConsoleNoAnsi);
144                 PatternFormatter[] formatters = list.toArray(new PatternFormatter[0]);
145                 formatterMap.put(property.getKey(), formatters);
146                 for (int i = 0; !needsLocation && i < formatters.length; ++i) {
147                     needsLocation = formatters[i].requiresLocation();
148                 }
149 
150                 patternMap.put(property.getKey(), property.getPattern());
151             } catch (final RuntimeException ex) {
152                 throw new IllegalArgumentException("Cannot parse pattern '" + property.getPattern() + "'", ex);
153             }
154         }
155         try {
156             final List<PatternFormatter> list = parser.parse(defaultPattern, alwaysWriteExceptions, disableAnsi,
157                     noConsoleNoAnsi);
158             defaultFormatters = list.toArray(new PatternFormatter[0]);
159             this.defaultPattern = defaultPattern;
160             for (int i = 0; !needsLocation && i < defaultFormatters.length; ++i) {
161                 needsLocation = defaultFormatters[i].requiresLocation();
162             }
163         } catch (final RuntimeException ex) {
164             throw new IllegalArgumentException("Cannot parse pattern '" + defaultPattern + "'", ex);
165         }
166         requiresLocation = needsLocation;
167     }
168 
169     @Override
170     public boolean requiresLocation() {
171         return requiresLocation;
172     }
173 
174     @Override
175     public PatternFormatter[] getFormatters(final LogEvent event) {
176         final Level level = event.getLevel();
177         if (level == null) {
178             return defaultFormatters;
179         }
180         for (final String key : formatterMap.keySet()) {
181             if (level.name().equalsIgnoreCase(key)) {
182                 return formatterMap.get(key);
183             }
184         }
185         return defaultFormatters;
186     }
187 
188     /**
189      * Creates a builder for a custom ScriptPatternSelector.
190      *
191      * @return a ScriptPatternSelector builder.
192      */
193     @PluginBuilderFactory
194     public static Builder newBuilder() {
195         return new Builder();
196     }
197 
198     /**
199      * Deprecated, use {@link #newBuilder()} instead.
200      * @param properties
201      * @param defaultPattern
202      * @param alwaysWriteExceptions
203      * @param noConsoleNoAnsi
204      * @param configuration
205      * @return a new MarkerPatternSelector.
206      * @deprecated Use {@link #newBuilder()} instead.
207      */
208     @Deprecated
209     public static LevelPatternSelector createSelector(
210             final PatternMatch[] properties,
211             final String defaultPattern,
212             final boolean alwaysWriteExceptions,
213             final boolean noConsoleNoAnsi,
214             final Configuration configuration) {
215         final Builder builder = newBuilder();
216         builder.setProperties(properties);
217         builder.setDefaultPattern(defaultPattern);
218         builder.setAlwaysWriteExceptions(alwaysWriteExceptions);
219         builder.setNoConsoleNoAnsi(noConsoleNoAnsi);
220         builder.setConfiguration(configuration);
221         return builder.build();
222     }
223 
224     @Override
225     public String toString() {
226         final StringBuilder sb = new StringBuilder();
227         boolean first = true;
228         for (final Map.Entry<String, String> entry : patternMap.entrySet()) {
229             if (!first) {
230                 sb.append(", ");
231             }
232             sb.append("key=\"").append(entry.getKey()).append("\", pattern=\"").append(entry.getValue()).append("\"");
233             first = false;
234         }
235         if (!first) {
236             sb.append(", ");
237         }
238         sb.append("default=\"").append(defaultPattern).append("\"");
239         return sb.toString();
240     }
241 }