use of com.microsoft.appcenter.ingestion.models.Log in project mobile-center-sdk-android by Microsoft.
the class DefaultLogSerializer method serializeContainer.
@NonNull
@Override
public String serializeContainer(@NonNull LogContainer logContainer) throws JSONException {
/* Init JSON serializer, in verbose: try to make it pretty. */
JSONStringer writer = null;
if (AppCenterLog.getLogLevel() <= android.util.Log.VERBOSE) {
try {
Constructor<JSONStringer> constructor = JSONStringer.class.getDeclaredConstructor(int.class);
constructor.setAccessible(true);
writer = constructor.newInstance(2);
} catch (Exception e) {
AppCenterLog.error(AppCenter.LOG_TAG, "Failed to setup pretty json, falling back to default one", e);
}
}
if (writer == null) {
writer = new JSONStringer();
}
/* Start writing JSON. */
writer.object();
writer.key(LOGS).array();
for (Log log : logContainer.getLogs()) {
writeLog(writer, log);
}
writer.endArray();
writer.endObject();
return writer.toString();
}
use of com.microsoft.appcenter.ingestion.models.Log in project mobile-center-sdk-android by Microsoft.
the class DatabasePersistence method getLogs.
@Override
@Nullable
public String getLogs(@NonNull String group, @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 and get scanner. */
DatabaseStorage.DatabaseScanner scanner = mDatabaseStorage.getScanner(COLUMN_GROUP, group);
/* Add logs to output parameter after deserialization if logs are not already sent. */
int count = 0;
Map<Long, Log> candidates = new TreeMap<>();
List<Long> failedDbIdentifiers = new ArrayList<>();
File largePayloadGroupDirectory = getLargePayloadGroupDirectory(group);
for (Iterator<ContentValues> iterator = scanner.iterator(); iterator.hasNext() && count < limit; ) {
ContentValues values = iterator.next();
Long dbIdentifier = values.getAsLong(DatabaseManager.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.");
DatabaseStorage.DatabaseScanner idScanner = mDatabaseStorage.getScanner(COLUMN_GROUP, group, true);
for (ContentValues idValues : idScanner) {
Long invalidId = idValues.getAsLong(DatabaseManager.PRIMARY_KEY);
if (!mPendingDbIdentifiers.contains(invalidId) && !candidates.containsKey(invalidId)) {
/* Found the record to delete that we could not read when selecting all fields. */
deleteLog(largePayloadGroupDirectory, invalidId);
AppCenterLog.error(LOG_TAG, "Empty database corrupted empty record deleted, id=" + invalidId);
break;
}
}
idScanner.close();
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 = StorageHelper.InternalStorage.read(file);
if (logPayload == null) {
throw new JSONException("Log payload is null and not stored as a file.");
}
} else {
logPayload = databasePayload;
}
candidates.put(dbIdentifier, getLogSerializer().deserializeLog(logPayload));
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);
}
}
}
scanner.close();
/* 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 = UUIDUtils.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;
}
use of com.microsoft.appcenter.ingestion.models.Log in project mobile-center-sdk-android by Microsoft.
the class ChannelLogDecorateTest method checkLogAttributes.
@Test
public void checkLogAttributes() throws DeviceInfoHelper.DeviceInfoException {
mockStatic(DeviceInfoHelper.class);
Device device = mock(Device.class);
when(DeviceInfoHelper.getDeviceInfo(any(Context.class))).thenReturn(device);
mockStatic(IdHelper.class);
Channel channel = new DefaultChannel(mock(Context.class), UUID.randomUUID().toString(), mock(Persistence.class), mock(Ingestion.class), mock(Handler.class));
channel.addGroup("", 0, 0, 0, null);
/* Test a log that should be decorated. */
for (int i = 0; i < 3; i++) {
Log log = mock(Log.class);
channel.enqueue(log, "");
verify(log).setDevice(device);
verify(log).setTimestamp(any(Date.class));
}
/* Check cache was used, meaning only 1 call to generate a device. */
verifyStatic();
DeviceInfoHelper.getDeviceInfo(any(Context.class));
/* Test a log that is already decorated. */
Log log2 = mock(Log.class);
when(log2.getDevice()).thenReturn(device);
when(log2.getTimestamp()).thenReturn(new Date(123L));
channel.enqueue(log2, "");
verify(log2, never()).setDevice(any(Device.class));
verify(log2, never()).setTimestamp(any(Date.class));
/* Simulate update to wrapper SDK. */
Device device2 = mock(Device.class);
when(DeviceInfoHelper.getDeviceInfo(any(Context.class))).thenReturn(device2);
channel.invalidateDeviceCache();
/* Generate some logs to verify device properties have been updated. */
for (int i = 0; i < 3; i++) {
Log log3 = mock(Log.class);
channel.enqueue(log3, "");
verify(log3).setDevice(device2);
verify(log3).setTimestamp(any(Date.class));
}
/* Check only 1 device has been generated after cache invalidate. */
verifyStatic(times(2));
DeviceInfoHelper.getDeviceInfo(any(Context.class));
}
use of com.microsoft.appcenter.ingestion.models.Log in project mobile-center-sdk-android by Microsoft.
the class DefaultChannelTest method listener.
@Test
public void listener() throws Persistence.PersistenceException {
@SuppressWarnings("ConstantConditions") DefaultChannel channel = new DefaultChannel(mock(Context.class), null, mock(Persistence.class), mock(IngestionHttp.class), mCoreHandler);
channel.addGroup(TEST_GROUP, 50, BATCH_TIME_INTERVAL, MAX_PARALLEL_BATCHES, null);
Channel.Listener listener = spy(new AbstractChannelListener());
channel.addListener(listener);
Log log = mock(Log.class);
channel.enqueue(log, TEST_GROUP);
verify(listener).onEnqueuingLog(log, TEST_GROUP);
verify(listener).shouldFilter(log);
/* Check no more calls after removing listener. */
log = mock(Log.class);
channel.removeListener(listener);
channel.enqueue(log, TEST_GROUP);
verifyNoMoreInteractions(listener);
}
use of com.microsoft.appcenter.ingestion.models.Log in project mobile-center-sdk-android by Microsoft.
the class DefaultChannelTest method shutdown.
@Test
public void shutdown() throws Exception {
Persistence mockPersistence = mock(Persistence.class);
IngestionHttp mockIngestion = mock(IngestionHttp.class);
Channel.GroupListener mockListener = mock(Channel.GroupListener.class);
when(mockPersistence.getLogs(any(String.class), anyInt(), Matchers.<List<Log>>any())).then(getGetLogsAnswer(1));
DefaultChannel channel = new DefaultChannel(mock(Context.class), UUIDUtils.randomUUID().toString(), mockPersistence, mockIngestion, mCoreHandler);
channel.addGroup(TEST_GROUP, 1, BATCH_TIME_INTERVAL, MAX_PARALLEL_BATCHES, mockListener);
/* Enqueuing 1 event. */
channel.enqueue(mock(Log.class), TEST_GROUP);
verify(mockListener).onBeforeSending(notNull(Log.class));
channel.shutdown();
verify(mockListener, never()).onFailure(any(Log.class), any(Exception.class));
verify(mockPersistence).clearPendingLogState();
}
Aggregations