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; 020 021import org.apache.logging.log4j.Logger; 022import org.apache.logging.log4j.core.Core; 023import org.apache.logging.log4j.core.StringLayout; 024import org.apache.logging.log4j.core.config.Configuration; 025import org.apache.logging.log4j.core.config.plugins.Plugin; 026import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute; 027import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory; 028import org.apache.logging.log4j.core.config.plugins.PluginConfiguration; 029import org.apache.logging.log4j.core.config.plugins.PluginElement; 030import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required; 031import org.apache.logging.log4j.core.layout.PatternLayout; 032import org.apache.logging.log4j.spi.ThreadContextMap; 033import org.apache.logging.log4j.spi.ThreadContextStack; 034import org.apache.logging.log4j.status.StatusLogger; 035import org.apache.logging.log4j.util.ReadOnlyStringMap; 036 037/** 038 * A configuration element for specifying a database column name mapping. 039 * 040 * @since 2.8 041 */ 042@Plugin(name = "ColumnMapping", category = Core.CATEGORY_NAME, printObject = true) 043public class ColumnMapping { 044 045 /** 046 * Builder for {@link ColumnMapping}. 047 */ 048 public static class Builder implements org.apache.logging.log4j.core.util.Builder<ColumnMapping> { 049 050 @PluginConfiguration 051 private Configuration configuration; 052 053 @PluginElement("Layout") 054 private StringLayout layout; 055 056 @PluginBuilderAttribute 057 private String literal; 058 059 @PluginBuilderAttribute 060 @Required(message = "No column name provided") 061 private String name; 062 063 @PluginBuilderAttribute 064 private String parameter; 065 066 @PluginBuilderAttribute 067 private String pattern; 068 069 @PluginBuilderAttribute 070 private String source; 071 072 @PluginBuilderAttribute 073 @Required(message = "No conversion type provided") 074 private Class<?> type = String.class; 075 076 @Override 077 public ColumnMapping build() { 078 if (pattern != null) { 079 layout = PatternLayout.newBuilder() 080 .withPattern(pattern) 081 .withConfiguration(configuration) 082 .build(); 083 } 084 if (!(layout == null 085 || literal == null 086 || Date.class.isAssignableFrom(type) 087 || ReadOnlyStringMap.class.isAssignableFrom(type) 088 || ThreadContextMap.class.isAssignableFrom(type) 089 || ThreadContextStack.class.isAssignableFrom(type))) { 090 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); 091 return null; 092 } 093 if (literal != null && parameter != null) { 094 LOGGER.error("Only one of 'literal' or 'parameter' can be set on the column mapping {}", this); 095 return null; 096 } 097 return new ColumnMapping(name, source, layout, literal, parameter, type); 098 } 099 100 public Builder setConfiguration(final Configuration configuration) { 101 this.configuration = configuration; 102 return this; 103 } 104 105 /** 106 * Layout of value to write to database (before type conversion). Not applicable if {@link #setType(Class)} is 107 * a {@link ReadOnlyStringMap}, {@link ThreadContextMap}, or {@link ThreadContextStack}. 108 * 109 * @return this. 110 */ 111 public Builder setLayout(final StringLayout layout) { 112 this.layout = layout; 113 return this; 114 } 115 116 /** 117 * Literal value to use for populating a column. This is generally useful for functions, stored procedures, 118 * etc. No escaping will be done on this value. 119 * 120 * @return this. 121 */ 122 public Builder setLiteral(final String literal) { 123 this.literal = literal; 124 return this; 125 } 126 127 /** 128 * Column name. 129 * 130 * @return this. 131 */ 132 public Builder setName(final String name) { 133 this.name = name; 134 return this; 135 } 136 137 /** 138 * Parameter value to use for populating a column, MUST contain a single parameter marker '?'. This is generally useful for functions, stored procedures, 139 * etc. No escaping will be done on this value. 140 * 141 * @return this. 142 */ 143 public Builder setParameter(final String parameter) { 144 this.parameter= parameter; 145 return this; 146 } 147 148 /** 149 * Pattern to use as a {@link PatternLayout}. Convenient shorthand for {@link #setLayout(StringLayout)} with a 150 * PatternLayout. 151 * 152 * @return this. 153 */ 154 public Builder setPattern(final String pattern) { 155 this.pattern = pattern; 156 return this; 157 } 158 159 /** 160 * Source name. Useful when combined with a {@link org.apache.logging.log4j.message.MapMessage} depending on the 161 * appender. 162 * 163 * @return this. 164 */ 165 public Builder setSource(final String source) { 166 this.source = source; 167 return this; 168 } 169 170 /** 171 * Class to convert value to before storing in database. If the type is compatible with {@link ThreadContextMap} or 172 * {@link ReadOnlyStringMap}, then the MDC will be used. If the type is compatible with {@link ThreadContextStack}, 173 * then the NDC will be used. If the type is compatible with {@link Date}, then the event timestamp will be used. 174 * 175 * @return this. 176 */ 177 public Builder setType(final Class<?> type) { 178 this.type = type; 179 return this; 180 } 181 182 @Override 183 public String toString() { 184 return "Builder [name=" + name + ", source=" + source + ", literal=" + literal + ", parameter=" + parameter 185 + ", pattern=" + pattern + ", type=" + type + ", layout=" + layout + "]"; 186 } 187 } 188 189 private static final Logger LOGGER = StatusLogger.getLogger(); 190 @PluginBuilderFactory 191 public static Builder newBuilder() { 192 return new Builder(); 193 } 194 195 private final StringLayout layout; 196 private final String literalValue; 197 private final String name; 198 private final String parameter; 199 private final String source; 200 private final Class<?> type; 201 202 private ColumnMapping(final String name, final String source, final StringLayout layout, final String literalValue, final String parameter, final Class<?> type) { 203 this.name = name; 204 this.source = source; 205 this.layout = layout; 206 this.literalValue = literalValue; 207 this.parameter = parameter; 208 this.type = type; 209 } 210 211 public StringLayout getLayout() { 212 return layout; 213 } 214 215 public String getLiteralValue() { 216 return literalValue; 217 } 218 219 public String getName() { 220 return name; 221 } 222 223 public String getParameter() { 224 return parameter; 225 } 226 227 public String getSource() { 228 return source; 229 } 230 231 public Class<?> getType() { 232 return type; 233 } 234 235 @Override 236 public String toString() { 237 return "ColumnMapping [name=" + name + ", source=" + source + ", literalValue=" + literalValue + ", parameter=" 238 + parameter + ", type=" + type + ", layout=" + layout + "]"; 239 } 240 241}