diff --git a/Mindscape.Raygun4Net.Core/IRaygunMessageInitializer.cs b/Mindscape.Raygun4Net.Core/IRaygunMessageInitializer.cs new file mode 100644 index 00000000..793df0e0 --- /dev/null +++ b/Mindscape.Raygun4Net.Core/IRaygunMessageInitializer.cs @@ -0,0 +1,20 @@ +using Mindscape.Raygun4Net.Messages; + +namespace Mindscape.Raygun4Net +{ + /// + /// Represents an initializer for objects. + /// + /// + /// The Raygun Clients use registered + /// to automatically add additional properties to objects. + /// + public interface IRaygunMessageInitializer + { + /// + /// Initializes properties of the specified object. + /// + /// The to initialize. + void Initialize(RaygunMessage raygunMessage); + } +} \ No newline at end of file diff --git a/Mindscape.Raygun4Net.Core/Mindscape.Raygun4Net.Core.csproj b/Mindscape.Raygun4Net.Core/Mindscape.Raygun4Net.Core.csproj index 5b219fcb..5099311b 100644 --- a/Mindscape.Raygun4Net.Core/Mindscape.Raygun4Net.Core.csproj +++ b/Mindscape.Raygun4Net.Core/Mindscape.Raygun4Net.Core.csproj @@ -121,6 +121,7 @@ SimpleJson.cs + diff --git a/Mindscape.Raygun4Net.WebApi.Tests/Mindscape.Raygun4Net.WebApi.Tests.csproj b/Mindscape.Raygun4Net.WebApi.Tests/Mindscape.Raygun4Net.WebApi.Tests.csproj index d3bb63fc..39828cb6 100644 --- a/Mindscape.Raygun4Net.WebApi.Tests/Mindscape.Raygun4Net.WebApi.Tests.csproj +++ b/Mindscape.Raygun4Net.WebApi.Tests/Mindscape.Raygun4Net.WebApi.Tests.csproj @@ -49,6 +49,7 @@ + diff --git a/Mindscape.Raygun4Net.WebApi.Tests/Model/FakeRaygunWebApiClient.cs b/Mindscape.Raygun4Net.WebApi.Tests/Model/FakeRaygunWebApiClient.cs index 42b0894e..a6cdf491 100644 --- a/Mindscape.Raygun4Net.WebApi.Tests/Model/FakeRaygunWebApiClient.cs +++ b/Mindscape.Raygun4Net.WebApi.Tests/Model/FakeRaygunWebApiClient.cs @@ -1,5 +1,6 @@ using Mindscape.Raygun4Net.Messages; using System; +using System.Collections; using System.Collections.Generic; namespace Mindscape.Raygun4Net.WebApi.Tests.Model @@ -15,5 +16,10 @@ public IEnumerable ExposeStripWrapperExceptions(Exception exception) { return base.StripWrapperExceptions(exception); } + + public RaygunMessage ExposeBuildMessage(Exception exception, IList tags, IDictionary userCustomData, DateTime? currentTime = null) + { + return base.BuildMessage(exception, tags, userCustomData, currentTime); + } } } diff --git a/Mindscape.Raygun4Net.WebApi.Tests/Model/TestRaygunMessageInitializer.cs b/Mindscape.Raygun4Net.WebApi.Tests/Model/TestRaygunMessageInitializer.cs new file mode 100644 index 00000000..1c677aa5 --- /dev/null +++ b/Mindscape.Raygun4Net.WebApi.Tests/Model/TestRaygunMessageInitializer.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; +using Mindscape.Raygun4Net.Messages; + +namespace Mindscape.Raygun4Net.WebApi.Tests.Model +{ + public class TestRaygunMessageInitializer : IRaygunMessageInitializer + { + public void Initialize(RaygunMessage raygunMessage) + { + if (raygunMessage.Details.UserCustomData == null) + { + raygunMessage.Details.UserCustomData = new Dictionary(); + } + raygunMessage.Details.UserCustomData["initializerTestData"] = "true"; + } + } +} \ No newline at end of file diff --git a/Mindscape.Raygun4Net.WebApi.Tests/RaygunWebApiClientTests.cs b/Mindscape.Raygun4Net.WebApi.Tests/RaygunWebApiClientTests.cs index 353daa69..f0daffe1 100644 --- a/Mindscape.Raygun4Net.WebApi.Tests/RaygunWebApiClientTests.cs +++ b/Mindscape.Raygun4Net.WebApi.Tests/RaygunWebApiClientTests.cs @@ -265,5 +265,14 @@ public void StripReflectionTypeLoadException() Assert.IsTrue(ex1.Data["Type"].ToString().Contains("FakeRaygunWebApiClient")); Assert.IsTrue(ex2.Data["Type"].ToString().Contains("WrapperException")); } + + [Test] + public void CanUseInitializerToSetData() + { + _client.AddMessageInitializer(new TestRaygunMessageInitializer()); + var message = _client.ExposeBuildMessage(_exception, null, null); + + Assert.IsTrue(message.Details.UserCustomData.Contains("initializerTestData")); + } } } diff --git a/Mindscape.Raygun4Net.WebApi/RaygunWebApiClient.cs b/Mindscape.Raygun4Net.WebApi/RaygunWebApiClient.cs index 4d9362cf..99968812 100644 --- a/Mindscape.Raygun4Net.WebApi/RaygunWebApiClient.cs +++ b/Mindscape.Raygun4Net.WebApi/RaygunWebApiClient.cs @@ -22,7 +22,6 @@ public class RaygunWebApiClient : RaygunClientBase private readonly List _wrapperExceptions = new List(); private readonly ThreadLocal _currentWebRequest = new ThreadLocal(() => null); - private readonly ThreadLocal _currentRequestMessage = new ThreadLocal(() => null); private static RaygunWebApiExceptionFilter _exceptionFilter; private static RaygunWebApiActionFilter _actionFilter; @@ -354,8 +353,6 @@ public void Send(Exception exception, IList tags, IDictionary userCustom { if (CanSend(exception)) { - _currentRequestMessage.Value = BuildRequestMessage(); - StripAndSend(exception, tags, userCustomData); FlagAsSent(exception); } @@ -390,15 +387,9 @@ public void SendInBackground(Exception exception, IList tags, IDictionar { if (CanSend(exception)) { - // We need to process the HttpRequestMessage on the current thread, - // otherwise it will be disposed while we are using it on the other thread. - RaygunRequestMessage currentRequestMessage = BuildRequestMessage(); DateTime currentTime = DateTime.UtcNow; - - ThreadPool.QueueUserWorkItem(c => { - _currentRequestMessage.Value = currentRequestMessage; - StripAndSend(exception, tags, userCustomData, currentTime); - }); + + StripAndSendInBackground(exception, tags, userCustomData, currentTime); FlagAsSent(exception); } } @@ -439,7 +430,7 @@ protected RaygunMessage BuildMessage(Exception exception, IList tags, ID protected RaygunMessage BuildMessage(Exception exception, IList tags, IDictionary userCustomData, DateTime? currentTime = null) { var message = RaygunWebApiMessageBuilder.New - .SetHttpDetails(_currentRequestMessage.Value) + .SetHttpDetails(BuildRequestMessage()) .SetTimeStamp(currentTime) .SetEnvironmentDetails() .SetMachineName(Environment.MachineName) @@ -455,6 +446,25 @@ protected RaygunMessage BuildMessage(Exception exception, IList tags, ID { message.Details.GroupingKey = customGroupingKey; } + + var initializers = GetRaygunMessageInitializers(); + foreach (var initializer in initializers) + { + try + { + initializer.Initialize(message); + } + catch (Exception ex) + { + System.Diagnostics.Trace.WriteLine(string.Format("Error Initializing RaygunMessage {0}", ex.Message)); + + if (RaygunSettings.Settings.ThrowOnError) + { + throw; + } + } + } + return message; } @@ -466,6 +476,21 @@ private void StripAndSend(Exception exception, IList tags, IDictionary u } } + private void StripAndSendInBackground(Exception exception, IList tags, IDictionary userCustomData, DateTime? currentTime = null) + { + var messages = new List(); + foreach (Exception e in StripWrapperExceptions(exception)) + { + messages.Add(BuildMessage(e, tags, userCustomData, currentTime)); + } + // Could use SendInBackground here, but like this we queue multiple sends on a single thread + ThreadPool.QueueUserWorkItem(c => { + foreach (var message in messages) { + Send(message); + } + }); + } + protected IEnumerable StripWrapperExceptions(Exception exception) { if (exception != null && _wrapperExceptions.Any(wrapperException => exception.GetType() == wrapperException && (exception.InnerException != null || exception is ReflectionTypeLoadException))) diff --git a/Mindscape.Raygun4Net/RaygunClientBase.cs b/Mindscape.Raygun4Net/RaygunClientBase.cs index 2418c44e..9c792efd 100644 --- a/Mindscape.Raygun4Net/RaygunClientBase.cs +++ b/Mindscape.Raygun4Net/RaygunClientBase.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Net; using Mindscape.Raygun4Net.Messages; @@ -115,6 +116,44 @@ protected string OnCustomGroupingKey(Exception exception, RaygunMessage message) return result; } + /// + /// List of registered objects. + /// + private List RaygunMessageInitializers { get; set; } = new List(); + + /// + /// Return a copy of the registered objects. + /// + /// + /// Ensures that changes made to the collection during processing does not cause problems. + /// + protected virtual List GetRaygunMessageInitializers() + { + return new List(RaygunMessageInitializers); + } + + /// + /// Register a to initialize properties on the . + /// + /// The to register. + public virtual void AddMessageInitializer(IRaygunMessageInitializer raygunMessageInitializer) + { + if (RaygunMessageInitializers == null) + { + RaygunMessageInitializers = new List(); + } + RaygunMessageInitializers.Add(raygunMessageInitializer); + } + + /// + /// Removes a from the list of registered objects. + /// + /// The to remove. + public virtual bool RemoveMessageInitializer(IRaygunMessageInitializer raygunMessageInitializer) + { + return RaygunMessageInitializers.Remove(raygunMessageInitializer); + } + /// /// Transmits an exception to Raygun.io synchronously. ///