Skip to content

Commit b32961e

Browse files
authored
Merge pull request #681 from hjgraca/feat(logging)-inner-exception
chore: Exception logging to include inner exceptions
2 parents 46d5b86 + c99100e commit b32961e

File tree

3 files changed

+122
-26
lines changed

3 files changed

+122
-26
lines changed

libraries/src/AWS.Lambda.Powertools.Logging/Internal/Converters/ExceptionConverter.cs

Lines changed: 37 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -57,39 +57,50 @@ public override Exception Read(ref Utf8JsonReader reader, Type typeToConvert, Js
5757
/// <param name="options">The JsonSerializer options.</param>
5858
public override void Write(Utf8JsonWriter writer, Exception value, JsonSerializerOptions options)
5959
{
60-
var exceptionType = value.GetType();
61-
var properties = ExceptionPropertyExtractor.ExtractProperties(value);
60+
void WriteException(Utf8JsonWriter w, Exception ex)
61+
{
62+
var exceptionType = ex.GetType();
63+
var properties = ExceptionPropertyExtractor.ExtractProperties(ex);
6264

63-
if (options.DefaultIgnoreCondition == JsonIgnoreCondition.WhenWritingNull)
64-
properties = properties.Where(prop => prop.Value != null);
65+
if (options.DefaultIgnoreCondition == JsonIgnoreCondition.WhenWritingNull)
66+
properties = properties.Where(prop => prop.Value != null);
6567

66-
var props = properties.ToArray();
67-
if (props.Length == 0)
68-
return;
68+
var props = properties.ToArray();
69+
if (props.Length == 0)
70+
return;
6971

70-
writer.WriteStartObject();
71-
writer.WriteString(ApplyPropertyNamingPolicy("Type", options), exceptionType.FullName);
72-
73-
foreach (var prop in props)
74-
{
75-
switch (prop.Value)
72+
w.WriteStartObject();
73+
w.WriteString(ApplyPropertyNamingPolicy("Type", options), exceptionType.FullName);
74+
75+
foreach (var prop in props)
7676
{
77-
case IntPtr intPtr:
78-
writer.WriteNumber(ApplyPropertyNamingPolicy(prop.Key, options), intPtr.ToInt64());
79-
break;
80-
case UIntPtr uIntPtr:
81-
writer.WriteNumber(ApplyPropertyNamingPolicy(prop.Key, options), uIntPtr.ToUInt64());
82-
break;
83-
case Type propType:
84-
writer.WriteString(ApplyPropertyNamingPolicy(prop.Key, options), propType.FullName);
85-
break;
86-
case string propString:
87-
writer.WriteString(ApplyPropertyNamingPolicy(prop.Key, options), propString);
88-
break;
77+
switch (prop.Value)
78+
{
79+
case IntPtr intPtr:
80+
w.WriteNumber(ApplyPropertyNamingPolicy(prop.Key, options), intPtr.ToInt64());
81+
break;
82+
case UIntPtr uIntPtr:
83+
w.WriteNumber(ApplyPropertyNamingPolicy(prop.Key, options), uIntPtr.ToUInt64());
84+
break;
85+
case Type propType:
86+
w.WriteString(ApplyPropertyNamingPolicy(prop.Key, options), propType.FullName);
87+
break;
88+
case string propString:
89+
w.WriteString(ApplyPropertyNamingPolicy(prop.Key, options), propString);
90+
break;
91+
}
8992
}
93+
94+
if (ex.InnerException != null)
95+
{
96+
w.WritePropertyName(ApplyPropertyNamingPolicy("InnerException", options));
97+
WriteException(w, ex.InnerException);
98+
}
99+
100+
w.WriteEndObject();
90101
}
91102

92-
writer.WriteEndObject();
103+
WriteException(writer, value);
93104
}
94105

95106
/// <summary>

libraries/src/AWS.Lambda.Powertools.Logging/Internal/Converters/ExceptionPropertyExtractor.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,5 +40,6 @@ private static IEnumerable<KeyValuePair<string, object>> GetBaseExceptionPropert
4040
yield return new KeyValuePair<string, object>(nameof(Exception.Message), ex.Message);
4141
yield return new KeyValuePair<string, object>(nameof(Exception.Source), ex.Source);
4242
yield return new KeyValuePair<string, object>(nameof(Exception.StackTrace), ex.StackTrace);
43+
yield return new KeyValuePair<string, object>(nameof(Exception.InnerException), ex.InnerException);
4344
}
4445
}

libraries/tests/AWS.Lambda.Powertools.Logging.Tests/PowertoolsLoggerTest.cs

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1090,6 +1090,90 @@ public void Log_WhenException_LogsExceptionDetails()
10901090
s.Contains("\"exception\":{\"type\":\"System.InvalidOperationException\",\"message\":\"TestError\",\"source\":\"AWS.Lambda.Powertools.Logging.Tests\",\"stack_trace\":\" at AWS.Lambda.Powertools.Logging.Tests.PowertoolsLoggerTest.Log_WhenException_LogsExceptionDetails()")
10911091
));
10921092
}
1093+
1094+
[Fact]
1095+
public void Log_Inner_Exception()
1096+
{
1097+
// Arrange
1098+
var loggerName = Guid.NewGuid().ToString();
1099+
var service = Guid.NewGuid().ToString();
1100+
var error = new InvalidOperationException("Parent exception message",
1101+
new ArgumentNullException(nameof(service), "Very important inner exception message"));
1102+
var logLevel = LogLevel.Information;
1103+
var randomSampleRate = 0.5;
1104+
1105+
var configurations = Substitute.For<IPowertoolsConfigurations>();
1106+
configurations.Service.Returns(service);
1107+
configurations.LogLevel.Returns(logLevel.ToString());
1108+
1109+
var systemWrapper = Substitute.For<ISystemWrapper>();
1110+
systemWrapper.GetRandom().Returns(randomSampleRate);
1111+
1112+
var loggerConfiguration = new LoggerConfiguration
1113+
{
1114+
Service = null,
1115+
MinimumLevel = LogLevel.None
1116+
};
1117+
1118+
var provider = new LoggerProvider(loggerConfiguration, configurations, systemWrapper);
1119+
var logger = provider.CreateLogger(loggerName);
1120+
1121+
logger.LogError(
1122+
error,
1123+
"Something went wrong and we logged an exception itself with an inner exception. This is a param {arg}",
1124+
12345);
1125+
1126+
// Assert
1127+
systemWrapper.Received(1).LogLine(Arg.Is<string>(s =>
1128+
s.Contains("\"exception\":{\"type\":\"" + error.GetType().FullName + "\",\"message\":\"" +
1129+
error.Message + "\"")
1130+
));
1131+
1132+
systemWrapper.Received(1).LogLine(Arg.Is<string>(s =>
1133+
s.Contains("\"level\":\"Error\",\"service\":\"" + service+ "\",\"name\":\"" + loggerName + "\",\"message\":\"Something went wrong and we logged an exception itself with an inner exception. This is a param 12345\",\"exception\":{\"type\":\"System.InvalidOperationException\",\"message\":\"Parent exception message\",\"inner_exception\":{\"type\":\"System.ArgumentNullException\",\"message\":\"Very important inner exception message (Parameter 'service')\"}}}")
1134+
));
1135+
}
1136+
1137+
[Fact]
1138+
public void Log_Nested_Inner_Exception()
1139+
{
1140+
// Arrange
1141+
var loggerName = Guid.NewGuid().ToString();
1142+
var service = Guid.NewGuid().ToString();
1143+
var error = new InvalidOperationException("Parent exception message",
1144+
new ArgumentNullException(nameof(service),
1145+
new Exception("Very important nested inner exception message")));
1146+
1147+
var logLevel = LogLevel.Information;
1148+
var randomSampleRate = 0.5;
1149+
1150+
var configurations = Substitute.For<IPowertoolsConfigurations>();
1151+
configurations.Service.Returns(service);
1152+
configurations.LogLevel.Returns(logLevel.ToString());
1153+
1154+
var systemWrapper = Substitute.For<ISystemWrapper>();
1155+
systemWrapper.GetRandom().Returns(randomSampleRate);
1156+
1157+
var loggerConfiguration = new LoggerConfiguration
1158+
{
1159+
Service = null,
1160+
MinimumLevel = LogLevel.None
1161+
};
1162+
1163+
var provider = new LoggerProvider(loggerConfiguration, configurations, systemWrapper);
1164+
var logger = provider.CreateLogger(loggerName);
1165+
1166+
1167+
logger.LogError(
1168+
error,
1169+
"Something went wrong and we logged an exception itself with an inner exception. This is a param {arg}",
1170+
12345);
1171+
1172+
// Assert
1173+
systemWrapper.Received(1).LogLine(Arg.Is<string>(s =>
1174+
s.Contains("\"message\":\"Something went wrong and we logged an exception itself with an inner exception. This is a param 12345\",\"exception\":{\"type\":\"System.InvalidOperationException\",\"message\":\"Parent exception message\",\"inner_exception\":{\"type\":\"System.ArgumentNullException\",\"message\":\"service\",\"inner_exception\":{\"type\":\"System.Exception\",\"message\":\"Very important nested inner exception message\"}}}}")
1175+
));
1176+
}
10931177

10941178
[Fact]
10951179
public void Log_WhenNestedException_LogsExceptionDetails()

0 commit comments

Comments
 (0)