Search in sources :

Example 91 with Log

use of com.microsoft.appcenter.ingestion.models.Log in project mobile-center-sdk-android by Microsoft.

the class DefaultChannel method handleSendingSuccess.

/**
 * The actual implementation to react to sending a batch to the server successfully.
 *
 * @param groupState   The group state.
 * @param currentState The current state.
 * @param batchId      The batch ID.
 */
private synchronized void handleSendingSuccess(@NonNull final GroupState groupState, int currentState, @NonNull final String batchId) {
    if (checkStateDidNotChange(groupState, currentState)) {
        String groupName = groupState.mName;
        mPersistence.deleteLogs(groupName, batchId);
        List<Log> removedLogsForBatchId = groupState.mSendingBatches.remove(batchId);
        GroupListener groupListener = groupState.mListener;
        if (groupListener != null) {
            for (Log log : removedLogsForBatchId) {
                groupListener.onSuccess(log);
            }
        }
        checkPendingLogs(groupName);
    }
}
Also used : AppCenterLog(com.microsoft.appcenter.utils.AppCenterLog) Log(com.microsoft.appcenter.ingestion.models.Log)

Example 92 with Log

use of com.microsoft.appcenter.ingestion.models.Log in project mobile-center-sdk-android by Microsoft.

the class DefaultChannel method triggerIngestion.

/**
 * This will, if we're not using the limit for pending batches, trigger sending of a new request.
 * It will also reset the counters for sending out items for both the number of items enqueued and
 * the handlers. It will do this even if we don't have reached the limit
 * of pending batches or the time interval.
 *
 * @param groupName the group name
 */
private synchronized void triggerIngestion(@NonNull final String groupName) {
    if (!mEnabled) {
        return;
    }
    final GroupState groupState = mGroupStates.get(groupName);
    int pendingLogCount = groupState.mPendingLogCount;
    int maxFetch = Math.min(pendingLogCount, groupState.mMaxLogsPerBatch);
    AppCenterLog.debug(LOG_TAG, "triggerIngestion(" + groupName + ") pendingLogCount=" + pendingLogCount);
    cancelTimer(groupState);
    /* Check if we have reached the maximum number of pending batches, log to LogCat and don't trigger another sending. */
    if (groupState.mSendingBatches.size() == groupState.mMaxParallelBatches) {
        AppCenterLog.debug(LOG_TAG, "Already sending " + groupState.mMaxParallelBatches + " batches of analytics data to the server.");
        return;
    }
    /* Get a batch from Persistence. */
    final List<Log> batch = new ArrayList<>(maxFetch);
    final int stateSnapshot = mCurrentState;
    final String batchId = mPersistence.getLogs(groupName, maxFetch, batch);
    /* Decrement counter. */
    groupState.mPendingLogCount -= maxFetch;
    /* Nothing more to do if no logs. */
    if (batchId == null) {
        return;
    }
    AppCenterLog.debug(LOG_TAG, "ingestLogs(" + groupState.mName + "," + batchId + ") pendingLogCount=" + groupState.mPendingLogCount);
    /* Call group listener before sending logs to ingestion service. */
    if (groupState.mListener != null) {
        for (Log log : batch) {
            groupState.mListener.onBeforeSending(log);
        }
    }
    /* Remember this batch. */
    groupState.mSendingBatches.put(batchId, batch);
    /*
         * Due to bug on old Android versions (verified on 4.0.4),
         * if we start an async task from here, i.e. the async persistence handler thread,
         * we end up with AsyncTask configured with the wrong Handler to use for onPostExecute
         * instead of using main thread as advertised in Javadoc (and its a static field there).
         *
         * Our SDK guards against an application that would make a first async task in non UI
         * thread before SDK is initialized, but we should also avoid corrupting AsyncTask
         * with our wrong handler to avoid creating bugs in the application code since we are
         * a library.
         *
         * So make sure we execute the async task from UI thread to avoid any issue.
         */
    HandlerUtils.runOnUiThread(new Runnable() {

        @Override
        public void run() {
            sendLogs(groupState, stateSnapshot, batch, batchId);
        }
    });
}
Also used : AppCenterLog(com.microsoft.appcenter.utils.AppCenterLog) Log(com.microsoft.appcenter.ingestion.models.Log) ArrayList(java.util.ArrayList)

Example 93 with Log

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;
}
Also used : ContentValues(android.content.ContentValues) AppCenterLog(com.microsoft.appcenter.utils.AppCenterLog) Log(com.microsoft.appcenter.ingestion.models.Log) ArrayList(java.util.ArrayList) JSONException(org.json.JSONException) TreeMap(java.util.TreeMap) DatabaseStorage(com.microsoft.appcenter.utils.storage.StorageHelper.DatabaseStorage) File(java.io.File) HashMap(java.util.HashMap) TreeMap(java.util.TreeMap) Map(java.util.Map) Nullable(android.support.annotation.Nullable)

Example 94 with Log

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);
}
Also used : Context(android.content.Context) Persistence(com.microsoft.appcenter.persistence.Persistence) IngestionHttp(com.microsoft.appcenter.ingestion.IngestionHttp) Log(com.microsoft.appcenter.ingestion.models.Log) Test(org.junit.Test)

Example 95 with Log

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();
}
Also used : Persistence(com.microsoft.appcenter.persistence.Persistence) Context(android.content.Context) IngestionHttp(com.microsoft.appcenter.ingestion.IngestionHttp) Log(com.microsoft.appcenter.ingestion.models.Log) Matchers.anyString(org.mockito.Matchers.anyString) HttpException(com.microsoft.appcenter.http.HttpException) SocketException(java.net.SocketException) IOException(java.io.IOException) CancellationException(com.microsoft.appcenter.CancellationException) Test(org.junit.Test)

Aggregations

Log (com.microsoft.appcenter.ingestion.models.Log)189 Test (org.junit.Test)150 AppCenterLog (com.microsoft.appcenter.utils.AppCenterLog)83 ArrayList (java.util.ArrayList)75 Context (android.content.Context)74 LogSerializer (com.microsoft.appcenter.ingestion.models.json.LogSerializer)65 UUID (java.util.UUID)57 PrepareForTest (org.powermock.core.classloader.annotations.PrepareForTest)56 Matchers.anyString (org.mockito.Matchers.anyString)51 LogContainer (com.microsoft.appcenter.ingestion.models.LogContainer)45 DefaultLogSerializer (com.microsoft.appcenter.ingestion.models.json.DefaultLogSerializer)44 Persistence (com.microsoft.appcenter.persistence.Persistence)38 EventLog (com.microsoft.appcenter.analytics.ingestion.models.EventLog)34 ServiceCallback (com.microsoft.appcenter.http.ServiceCallback)32 CommonSchemaLog (com.microsoft.appcenter.ingestion.models.one.CommonSchemaLog)32 HashMap (java.util.HashMap)32 StartSessionLog (com.microsoft.appcenter.analytics.ingestion.models.StartSessionLog)29 Channel (com.microsoft.appcenter.channel.Channel)27 Date (java.util.Date)27 InvocationOnMock (org.mockito.invocation.InvocationOnMock)26