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  
21  import org.apache.logging.log4j.Logger;
22  import org.apache.logging.log4j.core.Core;
23  import org.apache.logging.log4j.core.StringLayout;
24  import org.apache.logging.log4j.core.config.Configuration;
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.config.plugins.validation.constraints.Required;
31  import org.apache.logging.log4j.core.layout.PatternLayout;
32  import org.apache.logging.log4j.spi.ThreadContextMap;
33  import org.apache.logging.log4j.spi.ThreadContextStack;
34  import org.apache.logging.log4j.status.StatusLogger;
35  import org.apache.logging.log4j.util.ReadOnlyStringMap;
36  
37  /**
38   * A configuration element for specifying a database column name mapping.
39   *
40   * @since 2.8
41   */
42  @Plugin(name = "ColumnMapping", category = Core.CATEGORY_NAME, printObject = true)
43  public class ColumnMapping {
44  
45      private static final Logger LOGGER = StatusLogger.getLogger();
46  
47      private final String name;
48      private final StringLayout layout;
49      private final String literalValue;
50      private final Class<?> type;
51  
52      private ColumnMapping(final String name, final StringLayout layout, final String literalValue, final Class<?> type) {
53          this.name = name;
54          this.layout = layout;
55          this.literalValue = literalValue;
56          this.type = type;
57      }
58  
59      public String getName() {
60          return name;
61      }
62  
63      public StringLayout getLayout() {
64          return layout;
65      }
66  
67      public String getLiteralValue() {
68          return literalValue;
69      }
70  
71      public Class<?> getType() {
72          return type;
73      }
74  
75      @PluginBuilderFactory
76      public static Builder newBuilder() {
77          return new Builder();
78      }
79  
80      public static class Builder implements org.apache.logging.log4j.core.util.Builder<ColumnMapping> {
81  
82          @PluginBuilderAttribute
83          @Required(message = "No column name provided")
84          private String name;
85  
86          @PluginElement("Layout")
87          private StringLayout layout;
88  
89          @PluginBuilderAttribute
90          private String pattern;
91  
92          @PluginBuilderAttribute
93          private String literal;
94  
95          @PluginBuilderAttribute
96          @Required(message = "No conversion type provided")
97          private Class<?> type = String.class;
98  
99          @PluginConfiguration
100         private Configuration configuration;
101 
102         /**
103          * Column name.
104          */
105         public Builder setName(final String name) {
106             this.name = name;
107             return this;
108         }
109 
110         /**
111          * Layout of value to write to database (before type conversion). Not applicable if {@link #setType(Class)} is
112          * a {@link ReadOnlyStringMap}, {@link ThreadContextMap}, or {@link ThreadContextStack}.
113          */
114         public Builder setLayout(final StringLayout layout) {
115             this.layout = layout;
116             return this;
117         }
118 
119         /**
120          * Pattern to use as a {@link PatternLayout}. Convenient shorthand for {@link #setLayout(StringLayout)} with a
121          * PatternLayout.
122          */
123         public Builder setPattern(final String pattern) {
124             this.pattern = pattern;
125             return this;
126         }
127 
128         /**
129          * Literal value to use for populating a column. This is generally useful for functions, stored procedures,
130          * etc. No escaping will be done on this value.
131          */
132         public Builder setLiteral(final String literal) {
133             this.literal = literal;
134             return this;
135         }
136 
137         /**
138          * Class to convert value to before storing in database. If the type is compatible with {@link ThreadContextMap} or
139          * {@link ReadOnlyStringMap}, then the MDC will be used. If the type is compatible with {@link ThreadContextStack},
140          * then the NDC will be used. If the type is compatible with {@link Date}, then the event timestamp will be used.
141          */
142         public Builder setType(final Class<?> type) {
143             this.type = type;
144             return this;
145         }
146 
147         public Builder setConfiguration(final Configuration configuration) {
148             this.configuration = configuration;
149             return this;
150         }
151 
152         @Override
153         public ColumnMapping build() {
154             if (pattern != null) {
155                 layout = PatternLayout.newBuilder()
156                     .withPattern(pattern)
157                     .withConfiguration(configuration)
158                     .build();
159             }
160             if (!(layout != null
161                 || literal != null
162                 || Date.class.isAssignableFrom(type)
163                 || ReadOnlyStringMap.class.isAssignableFrom(type)
164                 || ThreadContextMap.class.isAssignableFrom(type)
165                 || ThreadContextStack.class.isAssignableFrom(type))) {
166                 LOGGER.error("No layout or literal value specified and type ({}) is not compatible with " +
167                     "ThreadContextMap, ThreadContextStack, or java.util.Date", type);
168                 return null;
169             }
170             return new ColumnMapping(name, layout, literal, type);
171         }
172     }
173 }