1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package org.apache.logging.log4j.core.appender.db.jdbc;
18
19 import java.io.StringReader;
20 import java.sql.Connection;
21 import java.sql.PreparedStatement;
22 import java.sql.SQLException;
23 import java.sql.Timestamp;
24 import java.util.ArrayList;
25 import java.util.List;
26
27 import org.apache.logging.log4j.core.LogEvent;
28 import org.apache.logging.log4j.core.appender.ManagerFactory;
29 import org.apache.logging.log4j.core.appender.db.AbstractDatabaseManager;
30 import org.apache.logging.log4j.core.layout.PatternLayout;
31
32
33
34
35 public final class JDBCDatabaseManager extends AbstractDatabaseManager {
36 private static final JDBCDatabaseManagerFactory FACTORY = new JDBCDatabaseManagerFactory();
37
38 private final List<Column> columns;
39 private final ConnectionSource connectionSource;
40 private final String sqlStatement;
41
42 private Connection connection;
43 private PreparedStatement statement;
44
45 private JDBCDatabaseManager(final String name, final int bufferSize, final ConnectionSource connectionSource,
46 final String sqlStatement, final List<Column> columns) {
47 super(name, bufferSize);
48 this.connectionSource = connectionSource;
49 this.sqlStatement = sqlStatement;
50 this.columns = columns;
51 }
52
53 @Override
54 protected void connectInternal() {
55 try {
56 this.connection = this.connectionSource.getConnection();
57 this.statement = this.connection.prepareStatement(this.sqlStatement);
58 } catch (final SQLException e) {
59 LOGGER.error("Failed to connect to relational database using JDBC connection source [{}] in manager [{}].",
60 this.connectionSource, this.getName(), e);
61 }
62 }
63
64 @Override
65 protected void disconnectInternal() {
66 try {
67 if (this.statement != null && !this.statement.isClosed()) {
68 this.statement.close();
69 }
70 } catch (final SQLException e) {
71 LOGGER.warn("Error while closing prepared statement in database manager [{}].", this.getName(), e);
72 }
73
74 try {
75 if (this.connection != null && !this.connection.isClosed()) {
76 this.connection.close();
77 }
78 } catch (final SQLException e) {
79 LOGGER.warn("Error while disconnecting from relational database in manager [{}].", this.getName(), e);
80 }
81 }
82
83 @Override
84 protected void writeInternal(final LogEvent event) {
85 StringReader reader = null;
86 try {
87 if (!this.isConnected() || this.connection == null || this.connection.isClosed()) {
88 LOGGER.error("Cannot write logging event; manager [{}] not connected to the database.", this.getName());
89 return;
90 }
91
92 int i = 1;
93 for (final Column column : this.columns) {
94 if (column.isEventTimestamp) {
95 this.statement.setTimestamp(i++, new Timestamp(event.getMillis()));
96 } else {
97 if (column.isClob) {
98 reader = new StringReader(column.layout.toSerializable(event));
99 if (column.isUnicode) {
100 this.statement.setNClob(i++, reader);
101 } else {
102 this.statement.setClob(i++, reader);
103 }
104 } else {
105 if (column.isUnicode) {
106 this.statement.setNString(i++, column.layout.toSerializable(event));
107 } else {
108 this.statement.setString(i++, column.layout.toSerializable(event));
109 }
110 }
111 }
112 }
113
114 if (this.statement.executeUpdate() == 0) {
115 LOGGER.warn("No records inserted in database table for log event in manager [{}].", this.getName());
116 }
117 } catch (final SQLException e) {
118 LOGGER.error("Failed to insert record for log event in manager [{}].", this.getName(), e);
119 } finally {
120 if (reader != null) {
121 reader.close();
122 }
123 }
124 }
125
126
127
128
129
130
131
132
133
134
135
136 public static JDBCDatabaseManager getJDBCDatabaseManager(final String name, final int bufferSize,
137 final ConnectionSource connectionSource,
138 final String tableName,
139 final ColumnConfig[] columnConfigs) {
140
141 return AbstractDatabaseManager.getManager(
142 name, new FactoryData(bufferSize, connectionSource, tableName, columnConfigs), FACTORY
143 );
144 }
145
146
147
148
149 private static final class FactoryData extends AbstractDatabaseManager.AbstractFactoryData {
150 private final ColumnConfig[] columnConfigs;
151 private final ConnectionSource connectionSource;
152 private final String tableName;
153
154 protected FactoryData(final int bufferSize, final ConnectionSource connectionSource, final String tableName,
155 final ColumnConfig[] columnConfigs) {
156 super(bufferSize);
157 this.connectionSource = connectionSource;
158 this.tableName = tableName;
159 this.columnConfigs = columnConfigs;
160 }
161 }
162
163
164
165
166 private static final class JDBCDatabaseManagerFactory implements ManagerFactory<JDBCDatabaseManager, FactoryData> {
167 @Override
168 public JDBCDatabaseManager createManager(final String name, final FactoryData data) {
169 final StringBuilder columnPart = new StringBuilder();
170 final StringBuilder valuePart = new StringBuilder();
171 final List<Column> columns = new ArrayList<Column>();
172 int i = 0;
173 for (final ColumnConfig config : data.columnConfigs) {
174 if (i++ > 0) {
175 columnPart.append(',');
176 valuePart.append(',');
177 }
178
179 columnPart.append(config.getColumnName());
180
181 if (config.getLiteralValue() != null) {
182 valuePart.append(config.getLiteralValue());
183 } else {
184 columns.add(new Column(
185 config.getLayout(), config.isEventTimestamp(), config.isUnicode(), config.isClob()
186 ));
187 valuePart.append('?');
188 }
189 }
190
191 final String sqlStatement = "INSERT INTO " + data.tableName + " (" + columnPart + ") VALUES (" +
192 valuePart + ")";
193
194 return new JDBCDatabaseManager(name, data.getBufferSize(), data.connectionSource, sqlStatement, columns);
195 }
196 }
197
198
199
200
201 private static final class Column {
202 private final PatternLayout layout;
203 private final boolean isEventTimestamp;
204 private final boolean isUnicode;
205 private final boolean isClob;
206
207 private Column(final PatternLayout layout, final boolean isEventDate, final boolean isUnicode,
208 final boolean isClob) {
209 this.layout = layout;
210 this.isEventTimestamp = isEventDate;
211 this.isUnicode = isUnicode;
212 this.isClob = isClob;
213 }
214 }
215 }