using System; using System.Collections.Generic; using System.Diagnostics; using System.Reflection; using System.Threading; using System.Web; using Exceptron.Client.Configuration; using Exceptron.Client.Message; namespace Exceptron.Client { public class ExceptronClient : IExceptronClient { internal IRestClient RestClient { private get; set; } /// <summary> /// Version of Client /// </summary> public string ClientVersion { get { return Assembly.GetExecutingAssembly().GetName().Version.ToString(); } } /// <summary> /// Name of Client /// </summary> public string ClientName { get { return "Official .NET"; } } /// <summary> /// Client Configuration /// </summary> public ExceptronConfiguration Configuration { get; private set; } /// <summary> /// Framework Type of the Host Application (.Net/mono) /// </summary> public string FrameworkType { get; set; } /// <summary> /// Creates a new instance of <see cref="ExceptronClient"/> /// Loads <see cref="ExceptronConfiguration"/> from application config file. /// </summary> /// <param name="applicationVersion">Version of the currently running application</param> public ExceptronClient(Version applicationVersion) : this(ExceptronConfiguration.ReadConfig(), applicationVersion) { FrameworkType = ".Net"; } private readonly string _applicationVersion; private readonly string _maxFrameworkVersion; /// <param name="exceptronConfiguration">exceptron client configuration</param> /// <param name="applicationVersion"> </param> public ExceptronClient(ExceptronConfiguration exceptronConfiguration, Version applicationVersion) { if (exceptronConfiguration == null) throw new ArgumentNullException("exceptronConfiguration"); if (applicationVersion == null) throw new ArgumentNullException("applicationVersion"); if (string.IsNullOrEmpty(exceptronConfiguration.ApiKey)) throw new ArgumentException("An API Key was not provided"); Configuration = exceptronConfiguration; RestClient = new RestClient(); _applicationVersion = applicationVersion.ToString(); _maxFrameworkVersion = GetMaximumFrameworkVersion(); FrameworkType = ".Net"; } /// <summary> /// Submit an exception to exceptron Servers. /// </summary> /// <param name="exception">Exception that is being reported</param> /// <param name="component" /// example="DataAccess, Configuration, Registration, etc." /// remarks="It is common to use the logger name that was used to log the exception as the component.">Component that experienced this exception.</param> /// <param name="severity">Severity of the exception being reported</param> /// <param name="message" /// example="Something went wrong while checking for application updates.">Any message that should be attached to this exceptions</param> /// <param name="userId" /// remarks="This Id does not have to be tied to the user's identity. /// You can use a system generated unique ID such as GUID. /// This field is used to report how many unique users are experiencing an error." /// example=" /// 62E5C8EF-0CA2-43AB-B278-FC6994F776ED /// Timmy@aol.com /// 26437 /// ">ID that will uniquely identify the user</param> /// <param name="httpContext"><see cref="System.Web.HttpContext"/> in which the exception occurred. If no <see cref="System.Web.HttpContext"/> is provided /// <see cref="ExceptronClient"/> will try to get the current <see cref="System.Web.HttpContext"/> from <see cref="System.Web.HttpContext.Current"/></param> /// <returns></returns> public ExceptionResponse SubmitException(Exception exception, string component, ExceptionSeverity severity = ExceptionSeverity.None, string message = null, string userId = null, HttpContext httpContext = null) { var exceptionData = new ExceptionData { Exception = exception, Component = component, Severity = severity, Message = message, UserId = userId, HttpContext = httpContext }; return SubmitException(exceptionData); } /// <summary> /// Submit an exception to exceptron Servers. /// </summary> /// <param name="exceptionData">Exception data to be reported to the server</param> public ExceptionResponse SubmitException(ExceptionData exceptionData) { try { ValidateState(exceptionData); var report = new ExceptionReport(); report.ap = Configuration.ApiKey; report.dn = ClientName; report.dv = ClientVersion; report.aver = _applicationVersion; report.ext = exceptionData.Exception.GetType().FullName; report.stk = ConvertToFrames(exceptionData.Exception); report.exm = exceptionData.Exception.Message; report.cmp = exceptionData.Component; report.uid = exceptionData.UserId; report.msg = exceptionData.Message; report.sv = (int)exceptionData.Severity; report.fv = _maxFrameworkVersion; report.ft = FrameworkType; SetHttpInfo(exceptionData, report); SetEnviromentInfo(report); var exceptionResponse = RestClient.Put<ExceptionResponse>(Configuration.Host, report); exceptionData.Exception.Data["et"] = exceptionResponse.RefId; return exceptionResponse; } catch (Exception e) { Trace.WriteLine("Unable to submit exception to exceptron. ", e.ToString()); if (Configuration.ThrowExceptions) { throw; } return new ExceptionResponse { Exception = e }; } } private void ValidateState(ExceptionData exceptionData) { if (string.IsNullOrEmpty(Configuration.ApiKey)) throw new InvalidOperationException("ApiKey has not been provided for this client."); if (exceptionData == null) throw new ArgumentNullException("exceptionData"); if (exceptionData.Exception == null) throw new ArgumentException("ExceptionData.Exception Cannot be null.", "exceptionData"); } private void SetEnviromentInfo(ExceptionReport report) { report.cul = Thread.CurrentThread.CurrentCulture.Name; try { report.os = Environment.OSVersion.VersionString; } catch (Exception) { if (Configuration.ThrowExceptions) throw; } if (Configuration.IncludeMachineName) { try { report.hn = Environment.MachineName; } catch (Exception) { if (Configuration.ThrowExceptions) throw; } } } private void SetHttpInfo(ExceptionData exceptionData, ExceptionReport report) { if (exceptionData.HttpContext == null && HttpContext.Current == null) return; if (exceptionData.HttpContext == null) { exceptionData.HttpContext = HttpContext.Current; } try { report.hm = exceptionData.HttpContext.Request.HttpMethod; //TODO:find proper way to find http status code. /* var httpException = exceptionData.Exception as HttpException; if (httpException != null) { report.sc = httpException.GetHttpCode(); }*/ report.url = exceptionData.HttpContext.Request.Url.ToString(); report.ua = exceptionData.HttpContext.Request.UserAgent; } catch (Exception) { if (Configuration.ThrowExceptions) throw; } } internal static List<Frame> ConvertToFrames(Exception exception) { if (exception == null) return null; var stackTrace = new StackTrace(exception, true); var frames = stackTrace.GetFrames(); if (frames == null) return null; var result = new List<Frame>(); for (int index = 0; index < frames.Length; index++) { var frame = frames[index]; var method = frame.GetMethod(); var declaringType = method.DeclaringType; var fileName = frame.GetFileName(); var currentFrame = new Frame { i = index, fn = fileName, ln = frame.GetFileLineNumber(), m = method.ToString(), }; currentFrame.m = currentFrame.m.Substring(currentFrame.m.IndexOf(' ')).Trim(); if (declaringType != null) { currentFrame.c = declaringType.FullName; } result.Add(currentFrame); } return result; } private string GetMaximumFrameworkVersion() { var clrVersion = Environment.Version; if (clrVersion.Major == 2) { //Check if 2.0 or 3.5 try { Assembly.Load("System.Core, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"); return "3.5"; } catch (Exception) { } return "2.0"; } if (clrVersion.Major == 4) { //Check if 4.0 or 4.5 try { Assembly.Load("System.Threading.Tasks.Parallel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"); return "4.5"; } catch (Exception) { } return "4.0"; } return "Unknown"; } } }