001/* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache license, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the license for the specific language governing permissions and 015 * limitations under the license. 016 */ 017package org.apache.logging.log4j.core.appender.db; 018 019import java.util.Date; 020import java.util.Locale; 021 022import org.apache.logging.log4j.Logger; 023import org.apache.logging.log4j.core.Core; 024import org.apache.logging.log4j.core.StringLayout; 025import org.apache.logging.log4j.core.config.Configuration; 026import org.apache.logging.log4j.core.config.plugins.Plugin; 027import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; 028import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; 029import org.apache.logging.log4j.core.config.plugins.PluginConfiguration; 030import org.apache.logging.log4j.core.config.plugins.PluginElement; 031import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required; 032import org.apache.logging.log4j.core.layout.PatternLayout; 033import org.apache.logging.log4j.spi.ThreadContextMap; 034import org.apache.logging.log4j.spi.ThreadContextStack; 035import org.apache.logging.log4j.status.StatusLogger; 036import org.apache.logging.log4j.util.ReadOnlyStringMap; 037 038/** 039 * A configuration element for specifying a database column name mapping. 040 * 041 * @since 2.8 042 */ 043@Plugin(name = "ColumnMapping", category = Core.CATEGORY_NAME, printObject = true) 044public class ColumnMapping { 045 046 /** 047 * The empty array. 048 */ 049 public static final ColumnMapping[] EMPTY_ARRAY = {}; 050 051 /** 052 * Builder for {@link ColumnMapping}. 053 */ 054 public static class Builder implements org.apache.logging.log4j.core.util.Builder<ColumnMapping> { 055 056 @PluginConfiguration 057 private Configuration configuration; 058 059 @PluginElement("Layout") 060 private StringLayout layout; 061 062 @PluginBuilderAttribute 063 private String literal; 064 065 @PluginBuilderAttribute 066 @Required(message = "No column name provided") 067 private String name; 068 069 @PluginBuilderAttribute 070 private String parameter; 071 072 @PluginBuilderAttribute 073 private String pattern; 074 075 @PluginBuilderAttribute 076 private String source; 077 078 @PluginBuilderAttribute 079 @Required(message = "No conversion type provided") 080 private Class<?> type = String.class; 081 082 @Override 083 public ColumnMapping build() { 084 if (pattern != null) { 085 layout = PatternLayout.newBuilder() 086 .withPattern(pattern) 087 .withConfiguration(configuration) 088 .withAlwaysWriteExceptions(false) 089 .build(); 090 } 091 if (!(layout == null 092 || literal == null 093 || Date.class.isAssignableFrom(type) 094 || ReadOnlyStringMap.class.isAssignableFrom(type) 095 || ThreadContextMap.class.isAssignableFrom(type) 096 || ThreadContextStack.class.isAssignableFrom(type))) { 097 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); 098 return null; 099 } 100 if (literal != null && parameter != null) { 101 LOGGER.error("Only one of 'literal' or 'parameter' can be set on the column mapping {}", this); 102 return null; 103 } 104 return new ColumnMapping(name, source, layout, literal, parameter, type); 105 } 106 107 public Builder setConfiguration(final Configuration configuration) { 108 this.configuration = configuration; 109 return this; 110 } 111 112 /** 113 * Layout of value to write to database (before type conversion). Not applicable if {@link #setType(Class)} is 114 * a {@link ReadOnlyStringMap}, {@link ThreadContextMap}, or {@link ThreadContextStack}. 115 * 116 * @return this. 117 */ 118 public Builder setLayout(final StringLayout layout) { 119 this.layout = layout; 120 return this; 121 } 122 123 /** 124 * Literal value to use for populating a column. This is generally useful for functions, stored procedures, 125 * etc. No escaping will be done on this value. 126 * 127 * @return this. 128 */ 129 public Builder setLiteral(final String literal) { 130 this.literal = literal; 131 return this; 132 } 133 134 /** 135 * Column name. 136 * 137 * @return this. 138 */ 139 public Builder setName(final String name) { 140 this.name = name; 141 return this; 142 } 143 144 /** 145 * Parameter value to use for populating a column, MUST contain a single parameter marker '?'. This is generally useful for functions, stored procedures, 146 * etc. No escaping will be done on this value. 147 * 148 * @return this. 149 */ 150 public Builder setParameter(final String parameter) { 151 this.parameter= parameter; 152 return this; 153 } 154 155 /** 156 * Pattern to use as a {@link PatternLayout}. Convenient shorthand for {@link #setLayout(StringLayout)} with a 157 * PatternLayout. 158 * 159 * @return this. 160 */ 161 public Builder setPattern(final String pattern) { 162 this.pattern = pattern; 163 return this; 164 } 165 166 /** 167 * Source name. Useful when combined with a {@link org.apache.logging.log4j.message.MapMessage} depending on the 168 * appender. 169 * 170 * @return this. 171 */ 172 public Builder setSource(final String source) { 173 this.source = source; 174 return this; 175 } 176 177 /** 178 * Class to convert value to before storing in database. If the type is compatible with {@link ThreadContextMap} or 179 * {@link ReadOnlyStringMap}, then the MDC will be used. If the type is compatible with {@link ThreadContextStack}, 180 * then the NDC will be used. If the type is compatible with {@link Date}, then the event timestamp will be used. 181 * 182 * @return this. 183 */ 184 public Builder setType(final Class<?> type) { 185 this.type = type; 186 return this; 187 } 188 189 @Override 190 public String toString() { 191 return "Builder [name=" + name + ", source=" + source + ", literal=" + literal + ", parameter=" + parameter 192 + ", pattern=" + pattern + ", type=" + type + ", layout=" + layout + "]"; 193 } 194 } 195 196 private static final Logger LOGGER = StatusLogger.getLogger(); 197 198 @PluginBuilderFactory 199 public static Builder newBuilder() { 200 return new Builder(); 201 } 202 203 public static String toKey(final String name) { 204 return name.toUpperCase(Locale.ROOT); 205 } 206 207 private final StringLayout layout; 208 private final String literalValue; 209 private final String name; 210 private final String nameKey; 211 private final String parameter; 212 private final String source; 213 private final Class<?> type; 214 215 private ColumnMapping(final String name, final String source, final StringLayout layout, final String literalValue, final String parameter, final Class<?> type) { 216 this.name = name; 217 this.nameKey = toKey(name); 218 this.source = source; 219 this.layout = layout; 220 this.literalValue = literalValue; 221 this.parameter = parameter; 222 this.type = type; 223 } 224 225 public StringLayout getLayout() { 226 return layout; 227 } 228 229 public String getLiteralValue() { 230 return literalValue; 231 } 232 233 public String getName() { 234 return name; 235 } 236 237 public String getNameKey() { 238 return nameKey; 239 } 240 241 public String getParameter() { 242 return parameter; 243 } 244 245 public String getSource() { 246 return source; 247 } 248 249 public Class<?> getType() { 250 return type; 251 } 252 253 @Override 254 public String toString() { 255 return "ColumnMapping [name=" + name + ", source=" + source + ", literalValue=" + literalValue + ", parameter=" 256 + parameter + ", type=" + type + ", layout=" + layout + "]"; 257 } 258 259}