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.appender.db;
18  
19  import java.util.Date;
20  import java.util.Locale;
21  
22  import org.apache.logging.log4j.Logger;
23  import org.apache.logging.log4j.core.Core;
24  import org.apache.logging.log4j.core.StringLayout;
25  import org.apache.logging.log4j.core.config.Configuration;
26  import org.apache.logging.log4j.core.config.plugins.Plugin;
27  import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
28  import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
29  import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
30  import org.apache.logging.log4j.core.config.plugins.PluginElement;
31  import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
32  import org.apache.logging.log4j.core.layout.PatternLayout;
33  import org.apache.logging.log4j.spi.ThreadContextMap;
34  import org.apache.logging.log4j.spi.ThreadContextStack;
35  import org.apache.logging.log4j.status.StatusLogger;
36  import org.apache.logging.log4j.util.ReadOnlyStringMap;
37  
38  /**
39   * A configuration element for specifying a database column name mapping.
40   *
41   * @since 2.8
42   */
43  @Plugin(name = "ColumnMapping", category = Core.CATEGORY_NAME, printObject = true)
44  public class ColumnMapping {
45  
46      /**
47       * Builder for {@link ColumnMapping}.
48       */
49      public static class Builder implements org.apache.logging.log4j.core.util.Builder<ColumnMapping> {
50  
51          @PluginConfiguration
52          private Configuration configuration;
53  
54          @PluginElement("Layout")
55          private StringLayout layout;
56  
57          @PluginBuilderAttribute
58          private String literal;
59  
60          @PluginBuilderAttribute
61          @Required(message = "No column name provided")
62          private String name;
63  
64          @PluginBuilderAttribute
65          private String parameter;
66  
67          @PluginBuilderAttribute
68          private String pattern;
69  
70          @PluginBuilderAttribute
71          private String source;
72  
73          @PluginBuilderAttribute
74          @Required(message = "No conversion type provided")
75          private Class<?> type = String.class;
76  
77          @Override
78          public ColumnMapping build() {
79              if (pattern != null) {
80                  layout = PatternLayout.newBuilder()
81                      .withPattern(pattern)
82                      .withConfiguration(configuration)
83                      .withAlwaysWriteExceptions(false)
84                      .build();
85              }
86              if (!(layout == null
87                  || literal == null
88                  || Date.class.isAssignableFrom(type)
89                  || ReadOnlyStringMap.class.isAssignableFrom(type)
90                  || ThreadContextMap.class.isAssignableFrom(type)
91                  || ThreadContextStack.class.isAssignableFrom(type))) {
92                  LOGGER.error("No 'layout' or 'literal' value specified and type ({}) is not compatible with ThreadContextMap, ThreadContextStack, or java.util.Date for the mapping", type, this);
93                  return null;
94              }
95              if (literal != null && parameter != null) {
96                  LOGGER.error("Only one of 'literal' or 'parameter' can be set on the column mapping {}", this);
97                  return null;
98              }
99              return new ColumnMapping(name, source, layout, literal, parameter, type);
100         }
101 
102         public Builder setConfiguration(final Configuration configuration) {
103             this.configuration = configuration;
104             return this;
105         }
106 
107         /**
108          * Layout of value to write to database (before type conversion). Not applicable if {@link #setType(Class)} is
109          * a {@link ReadOnlyStringMap}, {@link ThreadContextMap}, or {@link ThreadContextStack}.
110          *
111          * @return this.
112          */
113         public Builder setLayout(final StringLayout layout) {
114             this.layout = layout;
115             return this;
116         }
117 
118         /**
119          * Literal value to use for populating a column. This is generally useful for functions, stored procedures,
120          * etc. No escaping will be done on this value.
121          *
122          * @return this.
123          */
124         public Builder setLiteral(final String literal) {
125             this.literal = literal;
126             return this;
127         }
128 
129         /**
130          * Column name.
131          *
132          * @return this.
133          */
134         public Builder setName(final String name) {
135             this.name = name;
136             return this;
137         }
138 
139         /**
140          * Parameter value to use for populating a column, MUST contain a single parameter marker '?'. This is generally useful for functions, stored procedures,
141          * etc. No escaping will be done on this value.
142          *
143          * @return this.
144          */
145         public Builder setParameter(final String parameter) {
146             this.parameter= parameter;
147             return this;
148         }
149 
150         /**
151          * Pattern to use as a {@link PatternLayout}. Convenient shorthand for {@link #setLayout(StringLayout)} with a
152          * PatternLayout.
153          *
154          * @return this.
155          */
156         public Builder setPattern(final String pattern) {
157             this.pattern = pattern;
158             return this;
159         }
160 
161         /**
162          * Source name. Useful when combined with a {@link org.apache.logging.log4j.message.MapMessage} depending on the
163          * appender.
164          *
165          * @return this.
166          */
167         public Builder setSource(final String source) {
168             this.source = source;
169             return this;
170         }
171 
172         /**
173          * Class to convert value to before storing in database. If the type is compatible with {@link ThreadContextMap} or
174          * {@link ReadOnlyStringMap}, then the MDC will be used. If the type is compatible with {@link ThreadContextStack},
175          * then the NDC will be used. If the type is compatible with {@link Date}, then the event timestamp will be used.
176          *
177          * @return this.
178          */
179         public Builder setType(final Class<?> type) {
180             this.type = type;
181             return this;
182         }
183 
184         @Override
185         public String toString() {
186             return "Builder [name=" + name + ", source=" + source + ", literal=" + literal + ", parameter=" + parameter
187                     + ", pattern=" + pattern + ", type=" + type + ", layout=" + layout + "]";
188         }
189     }
190 
191     private static final Logger LOGGER = StatusLogger.getLogger();
192 
193     @PluginBuilderFactory
194     public static Builder newBuilder() {
195         return new Builder();
196     }
197 
198     public static String toKey(final String name) {
199         return name.toUpperCase(Locale.ROOT);
200     }
201 
202     private final StringLayout layout;
203     private final String literalValue;
204     private final String name;
205     private final String nameKey;
206     private final String parameter;
207     private final String source;
208     private final Class<?> type;
209 
210     private ColumnMapping(final String name, final String source, final StringLayout layout, final String literalValue, final String parameter, final Class<?> type) {
211         this.name = name;
212         this.nameKey = toKey(name);
213         this.source = source;
214         this.layout = layout;
215         this.literalValue = literalValue;
216         this.parameter = parameter;
217         this.type = type;
218     }
219 
220     public StringLayout getLayout() {
221         return layout;
222     }
223 
224     public String getLiteralValue() {
225         return literalValue;
226     }
227 
228     public String getName() {
229         return name;
230     }
231 
232     public String getNameKey() {
233         return nameKey;
234     }
235 
236     public String getParameter() {
237         return parameter;
238     }
239 
240     public String getSource() {
241         return source;
242     }
243 
244     public Class<?> getType() {
245         return type;
246     }
247 
248     @Override
249     public String toString() {
250         return "ColumnMapping [name=" + name + ", source=" + source + ", literalValue=" + literalValue + ", parameter="
251                 + parameter + ", type=" + type + ", layout=" + layout + "]";
252     }
253 
254 }