diff --git a/RaygunCore.AspNetCore/RaygunCore.AspNetCore.csproj b/RaygunCore.AspNetCore/RaygunCore.AspNetCore.csproj index 6c2f7a0..387dd6a 100644 --- a/RaygunCore.AspNetCore/RaygunCore.AspNetCore.csproj +++ b/RaygunCore.AspNetCore/RaygunCore.AspNetCore.csproj @@ -5,7 +5,7 @@ RaygunCore.AspNetCore Raygun provider for ASP.NET Core. raygun;core;netcore;aspnetcore;aspnetcoremvc - net6.0 + net7.0 diff --git a/RaygunCore.Test/RaygunClientTest.cs b/RaygunCore.Test/RaygunClientTest.cs new file mode 100644 index 0000000..a2c9cdf --- /dev/null +++ b/RaygunCore.Test/RaygunClientTest.cs @@ -0,0 +1,74 @@ +using System.Net; +using System.Text; +using System.Text.Json; +using Microsoft.Extensions.Options; +using RaygunCore.Messages; +using RaygunCore.Services; + +namespace RaygunCore.Test; + +public class RaygunClientTest +{ + [Fact] + public async Task Index() + { + string serverUrl = "http://localhost:8888/"; + var server = new HttpListener(); + server.Prefixes.Add(serverUrl); + server.Start(); + var resultTask = server.GetContextAsync().ContinueWith(task => + { + var context = task.Result; + string content; + using (var sr = new StreamReader(context.Request.InputStream, Encoding.UTF8)) + content = sr.ReadToEnd(); + + context.Response.ContentLength64 = 0; + context.Response.Close(); + return ( + Method: context.Request.HttpMethod, + ContentType: context.Request.ContentType, + Headers: context.Request.Headers, + Content: content + ); + }); + + string message = "Test message"; + var severity = RaygunSeverity.Critical; + var tags = new string[] { "tag1", "tag2" }; + var httpClientFactory = new TestHttpClientFactory(); + var raygunOptions = new RaygunOptions + { + ApiKey = "API_KEY", + ApiEndpoint = new Uri(serverUrl), + AppVersion = "1.0", + ThrowOnError = true + }; + var raygunMessageBuilder = new DefaultRaygunMessageBuilder(new IRaygunMessageProvider[] + { + new MainMessageProvider(Options.Create(raygunOptions)), + new TestUserMessageProvider() + }); + var raygunClient = new DefaultRaygunClient(httpClientFactory, raygunMessageBuilder, Enumerable.Empty(), Options.Create(raygunOptions)); + await raygunClient.SendAsync(message, severity, tags); + server.Stop(); + + var result = await resultTask; + result.Method.Should().Be("POST"); + result.Headers["X-ApiKey"].Should().Be(raygunOptions.ApiKey); + result.ContentType.Should().StartWith("application/json"); + result.Content.Should().NotBeNullOrEmpty(); + result.Content.Should().NotContain("\t"); + + var resultMessage = JsonSerializer.Deserialize(result.Content, new JsonSerializerOptions + { + PropertyNamingPolicy = JsonNamingPolicy.CamelCase + })!; + resultMessage.Details.Version.Should().Be(raygunOptions.AppVersion); + resultMessage.Details.Tags.Should().BeEquivalentTo(tags.Prepend(severity.ToString())); + resultMessage.Details.Error.Message.Should().Be(message); + resultMessage.Details.User?.Identifier.Should().Be(TestUserMessageProvider.Email); + resultMessage.Details.User?.Email.Should().Be(TestUserMessageProvider.Email); + resultMessage.Details.User?.IsAnonymous.Should().BeFalse(); + } +} \ No newline at end of file diff --git a/RaygunCore.Test/RaygunCore.Test.csproj b/RaygunCore.Test/RaygunCore.Test.csproj new file mode 100644 index 0000000..8bdf65d --- /dev/null +++ b/RaygunCore.Test/RaygunCore.Test.csproj @@ -0,0 +1,22 @@ + + + + + RaygunCore.Test + net7.0 + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/RaygunCore.Test/TestHttpClientFactory.cs b/RaygunCore.Test/TestHttpClientFactory.cs new file mode 100644 index 0000000..ca5033a --- /dev/null +++ b/RaygunCore.Test/TestHttpClientFactory.cs @@ -0,0 +1,15 @@ +namespace RaygunCore.Test; + +sealed class TestHttpClientFactory: IHttpClientFactory, IDisposable +{ + private readonly Lazy _handlerLazy = new(() => new HttpClientHandler()); + + public HttpClient CreateClient(string name) + => new (_handlerLazy.Value, disposeHandler: false); + + public void Dispose() + { + if (_handlerLazy.IsValueCreated) + _handlerLazy.Value.Dispose(); + } +} \ No newline at end of file diff --git a/RaygunCore.Test/TestUserMessageProvider.cs b/RaygunCore.Test/TestUserMessageProvider.cs new file mode 100644 index 0000000..7f71964 --- /dev/null +++ b/RaygunCore.Test/TestUserMessageProvider.cs @@ -0,0 +1,17 @@ +using RaygunCore.Messages; + +namespace RaygunCore.Test; + +class TestUserMessageProvider : IRaygunMessageProvider +{ + public const string Email = "user@example.net"; + + public void Apply(RaygunMessageDetails details) + { + details.User = new(Email) + { + Email = Email, + IsAnonymous = false + }; + } +} \ No newline at end of file diff --git a/RaygunCore.props b/RaygunCore.props index 466f30b..14278c4 100644 --- a/RaygunCore.props +++ b/RaygunCore.props @@ -6,7 +6,7 @@ https://github.com/anfomin/rayguncore https://github.com/anfomin/rayguncore LICENSE - 2.0.0 + 2.1.0 $(VERSION_SUFFIX) $(NoWarn);1573;1591 true diff --git a/RaygunCore.sln b/RaygunCore.sln index f09f38c..48d0c88 100644 --- a/RaygunCore.sln +++ b/RaygunCore.sln @@ -1,4 +1,4 @@ - + Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26124.0 @@ -7,6 +7,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RaygunCore", "RaygunCore\Ra EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RaygunCore.AspNetCore", "RaygunCore.AspNetCore\RaygunCore.AspNetCore.csproj", "{FFF7285C-4EF8-4EF6-8480-6865CEB61089}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RaygunCore.Test", "RaygunCore.Test\RaygunCore.Test.csproj", "{436221A5-06B9-4508-8A2F-BD32B3ADEF48}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -44,5 +46,17 @@ Global {FFF7285C-4EF8-4EF6-8480-6865CEB61089}.Release|x64.Build.0 = Release|x64 {FFF7285C-4EF8-4EF6-8480-6865CEB61089}.Release|x86.ActiveCfg = Release|x86 {FFF7285C-4EF8-4EF6-8480-6865CEB61089}.Release|x86.Build.0 = Release|x86 + {436221A5-06B9-4508-8A2F-BD32B3ADEF48}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {436221A5-06B9-4508-8A2F-BD32B3ADEF48}.Debug|Any CPU.Build.0 = Debug|Any CPU + {436221A5-06B9-4508-8A2F-BD32B3ADEF48}.Debug|x64.ActiveCfg = Debug|Any CPU + {436221A5-06B9-4508-8A2F-BD32B3ADEF48}.Debug|x64.Build.0 = Debug|Any CPU + {436221A5-06B9-4508-8A2F-BD32B3ADEF48}.Debug|x86.ActiveCfg = Debug|Any CPU + {436221A5-06B9-4508-8A2F-BD32B3ADEF48}.Debug|x86.Build.0 = Debug|Any CPU + {436221A5-06B9-4508-8A2F-BD32B3ADEF48}.Release|Any CPU.ActiveCfg = Release|Any CPU + {436221A5-06B9-4508-8A2F-BD32B3ADEF48}.Release|Any CPU.Build.0 = Release|Any CPU + {436221A5-06B9-4508-8A2F-BD32B3ADEF48}.Release|x64.ActiveCfg = Release|Any CPU + {436221A5-06B9-4508-8A2F-BD32B3ADEF48}.Release|x64.Build.0 = Release|Any CPU + {436221A5-06B9-4508-8A2F-BD32B3ADEF48}.Release|x86.ActiveCfg = Release|Any CPU + {436221A5-06B9-4508-8A2F-BD32B3ADEF48}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection EndGlobal diff --git a/RaygunCore/Messages/RaygunEnvironmentMessage.cs b/RaygunCore/Messages/RaygunEnvironmentMessage.cs index 019f053..318d472 100755 --- a/RaygunCore/Messages/RaygunEnvironmentMessage.cs +++ b/RaygunCore/Messages/RaygunEnvironmentMessage.cs @@ -10,7 +10,7 @@ public class RaygunEnvironmentMessage public string? Architecture { get; set; } public ulong? TotalVirtualMemory { get; set; } public ulong? AvailableVirtualMemory { get; set; } - public IList? DiskSpaceFree { get; set; } + public List? DiskSpaceFree { get; set; } public ulong? TotalPhysicalMemory { get; set; } public ulong? AvailablePhysicalMemory { get; set; } public double? UtcOffset { get; set; } diff --git a/RaygunCore/Messages/RaygunErrorMessage.cs b/RaygunCore/Messages/RaygunErrorMessage.cs index 0cfcbf6..5a46258 100755 --- a/RaygunCore/Messages/RaygunErrorMessage.cs +++ b/RaygunCore/Messages/RaygunErrorMessage.cs @@ -1,12 +1,10 @@ -using System.Collections; - namespace RaygunCore.Messages; public class RaygunErrorMessage { public string? ClassName { get; set; } public string Message { get; set; } = ""; - public IDictionary? Data { get; set; } + public Dictionary? Data { get; set; } public RaygunErrorMessage? InnerError { get; set; } public RaygunErrorMessage[]? InnerErrors { get; set; } public RaygunErrorStackTraceLineMessage[]? StackTrace { get; set; } diff --git a/RaygunCore/Messages/RaygunErrorStackTraceLineMessage.cs b/RaygunCore/Messages/RaygunErrorStackTraceLineMessage.cs index 9dc5e67..ccb140a 100755 --- a/RaygunCore/Messages/RaygunErrorStackTraceLineMessage.cs +++ b/RaygunCore/Messages/RaygunErrorStackTraceLineMessage.cs @@ -4,6 +4,6 @@ public class RaygunErrorStackTraceLineMessage { public int LineNumber { get; set; } public string ClassName { get; set; } = ""; - public string FileName { get; set; } = ""; public string MethodName { get; set; } = ""; + public string? FileName { get; set; } } \ No newline at end of file diff --git a/RaygunCore/Messages/RaygunMessage.cs b/RaygunCore/Messages/RaygunMessage.cs index 2b8dc36..f79d478 100755 --- a/RaygunCore/Messages/RaygunMessage.cs +++ b/RaygunCore/Messages/RaygunMessage.cs @@ -3,5 +3,5 @@ public class RaygunMessage { public DateTime OccurredOn { get; set; } = DateTime.UtcNow; - public RaygunMessageDetails Details { get; } = new RaygunMessageDetails(); + public RaygunMessageDetails Details { get; set; } = new(); } \ No newline at end of file diff --git a/RaygunCore/Messages/RaygunMessageDetails.cs b/RaygunCore/Messages/RaygunMessageDetails.cs index 670ac0f..4bcd76c 100755 --- a/RaygunCore/Messages/RaygunMessageDetails.cs +++ b/RaygunCore/Messages/RaygunMessageDetails.cs @@ -5,11 +5,11 @@ public class RaygunMessageDetails public string? MachineName { get; set; } public string? GroupingKey { get; set; } public string? Version { get; set; } - public RaygunClientMessage Client { get; } = new RaygunClientMessage(); - public RaygunEnvironmentMessage Environment { get; } = new RaygunEnvironmentMessage(); - public List Tags { get; } = new List(); - public Dictionary UserCustomData { get; } = new Dictionary(); - public RaygunErrorMessage Error { get; set; } = null!; + public RaygunClientMessage Client { get; set; } = new(); + public RaygunEnvironmentMessage Environment { get; set; } = new(); + public List Tags { get; set; } = new(); + public Dictionary UserCustomData { get; set; } = new(); + public RaygunErrorMessage Error { get; set; } = new(); public RaygunUserMessage? User { get; set; } public RaygunRequestMessage? Request { get; set; } public RaygunResponseMessage? Response { get; set; } diff --git a/RaygunCore/Messages/RaygunRequestMessage.cs b/RaygunCore/Messages/RaygunRequestMessage.cs index fb6c15b..571d805 100755 --- a/RaygunCore/Messages/RaygunRequestMessage.cs +++ b/RaygunCore/Messages/RaygunRequestMessage.cs @@ -6,10 +6,10 @@ public class RaygunRequestMessage public string? Url { get; set; } public string? HttpMethod { get; set; } public string? IPAddress { get; set; } - public IDictionary? Headers { get; set; } - public IDictionary? Cookies { get; set; } - public IDictionary? QueryString { get; set; } - public IDictionary? Form { get; set; } - public IDictionary? Data { get; set; } + public Dictionary? Headers { get; set; } + public Dictionary? Cookies { get; set; } + public Dictionary? QueryString { get; set; } + public Dictionary? Form { get; set; } + public Dictionary? Data { get; set; } public string? RawData { get; set; } } \ No newline at end of file diff --git a/RaygunCore/RaygunCore.csproj b/RaygunCore/RaygunCore.csproj index 36652e4..4efb2e9 100644 --- a/RaygunCore/RaygunCore.csproj +++ b/RaygunCore/RaygunCore.csproj @@ -5,16 +5,15 @@ RaygunCore Raygun provider for .NET Core projects. raygun;core;netcore - net6.0 + net7.0 - - - - - - - + + + + + + \ No newline at end of file diff --git a/RaygunCore/Services/DefaultRaygunClient.cs b/RaygunCore/Services/DefaultRaygunClient.cs index 7696bff..a008faa 100644 --- a/RaygunCore/Services/DefaultRaygunClient.cs +++ b/RaygunCore/Services/DefaultRaygunClient.cs @@ -1,7 +1,6 @@ -using System.Text; -using RaygunCore.Messages; +using System.Net.Http.Json; using Microsoft.Extensions.Options; -using Newtonsoft.Json; +using RaygunCore.Messages; namespace RaygunCore.Services; @@ -65,11 +64,9 @@ protected virtual HttpClient CreateClient() /// Custom text. /// The exception to deliver. protected virtual bool ShouldSend(string message, Exception? exception) - { - return (exception == null || !exception.IsSent()) + => (exception == null || !exception.IsSent()) && exception is not RaygunException && _validators.All(v => v.ShouldSend(message, exception)); - } /// /// Transmits a message to Raygun. @@ -80,7 +77,7 @@ protected async Task TransmitMessageAsync(RaygunMessage message) { try { - var result = await CreateClient().PostAsync(_options.ApiEndpoint, new JsonContent(message)); + var result = await CreateClient().PostAsync(_options.ApiEndpoint, JsonContent.Create(message)); result.EnsureSuccessStatusCode(); } catch (Exception ex) @@ -89,10 +86,4 @@ protected async Task TransmitMessageAsync(RaygunMessage message) throw new RaygunException(ex); } } - - class JsonContent : StringContent - { - public JsonContent(object obj) - : base(JsonConvert.SerializeObject(obj), Encoding.UTF8, "application/json") { } - } } \ No newline at end of file diff --git a/RaygunCore/Services/DefaultRaygunMessageBuilder.cs b/RaygunCore/Services/DefaultRaygunMessageBuilder.cs index 854410e..04cebd3 100644 --- a/RaygunCore/Services/DefaultRaygunMessageBuilder.cs +++ b/RaygunCore/Services/DefaultRaygunMessageBuilder.cs @@ -76,13 +76,12 @@ protected RaygunErrorMessage CreateErrorMessage(Exception exception) if (exception.Data != null) { - var data = new Dictionary(); + message.Data = new(); foreach (object key in exception.Data.Keys) { if (!ExceptionExtensions.SentKey.Equals(key)) - data[key] = exception.Data[key]; + message.Data[key] = exception.Data[key]; } - message.Data = data; } if (exception is AggregateException ae && ae.InnerExceptions != null) @@ -118,7 +117,7 @@ RaygunErrorStackTraceLineMessage[] BuildStackTrace(StackTrace stackTrace, bool i lines.Add(new RaygunErrorStackTraceLineMessage { - FileName = frame.GetFileName() ?? string.Empty, + FileName = frame.GetFileName(), LineNumber = lineNumber, ClassName = method.DeclaringType?.FullName ?? "(unknown)", MethodName = GenerateMethodName(method) @@ -138,12 +137,12 @@ string GenerateMethodName(MethodBase method) if (method is MethodInfo && method.IsGenericMethod) { sb.Append("<"); - sb.Append(String.Join(",", method.GetGenericArguments().Select(a => a.Name))); + sb.Append(string.Join(",", method.GetGenericArguments().Select(a => a.Name))); sb.Append(">"); } sb.Append("("); - sb.Append(String.Join(", ", method.GetParameters().Select(p => $"{p.ParameterType?.Name ?? ""} {p.Name}"))); + sb.Append(string.Join(", ", method.GetParameters().Select(p => $"{p.ParameterType?.Name ?? ""} {p.Name}"))); sb.Append(")"); return sb.ToString(); } diff --git a/RaygunCore/Services/RaygunLogger.cs b/RaygunCore/Services/RaygunLogger.cs index 1cacf71..7092f0a 100644 --- a/RaygunCore/Services/RaygunLogger.cs +++ b/RaygunCore/Services/RaygunLogger.cs @@ -8,7 +8,7 @@ namespace RaygunCore.Services; /// public class RaygunLogger : ILogger { - static HashSet _runningTasks = new HashSet(); + static HashSet _runningTasks = new(); public static IEnumerable RunningTasks => _runningTasks; readonly Lazy _client; @@ -19,8 +19,9 @@ public RaygunLogger(Lazy client) /// /// Scopes not implemeted. /// - public IDisposable BeginScope(TState state) - => new EmptyDisposable(); + public IDisposable? BeginScope(TState state) + where TState: notnull + => null; /// public bool IsEnabled(LogLevel logLevel) => logLevel >= LogLevel.Warning; @@ -50,9 +51,4 @@ RaygunSeverity GetSeverity(LogLevel logLevel) LogLevel.Critical => RaygunSeverity.Critical, _ => throw new NotSupportedException($"LogLevel {logLevel} is not supported") }; - - class EmptyDisposable : IDisposable - { - public void Dispose() { } - } } \ No newline at end of file