1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.log4j.db;
19
20 import org.apache.log4j.AppenderSkeleton;
21 import org.apache.log4j.db.dialect.SQLDialect;
22 import org.apache.log4j.db.dialect.Util;
23 import org.apache.log4j.helpers.LogLog;
24 import org.apache.log4j.spi.LocationInfo;
25 import org.apache.log4j.spi.LoggingEvent;
26 import org.apache.log4j.xml.DOMConfigurator;
27 import org.apache.log4j.xml.UnrecognizedElementHandler;
28 import org.w3c.dom.Element;
29
30 import java.lang.reflect.InvocationTargetException;
31 import java.lang.reflect.Method;
32 import java.sql.Connection;
33 import java.sql.PreparedStatement;
34 import java.sql.ResultSet;
35 import java.sql.SQLException;
36 import java.sql.Statement;
37 import java.util.Iterator;
38 import java.util.Properties;
39 import java.util.Set;
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120 public class DBAppender extends AppenderSkeleton implements UnrecognizedElementHandler {
121 static final String insertPropertiesSQL =
122 "INSERT INTO logging_event_property (event_id, mapped_key, mapped_value) VALUES (?, ?, ?)";
123 static final String insertExceptionSQL =
124 "INSERT INTO logging_event_exception (event_id, i, trace_line) VALUES (?, ?, ?)";
125 static final String insertSQL;
126 private static final Method GET_GENERATED_KEYS_METHOD;
127
128
129 static {
130 StringBuffer sql = new StringBuffer();
131 sql.append("INSERT INTO logging_event (");
132 sql.append("sequence_number, ");
133 sql.append("timestamp, ");
134 sql.append("rendered_message, ");
135 sql.append("logger_name, ");
136 sql.append("level_string, ");
137 sql.append("ndc, ");
138 sql.append("thread_name, ");
139 sql.append("reference_flag, ");
140 sql.append("caller_filename, ");
141 sql.append("caller_class, ");
142 sql.append("caller_method, ");
143 sql.append("caller_line) ");
144 sql.append(" VALUES (?, ?, ? ,?, ?, ?, ?, ?, ?, ?, ?, ?)");
145 insertSQL = sql.toString();
146
147
148
149 Method getGeneratedKeysMethod;
150 try {
151 getGeneratedKeysMethod = PreparedStatement.class.getMethod("getGeneratedKeys", null);
152 } catch(Exception ex) {
153 getGeneratedKeysMethod = null;
154 }
155 GET_GENERATED_KEYS_METHOD = getGeneratedKeysMethod;
156 }
157
158 ConnectionSource connectionSource;
159 boolean cnxSupportsGetGeneratedKeys = false;
160 boolean cnxSupportsBatchUpdates = false;
161 SQLDialect sqlDialect;
162 boolean locationInfo = false;
163
164
165 public DBAppender() {
166 super(false);
167 }
168
169 public void activateOptions() {
170 LogLog.debug("DBAppender.activateOptions called");
171
172 if (connectionSource == null) {
173 throw new IllegalStateException(
174 "DBAppender cannot function without a connection source");
175 }
176
177 sqlDialect = Util.getDialectFromCode(connectionSource.getSQLDialectCode());
178 if (GET_GENERATED_KEYS_METHOD != null) {
179 cnxSupportsGetGeneratedKeys = connectionSource.supportsGetGeneratedKeys();
180 } else {
181 cnxSupportsGetGeneratedKeys = false;
182 }
183 cnxSupportsBatchUpdates = connectionSource.supportsBatchUpdates();
184 if (!cnxSupportsGetGeneratedKeys && (sqlDialect == null)) {
185 throw new IllegalStateException(
186 "DBAppender cannot function if the JDBC driver does not support getGeneratedKeys method *and* without a specific SQL dialect");
187 }
188
189
190 super.activateOptions();
191 }
192
193
194
195
196 public ConnectionSource getConnectionSource() {
197 return connectionSource;
198 }
199
200
201
202
203
204 public void setConnectionSource(ConnectionSource connectionSource) {
205 LogLog.debug("setConnectionSource called for DBAppender");
206 this.connectionSource = connectionSource;
207 }
208
209 protected void append(LoggingEvent event) {
210 Connection connection = null;
211 try {
212 connection = connectionSource.getConnection();
213 connection.setAutoCommit(false);
214
215 PreparedStatement insertStatement;
216 if (cnxSupportsGetGeneratedKeys) {
217 insertStatement = connection.prepareStatement(insertSQL, Statement.RETURN_GENERATED_KEYS);
218 } else {
219 insertStatement = connection.prepareStatement(insertSQL);
220 }
221
222
223 insertStatement.setLong(1, 0);
224
225 insertStatement.setLong(2, event.getTimeStamp());
226 insertStatement.setString(3, event.getRenderedMessage());
227 insertStatement.setString(4, event.getLoggerName());
228 insertStatement.setString(5, event.getLevel().toString());
229 insertStatement.setString(6, event.getNDC());
230 insertStatement.setString(7, event.getThreadName());
231 insertStatement.setShort(8, DBHelper.computeReferenceMask(event));
232
233 LocationInfo li;
234
235 if (event.locationInformationExists() || locationInfo) {
236 li = event.getLocationInformation();
237 } else {
238 li = LocationInfo.NA_LOCATION_INFO;
239 }
240
241 insertStatement.setString(9, li.getFileName());
242 insertStatement.setString(10, li.getClassName());
243 insertStatement.setString(11, li.getMethodName());
244 insertStatement.setString(12, li.getLineNumber());
245
246 int updateCount = insertStatement.executeUpdate();
247 if (updateCount != 1) {
248 LogLog.warn("Failed to insert loggingEvent");
249 }
250
251 ResultSet rs = null;
252 Statement idStatement = null;
253 boolean gotGeneratedKeys = false;
254 if (cnxSupportsGetGeneratedKeys) {
255 try {
256 rs = (ResultSet) GET_GENERATED_KEYS_METHOD.invoke(insertStatement, null);
257 gotGeneratedKeys = true;
258 } catch(InvocationTargetException ex) {
259 Throwable target = ex.getTargetException();
260 if (target instanceof SQLException) {
261 throw (SQLException) target;
262 }
263 throw ex;
264 } catch(IllegalAccessException ex) {
265 LogLog.warn("IllegalAccessException invoking PreparedStatement.getGeneratedKeys", ex);
266 }
267 }
268
269 if (!gotGeneratedKeys) {
270 insertStatement.close();
271 insertStatement = null;
272
273 idStatement = connection.createStatement();
274 idStatement.setMaxRows(1);
275 rs = idStatement.executeQuery(sqlDialect.getSelectInsertId());
276 }
277
278
279
280 rs.next();
281 int eventId = rs.getInt(1);
282
283 rs.close();
284
285
286 if(insertStatement != null) {
287 insertStatement.close();
288 insertStatement = null;
289 }
290
291 if(idStatement != null) {
292 idStatement.close();
293 idStatement = null;
294 }
295
296 Set propertiesKeys = event.getPropertyKeySet();
297
298 if (propertiesKeys.size() > 0) {
299 PreparedStatement insertPropertiesStatement =
300 connection.prepareStatement(insertPropertiesSQL);
301
302 for (Iterator i = propertiesKeys.iterator(); i.hasNext();) {
303 String key = (String) i.next();
304 String value = (String) event.getProperty(key);
305
306
307 insertPropertiesStatement.setInt(1, eventId);
308 insertPropertiesStatement.setString(2, key);
309 insertPropertiesStatement.setString(3, value);
310
311 if (cnxSupportsBatchUpdates) {
312 insertPropertiesStatement.addBatch();
313 } else {
314 insertPropertiesStatement.execute();
315 }
316 }
317
318 if (cnxSupportsBatchUpdates) {
319 insertPropertiesStatement.executeBatch();
320 }
321
322 insertPropertiesStatement.close();
323 insertPropertiesStatement = null;
324 }
325
326 String[] strRep = event.getThrowableStrRep();
327
328 if (strRep != null) {
329 LogLog.debug("Logging an exception");
330
331 PreparedStatement insertExceptionStatement =
332 connection.prepareStatement(insertExceptionSQL);
333
334 for (short i = 0; i < strRep.length; i++) {
335 insertExceptionStatement.setInt(1, eventId);
336 insertExceptionStatement.setShort(2, i);
337 insertExceptionStatement.setString(3, strRep[i]);
338 if (cnxSupportsBatchUpdates) {
339 insertExceptionStatement.addBatch();
340 } else {
341 insertExceptionStatement.execute();
342 }
343 }
344 if (cnxSupportsBatchUpdates) {
345 insertExceptionStatement.executeBatch();
346 }
347 insertExceptionStatement.close();
348 insertExceptionStatement = null;
349 }
350
351 connection.commit();
352 } catch (Throwable sqle) {
353 LogLog.error("problem appending event", sqle);
354 } finally {
355 DBHelper.closeConnection(connection);
356 }
357 }
358
359 public void close() {
360 closed = true;
361 }
362
363
364
365
366
367 public boolean getLocationInfo() {
368 return locationInfo;
369 }
370
371
372
373
374
375
376 public void setLocationInfo(boolean locationInfo) {
377 this.locationInfo = locationInfo;
378 }
379
380
381
382
383
384 public boolean requiresLayout() {
385 return false;
386 }
387
388
389
390
391 public boolean parseUnrecognizedElement(Element element, Properties props) throws Exception {
392 if ("connectionSource".equals(element.getNodeName())) {
393 Object instance =
394 DOMConfigurator.parseElement(element, props, ConnectionSource.class);
395 if (instance instanceof ConnectionSource) {
396 ConnectionSource source = (ConnectionSource) instance;
397 source.activateOptions();
398 setConnectionSource(source);
399 }
400 return true;
401 }
402 return false;
403 }
404 }