private async Task DeleteLogsOnSuspendedAsync() { await _mutex.LockAsync().ConfigureAwait(false); var stateSnapshot = _stateKeeper.GetStateSnapshot(); try { if (SendingLog != null || FailedToSendLog != null) { await SignalDeletingLogs(stateSnapshot).ConfigureAwait(false); } } catch (StorageException) { MobileCenterLog.Warn(MobileCenterLog.LogTag, "Failed to invoke events for logs being deleted."); return; } catch (StatefulMutexException e) { MobileCenterLog.Warn(MobileCenterLog.LogTag, "The DeleteLogs operation has been cancelled", e); return; } finally { _mutex.Unlock(); } await _storage.DeleteLogsAsync(Name).ConfigureAwait(false); }
private async Task CountFromDiskAsync() { await _mutex.LockAsync().ConfigureAwait(false); var stateSnapshot = _stateKeeper.GetStateSnapshot(); _mutex.Unlock(); var logCount = await _storage.CountLogsAsync(Name).ConfigureAwait(false); try { await _mutex.LockAsync(stateSnapshot).ConfigureAwait(false); _pendingLogCount = logCount; CheckPendingLogs(); } catch (StatefulMutexException e) { MobileCenterLog.Warn(MobileCenterLog.LogTag, "The CountFromDisk operation has been cancelled", e); } finally { _mutex.Unlock(); } }
internal static Dictionary <long, Guid> SessionsFromString(string sessionsString) { var sessionsDict = new Dictionary <long, Guid>(); if (sessionsString == null) { return(sessionsDict); } var sessions = sessionsString.Split(StorageEntrySeparator); foreach (var sessionString in sessions) { var splitSession = sessionString.Split(StorageKeyValueSeparator); try { var time = long.Parse(splitSession[0]); var sid = Guid.Parse(splitSession[1]); sessionsDict.Add(time, sid); } catch (FormatException e) //TODO other exceptions? { MobileCenterLog.Warn(Analytics.Instance.LogTag, $"Ignore invalid session in store: {sessionString}", e); } } return(sessionsDict); }
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(); } }
public override NSArray AttachmentsWithCrashes(MSCrashes crashes, MSErrorReport msReport) { if (_owner.GetErrorAttachments == null) { return(null); } var report = ErrorReportCache.GetErrorReport(msReport); var attachments = _owner.GetErrorAttachments(report); if (attachments != null) { var nsArray = new NSMutableArray(); foreach (var attachment in attachments) { if (attachment != null) { nsArray.Add(attachment.internalAttachment); } else { MobileCenterLog.Warn(Crashes.LogTag, "Skipping null ErrorAttachmentLog in Crashes.GetErrorAttachments."); } } return(nsArray); } return(null); }
private async Task PersistLogAsync(Log log, State stateSnapshot) { try { await _storage.PutLogAsync(Name, log).ConfigureAwait(false); } catch (StorageException e) { MobileCenterLog.Error(MobileCenterLog.LogTag, "Error persisting log", e); return; } try { await _mutex.LockAsync(stateSnapshot).ConfigureAwait(false); _pendingLogCount++; if (_enabled) { CheckPendingLogs(); return; } MobileCenterLog.Warn(MobileCenterLog.LogTag, "Channel is temporarily disabled; log was saved to disk"); } catch (StatefulMutexException e) { MobileCenterLog.Warn(MobileCenterLog.LogTag, "The PersistLog operation has been cancelled", e); } finally { _mutex.Unlock(); } }
private void HandleSendingSuccess(State state, string batchId) { if (!_mutex.IsCurrent(state)) { return; } try { _storage.DeleteLogs(Name, batchId); } catch (StorageException e) { MobileCenterLog.Warn(MobileCenterLog.LogTag, $"Could not delete logs for batch {batchId}", e); throw; } finally { List <Log> removedLogs; using (_mutex.GetLock(state)) { removedLogs = _sendingBatches[batchId]; _sendingBatches.Remove(batchId); } if (SentLog != null) { foreach (var log in removedLogs) { SentLog?.Invoke(this, new SentLogEventArgs(log)); } } } }
public static void Log(string tag, string message, Exception exception = null, MobileCenterLogType type = MobileCenterLogType.Warn) { switch (type) { case MobileCenterLogType.Info: MobileCenterLog.Info(tag, message, exception); break; case MobileCenterLogType.Warn: MobileCenterLog.Warn(tag, message, exception); break; case MobileCenterLogType.Error: MobileCenterLog.Error(tag, message, exception); break; case MobileCenterLogType.Assert: MobileCenterLog.Assert(tag, message, exception); break; case MobileCenterLogType.Verbose: MobileCenterLog.Verbose(tag, message, exception); break; case MobileCenterLogType.Debug: MobileCenterLog.Debug(tag, message, exception); break; default: throw new Exception("MobileCenterLogType Does Not Exist"); } }
public void OnChannelGroupReady(IChannelGroup channelGroup, string appSecret) { MobileCenterLog.Warn(MobileCenterLog.LogTag, "Crashes service is not yet supported on this platform."); try { #if REFERENCE #else WatsonRegistrationManager.Start(appSecret); #pragma warning disable CS0612 // Type or member is obsolete MobileCenter.CorrelationIdChanged += (s, id) => { WatsonRegistrationManager.SetCorrelationId(id.ToString()); }; // Checking for null and setting id needs to be atomic to avoid // overwriting Guid newId = Guid.NewGuid(); MobileCenter.TestAndSetCorrelationId(Guid.Empty, ref newId); #pragma warning restore CS0612 // Type or member is obsolete #endif } catch (Exception e) { #if DEBUG throw new MobileCenterException("Failed to register crashes with Watson", e); #endif } }
private async Task PersistLogAsync(Log log, State state) { try { await _storage.PutLog(Name, log).ConfigureAwait(false); } catch (StorageException e) { MobileCenterLog.Error(MobileCenterLog.LogTag, "Error persisting log", e); return; } try { bool enabled; using (await _mutex.GetLockAsync(state).ConfigureAwait(false)) { _pendingLogCount++; enabled = _enabled; } if (enabled) { CheckPendingLogs(state); return; } MobileCenterLog.Warn(MobileCenterLog.LogTag, "Channel is temporarily disabled; log was saved to disk"); } catch (StatefulMutexException) { MobileCenterLog.Warn(MobileCenterLog.LogTag, "The PersistLog operation has been cancelled"); } }
public async Task EnqueueAsync(Log log) { try { State state; bool discardLogs; using (await _mutex.GetLockAsync().ConfigureAwait(false)) { state = _mutex.State; discardLogs = _discardLogs; } if (discardLogs) { MobileCenterLog.Warn(MobileCenterLog.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) { MobileCenterLog.Warn(MobileCenterLog.LogTag, "The Enqueue operation has been cancelled"); } }
///<exception cref="IngestionException"/> private async Task ExecuteAsyncHelper() { while (true) { try { await Ingestion.ExecuteCallAsync(this).ConfigureAwait(false); return; } catch (IngestionException e) { if (!e.IsRecoverable || _retryCount >= _retryIntervals.Length) { throw; } MobileCenterLog.Warn(MobileCenterLog.LogTag, "Failed to execute service call", e); } await _retryIntervals[_retryCount++]().ConfigureAwait(false); if (_tokenSource.Token.IsCancellationRequested) { throw new IngestionException("The operation has been cancelled"); } } }
private static Func <Task> GetDelayFunc(TimeSpan[] intervals, int retry) { return(async() => { var delayMilliseconds = (int)(intervals[retry].TotalMilliseconds / 2.0); delayMilliseconds += await GetRandomIntAsync(delayMilliseconds).ConfigureAwait(false); var message = $"Try #{retry} failed and will be retried in {delayMilliseconds} ms"; MobileCenterLog.Warn(MobileCenterLog.LogTag, message); await Task.Delay(delayMilliseconds).ConfigureAwait(false); }); }
/// <summary> /// If enabled, register push channel and send URI to backend. /// Also start intercepting pushes. /// If disabled and previously enabled, stop listening for pushes (they will still be received though). /// </summary> private void ApplyEnabledState(bool enabled) { if (enabled) { // We expect caller of this method to lock on _mutex, we can't do it here as that lock is not recursive MobileCenterLog.Debug(LogTag, "Getting push token..."); var state = _mutex.State; Task.Run(async() => { var channel = await new WindowsPushNotificationChannelManager().CreatePushNotificationChannelForApplicationAsync() .AsTask().ConfigureAwait(false); try { using (await _mutex.GetLockAsync(state).ConfigureAwait(false)) { var pushToken = channel.Uri; if (!string.IsNullOrEmpty(pushToken)) { // Save channel member _channel = channel; // Subscribe to push channel.PushNotificationReceived += OnPushNotificationReceivedHandler; // Send channel URI to backend MobileCenterLog.Debug(LogTag, $"Push token '{pushToken}'"); var pushInstallationLog = new PushInstallationLog(null, null, pushToken, Guid.NewGuid()); // Do not await the call to EnqueueAsync or the UI thread can be blocked! #pragma warning disable CS4014 Channel.EnqueueAsync(pushInstallationLog); #pragma warning restore } else { MobileCenterLog.Error(LogTag, "Push service registering with Mobile Center backend has failed."); } } } catch (StatefulMutexException) { MobileCenterLog.Warn(LogTag, "Push Enabled state changed after creating channel."); } }); } else if (_channel != null) { _channel.PushNotificationReceived -= OnPushNotificationReceivedHandler; } }
public DefaultScreenSizeProvider() { if (!ApiInformation.IsPropertyPresent(typeof(DisplayInformation).FullName, "ScreenHeightInRawPixels") || !ApiInformation.IsPropertyPresent(typeof(DisplayInformation).FullName, "ScreenWidthInRawPixels")) { MobileCenterLog.Warn(MobileCenterLog.LogTag, FailureMessage); _displayInformationEventSemaphore.Release(); return; } // Only try to get screen size once resuming event is invoked, because there's no point // in trying beforehand. ApplicationLifecycleHelper.Instance.ApplicationResuming += SetUpDisplayInformation; }
public void Pause() { lock (_lockObject) { if (_currentSessionState == SessionState.Inactive) { MobileCenterLog.Warn(Analytics.Instance.LogTag, "Trying to pause already inactive session."); return; } MobileCenterLog.Debug(Analytics.Instance.LogTag, "SessionTracker.Pause"); _lastPausedTime = TimeHelper.CurrentTimeInMilliseconds(); _currentSessionState = SessionState.Inactive; } }
static WpfHelper() { try { var assemblies = AppDomain.CurrentDomain.GetAssemblies(); PresentationFramework = assemblies.FirstOrDefault(assembly => assembly.GetName().Name == "PresentationFramework"); IsRunningOnWpf = PresentationFramework != null; } catch (AppDomainUnloadedException) { MobileCenterLog.Warn(MobileCenterLog.LogTag, "Unabled to determine whether this application is WPF or Windows Forms; proceeding as though it is Windows Forms."); } }
/// <summary> /// Asynchronously retrieves logs from storage and flags them to avoid duplicate retrievals on subsequent calls /// </summary> /// <param name="channelName">Name of the channel to retrieve logs from</param> /// <param name="limit">The maximum number of logs to retrieve</param> /// <param name="logs">A list to which the retrieved logs will be added</param> /// <returns>A batch ID for the set of returned logs; null if no logs are found</returns> /// <exception cref="StorageException"/> public async Task <string> GetLogsAsync(string channelName, int limit, List <Log> logs) { using (await _taskLockSource.GetTaskLockAsync().ConfigureAwait(false)) { logs?.Clear(); var retrievedLogs = new List <Log>(); MobileCenterLog.Debug(MobileCenterLog.LogTag, $"Trying to get up to {limit} logs from storage for {channelName}"); var idPairs = new List <Tuple <Guid?, long> >(); var failedToDeserializeALog = false; var retrievedEntries = await _storageAdapter.GetAsync <LogEntry>(entry => entry.Channel == channelName, limit) .ConfigureAwait(false); foreach (var entry in retrievedEntries) { if (_pendingDbIdentifiers.Contains(entry.Id)) { continue; } try { var log = LogSerializer.DeserializeLog(entry.Log); retrievedLogs.Add(log); idPairs.Add(Tuple.Create(log.Sid, Convert.ToInt64(entry.Id))); } catch (JsonException e) { MobileCenterLog.Error(MobileCenterLog.LogTag, "Cannot deserialize a log in storage", e); failedToDeserializeALog = true; await _storageAdapter.DeleteAsync <LogEntry>(row => row.Id == entry.Id).ConfigureAwait(false); } } if (failedToDeserializeALog) { MobileCenterLog.Warn(MobileCenterLog.LogTag, "Deleted logs that could not be deserialized"); } if (idPairs.Count == 0) { MobileCenterLog.Debug(MobileCenterLog.LogTag, $"No available logs in storage for channel '{channelName}'"); return(null); } // Process the results var batchId = Guid.NewGuid().ToString(); ProcessLogIds(channelName, batchId, idPairs); logs?.AddRange(retrievedLogs); return(batchId); } }
/// <summary> /// Validates properties. /// </summary> /// <param name="properties">Properties collection to validate.</param> /// <param name="logName">Log name.</param> /// <param name="logType">Log type.</param> /// <returns>Valid properties collection with maximum size of 5</returns> private IDictionary <string, string> ValidateProperties(IDictionary <string, string> properties, string logName, string logType) { if (properties == null) { return(null); } var result = new Dictionary <string, string>(); foreach (var property in properties) { if (result.Count >= MaxEventProperties) { MobileCenterLog.Warn(LogTag, $"{logType} '{logName}' : properties cannot contain more than {MaxEventProperties} items. Skipping other properties."); break; } // Skip empty property. var key = property.Key; var value = property.Value; if (string.IsNullOrEmpty(key)) { MobileCenterLog.Warn(LogTag, $"{logType} '{logName}' : a property key cannot be null or empty. Property will be skipped."); break; } if (value == null) { MobileCenterLog.Warn(LogTag, $"{logType} '{logName}' : property '{key}' : property value cannot be null. Property will be skipped."); break; } // Truncate exceeded property. if (key.Length > MaxEventPropertyKeyLength) { MobileCenterLog.Warn(LogTag, $"{logType} '{logName}' : property '{key}' : property key length cannot be longer than {MaxEventPropertyKeyLength} characters. Property key will be truncated."); key = key.Substring(0, MaxEventPropertyKeyLength); } if (value.Length > MaxEventPropertyValueLength) { MobileCenterLog.Warn(LogTag, $"{logType} '{logName}' : property '{key}' : property value length cannot be longer than {MaxEventPropertyValueLength} characters. Property value will be truncated."); value = value.Substring(0, MaxEventPropertyValueLength); } result.Add(key, value); } return(result); }
public void Resume() { lock (_lockObject) { if (_currentSessionState == SessionState.Active) { MobileCenterLog.Warn(Analytics.Instance.LogTag, "Trying to resume already active session."); return; } MobileCenterLog.Debug(Analytics.Instance.LogTag, "SessionTracker.Resume"); _lastResumedTime = TimeHelper.CurrentTimeInMilliseconds(); _currentSessionState = SessionState.Active; SendStartSessionIfNeeded(); } }
/// <summary> /// Validates name. /// </summary> /// <param name="name">Log name to validate.</param> /// <param name="logType">Log type.</param> /// <returns><c>true</c> if validation succeeds, otherwise <с>false</с>.</returns> private bool ValidateName(ref string name, string logType) { if (string.IsNullOrEmpty(name)) { MobileCenterLog.Error(LogTag, $"{logType} name cannot be null or empty."); return(false); } if (name.Length > MaxEventNameLength) { MobileCenterLog.Warn(LogTag, $"{logType} '{name}' : name length cannot be longer than {MaxEventNameLength} characters. Name will be truncated."); name = name.Substring(0, MaxEventNameLength); return(true); } return(true); }
private void Suspend(State state, bool deleteLogs, Exception exception) { try { IEnumerable <Log> unsentLogs = null; using (_mutex.GetLock(state)) { _enabled = false; _batchScheduled = false; _discardLogs = deleteLogs; if (deleteLogs) { unsentLogs = _sendingBatches.Values.SelectMany(batch => batch); _sendingBatches.Clear(); } state = _mutex.InvalidateState(); } if (unsentLogs != null) { foreach (var log in unsentLogs) { FailedToSendLog?.Invoke(this, new FailedToSendLogEventArgs(log, exception)); } } if (deleteLogs) { try { _ingestion.Close(); } catch (IngestionException e) { MobileCenterLog.Error(MobileCenterLog.LogTag, "Failed to close ingestion", e); } using (_mutex.GetLock(state)) { _pendingLogCount = 0; TriggerDeleteLogsOnSuspending(); } } _storage.ClearPendingLogState(Name); } catch (StatefulMutexException) { MobileCenterLog.Warn(MobileCenterLog.LogTag, "The Suspend operation has been cancelled"); } }
/// <summary> /// If enabled, register push channel and send URI to backend. /// Also start intercepting pushes. /// If disabled and previously enabled, stop listening for pushes (they will still be received though). /// </summary> private void ApplyEnabledState(bool enabled) { if (enabled) { // We expect caller of this method to lock on _mutex, we can't do it here as that lock is not recursive var stateSnapshot = _stateKeeper.GetStateSnapshot(); Task.Run(async() => { var channel = await new WindowsPushNotificationChannelManager().CreatePushNotificationChannelForApplicationAsync() .AsTask().ConfigureAwait(false); try { _mutex.Lock(stateSnapshot); var pushToken = channel.Uri; if (!string.IsNullOrEmpty(pushToken)) { // Save channel member _channel = channel; // Subscribe to push channel.PushNotificationReceived += OnPushNotificationReceivedHandler; // Send channel URI to backend MobileCenterLog.Debug(LogTag, $"Push token '{pushToken}'"); var pushInstallationLog = new PushInstallationLog(0, null, pushToken, Guid.NewGuid()); await Channel.Enqueue(pushInstallationLog).ConfigureAwait(false); } else { MobileCenterLog.Error(LogTag, "Push service registering with Mobile Center backend has failed."); } } catch (StatefulMutexException) { MobileCenterLog.Warn(LogTag, "Push Enabled state changed after creating channel."); } finally { _mutex.Unlock(); } }); } else if (_channel != null) { _channel.PushNotificationReceived -= OnPushNotificationReceivedHandler; } }
public async Task ClearAsync() { var state = _mutex.State; await _storage.DeleteLogs(Name).ConfigureAwait(false); try { using (await _mutex.GetLockAsync(state).ConfigureAwait(false)) { _pendingLogCount = 0; } } catch (StatefulMutexException) { MobileCenterLog.Warn(MobileCenterLog.LogTag, "The Clear operation has been cancelled"); } }
private void Resume(State state) { try { using (_mutex.GetLock(state)) { _enabled = true; _discardLogs = false; state = _mutex.InvalidateState(); } } catch (StatefulMutexException) { MobileCenterLog.Warn(MobileCenterLog.LogTag, "The Resume operation has been cancelled"); } CheckPendingLogs(state); }
public void OnChannelGroupReady(IChannelGroup channelGroup, string appSecret) { MobileCenterLog.Warn(MobileCenterLog.LogTag, "Crashes service is not yet supported on UWP."); try { #if REFERENCE #else WatsonRegistrationManager.Start(appSecret); #endif } catch (Exception e) { #if DEBUG throw new MobileCenterException("Failed to register crashes with Watson", e); #endif } }
private async Task TriggerIngestionAsync() { await _mutex.LockAsync().ConfigureAwait(false); var stateSnapshot = _stateKeeper.GetStateSnapshot(); try { if (!_enabled) { return; } MobileCenterLog.Debug(MobileCenterLog.LogTag, $"triggerIngestion({Name}) pendingLogCount={_pendingLogCount}"); _batchScheduled = false; if (_sendingBatches.Count >= _maxParallelBatches) { MobileCenterLog.Debug(MobileCenterLog.LogTag, "Already sending " + _maxParallelBatches + " batches of analytics data to the server"); return; } // Get a batch from storage var logs = new List <Log>(); _mutex.Unlock(); var batchId = await _storage.GetLogsAsync(Name, _maxLogsPerBatch, logs).ConfigureAwait(false); await _mutex.LockAsync(stateSnapshot).ConfigureAwait(false); if (batchId != null) { _sendingBatches.Add(batchId, logs); _pendingLogCount -= logs.Count; TriggerIngestion(logs, stateSnapshot, batchId); } } catch (StatefulMutexException e) { MobileCenterLog.Warn(MobileCenterLog.LogTag, "The TriggerIngestion operation has been cancelled", e); } finally { _mutex.Unlock(); } }
// Subscribe to the proper events and try to private void SetUpDisplayInformation(object sender, EventArgs e) { try { CoreApplication.MainView?.CoreWindow?.Dispatcher?.RunAsync( CoreDispatcherPriority.Normal, () => { try { // The exceptions that display information can throw are not documented, // so a catch-all is necessary. var displayInfo = DisplayInformation.GetForCurrentView(); UpdateDisplayInformation((int)displayInfo.ScreenHeightInRawPixels, (int)displayInfo.ScreenWidthInRawPixels); // Try to detect a change in screen size by attaching handlers to these events. // Since this code can execute multiple times on the same displayInfo, prevent // duplicate handlers by removing and then setting them. displayInfo.OrientationChanged -= UpdateDisplayInformationHandler; displayInfo.OrientationChanged += UpdateDisplayInformationHandler; displayInfo.DpiChanged -= UpdateDisplayInformationHandler; displayInfo.DpiChanged += UpdateDisplayInformationHandler; displayInfo.ColorProfileChanged -= UpdateDisplayInformationHandler; displayInfo.ColorProfileChanged += UpdateDisplayInformationHandler; // If everything succeeded, we must unsubscribe from the resuming event. ApplicationLifecycleHelper.Instance.ApplicationResuming -= SetUpDisplayInformation; } catch { MobileCenterLog.Warn(MobileCenterLog.LogTag, FailureMessage); } finally { _displayInformationEventSemaphore.Release(); } }); } catch (COMException) { // This is reached if the MainView is not ready to be accessed yet. _displayInformationEventSemaphore.Release(); MobileCenterLog.Warn(MobileCenterLog.LogTag, FailureMessage); } }
internal static byte[] SerializeException(Exception exception) { var ms = new MemoryStream(); var formatter = new BinaryFormatter(); try { formatter.Serialize(ms, exception); } catch (SerializationException e) { MobileCenterLog.Warn(Crashes.LogTag, "Failed to serialize exception for client side inspection", e); ms = new MemoryStream(); formatter.Serialize(ms, e); } return(ms.ToArray()); }
public Task Shutdown() { ThrowIfDisposed(); var tasks = new List <Task>(); lock (_channelGroupLock) { foreach (var channel in _channels) { tasks.Add(channel.Shutdown()); } MobileCenterLog.Debug(MobileCenterLog.LogTag, "Waiting for storage to finish operations"); if (!_storage.Shutdown(_shutdownTimeout)) { MobileCenterLog.Warn(MobileCenterLog.LogTag, "Storage taking too long to finish operations; shutting down channel without waiting any longer."); } } return(Task.WhenAll(tasks)); }