Skip to content
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

Implement gRPC destructurer #563

Merged
merged 2 commits into from
Aug 15, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 18 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,6 +204,24 @@ In addition, the `ApiException.Content` property can be logged with the followin

Be careful with this option as the HTTP body could be very large and/or contain sensitive information.

### Serilog.Exceptions.Grpc

[![Serilog.Exceptions.Grpc NuGet Package](https://img.shields.io/nuget/v/Serilog.Exceptions.Grpc.svg)](https://www.nuget.org/packages/Serilog.Exceptions.Grpc/)
[![Serilog.Exceptions.Grpc NuGet Package Downloads](https://img.shields.io/nuget/dt/Serilog.Exceptions.Grpc)](https://www.nuget.org/packages/Serilog.Exceptions.Grpc)

Add the [Serilog.Exceptions.Grpc](https://www.nuget.org/packages/Serilog.Exceptions.Grpc/) NuGet package to your project to avoid the reflection based destructurer for `RpcException` when using [Grpc.Net.Client](https://www.nuget.org/packages/Grpc.Net.Client/):

```
Install-Package Serilog.Exceptions.Grpc
```

Add the `RpcExceptionDestructurer` during setup:
```csharp
.Enrich.WithExceptionDetails(new DestructuringOptionsBuilder()
.WithDefaultDestructurers()
.WithDestructurers(new[] { new RpcExceptionDestructurer() }))
```

## Custom Exception Destructurers

You may want to add support for destructuring your own exceptions without relying on reflection. To do this, create your own destructuring class implementing `ExceptionDestructurer` (You can take a look at [this](https://github.com/RehanSaeed/Serilog.Exceptions/blob/main/Source/Serilog.Exceptions/Destructurers/ArgumentExceptionDestructurer.cs) for `ArgumentException`), then simply add it like so:
Expand Down
7 changes: 7 additions & 0 deletions Serilog.Exceptions.sln
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Exceptions.MsSqlSer
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Exceptions.Refit", "Source\Serilog.Exceptions.Refit\Serilog.Exceptions.Refit.csproj", "{0EABF22F-F070-4F8D-B165-DD4C4AB62820}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Serilog.Exceptions.Grpc", "Source\Serilog.Exceptions.Grpc\Serilog.Exceptions.Grpc.csproj", "{62CD1306-225F-4FE4-8C2C-45D457E84D36}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -130,6 +132,10 @@ Global
{0EABF22F-F070-4F8D-B165-DD4C4AB62820}.Debug|Any CPU.Build.0 = Debug|Any CPU
{0EABF22F-F070-4F8D-B165-DD4C4AB62820}.Release|Any CPU.ActiveCfg = Release|Any CPU
{0EABF22F-F070-4F8D-B165-DD4C4AB62820}.Release|Any CPU.Build.0 = Release|Any CPU
{62CD1306-225F-4FE4-8C2C-45D457E84D36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{62CD1306-225F-4FE4-8C2C-45D457E84D36}.Debug|Any CPU.Build.0 = Debug|Any CPU
{62CD1306-225F-4FE4-8C2C-45D457E84D36}.Release|Any CPU.ActiveCfg = Release|Any CPU
{62CD1306-225F-4FE4-8C2C-45D457E84D36}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -147,6 +153,7 @@ Global
{4F089B23-3121-4935-B24E-7A9A497BD9FE} = {2C245036-D7F6-4F7C-9BB6-5AFBCCE480F7}
{0A21D2AD-024B-4F3D-95F4-BAEFEEE95945} = {C5508012-7216-4ABE-AB2F-B166ED5FF94F}
{0EABF22F-F070-4F8D-B165-DD4C4AB62820} = {C5508012-7216-4ABE-AB2F-B166ED5FF94F}
{62CD1306-225F-4FE4-8C2C-45D457E84D36} = {C5508012-7216-4ABE-AB2F-B166ED5FF94F}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {BE74AFAC-AC6F-4B80-860F-15C22BEE1A38}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
namespace Serilog.Exceptions.Grpc.Destructurers;

using System;
using System.Collections.Generic;
using global::Grpc.Core;
using Serilog.Exceptions.Core;
using Serilog.Exceptions.Destructurers;

/// <summary>
/// A destructurer for <see cref="RpcException"/>.
/// </summary>
/// <seealso cref="ExceptionDestructurer" />
public class RpcExceptionDestructurer : ExceptionDestructurer
{
/// <inheritdoc />
public override Type[] TargetTypes => new[] { typeof(RpcException) };

/// <inheritdoc />
public override void Destructure(
Exception exception,
IExceptionPropertiesBag propertiesBag,
Func<Exception, IReadOnlyDictionary<string, object?>?> destructureException)
{
base.Destructure(exception, propertiesBag, destructureException);

var rpcException = (RpcException)exception;

#pragma warning disable CA1062 // Validate arguments of public methods
propertiesBag.AddProperty(nameof(RpcException.Status.StatusCode), rpcException.Status.StatusCode);
propertiesBag.AddProperty(nameof(RpcException.Status.Detail), rpcException.Status.Detail);

foreach (var trailer in rpcException.Trailers)
{
if (trailer.IsBinary)
{
continue;
}

propertiesBag.AddProperty($"{nameof(RpcException.Trailers)}.{trailer.Key}", trailer.Value);
}
#pragma warning restore CA1062 // Validate arguments of public methods
}
}
3 changes: 3 additions & 0 deletions Source/Serilog.Exceptions.Grpc/Properties/AssemblyInfo.cs
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 Source/Serilog.Exceptions.Grpc/Serilog.Exceptions.Grpc.csproj
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
<Project Sdk="Microsoft.NET.Sdk">

<PropertyGroup Label="Build">
<TargetFrameworks>netstandard2.1;netstandard2.0;netstandard1.5;net462</TargetFrameworks>
</PropertyGroup>

<PropertyGroup Label="Package">
<Product>Serilog Exceptions gRPC</Product>
<Description>Log exception details and custom properties that are not output in Exception.ToString(). Contains custom destructurers for gRPC exceptions.</Description>
<PackageTags>Serilog;Exception;Log;Logging;Detail;Details;gRPC</PackageTags>
</PropertyGroup>

<ItemGroup Label="Project References">
<ProjectReference Include="..\Serilog.Exceptions\Serilog.Exceptions.csproj" />
</ItemGroup>

<ItemGroup Label="Package References">
<PackageReference Include="Grpc.Core.Api" Version="2.47.0" />
</ItemGroup>

</Project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
namespace Serilog.Exceptions.Test.Destructurers;

using System;
using System.Net;
using global::Grpc.Core;
using Serilog.Exceptions.Core;
using Serilog.Exceptions.Grpc.Destructurers;
using Xunit;
using static LogJsonOutputUtils;

public class RpcExceptionDestructurerTest
{
[Fact]
public void RpcException_StatusCodeIsLoggedAsProperty()
{
var options = new DestructuringOptionsBuilder().WithDestructurers(new[] { new RpcExceptionDestructurer() });
var rpcException = new RpcException(new Status(StatusCode.Aborted, string.Empty));

Test_LoggedExceptionContainsProperty(rpcException, nameof(RpcException.Status.StatusCode), nameof(StatusCode.Aborted), options);
}

[Fact]
public void RpcException_StatusDetailIsLoggedAsProperty()
{
var options = new DestructuringOptionsBuilder().WithDestructurers(new[] { new RpcExceptionDestructurer() });
var testDetail = "details";
var rpcException = new RpcException(new Status(StatusCode.Aborted, testDetail));

Test_LoggedExceptionContainsProperty(rpcException, nameof(RpcException.Status.Detail), testDetail, options);
}

[Fact]
public void RpcException_TrailersAreLoggedAsProperty()
{
var options = new DestructuringOptionsBuilder().WithDestructurers(new[] { new RpcExceptionDestructurer() });
const string stringTrailerKey1 = "key1";
const string stringTrailerValue1 = "stringTrailerValue1";
const string stringTrailerKey2 = "key2";
const string stringTrailerValue2 = "stringTrailerValue2";
var metadata = new Metadata { { stringTrailerKey1, stringTrailerValue1 }, { stringTrailerKey2, stringTrailerValue2 } };

var rpcException = new RpcException(new Status(StatusCode.Aborted, string.Empty), metadata);

Test_LoggedExceptionContainsProperty(rpcException, $"{nameof(RpcException.Trailers)}.{stringTrailerKey1}", stringTrailerValue1, options);
Test_LoggedExceptionContainsProperty(rpcException, $"{nameof(RpcException.Trailers)}.{stringTrailerKey2}", stringTrailerValue2, options);
}

[Fact]
public void RpcException_BinaryTrailersAreNotLoggedAsProperty()
{
var options = new DestructuringOptionsBuilder().WithDestructurers(new[] { new RpcExceptionDestructurer() });
const string stringTrailerKey1 = "key-bin";
var metadata = new Metadata { { stringTrailerKey1, new byte[] { 1 } } };

var rpcException = new RpcException(new Status(StatusCode.Aborted, string.Empty), metadata);

Test_LoggedExceptionDoesNotContainProperty(rpcException, $"{nameof(RpcException.Trailers)}.{stringTrailerKey1}", options);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@

<ItemGroup Label="Project References">
<ProjectReference Include="..\..\Source\Serilog.Exceptions.EntityFrameworkCore\Serilog.Exceptions.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\Source\Serilog.Exceptions.Grpc\Serilog.Exceptions.Grpc.csproj" />
<ProjectReference Include="..\..\Source\Serilog.Exceptions.Refit\Serilog.Exceptions.Refit.csproj" />
<ProjectReference Include="..\..\Source\Serilog.Exceptions\Serilog.Exceptions.csproj" />
</ItemGroup>
Expand Down