in sdk/appcenter/src/main/java/com/microsoft/appcenter/persistence/DatabasePersistence.java [421:568]
public String getLogs(@NonNull String group, @NonNull Collection<String> pausedTargetKeys, @IntRange(from = 0) int limit, @NonNull List<Log> outLogs) {
/* Log. */
AppCenterLog.debug(LOG_TAG, "Trying to get " + limit + " logs from the Persistence database for " + group);
/* Query database. */
SQLiteQueryBuilder builder = SQLiteUtils.newSQLiteQueryBuilder();
builder.appendWhere(COLUMN_GROUP + " = ?");
List<String> selectionArgs = new ArrayList<>();
selectionArgs.add(group);
if (!pausedTargetKeys.isEmpty()) {
StringBuilder filter = new StringBuilder();
for (int i = 0; i < pausedTargetKeys.size(); i++) {
filter.append("?,");
}
filter.deleteCharAt(filter.length() - 1);
builder.appendWhere(" AND ");
builder.appendWhere(COLUMN_TARGET_KEY + " NOT IN (" + filter.toString() + ")");
selectionArgs.addAll(pausedTargetKeys);
}
/* Add logs to output parameter after deserialization if logs are not already sent. */
int count = 0;
Map<Long, Log> candidates = new LinkedHashMap<>();
List<Long> failedDbIdentifiers = new ArrayList<>();
File largePayloadGroupDirectory = getLargePayloadGroupDirectory(group);
String[] selectionArgsArray = selectionArgs.toArray(new String[0]);
Cursor cursor = null;
ContentValues values;
try {
cursor = mDatabaseManager.getCursor(builder, null, selectionArgsArray, GET_SORT_ORDER);
} catch (RuntimeException e) {
AppCenterLog.error(LOG_TAG, "Failed to get logs: ", e);
}
while (cursor != null &&
(values = mDatabaseManager.nextValues(cursor)) != null &&
count < limit) {
Long dbIdentifier = values.getAsLong(PRIMARY_KEY);
/*
* When we can't even read the identifier (in this case ContentValues is most likely empty).
* That probably means it contained a record larger than 2MB (from a previous SDK version)
* and we hit the cursor limit.
* Get rid of first non pending log.
*/
if (dbIdentifier == null) {
AppCenterLog.error(LOG_TAG, "Empty database record, probably content was larger than 2MB, need to delete as it's now corrupted.");
List<Long> corruptedIds = getLogsIds(builder, selectionArgsArray);
for (Long corruptedId : corruptedIds) {
if (!mPendingDbIdentifiers.contains(corruptedId) && !candidates.containsKey(corruptedId)) {
/* Found the record to delete that we could not read when selecting all fields. */
deleteLog(largePayloadGroupDirectory, corruptedId);
AppCenterLog.error(LOG_TAG, "Empty database corrupted empty record deleted, id=" + corruptedId);
break;
}
}
continue;
}
/* If the log is already in pending state, then skip. Otherwise put the log to candidate container. */
if (!mPendingDbIdentifiers.contains(dbIdentifier)) {
try {
/* Deserialize JSON to Log. */
String logPayload;
String databasePayload = values.getAsString(COLUMN_LOG);
if (databasePayload == null) {
File file = getLargePayloadFile(largePayloadGroupDirectory, dbIdentifier);
AppCenterLog.debug(LOG_TAG, "Read payload file " + file);
logPayload = FileManager.read(file);
if (logPayload == null) {
throw new JSONException("Log payload is null and not stored as a file.");
}
} else {
logPayload = databasePayload;
}
String databasePayloadType = values.getAsString(COLUMN_DATA_TYPE);
Log log = getLogSerializer().deserializeLog(logPayload, databasePayloadType);
/* Restore target token. */
String targetToken = values.getAsString(COLUMN_TARGET_TOKEN);
if (targetToken != null) {
CryptoUtils.DecryptedData data = CryptoUtils.getInstance(mContext).decrypt(targetToken);
log.addTransmissionTarget(data.getDecryptedData());
}
/* Add log to list and count. */
candidates.put(dbIdentifier, log);
count++;
} catch (JSONException e) {
/* If it is not able to deserialize, delete and get another log. */
AppCenterLog.error(LOG_TAG, "Cannot deserialize a log in the database", e);
/* Put the failed identifier to delete. */
failedDbIdentifiers.add(dbIdentifier);
}
}
}
if (cursor != null) {
try {
cursor.close();
} catch (RuntimeException ignore) {
}
}
/* Delete any logs that cannot be de-serialized. */
if (failedDbIdentifiers.size() > 0) {
for (long id : failedDbIdentifiers) {
deleteLog(largePayloadGroupDirectory, id);
}
AppCenterLog.warn(LOG_TAG, "Deleted logs that cannot be deserialized");
}
/* No logs found. */
if (candidates.size() <= 0) {
AppCenterLog.debug(LOG_TAG, "No logs found in the Persistence database at the moment");
return null;
}
/* Generate an ID. */
String id = UUID.randomUUID().toString();
/* Log. */
AppCenterLog.debug(LOG_TAG, "Returning " + candidates.size() + " log(s) with an ID, " + id);
AppCenterLog.debug(LOG_TAG, "The SID/ID pairs for returning log(s) is/are:");
List<Long> pendingDbIdentifiersGroup = new ArrayList<>();
for (Map.Entry<Long, Log> entry : candidates.entrySet()) {
Long dbIdentifier = entry.getKey();
/* Change a database identifier to pending state. */
mPendingDbIdentifiers.add(dbIdentifier);
/* Store a database identifier to a group of the ID. */
pendingDbIdentifiersGroup.add(dbIdentifier);
/* Add to output parameter. */
outLogs.add(entry.getValue());
/* Log. */
AppCenterLog.debug(LOG_TAG, "\t" + entry.getValue().getSid() + " / " + dbIdentifier);
}
/* Update pending IDs. */
mPendingDbIdentifiersGroups.put(group + id, pendingDbIdentifiersGroup);
return id;
}