/// <summary> /// This action runs in the background and uploads the items in the local queue /// to an azure queue /// </summary> private void UploadItems() { var batch = new AnalyticsItemList(); while (true) { try { AnalyticsItem item; if (this.localQueue.TryDequeue(out item)) { batch.Add(item); if (batch.Count >= BatchSize) { SaveBatch(batch); } } else { SaveBatch(batch); // not a lot of items in the queue. sleep for one second Thread.Sleep(1000); } // update counter to indicate currentq ueue length Counter.SetValue(CounterNames.AnalyticsLocalQueueLength, this.localQueue.Count); } catch (ThreadAbortException) { break; } catch (Exception ex) { // never exit Log.Error(ex, "Error saving analytics batch"); } } }
/// <summary> /// Parse a list of items form an Xml String /// </summary> /// <param name="str"> /// the input string /// </param> /// <returns> /// a list of items /// </returns> public static AnalyticsItemList Deserialize(string str) { try { var serializerSettings = new JsonSerializerSettings { NullValueHandling = NullValueHandling.Ignore }; return(JsonConvert.DeserializeObject <AnalyticsItemList>(str, serializerSettings)); } catch (Exception e) { Log.Verbose("Original String:{0}; Error: {1}", str, e); //Don't do anything this is expected until all the systems will migrate to json } // Backward compatability to xml var list = new AnalyticsItemList(); try { XElement document = XElement.Parse(str); foreach (XElement node in document.Elements()) { AnalyticsItem item = AnalyticsItem.Parse(node); if (item != null) { list.Add(item); } } } catch (Exception e) { // log and ignore the invalid message Log.Error(e, "Original String:{0}", str); } return(list); }
/// <summary> /// Saves a batch to the local azure queue /// </summary> /// <param name="batch"> /// a lits of items to be logged /// </param> private void SaveBatch(AnalyticsItemList batch) { try { if (batch == null) { Log.Error("AnalyticsClient.SaveBatch() called with null batch parameter."); return; } if (batch.Count == 0) { return; } Counter.SetValue(CounterNames.AnalyticsAzureBatchSize, batch.Count); // Enqueue to azure Queue string message = batch.ToJsonString(); Task enqueueTask = queue.EnqueueAsync(new CloudQueueMessage(message)); Counter.Increment(CounterNames.AnalyticsLocalQueuePerSec); enqueueTask.ContinueWith( contTask => { if (contTask.Exception != null && contTask.Exception.InnerException is StorageException) { EnqueueOperationOperationRateLogger.Failure(EventCode.AnalyticsClientStorageError, contTask.Exception.InnerException, "Error enqueuing item to azure queue"); } else if (contTask.Exception != null) { Log.Error(EventCode.AnalyticsClientUnexpectedError, contTask.Exception, "Error enqueuing item to azure queue"); } else { EnqueueOperationOperationRateLogger.Success(); } }, TaskContinuationOptions.ExecuteSynchronously); Counter.SetValue(CounterNames.AnalyticsAzureQueuePendingWrites, queue.PendingWrites); // if too many pending writers. Start throtling int wait = 0; bool warningSent = false; bool errorSent = false; const int SleepTime = 100; while (queue.PendingWrites > MaxPendingWriters) { wait++; int totalSleepMilliseconds = wait * SleepTime; // Send warning after 10 seconds of waiting if (totalSleepMilliseconds > 10000 && !warningSent) { Log.Warn( "The maximum # of pending async writes exceeds the maximum. Value={0}, waiting for {1} milliseconds", queue.PendingWrites, totalSleepMilliseconds); warningSent = true; } // Send Error after the timeout of azure send operation passed if (totalSleepMilliseconds >= AzureQueueWaitTimeout.TotalMilliseconds * 2 && !errorSent) { Log.Error( "The maximum # of pending async writes exceeds the maximum. Value={0}, waiting for {1} milliseconds", queue.PendingWrites, totalSleepMilliseconds); errorSent = true; } Thread.Sleep(SleepTime); } // clear batch batch.Clear(); } catch (Exception e) { Log.Error("Error in Analytics.Client.SaveBatch() " + e); } }