1
2
3
4
5
6
7
8
9
10
11
12
13
14 package ch.qos.logback.classic.db;
15
16 import java.lang.reflect.Method;
17 import java.sql.Connection;
18 import java.sql.PreparedStatement;
19 import java.sql.SQLException;
20 import java.util.HashMap;
21 import java.util.Iterator;
22 import java.util.Map;
23 import java.util.Set;
24
25 import ch.qos.logback.classic.db.names.DBNameResolver;
26 import ch.qos.logback.classic.db.names.DefaultDBNameResolver;
27 import ch.qos.logback.classic.spi.ILoggingEvent;
28 import ch.qos.logback.classic.spi.IThrowableProxy;
29 import ch.qos.logback.classic.spi.StackTraceElementProxy;
30 import ch.qos.logback.classic.spi.ThrowableProxyUtil;
31 import ch.qos.logback.core.CoreConstants;
32 import ch.qos.logback.core.db.DBAppenderBase;
33
34
35
36
37
38
39
40
41
42
43
44
45 public class DBAppender extends DBAppenderBase<ILoggingEvent> {
46 protected String insertPropertiesSQL;
47 protected String insertExceptionSQL;
48 protected String insertSQL;
49 protected static final Method GET_GENERATED_KEYS_METHOD;
50
51 private DBNameResolver dbNameResolver;
52
53 static final int TIMESTMP_INDEX = 1;
54 static final int FORMATTED_MESSAGE_INDEX = 2;
55 static final int LOGGER_NAME_INDEX = 3;
56 static final int LEVEL_STRING_INDEX = 4;
57 static final int THREAD_NAME_INDEX = 5;
58 static final int REFERENCE_FLAG_INDEX = 6;
59 static final int ARG0_INDEX = 7;
60 static final int ARG1_INDEX = 8;
61 static final int ARG2_INDEX = 9;
62 static final int ARG3_INDEX = 10;
63 static final int CALLER_FILENAME_INDEX = 11;
64 static final int CALLER_CLASS_INDEX = 12;
65 static final int CALLER_METHOD_INDEX = 13;
66 static final int CALLER_LINE_INDEX = 14;
67 static final int EVENT_ID_INDEX = 15;
68
69 static {
70
71 Method getGeneratedKeysMethod;
72 try {
73
74 getGeneratedKeysMethod = PreparedStatement.class.getMethod(
75 "getGeneratedKeys", (Class[]) null);
76 } catch (Exception ex) {
77 getGeneratedKeysMethod = null;
78 }
79 GET_GENERATED_KEYS_METHOD = getGeneratedKeysMethod;
80 }
81
82 public DBAppender() {
83 }
84
85 public void setDbNameResolver(DBNameResolver dbNameResolver) {
86 this.dbNameResolver = dbNameResolver;
87 }
88
89 @Override
90 public void start() {
91 if (dbNameResolver == null)
92 dbNameResolver = new DefaultDBNameResolver();
93 insertExceptionSQL = SQLBuilder.buildInsertExceptionSQL(dbNameResolver);
94 insertPropertiesSQL = SQLBuilder.buildInsertPropertiesSQL(dbNameResolver);
95 insertSQL = SQLBuilder.buildInsertSQL(dbNameResolver);
96 super.start();
97 }
98
99 @Override
100 protected void subAppend(ILoggingEvent event, Connection connection,
101 PreparedStatement insertStatement) throws Throwable {
102
103 bindLoggingEventWithInsertStatement(insertStatement, event);
104 bindLoggingEventArgumentsWithPreparedStatement(insertStatement, event.getArgumentArray());
105
106
107 bindCallerDataWithPreparedStatement(insertStatement, event.getCallerData());
108
109 int updateCount = insertStatement.executeUpdate();
110 if (updateCount != 1) {
111 addWarn("Failed to insert loggingEvent");
112 }
113 }
114
115 protected void secondarySubAppend(ILoggingEvent event, Connection connection,
116 long eventId) throws Throwable {
117 Map<String, String> mergedMap = mergePropertyMaps(event);
118 insertProperties(mergedMap, connection, eventId);
119
120 if (event.getThrowableProxy() != null) {
121 insertThrowable(event.getThrowableProxy(), connection, eventId);
122 }
123 }
124
125 void bindLoggingEventWithInsertStatement(PreparedStatement stmt,
126 ILoggingEvent event) throws SQLException {
127 stmt.setLong(TIMESTMP_INDEX, event.getTimeStamp());
128 stmt.setString(FORMATTED_MESSAGE_INDEX, event.getFormattedMessage());
129 stmt.setString(LOGGER_NAME_INDEX, event.getLoggerName());
130 stmt.setString(LEVEL_STRING_INDEX, event.getLevel().toString());
131 stmt.setString(THREAD_NAME_INDEX, event.getThreadName());
132 stmt.setShort(REFERENCE_FLAG_INDEX, DBHelper.computeReferenceMask(event));
133 }
134
135 void bindLoggingEventArgumentsWithPreparedStatement(PreparedStatement stmt,
136 Object[] argArray) throws SQLException {
137
138 int arrayLen = argArray != null ? argArray.length : 0;
139
140 for(int i = 0; i < arrayLen && i < 4; i++) {
141 stmt.setString(ARG0_INDEX+i, asStringTruncatedTo254(argArray[i]));
142 }
143 if(arrayLen < 4) {
144 for(int i = arrayLen; i < 4; i++) {
145 stmt.setString(ARG0_INDEX+i, null);
146 }
147 }
148 }
149
150 String asStringTruncatedTo254(Object o) {
151 String s = null;
152 if(o != null) {
153 s= o.toString();
154 }
155
156 if(s == null) {
157 return null;
158 }
159 if(s.length() <= 254) {
160 return s;
161 } else {
162 return s.substring(0, 254);
163 }
164 }
165
166 void bindCallerDataWithPreparedStatement(PreparedStatement stmt,
167 StackTraceElement[] callerDataArray) throws SQLException {
168 StackTraceElement callerData = callerDataArray[0];
169 if (callerData != null) {
170 stmt.setString(CALLER_FILENAME_INDEX, callerData.getFileName());
171 stmt.setString(CALLER_CLASS_INDEX, callerData.getClassName());
172 stmt.setString(CALLER_METHOD_INDEX, callerData.getMethodName());
173 stmt.setString(CALLER_LINE_INDEX, Integer.toString(callerData.getLineNumber()));
174 }
175 }
176
177 Map<String, String> mergePropertyMaps(ILoggingEvent event) {
178 Map<String, String> mergedMap = new HashMap<String, String>();
179
180
181
182 Map<String, String> loggerContextMap = event.getLoggerContextVO()
183 .getPropertyMap();
184 Map<String, String> mdcMap = event.getMDCPropertyMap();
185 if (loggerContextMap != null) {
186 mergedMap.putAll(loggerContextMap);
187 }
188 if (mdcMap != null) {
189 mergedMap.putAll(mdcMap);
190 }
191
192 return mergedMap;
193 }
194
195 @Override
196 protected Method getGeneratedKeysMethod() {
197 return GET_GENERATED_KEYS_METHOD;
198 }
199
200 @Override
201 protected String getInsertSQL() {
202 return insertSQL;
203 }
204
205 protected void insertProperties(Map<String, String> mergedMap,
206 Connection connection, long eventId) throws SQLException {
207 Set propertiesKeys = mergedMap.keySet();
208 if (propertiesKeys.size() > 0) {
209 PreparedStatement insertPropertiesStatement = connection
210 .prepareStatement(insertPropertiesSQL);
211
212 for (Iterator i = propertiesKeys.iterator(); i.hasNext();) {
213 String key = (String) i.next();
214 String value = (String) mergedMap.get(key);
215
216 insertPropertiesStatement.setLong(1, eventId);
217 insertPropertiesStatement.setString(2, key);
218 insertPropertiesStatement.setString(3, value);
219
220 if (cnxSupportsBatchUpdates) {
221 insertPropertiesStatement.addBatch();
222 } else {
223 insertPropertiesStatement.execute();
224 }
225 }
226
227 if (cnxSupportsBatchUpdates) {
228 insertPropertiesStatement.executeBatch();
229 }
230
231 insertPropertiesStatement.close();
232 insertPropertiesStatement = null;
233 }
234 }
235
236
237
238
239
240 void updateExceptionStatement(PreparedStatement exceptionStatement,
241 String txt, short i, long eventId) throws SQLException {
242 exceptionStatement.setLong(1, eventId);
243 exceptionStatement.setShort(2, i);
244 exceptionStatement.setString(3, txt);
245 if (cnxSupportsBatchUpdates) {
246 exceptionStatement.addBatch();
247 } else {
248 exceptionStatement.execute();
249 }
250 }
251
252 short buildExceptionStatement(IThrowableProxy tp, short baseIndex,
253 PreparedStatement insertExceptionStatement, long eventId)
254 throws SQLException {
255
256 StringBuilder buf = new StringBuilder();
257 ThrowableProxyUtil.subjoinFirstLine(buf, tp);
258 updateExceptionStatement(insertExceptionStatement, buf.toString(),
259 baseIndex++, eventId);
260
261 int commonFrames = tp.getCommonFrames();
262 StackTraceElementProxy[] stepArray = tp.getStackTraceElementProxyArray();
263 for (int i = 0; i < stepArray.length - commonFrames; i++) {
264 StringBuilder sb = new StringBuilder();
265 sb.append(CoreConstants.TAB);
266 ThrowableProxyUtil.subjoinSTEP(sb, stepArray[i]);
267 updateExceptionStatement(insertExceptionStatement, sb.toString(),
268 baseIndex++, eventId);
269 }
270
271 if (commonFrames > 0) {
272 StringBuilder sb = new StringBuilder();
273 sb.append(CoreConstants.TAB).append("... ").append(commonFrames).append(
274 " common frames omitted");
275 updateExceptionStatement(insertExceptionStatement, sb.toString(),
276 baseIndex++, eventId);
277 }
278
279 return baseIndex;
280 }
281
282 protected void insertThrowable(IThrowableProxy tp, Connection connection,
283 long eventId) throws SQLException {
284
285 PreparedStatement exceptionStatement = connection
286 .prepareStatement(insertExceptionSQL);
287
288 short baseIndex = 0;
289 while (tp != null) {
290 baseIndex = buildExceptionStatement(tp, baseIndex, exceptionStatement,
291 eventId);
292 tp = tp.getCause();
293 }
294
295 if (cnxSupportsBatchUpdates) {
296 exceptionStatement.executeBatch();
297 }
298 exceptionStatement.close();
299 exceptionStatement = null;
300
301 }
302 }