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