private async Task SignalDeletingLogs(IList <string> sendingBatches) { var logs = new List <Log>(); try { do { var batchId = await _storage.GetLogsAsync(Name, ClearBatchSize, logs).ConfigureAwait(false); if (sendingBatches.Contains(batchId)) { continue; } foreach (var log in logs) { AppCenterLog.Debug(AppCenterLog.LogTag, $"Invoke SendingLog for channel '{Name}'"); SendingLog?.Invoke(this, new SendingLogEventArgs(log)); AppCenterLog.Debug(AppCenterLog.LogTag, $"Invoke FailedToSendLog event for channel '{Name}'"); FailedToSendLog?.Invoke(this, new FailedToSendLogEventArgs(log, new CancellationException())); } }while (logs.Count >= ClearBatchSize); } catch { AppCenterLog.Warn(AppCenterLog.LogTag, "Failed to invoke events for logs being deleted."); } }
private void TriggerIngestion(State state, IList <Log> logs, string batchId) { // Before sending logs, trigger the sending event for this channel if (SendingLog != null) { foreach (var eventArgs in logs.Select(log => new SendingLogEventArgs(log))) { SendingLog?.Invoke(this, eventArgs); } } // If the optional Install ID has no value, default to using empty GUID // TODO When InstallId will really be async, we should not use Result anymore var rawInstallId = AppCenter.GetInstallIdAsync().Result; var installId = rawInstallId.HasValue ? rawInstallId.Value : Guid.Empty; using (var serviceCall = _ingestion.PrepareServiceCall(_appSecret, installId, logs)) { serviceCall.ExecuteAsync().ContinueWith(completedTask => { var ingestionException = completedTask.Exception?.InnerException as IngestionException; if (ingestionException == null) { HandleSendingSuccess(state, batchId); } else { HandleSendingFailure(state, batchId, ingestionException); } }); } }
public async Task EnqueueAsync(Log log) { try { State state; bool discardLogs; using (await _mutex.GetLockAsync().ConfigureAwait(false)) { state = _mutex.State; discardLogs = _discardLogs; } if (discardLogs) { AppCenterLog.Warn(AppCenterLog.LogTag, "Channel is disabled; logs are discarded"); SendingLog?.Invoke(this, new SendingLogEventArgs(log)); FailedToSendLog?.Invoke(this, new FailedToSendLogEventArgs(log, new CancellationException())); } EnqueuingLog?.Invoke(this, new EnqueuingLogEventArgs(log)); await PrepareLogAsync(log, state).ConfigureAwait(false); await PersistLogAsync(log, state).ConfigureAwait(false); } catch (StatefulMutexException) { AppCenterLog.Warn(AppCenterLog.LogTag, "The Enqueue operation has been cancelled"); } }
public Task Enqueue(Log log) { _mutex.Lock(); var stateSnapshot = _stateKeeper.GetStateSnapshot(); try { if (_discardLogs) { MobileCenterLog.Warn(MobileCenterLog.LogTag, "Channel is disabled; logs are discarded"); _mutex.Unlock(); SendingLog?.Invoke(this, new SendingLogEventArgs(log)); FailedToSendLog?.Invoke(this, new FailedToSendLogEventArgs(log, new CancellationException())); return(null); } _mutex.Unlock(); EnqueuingLog?.Invoke(this, new EnqueuingLogEventArgs(log)); _mutex.Lock(stateSnapshot); return(Task.Run(async() => { await PrepareLogAsync(log).ConfigureAwait(false); await PersistLogAsync(log, stateSnapshot).ConfigureAwait(false); })); } catch (StatefulMutexException e) { MobileCenterLog.Warn(MobileCenterLog.LogTag, "The Enqueue operation has been cancelled", e); return(null); } finally { _mutex.Unlock(); } }
private async Task TriggerIngestionAsync(State state) { using (await _mutex.GetLockAsync(state).ConfigureAwait(false)) { if (!_enabled || !_batchScheduled) { return; } AppCenterLog.Debug(AppCenterLog.LogTag, $"TriggerIngestion({Name}) pending log count: {_pendingLogCount}"); _batchScheduled = false; if (_sendingBatches.Count >= _maxParallelBatches) { AppCenterLog.Debug(AppCenterLog.LogTag, $"Already sending {_maxParallelBatches} batches of analytics data to the server"); return; } } // Get a batch from storage var logs = new List <Log>(); var batchId = await _storage.GetLogsAsync(Name, _maxLogsPerBatch, logs).ConfigureAwait(false); if (batchId != null) { using (await _mutex.GetLockAsync(state).ConfigureAwait(false)) { _sendingBatches.Add(batchId, logs); _pendingLogCount -= logs.Count; } try { // Before sending logs, trigger the sending event for this channel if (SendingLog != null) { foreach (var log in logs) { AppCenterLog.Debug(AppCenterLog.LogTag, $"Invoke SendingLog event for channel '{Name}'"); SendingLog?.Invoke(this, new SendingLogEventArgs(log)); } } // If the optional Install ID has no value, default to using empty GUID var installId = await AppCenter.GetInstallIdAsync().ConfigureAwait(false) ?? Guid.Empty; var ingestionCall = _ingestion.Call(_appSecret, installId, logs); using (await _mutex.GetLockAsync(state).ConfigureAwait(false)) { _calls.Add(ingestionCall); } ingestionCall.ContinueWith(call => HandleSendingResult(state, batchId, call)); CheckPendingLogs(state); } catch (StorageException) { AppCenterLog.Warn(AppCenterLog.LogTag, "Something went wrong sending logs to ingestion"); } } }
/// <summary> /// Enqueue a log asynchronously. /// </summary> /// <param name="log">log to enqueue.</param> /// <returns>The async Task for this operation.</returns> public async Task EnqueueAsync(Log log) { try { State state; bool discardLogs; using (await _mutex.GetLockAsync().ConfigureAwait(false)) { state = _mutex.State; discardLogs = _discardLogs; } if (discardLogs) { AppCenterLog.Warn(AppCenterLog.LogTag, "Channel is disabled; logs are discarded"); AppCenterLog.Debug(AppCenterLog.LogTag, $"Invoke SendingLog event for channel '{Name}'"); SendingLog?.Invoke(this, new SendingLogEventArgs(log)); AppCenterLog.Debug(AppCenterLog.LogTag, $"Invoke FailedToSendLog event for channel '{Name}'"); FailedToSendLog?.Invoke(this, new FailedToSendLogEventArgs(log, new CancellationException())); return; } AppCenterLog.Debug(AppCenterLog.LogTag, $"Invoke EnqueuingLog event for channel '{Name}'"); EnqueuingLog?.Invoke(this, new EnqueuingLogEventArgs(log)); await PrepareLogAsync(log, state).ConfigureAwait(false); AppCenterLog.Debug(AppCenterLog.LogTag, $"Invoke FilteringLog event for channel '{Name}'"); var filteringLogEventArgs = new FilteringLogEventArgs(log); FilteringLog?.Invoke(this, filteringLogEventArgs); if (filteringLogEventArgs.FilterRequested) { AppCenterLog.Warn(AppCenterLog.LogTag, $"Filtering out a log of type '{log.GetType()}' at the request of an event handler."); } else { await PersistLogAsync(log, state).ConfigureAwait(false); } } catch (StatefulMutexException) { AppCenterLog.Warn(AppCenterLog.LogTag, "The Enqueue operation has been canceled"); } }
private async Task SignalDeletingLogs(State stateSnapshot) { var logs = new List <Log>(); _mutex.Unlock(); await _storage.GetLogsAsync(Name, ClearBatchSize, logs).ConfigureAwait(false); await _mutex.LockAsync(stateSnapshot).ConfigureAwait(false); foreach (var log in logs) { _mutex.Unlock(); SendingLog?.Invoke(this, new SendingLogEventArgs(log)); FailedToSendLog?.Invoke(this, new FailedToSendLogEventArgs(log, new CancellationException())); await _mutex.LockAsync(stateSnapshot).ConfigureAwait(false); } if (logs.Count >= ClearBatchSize) { await SignalDeletingLogs(stateSnapshot).ConfigureAwait(false); } }
private Task SignalDeletingLogs() { var logs = new List <Log>(); return(_storage.GetLogsAsync(Name, ClearBatchSize, logs) .ContinueWith(completedTask => { if (completedTask.IsFaulted) { AppCenterLog.Warn(AppCenterLog.LogTag, "Failed to invoke events for logs being deleted."); return; } foreach (var log in logs) { SendingLog?.Invoke(this, new SendingLogEventArgs(log)); FailedToSendLog?.Invoke(this, new FailedToSendLogEventArgs(log, new CancellationException())); } if (logs.Count >= ClearBatchSize) { SignalDeletingLogs(); } })); }
private void AnyChannelSendingLog(object sender, SendingLogEventArgs e) { SendingLog?.Invoke(sender, e); }
private void TriggerIngestion(IList <Log> logs, State stateSnapshot, string batchId) { // Before sending logs, trigger the sending event for this channel if (SendingLog != null) { foreach (var eventArgs in logs.Select(log => new SendingLogEventArgs(log))) { _mutex.Unlock(); SendingLog?.Invoke(this, eventArgs); _mutex.Lock(stateSnapshot); } } // If the optional Install ID has no value, default to using empty GUID var installId = MobileCenter.InstallId.HasValue ? MobileCenter.InstallId.Value : Guid.Empty; var serviceCall = _ingestion.PrepareServiceCall(_appSecret, installId, logs); serviceCall.ServiceCallFailedCallback = exception => { serviceCall.Dispose(); HandleSendingFailure(batchId, exception); }; serviceCall.ServiceCallSucceededCallback = async() => { serviceCall.Dispose(); if (!_stateKeeper.IsCurrent(stateSnapshot)) { return; } try { await _storage.DeleteLogsAsync(Name, batchId).ConfigureAwait(false); } catch (StorageException e) { MobileCenterLog.Warn(MobileCenterLog.LogTag, $"Could not delete logs for batch {batchId}", e); } try { await _mutex.LockAsync(stateSnapshot).ConfigureAwait(false); var removedLogs = _sendingBatches[batchId]; _sendingBatches.Remove(batchId); if (SentLog != null) { foreach (var log in removedLogs) { _mutex.Unlock(); SentLog?.Invoke(this, new SentLogEventArgs(log)); _mutex.Lock(stateSnapshot); } } CheckPendingLogs(); } catch (StatefulMutexException) { } finally { _mutex.Unlock(); } }; serviceCall.Execute(); }