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