-
-
Notifications
You must be signed in to change notification settings - Fork 53
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add ApiExceptionDestructurer for Refit.ApiException (Refit NuGet) #398
Merged
RehanSaeed
merged 8 commits into
RehanSaeed:main
from
git-heath:feature/refit-destructurer
Sep 28, 2021
Merged
Changes from 5 commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
8bb3630
- Add ApiExceptionDestructurer for Refit.ApiException (Refit NuGet)
1686e49
Removed unrequired InternalsVisibleTo from Serilog.Exception.Refit pr…
0a5c0b3
Make destructure of HTTP content optional.
f6bbf5c
Disable reflection based destructurer for tests.
4994147
Revert to VS version 17 in .sln file
e1fa29b
Correct documentation
92a2f99
Removed necessary assignment.
0d12b7e
Put lowest possible version of refit, for the major version 6.
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
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
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
71 changes: 71 additions & 0 deletions
71
Source/Serilog.Exceptions.Refit/Destructurers/ApiExceptionDestructurer.cs
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
namespace Serilog.Exceptions.Refit.Destructurers | ||
{ | ||
using System; | ||
using System.Collections.Generic; | ||
using global::Refit; | ||
using Serilog.Exceptions.Core; | ||
using Serilog.Exceptions.Destructurers; | ||
|
||
/// <summary> | ||
/// A destructurer for the Refit <see cref="ApiException"/>. | ||
/// </summary> | ||
/// <seealso cref="ExceptionDestructurer" /> | ||
public class ApiExceptionDestructurer : ExceptionDestructurer | ||
{ | ||
private readonly bool destructureCommonExceptionProperties = true; | ||
RehanSaeed marked this conversation as resolved.
Show resolved
Hide resolved
|
||
private readonly bool destructureHttpContent; | ||
|
||
/// <summary> | ||
/// Initializes a new instance of the <see cref="ApiExceptionDestructurer"/> class. | ||
/// </summary> | ||
/// <param name="destructureCommonExceptionProperties">Destructure common public Exception properties or not.</param> | ||
/// <param name="destructureHttpContent">Destructure the HTTP body. This is left optional due to possible security and log size concerns.</param> | ||
public ApiExceptionDestructurer(bool destructureCommonExceptionProperties = true, bool destructureHttpContent = false) | ||
{ | ||
this.destructureCommonExceptionProperties = destructureCommonExceptionProperties; | ||
this.destructureHttpContent = destructureHttpContent; | ||
} | ||
|
||
/// <inheritdoc cref="IExceptionDestructurer.TargetTypes"/> | ||
public override Type[] TargetTypes => new[] { typeof(ApiException) }; | ||
|
||
/// <inheritdoc /> | ||
public override void Destructure(Exception exception, IExceptionPropertiesBag propertiesBag, Func<Exception, IReadOnlyDictionary<string, object?>?> destructureException) | ||
{ | ||
if (this.destructureCommonExceptionProperties) | ||
{ | ||
base.Destructure(exception, propertiesBag, destructureException); | ||
} | ||
else | ||
{ | ||
// Argument checks are usually done in <see cref="ExceptionDestructurer.Destructure"/> | ||
// but as we didn't call this method we need to do the checks here. | ||
if (exception is null) | ||
{ | ||
throw new ArgumentNullException(nameof(propertiesBag)); | ||
} | ||
|
||
if (propertiesBag is null) | ||
{ | ||
throw new ArgumentNullException(nameof(propertiesBag)); | ||
} | ||
|
||
if (destructureException is null) | ||
{ | ||
throw new ArgumentNullException(nameof(destructureException)); | ||
} | ||
} | ||
|
||
#pragma warning disable CA1062 // Validate arguments of public methods | ||
var apiException = (ApiException)exception; | ||
if (this.destructureHttpContent) | ||
{ | ||
propertiesBag.AddProperty(nameof(ApiException.Content), apiException.Content); | ||
} | ||
|
||
propertiesBag.AddProperty(nameof(ApiException.Uri), apiException.Uri); | ||
propertiesBag.AddProperty(nameof(ApiException.StatusCode), apiException.StatusCode); | ||
#pragma warning restore CA1062 // Validate arguments of public methods | ||
} | ||
} | ||
} |
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
using System; | ||
|
||
[assembly: CLSCompliant(true)] |
21 changes: 21 additions & 0 deletions
21
Source/Serilog.Exceptions.Refit/Serilog.Exceptions.Refit.csproj
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
<Project Sdk="Microsoft.NET.Sdk"> | ||
|
||
<PropertyGroup Label="Build"> | ||
<TargetFrameworks>net5.0;netstandard2.0</TargetFrameworks> | ||
</PropertyGroup> | ||
|
||
<PropertyGroup Label="Package"> | ||
<Product>Serilog Exceptions Refit</Product> | ||
<Description>Log exception details and custom properties that are not output in Exception.ToString(). Contains custom destructurers for Refit exceptions.</Description> | ||
<PackageTags>Serilog;Exception;Log;Logging;Detail;Details;Refit</PackageTags> | ||
</PropertyGroup> | ||
|
||
<ItemGroup Label="Project References"> | ||
<ProjectReference Include="..\..\Source\Serilog.Exceptions\Serilog.Exceptions.csproj" /> | ||
</ItemGroup> | ||
|
||
<ItemGroup Label="Package References"> | ||
<PackageReference Include="Refit" Version="6.0.94" /> | ||
RehanSaeed marked this conversation as resolved.
Show resolved
Hide resolved
|
||
</ItemGroup> | ||
|
||
</Project> |
100 changes: 100 additions & 0 deletions
100
Tests/Serilog.Exceptions.Test/Destructurers/ApiExceptionDestructurerTest.cs
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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,100 @@ | ||
namespace Serilog.Exceptions.Test.Destructurers | ||
{ | ||
using System; | ||
using System.Net; | ||
using System.Net.Http; | ||
using System.Net.Http.Json; | ||
using System.Threading.Tasks; | ||
using global::Refit; | ||
using Serilog.Exceptions.Core; | ||
using Serilog.Exceptions.Refit.Destructurers; | ||
using Xunit; | ||
using static LogJsonOutputUtils; | ||
|
||
public class ApiExceptionDestructurerTest | ||
{ | ||
[Fact] | ||
public async Task ApiException_HttpStatusCodeIsLoggedAsPropertyAsync() | ||
{ | ||
using var message = new HttpRequestMessage(HttpMethod.Get, new Uri("https://foobar.com")); | ||
using var response = new HttpResponseMessage(HttpStatusCode.InternalServerError); | ||
var options = new DestructuringOptionsBuilder().WithDestructurers(new[] { new ApiExceptionDestructurer() }); | ||
var apiException = await ApiException.Create(message, HttpMethod.Get, response, new RefitSettings()).ConfigureAwait(false); | ||
|
||
Test_LoggedExceptionContainsProperty(apiException, nameof(ApiException.StatusCode), nameof(HttpStatusCode.InternalServerError), options); | ||
} | ||
|
||
[Fact] | ||
public async Task ApiException_UriIsLoggedAsPropertyAsync() | ||
{ | ||
var requestUri = new Uri("https://foobar.com"); | ||
using var message = new HttpRequestMessage(HttpMethod.Get, requestUri); | ||
using var response = new HttpResponseMessage(HttpStatusCode.InternalServerError); | ||
var options = new DestructuringOptionsBuilder().WithDestructurers(new[] { new ApiExceptionDestructurer() }); | ||
var apiException = await ApiException.Create(message, HttpMethod.Get, response, new RefitSettings()).ConfigureAwait(false); | ||
|
||
Test_LoggedExceptionContainsProperty(apiException, nameof(ApiException.Uri), requestUri.ToString(), options); | ||
} | ||
|
||
[Fact] | ||
public async Task ApiException_ByDefaultContentIsNotLoggedAsPropertyAsync() | ||
{ | ||
var requestUri = new Uri("https://foobar.com"); | ||
using var message = new HttpRequestMessage(HttpMethod.Get, requestUri); | ||
using var response = new HttpResponseMessage(HttpStatusCode.InternalServerError); | ||
var options = new DestructuringOptionsBuilder().WithDestructurers(new[] { new ApiExceptionDestructurer() }); | ||
response.Content = JsonContent.Create("hello"); | ||
|
||
var apiException = await ApiException.Create(message, HttpMethod.Get, response, new RefitSettings()).ConfigureAwait(false); | ||
|
||
Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(ApiException.Content), options); | ||
} | ||
|
||
[Fact] | ||
public async Task ApiException_WhenSpecifiedContentIsLoggedAsPropertyAsync() | ||
{ | ||
var requestUri = new Uri("https://foobar.com"); | ||
using var message = new HttpRequestMessage(HttpMethod.Get, requestUri); | ||
using var response = new HttpResponseMessage(HttpStatusCode.InternalServerError); | ||
var options = new DestructuringOptionsBuilder().WithDestructurers(new[] { new ApiExceptionDestructurer(destructureHttpContent: true) }); | ||
response.Content = JsonContent.Create("hello"); | ||
|
||
var apiException = await ApiException.Create(message, HttpMethod.Get, response, new RefitSettings()).ConfigureAwait(false); | ||
|
||
Test_LoggedExceptionContainsProperty(apiException, nameof(ApiException.Content), "\"hello\"", options); | ||
} | ||
|
||
[Fact] | ||
public async Task ApiException_ByDefaultCommonPropertiesLoggedAsPropertiesAsync() | ||
{ | ||
var requestUri = new Uri("https://foobar.com"); | ||
using var message = new HttpRequestMessage(HttpMethod.Get, requestUri); | ||
using var response = new HttpResponseMessage(HttpStatusCode.InternalServerError); | ||
var options = new DestructuringOptionsBuilder().WithDestructurers(new[] { new ApiExceptionDestructurer() }); | ||
var apiException = await ApiException.Create(message, HttpMethod.Get, response, new RefitSettings()).ConfigureAwait(false); | ||
|
||
// No need to test all properties, just a handful is sufficient | ||
Test_LoggedExceptionContainsProperty(apiException, nameof(Exception.StackTrace), apiException.StackTrace, options); | ||
Test_LoggedExceptionContainsProperty(apiException, nameof(Exception.Message), apiException.Message, options); | ||
Test_LoggedExceptionContainsProperty(apiException, nameof(Type), apiException.GetType().ToString(), options); | ||
} | ||
|
||
[Fact] | ||
public async Task ApiException_WhenSpecifiedCommonPropertiesNotLoggedAsPropertiesAsync() | ||
{ | ||
var requestUri = new Uri("https://foobar.com"); | ||
using var message = new HttpRequestMessage(HttpMethod.Get, requestUri); | ||
using var response = new HttpResponseMessage(HttpStatusCode.InternalServerError); | ||
var options = new DestructuringOptionsBuilder().WithDestructurers(new[] { new ApiExceptionDestructurer(destructureCommonExceptionProperties: false) }); | ||
var apiException = await ApiException.Create(message, HttpMethod.Get, response, new RefitSettings()).ConfigureAwait(false); | ||
|
||
Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(Exception.StackTrace), options); | ||
Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(Exception.Message), options); | ||
Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(Exception.InnerException), options); | ||
Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(Exception.HelpLink), options); | ||
Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(Exception.Data), options); | ||
Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(Exception.HResult), options); | ||
Test_LoggedExceptionDoesNotContainProperty(apiException, nameof(Type), options); | ||
} | ||
} | ||
} |
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
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
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just asking, shouldn't it be
destructureCommonExceptionProperties: false
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Absolutely, well spotted. Now corrected, also added docmentation for the other optional constructor parameter.