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.jdbc;
18  
19  import java.io.Serializable;
20  import java.nio.charset.Charset;
21  import java.sql.PreparedStatement;
22  import java.util.Arrays;
23  import java.util.Objects;
24  
25  import org.apache.logging.log4j.core.Appender;
26  import org.apache.logging.log4j.core.Core;
27  import org.apache.logging.log4j.core.Filter;
28  import org.apache.logging.log4j.core.Layout;
29  import org.apache.logging.log4j.core.appender.AbstractAppender;
30  import org.apache.logging.log4j.core.appender.db.AbstractDatabaseAppender;
31  import org.apache.logging.log4j.core.appender.db.ColumnMapping;
32  import org.apache.logging.log4j.core.config.plugins.Plugin;
33  import org.apache.logging.log4j.core.config.plugins.PluginBuilderAttribute;
34  import org.apache.logging.log4j.core.config.plugins.PluginBuilderFactory;
35  import org.apache.logging.log4j.core.config.plugins.PluginElement;
36  import org.apache.logging.log4j.core.config.plugins.convert.TypeConverter;
37  import org.apache.logging.log4j.core.config.plugins.validation.constraints.Required;
38  import org.apache.logging.log4j.core.util.Assert;
39  import org.apache.logging.log4j.core.util.Booleans;
40  
41  /**
42   * This Appender writes logging events to a relational database using standard JDBC mechanisms. It takes a list of
43   * {@link ColumnConfig}s and/or {@link ColumnMapping}s with which it determines how to save the event data into the
44   * appropriate columns in the table. ColumnMapping is new as of Log4j 2.8 and supports
45   * {@linkplain TypeConverter type conversion} and persistence using {@link PreparedStatement#setObject(int, Object)}.
46   * A {@link ConnectionSource} plugin instance instructs the appender (and {@link JdbcDatabaseManager}) how to connect to
47   * the database. This appender can be reconfigured at run time.
48   *
49   * @see ColumnConfig
50   * @see ColumnMapping
51   * @see ConnectionSource
52   */
53  @Plugin(name = "JDBC", category = Core.CATEGORY_NAME, elementType = Appender.ELEMENT_TYPE, printObject = true)
54  public final class JdbcAppender extends AbstractDatabaseAppender<JdbcDatabaseManager> {
55  
56      private final String description;
57  
58      private JdbcAppender(final String name, final Filter filter, final boolean ignoreExceptions,
59                           final JdbcDatabaseManager manager) {
60          super(name, filter, ignoreExceptions, manager);
61          this.description = this.getName() + "{ manager=" + this.getManager() + " }";
62      }
63  
64      @Override
65      public String toString() {
66          return this.description;
67      }
68  
69      /**
70       * Factory method for creating a JDBC appender within the plugin manager.
71       *
72       * @see Builder
73       * @deprecated use {@link #newBuilder()}
74       */
75      @Deprecated
76      public static <B extends Builder<B>> JdbcAppender createAppender(final String name, final String ignore,
77                                                                       final Filter filter,
78                                                                       final ConnectionSource connectionSource,
79                                                                       final String bufferSize, final String tableName,
80                                                                       final ColumnConfig[] columnConfigs) {
81          Assert.requireNonEmpty(name, "Name cannot be empty");
82          Objects.requireNonNull(connectionSource, "ConnectionSource cannot be null");
83          Assert.requireNonEmpty(tableName, "Table name cannot be empty");
84          Assert.requireNonEmpty(columnConfigs, "ColumnConfigs cannot be empty");
85  
86          final int bufferSizeInt = AbstractAppender.parseInt(bufferSize, 0);
87          final boolean ignoreExceptions = Booleans.parseBoolean(ignore, true);
88  
89          return JdbcAppender.<B>newBuilder()
90              .setBufferSize(bufferSizeInt)
91              .setColumnConfigs(columnConfigs)
92              .setConnectionSource(connectionSource)
93              .setTableName(tableName)
94              .withName(name)
95              .withIgnoreExceptions(ignoreExceptions)
96              .withFilter(filter)
97              .build();
98      }
99  
100     @PluginBuilderFactory
101     public static <B extends Builder<B>> B newBuilder() {
102         return new Builder<B>().asBuilder();
103     }
104 
105     public static class Builder<B extends Builder<B>> extends AbstractAppender.Builder<B>
106         implements org.apache.logging.log4j.core.util.Builder<JdbcAppender> {
107 
108         @PluginElement("ConnectionSource")
109         @Required(message = "No ConnectionSource provided")
110         private ConnectionSource connectionSource;
111 
112         @PluginBuilderAttribute
113         private int bufferSize;
114 
115         @PluginBuilderAttribute
116         @Required(message = "No table name provided")
117         private String tableName;
118 
119         @PluginElement("ColumnConfigs")
120         private ColumnConfig[] columnConfigs;
121 
122         @PluginElement("ColumnMappings")
123         private ColumnMapping[] columnMappings;
124 
125         /**
126          * The connections source from which database connections should be retrieved.
127          */
128         public B setConnectionSource(final ConnectionSource connectionSource) {
129             this.connectionSource = connectionSource;
130             return asBuilder();
131         }
132 
133         /**
134          * If an integer greater than 0, this causes the appender to buffer log events and flush whenever the buffer
135          * reaches this size.
136          */
137         public B setBufferSize(final int bufferSize) {
138             this.bufferSize = bufferSize;
139             return asBuilder();
140         }
141 
142         /**
143          * The name of the database table to insert log events into.
144          */
145         public B setTableName(final String tableName) {
146             this.tableName = tableName;
147             return asBuilder();
148         }
149 
150         /**
151          * Information about the columns that log event data should be inserted into and how to insert that data.
152          */
153         public B setColumnConfigs(final ColumnConfig... columnConfigs) {
154             this.columnConfigs = columnConfigs;
155             return asBuilder();
156         }
157 
158         public B setColumnMappings(final ColumnMapping... columnMappings) {
159             this.columnMappings = columnMappings;
160             return asBuilder();
161         }
162 
163         @Override
164         public JdbcAppender build() {
165             if (Assert.isEmpty(columnConfigs) && Assert.isEmpty(columnMappings)) {
166                 LOGGER.error("Cannot create JdbcAppender without any columns configured.");
167                 return null;
168             }
169             final String managerName = "JdbcManager{name=" + getName() + ", bufferSize=" + bufferSize + ", tableName=" +
170                 tableName + ", columnConfigs=" + Arrays.toString(columnConfigs) + ", columnMappings=" +
171                 Arrays.toString(columnMappings) + '}';
172             final JdbcDatabaseManager manager = JdbcDatabaseManager.getManager(managerName, bufferSize,
173                 connectionSource, tableName, columnConfigs, columnMappings);
174             if (manager == null) {
175                 return null;
176             }
177             return new JdbcAppender(getName(), getFilter(), isIgnoreExceptions(), manager);
178         }
179 
180         @Override
181         @Deprecated
182         public Layout<? extends Serializable> getLayout() {
183             throw new UnsupportedOperationException();
184         }
185 
186         @Override
187         @Deprecated
188         public B withLayout(final Layout<? extends Serializable> layout) {
189             throw new UnsupportedOperationException();
190         }
191 
192         @Override
193         @Deprecated
194         public Layout<? extends Serializable> getOrCreateLayout() {
195             throw new UnsupportedOperationException();
196         }
197 
198         @Override
199         @Deprecated
200         public Layout<? extends Serializable> getOrCreateLayout(final Charset charset) {
201             throw new UnsupportedOperationException();
202         }
203     }
204 }