-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathSQLCipherAppender.java
309 lines (267 loc) · 10.2 KB
/
SQLCipherAppender.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
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
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
package com.torointl.wasabi.testproject;
import android.content.ContentValues;
import android.content.Context;
import net.sqlcipher.database.SQLiteDatabase;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import ch.qos.logback.classic.spi.ILoggingEvent;
import ch.qos.logback.classic.spi.IThrowableProxy;
import ch.qos.logback.classic.spi.StackTraceElementProxy;
import ch.qos.logback.classic.spi.ThrowableProxyUtil;
import ch.qos.logback.core.CoreConstants;
import ch.qos.logback.core.UnsynchronizedAppenderBase;
/**
*
* @author Nelson MELINA <[email protected]>
* Created on 09/09/2014.
*/
public abstract class SQLCipherAppender
extends UnsynchronizedAppenderBase<ILoggingEvent> {
/**
* SQLCipher database.
*/
private SQLiteDatabase db;
/**
*
* @see ch.qos.logback.core.UnsynchronizedAppenderBase#start()
*/
@Override
public final void start() {
this.started = false;
LoggingRecordDBHelper helper =
new LoggingRecordDBHelper(getApplicationContext(), null);
this.db = helper.getWritableDatabase(getDbPassword());
super.start();
this.started = true;
}
/**
*
* @see ch.qos.logback.core.UnsynchronizedAppenderBase#stop()
*/
@Override
public final void stop() {
this.db.close();
}
/**
* Retrieve securely the password of the database.
* @return password for the DB
*/
abstract String getDbPassword();
/**
* Retrieve your application's context.
* @return Context of the application
*/
abstract Context getApplicationContext();
/**
* @see ch.qos.logback.core.UnsynchronizedAppenderBase#append
* @param eventObject instance of the event object.
*/
@Override
public final void append(final ILoggingEvent eventObject) {
if (isStarted()) {
long eventId = subAppend(eventObject);
if (eventId != -1) {
secondarySubAppend(eventObject, eventId);
}
}
}
/**
* Inserts the main details of a log event into the database.
*
* @param eventObject the event to insert
* @return the row ID of the newly inserted event;
* -1 if the insertion failed
*/
private long subAppend(final ILoggingEvent eventObject) {
ContentValues values = new ContentValues();
values.put(
LogbackDBContract.LoggingEventEntry.COLUMN_NAME_TIMESTAMP,
eventObject.getTimeStamp());
values.put(
LogbackDBContract.LoggingEventEntry.
COLUMN_NAME_FORMATTED_MESSAGE,
eventObject.getFormattedMessage());
values.put(
LogbackDBContract.LoggingEventEntry.COLUMN_NAME_TIMESTAMP,
eventObject.getTimeStamp());
values.put(
LogbackDBContract.LoggingEventEntry.COLUMN_NAME_LOGGER_NAME,
eventObject.getLoggerName());
values.put(
LogbackDBContract.LoggingEventEntry.COLUMN_NAME_THREAD_NAME,
eventObject.getThreadName());
values.put(LogbackDBContract.LoggingEventEntry
.COLUMN_NAME_REFERENCE_FLAG,
computeReferenceMask(eventObject));
StackTraceElement caller = eventObject.getCallerData()[0];
values.put(LogbackDBContract.LoggingEventEntry
.COLUMN_NAME_CALLER_FILENAME,
caller.getFileName());
values.put(LogbackDBContract.LoggingEventEntry
.COLUMN_NAME_CALLER_CLASS,
caller.getClassName());
values.put(LogbackDBContract.LoggingEventEntry
.COLUMN_NAME_CALLER_METHOD,
caller.getMethodName());
values.put(LogbackDBContract.LoggingEventEntry
.COLUMN_NAME_CALLER_LINE,
caller.getLineNumber());
return db.insert(LogbackDBContract.LoggingEventEntry
.TABLE_NAME, null, values);
}
/**
* Updates an existing row of an event with the secondary
* details of the event.
* This includes MDC properties and any exception information.
*
* @param event the event containing the details to insert
* @param eventId the row ID of the event to modify
*/
private void secondarySubAppend(final ILoggingEvent event,
final long eventId) {
Map<String, String> mergedMap = mergePropertyMaps(event);
insertProperties(mergedMap, eventId);
if (event.getThrowableProxy() != null) {
insertThrowable(event.getThrowableProxy(), eventId);
}
}
/**
* reference mask value if properties for this event exist.
*/
private static final short PROPERTIES_EXIST = 0x01;
/**
* reference mask value if an exception for this event exists.
*/
private static final short EXCEPTION_EXISTS = 0x02;
/**
* Computes the reference mask for a logging event, including
* flags to indicate whether MDC properties or exception info
* is available for the event.
*
* @param event the logging event to evaluate
* @return the 16-bit reference mask
*/
private static short computeReferenceMask(final ILoggingEvent event) {
short mask = 0;
int mdcPropSize = 0;
if (event.getMDCPropertyMap() != null) {
mdcPropSize = event.getMDCPropertyMap().keySet().size();
}
int contextPropSize = 0;
if (event.getLoggerContextVO().getPropertyMap() != null) {
contextPropSize = event.getLoggerContextVO()
.getPropertyMap().size();
}
if (mdcPropSize > 0 || contextPropSize > 0) {
mask = PROPERTIES_EXIST;
}
if (event.getThrowableProxy() != null) {
mask |= EXCEPTION_EXISTS;
}
return mask;
}
/**
* Merges a log event's properties with the properties
* of the logger context.
* The context properties are first in the map, and then
* the event's properties are appended.
*
* @param event the logging event to evaluate
* @return the merged properties map
*/
private Map<String, String> mergePropertyMaps(final ILoggingEvent event) {
Map<String, String> mergedMap = new HashMap<String, String>();
// we add the context properties first, then the event properties, since
// we consider that event-specific properties should have priority over
// context-wide properties.
Map<String, String> loggerContextMap = event.getLoggerContextVO()
.getPropertyMap();
if (loggerContextMap != null) {
mergedMap.putAll(loggerContextMap);
}
Map<String, String> mdcMap = event.getMDCPropertyMap();
if (mdcMap != null) {
mergedMap.putAll(mdcMap);
}
return mergedMap;
}
/**
* Updates an existing row with property details
* (context properties and event's properties).
*
* @param mergedMap the properties of the context
* plus the event's properties
* @param eventId the row ID of the event
*/
private void insertProperties(final Map<String, String> mergedMap,
final long eventId) {
if (mergedMap.size() > 0) {
ContentValues values;
for (Entry<String, String> entry : mergedMap.entrySet()) {
values = new ContentValues();
values.put(LogbackDBContract.LoggingEventPropertyEntry._ID,
eventId);
values.put(LogbackDBContract.LoggingEventPropertyEntry
.COLUMN_NAME_MAPPED_KEY,
entry.getKey());
values.put(LogbackDBContract.LoggingEventPropertyEntry
.COLUMN_NAME_MAPPED_VALUE,
entry.getValue());
db.insert(LogbackDBContract.LoggingEventPropertyEntry
.TABLE_NAME, null, values);
}
}
}
/**
* Insert a trace line related to an exception into the DB.
*
* @param txt Trace line.
* @param i index of the trace line
* @param eventId event's id
*/
private void insertException(final String txt, final short i,
final long eventId) {
ContentValues values = new ContentValues();
values.put(LogbackDBContract.LoggingEventExceptionEntry._ID,
eventId);
values.put(LogbackDBContract.LoggingEventExceptionEntry
.COLUMN_NAME_I,
i);
values.put(LogbackDBContract.LoggingEventExceptionEntry
.COLUMN_NAME_TRACE_LINE,
txt);
db.insert(LogbackDBContract.LoggingEventExceptionEntry
.TABLE_NAME, null, values);
}
/**
*
* @param tp exception related to the event
* @param eventId event's id
*/
private void insertThrowable(IThrowableProxy tp, final long eventId) {
short baseIndex = 0;
for (; tp != null; tp = tp.getCause()) {
StringBuilder buf = new StringBuilder();
ThrowableProxyUtil.subjoinFirstLine(buf, tp);
insertException(buf.toString(), baseIndex++, eventId);
int commonFrames = tp.getCommonFrames();
StackTraceElementProxy[] stepArray =
tp.getStackTraceElementProxyArray();
for (int i = 0; i < stepArray.length - commonFrames; i++) {
StringBuilder sb = new StringBuilder();
sb.append(CoreConstants.TAB);
ThrowableProxyUtil.subjoinSTEP(sb, stepArray[i]);
insertException(sb.toString(), baseIndex++, eventId);
}
if (commonFrames > 0) {
StringBuilder sb = new StringBuilder();
sb.append(CoreConstants.TAB)
.append("... ")
.append(commonFrames)
.append(" common frames omitted");
insertException(sb.toString(), baseIndex++, eventId);
}
}
}
}