/// <summary> /// Gets new session breadcrumbs. /// </summary> /// <returns> The breadcrumbs. </returns> private static Breadcrumbs SessionStart(string name, int maxCount) { Breadcrumbs answer = null; try { // Breadcrumbs answer has previous_session == the previous current_session // and new current_session == empty but for new session_start breadcrumb . string path = Path.Combine(StorageHelper.CrittercismPath(), name + ".js"); if (StorageHelper.FileExists(path)) { answer = StorageHelper.Load(path, typeof(Breadcrumbs)) as Breadcrumbs; } if (answer == null) { answer = new Breadcrumbs(name, maxCount); } answer.previous_session = answer.current_session; answer.current_session = new List <Breadcrumb>(); if (name == "UserBreadcrumbs") { Dictionary <string, Object> data = new Dictionary <string, Object>(); data["text"] = "session_start"; data["level"] = (int)BreadcrumbTextType.Normal; answer.current_session.Add(new Breadcrumb(BreadcrumbType.Text, data)); } } catch (Exception ie) { Crittercism.LogInternalException(ie); }; return(answer); }
// SendRequest #if WINDOWS_PHONE_APP private bool SendRequest(MessageReport messageReport) { //Debug.WriteLine("SendRequest: " + messageReport.GetType().Name); bool sendCompleted = false; Debug.WriteLine("SendRequest: ENTER"); try { HttpWebRequest request = messageReport.WebRequest(); if (request != null) { string postBody = messageReport.PostBody(); Task <Stream> writerTask = request.GetRequestStreamAsync(); using (Stream stream = writerTask.Result) { SendRequestWritePostBody(stream, postBody); } Task <WebResponse> responseTask = request.GetResponseAsync(); using (HttpWebResponse response = (HttpWebResponse)responseTask.Result) { sendCompleted = DidReceiveResult(messageReport, response); } } } catch (Exception ie) { Crittercism.LogInternalException(ie); } Debug.WriteLine("SendRequest: EXIT ---> " + sendCompleted); return(sendCompleted); }
private bool SendRequest(MessageReport messageReport) { //////////////////////////////////////////////////////////////// // NOTE: sendCompleted is reported as "true" if we get back // an HTTP status code from the platform web server. It is // independent of whether or not the HTTP status code is an // HTTP status code we like. Therefore, a 500 internal server // error WILL count as sendCompleted == "true" . OTOH, if // there is no network connectivity at all (e.g. airplane mode // or out of network coverage), there is no HTTP status code, // only some WebException . We will report "false" in that case // and schedule a future retry. //////////////////////////////////////////////////////////////// //Debug.WriteLine("SendRequest: " + messageReport.GetType().Name); bool sendCompleted = true; Debug.WriteLine("SendRequest: ENTER"); try { HttpWebRequest request = messageReport.WebRequest(); if (request != null) { string postBody = messageReport.PostBody(); ManualResetEvent resetEvent = new ManualResetEvent(false); request.BeginGetRequestStream( (result) => { //Debug.WriteLine("SendRequest: BeginGetRequestStream"); try { using (Stream stream = request.EndGetRequestStream(result)) { SendRequestWritePostBody(stream, postBody); } request.BeginGetResponse( (asyncResponse) => { sendCompleted = DidReceiveResult(messageReport, request, asyncResponse); resetEvent.Set(); }, null); } catch { resetEvent.Set(); } }, null); { #if DEBUG Stopwatch stopWatch = new Stopwatch(); stopWatch.Start(); #endif resetEvent.WaitOne(); #if DEBUG stopWatch.Stop(); Debug.WriteLine("SendRequest: TOTAL SECONDS == " + stopWatch.Elapsed.TotalSeconds); #endif } } } catch (Exception ie) { Crittercism.LogInternalException(ie); } Debug.WriteLine("SendRequest: EXIT ---> " + sendCompleted); return(sendCompleted); }
/// <summary> /// Saves to disk. /// </summary> /// <param name="Data"> The data. </param> /// <returns> true if it succeeds, false if it fails. </returns> internal static bool Save(object data) { bool answer = false; try { string path = Path.Combine(CrittercismPath(), data.GetType().Name + ".js"); answer = Save(data, path); } catch (Exception ie) { Crittercism.LogInternalException(ie); }; return(answer); }
/// <summary> /// Loads from disk. /// </summary> /// <param name="dataType"> Type of the data. </param> /// <returns> true if it succeeds, false if it fails. </returns> internal static object Load(Type dataType) { object data = null; try { string path = Path.Combine(CrittercismPath(), dataType.Name + ".js"); data = Load(path, dataType); } catch (Exception ie) { Crittercism.LogInternalException(ie); }; return(data); }
internal static MessageReport LoadMessage(string name) { // name is wrt MessagesPath "Crittercism\Messages", e.g "Crash_<guid>" // path is Crittercism\Messages\name MessageReport messageReport = null; try { string path = Path.Combine(MessagesPath, name); string[] nameSplit = name.Split('_'); switch (nameSplit[0]) { case "AppLoad": messageReport = (AppLoad)StorageHelper.Load(path, typeof(AppLoad)); break; case "APMReport": messageReport = (APMReport)StorageHelper.Load(path, typeof(APMReport)); break; case "HandledException": messageReport = (HandledException)StorageHelper.Load(path, typeof(HandledException)); break; case "CrashReport": messageReport = (CrashReport)StorageHelper.Load(path, typeof(CrashReport)); break; case "MetadataReport": messageReport = (MetadataReport)StorageHelper.Load(path, typeof(MetadataReport)); break; default: // Skip this file. break; } if (messageReport == null) { // Possibly file is still being written. Skip file for // now by returning null . } else { messageReport.Name = name; messageReport.CreationTime = StorageHelper.GetCreationTime(path); messageReport.Saved = true; } } catch (Exception ie) { Crittercism.LogInternalException(ie); } return(messageReport); }
internal void DidReceiveResponseShared(MessageReport messageReport, HttpWebResponse response) { try { //Debug.WriteLine("DidReceiveResponseShared: response.StatusCode == " + (int)response.StatusCode); if ((((long)response.StatusCode) / 100) == 2) { // 2xx Success using (StreamReader reader = (new StreamReader(response.GetResponseStream()))) { string responseText = reader.ReadToEnd(); messageReport.DidReceiveResponse(responseText); } } } catch (Exception ie) { Crittercism.LogInternalException(ie); } }
/// <summary> /// Save JSON serializable data object to disk. /// </summary> /// <param name="Data"> The data. </param> /// <returns> true if it succeeds, false if it fails. </returns> internal static bool Save(object data, string path) { bool answer = false; try { Debug.WriteLine("Save: " + Path.Combine(StoragePath(), path)); string dataString = JsonConvert.SerializeObject(data); Debug.WriteLine("JSON:"); Debug.WriteLine(dataString); SaveString(path, dataString); answer = true; } catch (Exception ie) { Crittercism.LogInternalException(ie); }; return(answer); }
/// <summary> /// Saves to disk. /// </summary> /// <returns> true if it succeeds, false if it fails. </returns> private bool Save() { bool answer = false; try { lock (this) { if (!saved) { answer = StorageHelper.Save(this); saved = true; } } } catch (Exception ie) { Crittercism.LogInternalException(ie); } return(answer); }
private void AddBreadcrumb(BreadcrumbType breadcrumbType, Object data) { try { lock (this) { current_session.Add(new Breadcrumb(breadcrumbType, data)); if (current_session.Count > maxCount) { // Remove the oldest breadcrumb after the "session_start". // Only userBreadcrumbs will contain a "session_start". current_session.RemoveAt((this == userBreadcrumbs) ? 1 : 0); } saved = false; }; } catch (Exception ie) { Crittercism.LogInternalException(ie); // explicit nop } }
private void ReadStep() { Debug.WriteLine("ReadStep: ENTER"); try { int retry = 0; while (Crittercism.initialized && (Crittercism.MessageQueue != null) && (Crittercism.MessageQueue.Count > 0) && (NetworkInterface.GetIsNetworkAvailable()) && (retry < 3)) { if (SendMessage()) { retry = 0; } else { // TODO: Use System.Timers.Timer to generate an event // 5 minutes from now, wait for it, then proceed. retry++; Debug.WriteLine("ReadStep: retry == " + retry); } } ; if (Crittercism.initialized) { // Opportune time to save Crittercism state. Unable to make the MessageQueue // shorter either because SendMessage failed or MessageQueue has gone empty. // The readerThread will be going into a do nothing wait state after this. // (If Crittercism.initialized==false, we are shut down or shutting down, and // we must not call Crittercism.Save since this can lead to DEADLOCK. // Crittercism.Shutdown may have lock on Crittercism.lockObject, and is waiting // for our readerThread to exit. Crittercism.Save would try to acquire // Crittercism.lockObject, but can't.) Crittercism.Save(); } ; } catch (Exception ie) { Crittercism.LogInternalException(ie); } Debug.WriteLine("ReadStep: EXIT"); }
private bool SendMessage() { //Debug.WriteLine("SendMessage: ENTER"); bool sendCompleted = false; try { if ((Crittercism.MessageQueue != null) && (Crittercism.MessageQueue.Count > 0)) { if ((Crittercism.TestNetwork != null) || NetworkInterface.GetIsNetworkAvailable()) { MessageReport messageReport = Crittercism.MessageQueue.Peek(); Debug.WriteLine("SendMessage: " + messageReport.GetType().Name); Crittercism.MessageQueue.Dequeue(); messageReport.Delete(); try { if (Crittercism.TestNetwork != null) { // This case used by UnitTest . sendCompleted = Crittercism.TestNetwork.SendRequest(messageReport); } else { sendCompleted = SendRequest(messageReport); } } catch (Exception ie) { Crittercism.LogInternalException(ie); } if (!sendCompleted) { Crittercism.MessageQueue.Enqueue(messageReport); } } } ; } catch (Exception ie) { Crittercism.LogInternalException(ie); } //Debug.WriteLine("SendMessage: EXIT ---> "+sendCompleted); return(sendCompleted); }
internal void ReadQueue() { Debug.WriteLine("ReadQueue: ENTER"); try { while (Crittercism.initialized) { ReadStep(); Debug.WriteLine("ReadQueue: SLEEP"); // wake up again 300000 milliseconds == 5 minute timeout from now // even without prompting. Useful if last SendMessage failed and // it seems time to try again despite no new messages have poured // into the MessageQueue which would have "Set" the readerEvent. const int READQUEUE_MILLISECONDS_TIMEOUT = 300000; Crittercism.readerEvent.WaitOne(READQUEUE_MILLISECONDS_TIMEOUT); Debug.WriteLine("ReadQueue: WAKE"); } ; } catch (Exception ie) { Crittercism.LogInternalException(ie); } Debug.WriteLine("ReadQueue: EXIT"); }
/// <summary> /// Deletes the message from disk. /// </summary> /// <returns> true if it succeeds, false if it fails. </returns> internal bool Delete() { bool answer = false; try { lock (this) { if (Saved) { string path = Path.Combine(MessagesPath, this.Name); if (StorageHelper.FileExists(path)) { StorageHelper.DeleteFile(path); Saved = false; } } answer = true; } } catch (Exception ie) { Crittercism.LogInternalException(ie); }; return(answer); }
internal static bool FolderExists(string path) { //Debug.WriteLine("FolderExists: "+path); bool answer = false; #if NETFX_CORE try { StorageFolder folder = TryGetFolder(path); if (folder != null) { //Debug.WriteLine("FolderExists: answer=true"); answer = true; } } catch (Exception ie) { Crittercism.LogInternalException(ie); } #else answer = GetStore().DirectoryExists(path); #endif //Debug.WriteLine("FolderExists --> "+answer); return(answer); }
/// <summary> /// Saves the message to disk. /// </summary> /// <returns> true if it succeeds, false if it fails. </returns> public bool Save() { // On-disk serialization in JSON alone isn't C# type-preserving. // So, this.GetType().Name+"_" is prefixed to file Name . bool answer = false; try { lock (this) { if (!Saved) { Name = this.GetType().Name + "_" + Guid.NewGuid().ToString() + ".js"; string path = Path.Combine(MessagesPath, Name); StorageHelper.Save(this, path); Saved = true; answer = true; } } } catch (Exception ie) { Crittercism.LogInternalException(ie); }; return(answer); }
/// <summary> /// Load JSON deserializable data object from IsolatedStorageFile path. /// </summary> /// <param name="dataType"> Type of the data. </param> /// <returns> true if it succeeds, false if it fails. </returns> internal static object Load(string path, Type dataType) { object data = null; try { Debug.WriteLine("Load: " + Path.Combine(StoragePath(), path)); if (FileExists(path)) { string dataString = LoadString(path); if (dataString == null) { // Unable to read file. Maybe something is still writing // it? Return null in this case. Will try again later. } else { try { data = JsonConvert.DeserializeObject(dataString, dataType); } catch (Exception) { Debug.WriteLine("Load: Unable to parse " + path); // Try to DeleteFile anything we can't parse. It might be // a partly written file terminated by an earlier crash. try { StorageHelper.DeleteFile(path); } catch (Exception) { } } } } else { Debug.WriteLine("Load: File doesn't exist " + path); } } catch (Exception ie) { Crittercism.LogInternalException(ie); } return(data); }
//////////////////////////////////////////////////////////////// // EXAMPLE APM "config" EXTRACTED FROM PLATFORM AppLoad RESPONSE JSON // {"net":{"enabled":true, // "persist":false, // "interval":10}} // See example in AppLoad.cs for context. //////////////////////////////////////////////////////////////// internal static void SettingsChange() { try { if (Crittercism.Settings != null) { // Both "apm" and "config" should be non-null since other // code already sanity checked Crittercism.Settings, but checking // again doesn't hurt anything. JObject apm = Crittercism.Settings["apm"] as JObject; if (apm != null) { JObject config = apm["net"] as JObject; if (config != null) { if (config["enabled"] != null) { bool enabled = (bool)((JValue)(config["enabled"])).Value; if (enabled) { // NOTE: Platform sends "interval" in seconds, but method Enable wants // that time converted to milliseconds. int interval = (int)(Convert.ToDouble(((JValue)(config["interval"])).Value) * TimeUtils.MSEC_PER_SEC); Enable(interval); } else { Disable(); } } } } } } catch (Exception ie) { Crittercism.LogInternalException(ie); } }