public string SaveCrashReport(RaygunMessage crashReport, int maxReportsStored) { try { // Can only save the report if we havnt reached the report count limit. if (IsFileLimitReached(Math.Min(maxReportsStored, MAX_STORED_REPORTS_UPPER_LIMIT))) { RaygunLogger.Warning("Failed to store crash report - Reached max crash reports stored on device"); return(null); } // Convert to JSON text. var reportJson = SimpleJson.SerializeObject(crashReport); // Generate a unique name for our report. var fileName = GetUniqueAcendingJsonName(); // Save the report to disk. return(StoreCrashReport(fileName, reportJson)); } catch (Exception ex) { RaygunLogger.Error(string.Format("Failed to store crash report - Error saving message to isolated storage {0}", ex.Message)); } return(null); }
public void RemoveFile(string filePath) { // Validate the file path. if (string.IsNullOrEmpty(filePath)) { RaygunLogger.Debug("Failed to remove file - Invalid file path"); return; } // Check a file exists at this path. if (!File.Exists(filePath)) { RaygunLogger.Debug("Failed to remove file - File does not exist"); return; } try { File.Delete(filePath); } catch (Exception e) { RaygunLogger.Error("Failed to remove file - Due to error: " + e.Message); } }
/// <summary> /// Initializes a new instance of the <see cref="RaygunClient" /> class. /// </summary> /// <param name="apiKey">The API key.</param> public RaygunClient(string apiKey) { _apiKey = apiKey; _fileManager = new RaygunFileManager(); _fileManager.Intialise(); MaxReportsStoredOnDevice = RaygunFileManager.MAX_STORED_REPORTS_UPPER_LIMIT; // Setting default user information. var anonUser = GetAnonymousUserInfo(); _userInfo = anonUser; _user = anonUser.Identifier; _wrapperExceptions.Add(typeof(TargetInvocationException)); _wrapperExceptions.Add(typeof(AggregateException)); try { var clientVersion = new AssemblyName(GetType().Assembly.FullName).Version.ToString(); RaygunLogger.Info(string.Format("Configuring Raygun ({0})", clientVersion)); } catch { // Ignore } }
internal int SendMessage(string message) { RaygunLogger.Verbose("Sending JSON -------------------------------"); RaygunLogger.Verbose(message); RaygunLogger.Verbose("--------------------------------------------"); using (var client = new WebClient()) { client.Headers.Add("X-ApiKey", _apiKey); client.Headers.Add("content-type", "application/json; charset=utf-8"); client.Encoding = System.Text.Encoding.UTF8; try { client.UploadString(RaygunSettings.Settings.ApiEndpoint, message); } catch (Exception e) { RaygunLogger.Error(string.Format("Error Logging Exception to Raygun.io {0}", e.Message)); if (e.GetType().Name == "WebException") { WebException we = (WebException)e; HttpWebResponse response = (HttpWebResponse)we.Response; return((int)response.StatusCode); } } } return((int)HttpStatusCode.Accepted); }
private async Task SendStoredReportAsync(HttpClient client, RaygunFile report) { try { RaygunLogger.Verbose("Sending JSON -------------------------------"); RaygunLogger.Verbose(report.Data); RaygunLogger.Verbose("--------------------------------------------"); // Create the request contnet. HttpContent content = new StringContent(report.Data, System.Text.Encoding.UTF8, "application/json"); // Add API key to headers. content.Headers.Add("X-ApiKey", _apiKey); // Perform the request. var response = await client.PostAsync(RaygunSettings.Settings.ApiEndpoint, content); // Check the response. var statusCode = (int)response.StatusCode; RaygunLogger.LogResponseStatusCode(statusCode); // Remove the stored crash report if it was sent successfully. if (statusCode == (int)RaygunResponseStatusCode.Accepted) { _fileManager.RemoveFile(report.Path); // We can delete the file from disk now. } } catch (Exception e) { RaygunLogger.Error("Failed to send stored crash report due to error: " + e.Message); } }
/// <summary> /// Sends a pulse timing event to Raygun. The message is sent on a background thread. /// </summary> /// <param name="eventType">The type of event that occurred.</param> /// <param name="name">The name of the event resource such as the activity name or URL of a network call.</param> /// <param name="milliseconds">The duration of the event in milliseconds.</param> public void SendPulseTimingEvent(RaygunPulseEventType eventType, string name, long milliseconds) { lock (_batchLock) { try { if (_activeBatch == null) { _activeBatch = new PulseEventBatch(this); } if (_activeBatch != null && !_activeBatch.IsLocked) { EnsurePulseSessionStarted(); PendingEvent pendingEvent = new PendingEvent(eventType, name, milliseconds, _sessionId); _activeBatch.Add(pendingEvent); } else { ThreadPool.QueueUserWorkItem(c => SendPulseTimingEventCore(eventType, name, milliseconds)); } } catch (Exception e) { RaygunLogger.Error(string.Format("Error sending pulse timing event to Raygun: {0}", e.Message)); } } }
private string GetVersion() { string version = ApplicationVersion; if (String.IsNullOrWhiteSpace(version)) { try { Context context = RaygunClient.Context; PackageManager manager = context.PackageManager; PackageInfo info = manager.GetPackageInfo(context.PackageName, 0); version = info.VersionCode + " / " + info.VersionName; } catch (Exception ex) { RaygunLogger.Warning(string.Format("Error retrieving package version {0}", ex.Message)); } } if (String.IsNullOrWhiteSpace(version)) { version = "Not supplied"; } return(version); }
private void SendAllStoredCrashReports() { if (!HasInternetConnection) { RaygunLogger.Debug("Not sending stored crash reports due to no internet connection"); return; } // Get all stored crash reports. var reports = _fileManager.GetAllStoredCrashReports(); RaygunLogger.Debug(string.Format("Attempting to send {0} stored crash report(s)", reports.Count)); // Quick escape if there's no crash reports. if (reports.Count == 0) { return; } // Run on another thread. Task.Run(async() => { // Use a single HttpClient for all requests. using (var client = new HttpClient()) { foreach (var report in reports) { try { RaygunLogger.Verbose("Sending JSON -------------------------------"); RaygunLogger.Verbose(report.Data); RaygunLogger.Verbose("--------------------------------------------"); // Create the request contnet. HttpContent content = new StringContent(report.Data, System.Text.Encoding.UTF8, "application/json"); // Add API key to headers. content.Headers.Add("X-ApiKey", _apiKey); // Perform the request. var response = await client.PostAsync(RaygunSettings.Settings.ApiEndpoint, content); // Check the response. var statusCode = (int)response.StatusCode; RaygunLogger.LogResponseStatusCode(statusCode); // Remove the stored crash report if it was sent successfully. if (statusCode == (int)RaygunResponseStatusCode.Accepted) { _fileManager.RemoveFile(report.Path); // We can delete the file from disk now. } } catch (Exception e) { RaygunLogger.Error("Failed to send stored crash report due to error: " + e.Message); } } } }); }
public List <RaygunFile> GetAllStoredCrashReports() { var allReports = new List <RaygunFile>(); try { // Get the paths to all the files in the crash report directory. var files = Directory.GetFiles(GetCrashReportDirectory()); foreach (var file in files) { // Generate the full file path. var filePath = Path.Combine(GetCrashReportDirectory(), file); // Read the content of the file on disk. var raygunFile = ReadCrashReportFromDisk(filePath); // Add it to our list if valid. if (raygunFile != null && raygunFile.Data != null) { allReports.Add(raygunFile); } } } catch (Exception ex) { RaygunLogger.Error(string.Format("Error sending stored messages to Raygun.io {0}", ex.Message)); } return(allReports); }
public IRaygunMessageBuilder SetVersion(string version) { if (String.IsNullOrWhiteSpace(version)) { try { Context context = RaygunClient.Context; PackageManager manager = context.PackageManager; PackageInfo info = manager.GetPackageInfo(context.PackageName, 0); version = info.VersionCode + " / " + info.VersionName; } catch (Exception ex) { RaygunLogger.Debug(string.Format("Error retrieving package version {0}", ex.Message)); } } if (String.IsNullOrWhiteSpace(version)) { version = "Not supplied"; } _raygunMessage.Details.Version = version; return(this); }
/// <summary> /// Causes Raygun to automatically send session and view events for Raygun Pulse. /// </summary> /// <param name="mainActivity">The main/entry activity of the Android app.</param> /// <returns>The RaygunClient to chain other methods.</returns> public RaygunClient AttachPulse(Activity mainActivity) { RaygunLogger.Debug("Enabling Real User Monitoring"); Pulse.Attach(this, mainActivity); return(this); }
private bool ValidateApiKey() { if (string.IsNullOrEmpty(_apiKey)) { RaygunLogger.Error("ApiKey has not been provided, exception will not be logged"); return(false); } return(true); }
private void SendCore(PulseEventBatch batch) { try { EnsurePulseSessionStarted(); string version = GetVersion(); string os = "Android"; string osVersion = Android.OS.Build.VERSION.Release; string platform = string.Format("{0} {1}", Android.OS.Build.Manufacturer, Android.OS.Build.Model); RaygunPulseMessage message = new RaygunPulseMessage(); RaygunLogger.Debug("BatchSize: " + batch.PendingEventCount); RaygunPulseDataMessage[] eventMessages = new RaygunPulseDataMessage[batch.PendingEventCount]; int index = 0; foreach (PendingEvent pendingEvent in batch.PendingEvents) { RaygunPulseDataMessage dataMessage = new RaygunPulseDataMessage(); dataMessage.SessionId = pendingEvent.SessionId; dataMessage.Timestamp = pendingEvent.Timestamp; dataMessage.Version = version; dataMessage.OS = os; dataMessage.OSVersion = osVersion; dataMessage.Platform = platform; dataMessage.Type = "mobile_event_timing"; dataMessage.User = batch.UserInfo; string type = pendingEvent.EventType == RaygunPulseEventType.ViewLoaded ? "p" : "n"; RaygunPulseData data = new RaygunPulseData() { Name = pendingEvent.Name, Timing = new RaygunPulseTimingMessage() { Type = type, Duration = pendingEvent.Duration } }; RaygunPulseData[] dataArray = { data }; string dataStr = SimpleJson.SerializeObject(dataArray); dataMessage.Data = dataStr; eventMessages[index] = dataMessage; index++; } message.EventData = eventMessages; Send(message); } catch (Exception e) { RaygunLogger.Error(string.Format("Error sending pulse event batch to Raygun: {0}", e.Message)); } }
/// <summary> /// Causes Raygun to listen to and send all unhandled exceptions and unobserved task exceptions. /// </summary> /// <returns>The RaygunClient to chain other methods.</returns> public RaygunClient AttachCrashReporting() { RaygunLogger.Debug("Enabling Crash Reporting"); RaygunClient.DetachCrashReporting(); SetUnhandledExceptionHandlers(); return(this); }
private static void SetUnhandledExceptionHandlers() { if (!_exceptionHandlersSet) { _exceptionHandlersSet = true; RaygunLogger.Debug("Adding exception handlers"); AppDomain.CurrentDomain.UnhandledException += CurrentDomain_UnhandledException; TaskScheduler.UnobservedTaskException += TaskScheduler_UnobservedTaskException; } }
private static void RemoveUnhandledExceptionHandlers() { if (_exceptionHandlersSet) { _exceptionHandlersSet = false; RaygunLogger.Debug("Removing exception handlers"); AppDomain.CurrentDomain.UnhandledException -= CurrentDomain_UnhandledException; TaskScheduler.UnobservedTaskException -= TaskScheduler_UnobservedTaskException; } }
internal int SendMessage(string message, int timeout) { RaygunLogger.Verbose("Sending JSON -------------------------------"); RaygunLogger.Verbose(message); RaygunLogger.Verbose("--------------------------------------------"); int statusCode = 0; using (var client = new HttpClient()) { try { // Set the timeout if (timeout > 0) { client.Timeout = TimeSpan.FromMilliseconds(timeout); } // Create the request contnet. HttpContent content = new StringContent(message, System.Text.Encoding.UTF8, "application/json"); // Add API key to headers. content.Headers.Add("X-ApiKey", _apiKey); // Perform the request. var task = client.PostAsync(RaygunSettings.Settings.ApiEndpoint, content); task.Wait(); var response = task.Result; // Check the response. statusCode = (int)response.StatusCode; } catch (Exception e) { RaygunLogger.Error(string.Format("Failed to send message due to error {0}: {1}", e.GetType().Name, e.Message)); // Was this due to the request timing out? const string TaskCanceledEx = "TaskCanceledException"; if (e.GetType().Name == TaskCanceledEx || e.InnerException.GetType().Name == TaskCanceledEx) { statusCode = (int)HttpStatusCode.RequestTimeout; } else { statusCode = (int)HttpStatusCode.BadRequest; } } } return(statusCode); }
/// <summary> /// Asynchronously transmits a message to Raygun.io. /// </summary> /// <param name="exception">The exception to deliver.</param> /// <param name="tags">A list of strings associated with the message.</param> /// <param name="userCustomData">A key-value collection of custom data that will be added to the payload.</param> public void SendInBackground(Exception exception, IList <string> tags, IDictionary userCustomData) { if (CanSend(exception)) { ThreadPool.QueueUserWorkItem(c => StripAndSend(exception, tags, userCustomData, 0)); FlagAsSent(exception); } else { RaygunLogger.Debug("Not sending exception in background"); } }
/// <summary> /// Transmits an exception to Raygun.io synchronously specifying a list of string tags associated /// with the message for identification, as well as sending a key-value collection of custom data. /// This uses the version number of the originating assembly. /// </summary> /// <param name="exception">The exception to deliver.</param> /// <param name="tags">A list of strings associated with the message.</param> /// <param name="userCustomData">A key-value collection of custom data that will be added to the payload.</param> public void Send(Exception exception, IList <string> tags, IDictionary userCustomData) { if (CanSend(exception)) { StripAndSend(exception, tags, userCustomData, SynchronousTimeout); FlagAsSent(exception); } else { RaygunLogger.Debug("Not sending exception"); } }
public void Intialise() { try { var crashReportDirectory = GetCrashReportDirectory(); if (!Directory.Exists(crashReportDirectory)) { Directory.CreateDirectory(crashReportDirectory); } } catch (Exception e) { RaygunLogger.Error("Failed to initialise file manager due to error: " + e.Message); } }
protected void FlagAsSent(Exception exception) { if (exception != null && exception.Data != null) { try { Type[] genericTypes = exception.Data.GetType().GetGenericArguments(); if (genericTypes.Length == 0 || genericTypes[0].IsAssignableFrom(typeof(string))) { exception.Data[SentKey] = true; } } catch (Exception ex) { RaygunLogger.Debug($"Failed to flag exception as sent: {ex.Message}"); } } }
private void Send(RaygunPulseMessage raygunPulseMessage) { if (ValidateApiKey()) { string message = null; try { message = SimpleJson.SerializeObject(raygunPulseMessage); } catch (Exception ex) { RaygunLogger.Error(string.Format("Error serializing message {0}", ex.Message)); } if (message != null) { SendPulseMessage(message); } } }
private bool SendPulseMessage(string message) { using (var client = new WebClient()) { client.Headers.Add("X-ApiKey", _apiKey); client.Headers.Add("content-type", "application/json; charset=utf-8"); client.Encoding = System.Text.Encoding.UTF8; try { client.UploadString(RaygunSettings.Settings.PulseEndpoint, message); } catch (Exception ex) { RaygunLogger.Error(string.Format("Error Logging Pulse message to Raygun.io {0}", ex.Message)); return(false); } } return(true); }
private void Send(RaygunMessage raygunMessage, int timeout) { if (!ValidateApiKey()) { RaygunLogger.Error("Failed to send due to invalid API key"); return; } bool canSend = OnSendingMessage(raygunMessage); if (!canSend) { RaygunLogger.Debug("Sending message cancelled"); return; } // No internet then we store the report. if (!HasInternetConnection) { var path = _fileManager.SaveCrashReport(raygunMessage, MaxReportsStoredOnDevice); if (!string.IsNullOrEmpty(path)) { RaygunLogger.Debug("Saved crash report to: " + path); } return; } try { // Create the json data. var jsonData = SimpleJson.SerializeObject(raygunMessage); var statusCode = SendMessage(jsonData, timeout); RaygunLogger.LogResponseStatusCode(statusCode); // Save the message if the application is currently being rate limited or there was a timeout. if (statusCode == (int)RaygunResponseStatusCode.RateLimited || statusCode == (int)RaygunResponseStatusCode.RequestTimeout || statusCode == (int)RaygunResponseStatusCode.GatewayTimeout) { var path = _fileManager.SaveCrashReport(raygunMessage, MaxReportsStoredOnDevice); if (!string.IsNullOrEmpty(path)) { RaygunLogger.Debug("Saved crash report to: " + path); } } } catch (Exception e) { RaygunLogger.Error(string.Format("Failed to send message due to error {0}: {1}", e.GetType().Name, e.Message)); var path = _fileManager.SaveCrashReport(raygunMessage, MaxReportsStoredOnDevice); if (!string.IsNullOrEmpty(path)) { RaygunLogger.Debug("Saved crash report to: " + path); } } }
private void SendAllStoredCrashReports() { if (!HasInternetConnection) { RaygunLogger.Debug("Not sending stored crash reports due to no internet connection"); return; } // Get all stored crash reports. var reports = _fileManager.GetAllStoredCrashReports(); RaygunLogger.Info(string.Format("Attempting to send {0} stored crash report(s)", reports.Count)); // Quick escape if there's no crash reports. if (reports.Count == 0) { return; } try { Task.Run(async() => { // Use a single HttpClient for all requests. using (var client = new HttpClient()) { foreach (var report in reports) { await SendStoredReportAsync(client, report); } } }).ContinueWith(t => { if (t != null && t.IsFaulted) { RaygunLogger.Error("Fault occurred when sending stored reports - clearing stored reports"); try { // If there was an issue then clear the stored reports. _fileManager.RemoveFiles(reports); } catch (Exception e) { RaygunLogger.Error("Failed to remove stored report due to error: " + e.Message); } } if (t != null && t.Exception != null) { // Consume all errors as we dont want them being sent. t.Exception.Handle((e) => { RaygunLogger.Error("Error occurred while sending stored reports: " + e.Message); return(true); // Handled }); } }); } catch (Exception e) { RaygunLogger.Error("Failed to send stored reports due to error: " + e.Message); } }