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.jdbc;
018
019import org.apache.logging.log4j.Logger;
020import org.apache.logging.log4j.core.Core;
021import org.apache.logging.log4j.core.appender.db.ColumnMapping;
022import org.apache.logging.log4j.core.config.Configuration;
023import org.apache.logging.log4j.core.config.plugins.Plugin;
024import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
025import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
026import org.apache.logging.log4j.core.config.plugins.PluginConfiguration;
027import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
028import org.apache.logging.log4j.core.layout.PatternLayout;
029import org.apache.logging.log4j.core.util.Booleans;
030import org.apache.logging.log4j.status.StatusLogger;
031import org.apache.logging.log4j.util.Strings;
032
033/**
034 * A configuration element used to configure which event properties are logged to which columns in the database table.
035 *
036 * @see ColumnMapping
037 */
038@Plugin(name = "Column", category = Core.CATEGORY_NAME, printObject = true)
039public final class ColumnConfig {
040    public static class Builder implements org.apache.logging.log4j.core.util.Builder<ColumnConfig> {
041
042        @PluginConfiguration
043        private Configuration configuration;
044
045        @PluginBuilderAttribute
046        @Required(message = "No name provided")
047        private String name;
048
049        @PluginBuilderAttribute
050        private String pattern;
051
052        @PluginBuilderAttribute
053        private String literal;
054
055        @PluginBuilderAttribute
056        private boolean isEventTimestamp;
057
058        @PluginBuilderAttribute
059        private boolean isUnicode = true;
060
061        @PluginBuilderAttribute
062        private boolean isClob;
063
064        @Override
065        public ColumnConfig build() {
066            if (Strings.isEmpty(name)) {
067                LOGGER.error("The column config is not valid because it does not contain a column name.");
068                return null;
069            }
070
071            final boolean isPattern = Strings.isNotEmpty(pattern);
072            final boolean isLiteralValue = Strings.isNotEmpty(literal);
073
074            if ((isPattern && isLiteralValue) || (isPattern && isEventTimestamp) || (isLiteralValue && isEventTimestamp)) {
075                LOGGER.error("The pattern, literal, and isEventTimestamp attributes are mutually exclusive.");
076                return null;
077            }
078
079            if (isEventTimestamp) {
080                return new ColumnConfig(name, null, null, true, false, false);
081            }
082
083            if (isLiteralValue) {
084                return new ColumnConfig(name, null, literal, false, false, false);
085            }
086
087            if (isPattern) {
088                final PatternLayout layout =
089                    PatternLayout.newBuilder()
090                        .withPattern(pattern)
091                        .withConfiguration(configuration)
092                        .withAlwaysWriteExceptions(false)
093                        .build();
094                return new ColumnConfig(name, layout, null, false, isUnicode, isClob);
095            }
096
097            LOGGER.error("To configure a column you must specify a pattern or literal or set isEventDate to true.");
098            return null;
099        }
100
101        /**
102         * If {@code "true"}, indicates that the column is a character LOB (CLOB).
103         *
104         * @return this.
105         */
106        public Builder setClob(final boolean clob) {
107            isClob = clob;
108            return this;
109        }
110
111        /**
112         * The configuration object.
113         *
114         * @return this.
115         */
116        public Builder setConfiguration(final Configuration configuration) {
117            this.configuration = configuration;
118            return this;
119        }
120
121        /**
122         * If {@code "true"}, indicates that this column is a date-time column in which the event timestamp should be
123         * inserted. Mutually exclusive with {@code pattern!=null} and {@code literal!=null}.
124         *
125         * @return this.
126         */
127        public Builder setEventTimestamp(final boolean eventTimestamp) {
128            isEventTimestamp = eventTimestamp;
129            return this;
130        }
131
132        /**
133         * The literal value to insert into the column as-is without any quoting or escaping. Mutually exclusive with
134         * {@code pattern!=null} and {@code eventTimestamp=true}.
135         *
136         * @return this.
137         */
138        public Builder setLiteral(final String literal) {
139            this.literal = literal;
140            return this;
141        }
142
143        /**
144         * The name of the database column as it exists within the database table.
145         *
146         * @return this.
147         */
148        public Builder setName(final String name) {
149            this.name = name;
150            return this;
151        }
152
153        /**
154         * The {@link PatternLayout} pattern to insert in this column. Mutually exclusive with
155         * {@code literal!=null} and {@code eventTimestamp=true}
156         *
157         * @return this.
158         */
159        public Builder setPattern(final String pattern) {
160            this.pattern = pattern;
161            return this;
162        }
163
164        /**
165         * If {@code "true"}, indicates that the column is a Unicode String.
166         *
167         * @return this.
168         */
169        public Builder setUnicode(final boolean unicode) {
170            isUnicode = unicode;
171            return this;
172        }
173    }
174
175    private static final Logger LOGGER = StatusLogger.getLogger();
176    /**
177     * Factory method for creating a column config within the plugin manager.
178     *
179     * @see Builder
180     * @deprecated use {@link #newBuilder()}
181     */
182    @Deprecated
183    public static ColumnConfig createColumnConfig(final Configuration config, final String name, final String pattern,
184                                                  final String literalValue, final String eventTimestamp,
185                                                  final String unicode, final String clob) {
186        if (Strings.isEmpty(name)) {
187            LOGGER.error("The column config is not valid because it does not contain a column name.");
188            return null;
189        }
190
191        final boolean isEventTimestamp = Boolean.parseBoolean(eventTimestamp);
192        final boolean isUnicode = Booleans.parseBoolean(unicode, true);
193        final boolean isClob = Boolean.parseBoolean(clob);
194
195        return newBuilder()
196            .setConfiguration(config)
197            .setName(name)
198            .setPattern(pattern)
199            .setLiteral(literalValue)
200            .setEventTimestamp(isEventTimestamp)
201            .setUnicode(isUnicode)
202            .setClob(isClob)
203            .build();
204    }
205    @PluginBuilderFactory
206    public static Builder newBuilder() {
207        return new Builder();
208    }
209    private final String columnName;
210    private final String columnNameKey;
211    private final PatternLayout layout;
212    private final String literalValue;
213
214    private final boolean eventTimestamp;
215
216    private final boolean unicode;
217
218    private final boolean clob;
219
220    private ColumnConfig(final String columnName, final PatternLayout layout, final String literalValue,
221                         final boolean eventDate, final boolean unicode, final boolean clob) {
222        this.columnName = columnName;
223        this.columnNameKey = ColumnMapping.toKey(columnName);
224        this.layout = layout;
225        this.literalValue = literalValue;
226        this.eventTimestamp = eventDate;
227        this.unicode = unicode;
228        this.clob = clob;
229    }
230
231    public String getColumnName() {
232        return this.columnName;
233    }
234
235    public String getColumnNameKey() {
236        return this.columnNameKey;
237    }
238
239    public PatternLayout getLayout() {
240        return this.layout;
241    }
242
243    public String getLiteralValue() {
244        return this.literalValue;
245    }
246
247    public boolean isClob() {
248        return this.clob;
249    }
250
251    public boolean isEventTimestamp() {
252        return this.eventTimestamp;
253    }
254
255    public boolean isUnicode() {
256        return this.unicode;
257    }
258
259    @Override
260    public String toString() {
261        return "{ name=" + this.columnName + ", layout=" + this.layout + ", literal=" + this.literalValue
262                + ", timestamp=" + this.eventTimestamp + " }";
263    }
264}