Skip to content

fix: Stack trace lost when exception is thrown the when using decorator #357

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

Merged
merged 4 commits into from
Aug 15, 2023
Merged
Show file tree
Hide file tree
Changes from 2 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
Original file line number Diff line number Diff line change
Expand Up @@ -38,11 +38,10 @@ public interface IMethodAspectHandler
/// <summary>
/// Called when [exception].
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="eventArgs">The <see cref="AspectEventArgs" /> instance containing the event data.</param>
/// <param name="exception">The exception.</param>
/// <returns>T.</returns>
T OnException<T>(AspectEventArgs eventArgs, Exception exception);
void OnException(AspectEventArgs eventArgs, Exception exception);

/// <summary>
/// Handles the <see cref="E:Exit" /> event.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,8 @@ protected internal sealed override T WrapSync<T>(Func<object[], T> target, objec
}
catch (Exception exception)
{
return AspectHandler.OnException<T>(eventArgs, exception);
AspectHandler.OnException(eventArgs, exception);
throw;
}
finally
{
Expand Down Expand Up @@ -92,7 +93,8 @@ protected internal sealed override async Task<T> WrapAsync<T>(Func<object[], Tas
}
catch (Exception exception)
{
return AspectHandler.OnException<T>(eventArgs, exception);
AspectHandler.OnException(eventArgs, exception);
throw;
}
finally
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
using System;
using System.IO;
using System.Linq;
using System.Runtime.ExceptionServices;
using System.Text.Json;
using System.Text.Json.Serialization;
using AWS.Lambda.Powertools.Common;
Expand Down Expand Up @@ -203,16 +204,16 @@ public void OnSuccess(AspectEventArgs eventArgs, object result)
/// <summary>
/// Called when [exception].
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="eventArgs">
/// The <see cref="T:AWS.Lambda.Powertools.Aspects.AspectEventArgs" /> instance containing the
/// event data.
/// </param>
/// <param name="exception">The exception.</param>
/// <returns>T.</returns>
public T OnException<T>(AspectEventArgs eventArgs, Exception exception)
public void OnException(AspectEventArgs eventArgs, Exception exception)
{
throw exception;
// The purpose of ExceptionDispatchInfo.Capture is to capture a potentially mutating exception's StackTrace at a point in time:
// https://learn.microsoft.com/en-us/dotnet/standard/exceptions/best-practices-for-exceptions#capture-exceptions-to-rethrow-later
ExceptionDispatchInfo.Capture(exception).Throw();
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

using System;
using System.Collections.Generic;
using System.Runtime.ExceptionServices;
using AWS.Lambda.Powertools.Common;

namespace AWS.Lambda.Powertools.Metrics;
Expand Down Expand Up @@ -122,14 +123,14 @@ public void OnSuccess(AspectEventArgs eventArgs, object result)
/// <summary>
/// OnException runs when an unhandled exception occurs inside the method marked with the Metrics Attribute
/// </summary>
/// <typeparam name="T">Type of Exception expected</typeparam>
/// <param name="eventArgs">Aspect Arguments</param>
/// <param name="exception">Exception thrown by the method marked with Metrics Attribute</param>
/// <returns>Type of the Exception expected</returns>
/// <exception cref="Exception">Generic unhandled exception</exception>
public T OnException<T>(AspectEventArgs eventArgs, Exception exception)
public void OnException(AspectEventArgs eventArgs, Exception exception)
{
throw exception;
// The purpose of ExceptionDispatchInfo.Capture is to capture a potentially mutating exception's StackTrace at a point in time:
// https://learn.microsoft.com/en-us/dotnet/standard/exceptions/best-practices-for-exceptions#capture-exceptions-to-rethrow-later
ExceptionDispatchInfo.Capture(exception).Throw();
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
*/

using System;
using System.Runtime.ExceptionServices;
using System.Text;
using AWS.Lambda.Powertools.Common;

Expand Down Expand Up @@ -152,14 +153,12 @@ public void OnSuccess(AspectEventArgs eventArgs, object result)
/// <summary>
/// Called when [exception].
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="eventArgs">
/// The <see cref="T:AWS.Lambda.Powertools.Aspects.AspectEventArgs" /> instance containing the
/// event data.
/// </param>
/// <param name="exception">The exception.</param>
/// <returns>T.</returns>
public T OnException<T>(AspectEventArgs eventArgs, Exception exception)
public void OnException(AspectEventArgs eventArgs, Exception exception)
{
if (CaptureError())
{
Expand Down Expand Up @@ -187,7 +186,9 @@ public T OnException<T>(AspectEventArgs eventArgs, Exception exception)
);
}

throw exception;
// The purpose of ExceptionDispatchInfo.Capture is to capture a potentially mutating exception's StackTrace at a point in time:
// https://learn.microsoft.com/en-us/dotnet/standard/exceptions/best-practices-for-exceptions#capture-exceptions-to-rethrow-later
ExceptionDispatchInfo.Capture(exception).Throw();
}

/// <summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
<ItemGroup>
<PackageReference Include="Amazon.Lambda.ApplicationLoadBalancerEvents" Version="2.1.0" />
<PackageReference Include="Amazon.Lambda.APIGatewayEvents" Version="2.5.0" />
<PackageReference Include="Amazon.Lambda.Core" Version="2.1.0" />
<PackageReference Include="coverlet.collector" Version="3.2.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Globalization;
using System.Threading.Tasks;

namespace AWS.Lambda.Powertools.Logging.Tests.Handlers;

public class ExceptionFunctionHandler
{
[Logging]
public async Task<string> Handle(string input)
{
ThisThrows();

await Task.Delay(1);

return input.ToUpper(CultureInfo.InvariantCulture);
}

private void ThisThrows()
{
throw new NullReferenceException();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Threading.Tasks;
using Xunit;

namespace AWS.Lambda.Powertools.Logging.Tests.Handlers;

public sealed class ExceptionFunctionHandlerTests
{
[Fact]
public async Task Stack_Trace_Included_When_Decorator_Present()
{
// Arrange
var handler = new ExceptionFunctionHandler();

// Act
Task Handle() => handler.Handle("whatever");

// Assert
var tracedException = await Assert.ThrowsAsync<NullReferenceException>(Handle);
Assert.StartsWith("at AWS.Lambda.Powertools.Logging.Tests.Handlers.ExceptionFunctionHandler.ThisThrows()", tracedException.StackTrace?.TrimStart());

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
</PropertyGroup>

<ItemGroup>
<PackageReference Include="Amazon.Lambda.Core" Version="2.1.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.5.0-preview-20221221-03" />
<PackageReference Include="xunit" Version="2.4.2" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.5">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Globalization;
using System.Threading.Tasks;

namespace AWS.Lambda.Powertools.Metrics.Tests.Handlers;

public class ExceptionFunctionHandler
{
[Metrics(Namespace = "ns", Service = "svc")]
public async Task<string> Handle(string input)
{
ThisThrows();

await Task.Delay(1);

return input.ToUpper(CultureInfo.InvariantCulture);
}

private void ThisThrows()
{
throw new NullReferenceException();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
using System;
using System.Threading.Tasks;
using Xunit;

namespace AWS.Lambda.Powertools.Metrics.Tests.Handlers;

[Collection("Sequential")]
public sealed class ExceptionFunctionHandlerTests
{
[Fact]
public async Task Stack_Trace_Included_When_Decorator_Present()
{
// Arrange
Metrics.ResetForTest();
var handler = new ExceptionFunctionHandler();

// Act
Task Handle() => handler.Handle("whatever");

// Assert
var tracedException = await Assert.ThrowsAsync<NullReferenceException>(Handle);
Assert.StartsWith("at AWS.Lambda.Powertools.Metrics.Tests.Handlers.ExceptionFunctionHandler.ThisThrows()", tracedException.StackTrace?.TrimStart());

}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ public class MetricsTests
public void Metrics_Set_Execution_Environment_Context()
{
// Arrange
Metrics.ResetForTest();
var assemblyName = "AWS.Lambda.Powertools.Metrics";
var assemblyVersion = "1.0.0";

Expand All @@ -32,5 +33,7 @@ public void Metrics_Set_Execution_Environment_Context()
v.GetEnvironmentVariable(
"AWS_EXECUTION_ENV"
), Times.Once);


}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
</ItemGroup>

<ItemGroup>
<PackageReference Include="Amazon.Lambda.Core" Version="2.1.0" />
<PackageReference Include="coverlet.collector" Version="3.2.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Globalization;
using System.Threading.Tasks;

namespace AWS.Lambda.Powertools.Tracing.Tests.Handlers;

public class ExceptionFunctionHandler
{
[Tracing(CaptureMode = TracingCaptureMode.ResponseAndError)]
public async Task<string> Handle(string input)
{
ThisThrows();

await Task.Delay(1);

return input.ToUpper(CultureInfo.InvariantCulture);
}

private void ThisThrows()
{
throw new NullReferenceException();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
using System;
using System.Threading.Tasks;
using Xunit;

namespace AWS.Lambda.Powertools.Tracing.Tests.Handlers;

public sealed class ExceptionFunctionHandlerTests
{
[Fact]
public async Task Stack_Trace_Included_When_Decorator_Present()
{
// Arrange
var handler = new ExceptionFunctionHandler();

// Act
Task Handle() => handler.Handle("whatever");

// Assert
var tracedException = await Assert.ThrowsAsync<NullReferenceException>(Handle);
Assert.StartsWith("at AWS.Lambda.Powertools.Tracing.Tests.Handlers.ExceptionFunctionHandler.ThisThrows()", tracedException.StackTrace?.TrimStart());

}
}
Loading