Created
May 5, 2013 07:46
-
-
Save bscheiman/5520053 to your computer and use it in GitHub Desktop.
Raygun wrapper for MonoTouch / Xamarin.iOS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
public static class Raygun { | |
private static string ApiKey { get; set; } | |
private static Uri BaseUrl = new Uri("https://api.raygun.io/entries"); | |
[DllImport(MonoTouch.Constants.SystemLibrary)] | |
internal static extern int sysctlbyname([MarshalAs(UnmanagedType.LPStr)] string property, IntPtr output, IntPtr oldLen, IntPtr newp, | |
uint newlen); | |
public static void Attach(string apiKey) { | |
ApiKey = apiKey; | |
AppDomain.CurrentDomain.UnhandledException += (s, e) => { | |
Submit(e.ExceptionObject as Exception); | |
}; | |
TaskScheduler.UnobservedTaskException += (object sender, UnobservedTaskExceptionEventArgs e) => { | |
Submit(e.Exception); | |
}; | |
} | |
private static string GetHardwareVersion() { | |
const string HardwareProperty = "hw.machine"; | |
var pLen = Marshal.AllocHGlobal(sizeof(int)); | |
sysctlbyname(HardwareProperty, IntPtr.Zero, pLen, IntPtr.Zero, 0); | |
var length = Marshal.ReadInt32(pLen); | |
if (length == 0) { | |
Marshal.FreeHGlobal(pLen); | |
return "Unknown"; | |
} | |
var pStr = Marshal.AllocHGlobal(length); | |
sysctlbyname(HardwareProperty, pStr, pLen, IntPtr.Zero, 0); | |
var hardwareStr = Marshal.PtrToStringAnsi(pStr); | |
Marshal.FreeHGlobal(pLen); | |
Marshal.FreeHGlobal(pStr); | |
return hardwareStr; | |
} | |
public static void Submit(Exception ex) { | |
if (ex == null) | |
return; | |
if (string.IsNullOrEmpty(ApiKey)) { | |
Alert.Show("Raygun", "Empty API Key!"); | |
return; | |
} | |
var ae = ex as AggregateException; | |
if (ae != null) | |
ex = ae.Flatten(); | |
var hardware = GetHardwareVersion(); | |
var version = NSBundle.MainBundle.ObjectForInfoDictionary("CFBundleVersion").ToString(); | |
var message = new RaygunMessage { | |
occurredOn = DateTime.UtcNow.ToString("o"), | |
details = new Details { | |
machineName = hardware, | |
version = version, | |
client = new Client { | |
name = hardware, | |
version = version | |
}, | |
error = GetErrorFromException(ex) | |
} | |
}; | |
using (var wc = new WebClient()) { | |
wc.Encoding = Encoding.UTF8; | |
wc.Headers[HttpRequestHeader.ContentType] = "application/json"; | |
wc.Headers.Add("X-ApiKey", ApiKey); | |
wc.UploadStringAsync(BaseUrl, "POST", message.ToJson()); | |
} | |
} | |
static List<StackTrace> BuildStackTrace(Exception ex) { | |
var list = new List<StackTrace>(); | |
var stackTrace = new System.Diagnostics.StackTrace(ex, true); | |
var frames = stackTrace.GetFrames(); | |
if (frames == null || frames.Length == 0) { | |
list.Add(new StackTrace { fileName = "none", lineNumber = 0 }); | |
return list; | |
} | |
foreach (var frame in frames) { | |
var method = frame.GetMethod(); | |
if (method != null) { | |
int lineNumber = frame.GetFileLineNumber(); | |
if (lineNumber == 0) | |
lineNumber = frame.GetILOffset(); | |
var methodName = GenerateMethodName(method); | |
string file = frame.GetFileName(); | |
string className = method.ReflectedType != null ? method.ReflectedType.FullName : "(unknown)"; | |
var line = new StackTrace { | |
fileName = file, | |
lineNumber = lineNumber, | |
methodName = methodName, | |
className = className | |
}; | |
list.Add(line); | |
} | |
} | |
return list; | |
} | |
private static string GenerateMethodName(MethodBase method) { | |
var sb = new StringBuilder(); | |
sb.Append(method.Name); | |
if (method is MethodInfo && method.IsGenericMethod) { | |
Type[] genericArguments = method.GetGenericArguments(); | |
sb.Append("["); | |
int index2 = 0; | |
bool flag2 = true; | |
for (; index2 < genericArguments.Length; ++index2) { | |
if (!flag2) | |
sb.Append(","); | |
else | |
flag2 = false; | |
sb.Append(genericArguments[index2].Name); | |
} | |
sb.Append("]"); | |
} | |
sb.Append("("); | |
ParameterInfo[] parameters = method.GetParameters(); | |
bool flag3 = true; | |
for (int index2 = 0; index2 < parameters.Length; ++index2) { | |
if (!flag3) | |
sb.Append(", "); | |
else | |
flag3 = false; | |
string str2 = "<UnknownType>"; | |
if (parameters[index2].ParameterType != null) | |
str2 = parameters[index2].ParameterType.Name; | |
sb.Append(str2 + " " + parameters[index2].Name); | |
} | |
sb.Append(")"); | |
return sb.ToString(); | |
} | |
private static Error GetErrorFromException(Exception ex) { | |
var exceptionType = ex.GetType(); | |
var error = new Error { | |
message = string.Format("{0}: {1}", exceptionType.Name, ex.Message), | |
stackTrace = BuildStackTrace(ex), | |
className = exceptionType.FullName, | |
data = ex.Data | |
}; | |
if (ex.InnerException != null) | |
error.innerError = GetErrorFromException(ex.InnerException); | |
return error; | |
} | |
public class Client { | |
public string name { get; set; } | |
public string version { get; set; } | |
public string clientUrl { get; set; } | |
} | |
public class StackTrace { | |
public int lineNumber { get; set; } | |
public string className { get; set; } | |
public string fileName { get; set; } | |
public string methodName { get; set; } | |
} | |
public class Error { | |
public Error innerError { get; set; } | |
public IDictionary data { get; set; } | |
public string className { get; set; } | |
public string message { get; set; } | |
public List<StackTrace> stackTrace { get; set; } | |
} | |
public class Details { | |
public string machineName { get; set; } | |
public string version { get; set; } | |
public Client client { get; set; } | |
public Error error { get; set; } | |
} | |
public class RaygunMessage { | |
public string occurredOn { get; set; } | |
public Details details { get; set; } | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment