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 * Builder for {@link ColumnMapping}. 048 */ 049 public static class Builder implements org.apache.logging.log4j.core.util.Builder<ColumnMapping> { 050 051 @PluginConfiguration 052 private Configuration configuration; 053 054 @PluginElement("Layout") 055 private StringLayout layout; 056 057 @PluginBuilderAttribute 058 private String literal; 059 060 @PluginBuilderAttribute 061 @Required(message = "No column name provided") 062 private String name; 063 064 @PluginBuilderAttribute 065 private String parameter; 066 067 @PluginBuilderAttribute 068 private String pattern; 069 070 @PluginBuilderAttribute 071 private String source; 072 073 @PluginBuilderAttribute 074 @Required(message = "No conversion type provided") 075 private Class<?> type = String.class; 076 077 @Override 078 public ColumnMapping build() { 079 if (pattern != null) { 080 layout = PatternLayout.newBuilder() 081 .withPattern(pattern) 082 .withConfiguration(configuration) 083 .withAlwaysWriteExceptions(false) 084 .build(); 085 } 086 if (!(layout == null 087 || literal == null 088 || Date.class.isAssignableFrom(type) 089 || ReadOnlyStringMap.class.isAssignableFrom(type) 090 || ThreadContextMap.class.isAssignableFrom(type) 091 || ThreadContextStack.class.isAssignableFrom(type))) { 092 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); 093 return null; 094 } 095 if (literal != null && parameter != null) { 096 LOGGER.error("Only one of 'literal' or 'parameter' can be set on the column mapping {}", this); 097 return null; 098 } 099 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}