Skip to content

Commit e1c72a2

Browse files
authored
Merge pull request #961 from aws-powertools/fix/logger-duplicate-header-key-exception
fix: handle duplicate keys in logger state to prevent ArgumentException
2 parents 2a131e3 + 9232e86 commit e1c72a2

File tree

2 files changed

+59
-5
lines changed

2 files changed

+59
-5
lines changed

libraries/src/AWS.Lambda.Powertools.Logging/Internal/PowertoolsLogger.cs

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -396,14 +396,26 @@ private static bool CustomFormatter<TState>(TState state, Exception exception, o
396396
return false;
397397

398398
#if NET8_0_OR_GREATER
399-
var stateKeys = (state as IEnumerable<KeyValuePair<string, object>>)?
400-
.ToDictionary(i => i.Key, i => PowertoolsLoggerHelpers.ObjectToDictionary(i.Value));
399+
var stateKeys = new Dictionary<string, object>();
400+
if (state is IEnumerable<KeyValuePair<string, object>> keyValuePairs)
401+
{
402+
foreach (var kvp in keyValuePairs)
403+
{
404+
stateKeys[kvp.Key] = PowertoolsLoggerHelpers.ObjectToDictionary(kvp.Value);
405+
}
406+
}
401407
#else
402-
var stateKeys = (state as IEnumerable<KeyValuePair<string, object>>)?
403-
.ToDictionary(i => i.Key, i => i.Value);
408+
var stateKeys = new Dictionary<string, object>();
409+
if (state is IEnumerable<KeyValuePair<string, object>> keyValuePairs)
410+
{
411+
foreach (var kvp in keyValuePairs)
412+
{
413+
stateKeys[kvp.Key] = kvp.Value;
414+
}
415+
}
404416
#endif
405417

406-
if (stateKeys is null || stateKeys.Count != 2)
418+
if (stateKeys.Count != 2)
407419
return false;
408420

409421
if (!stateKeys.TryGetValue(_originalformat, out var originalFormat))

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

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1750,6 +1750,48 @@ public void Log_Cold_Start(bool willLog, string awsInitType)
17501750
// Assert
17511751
Assert.Contains($"\"coldStart\":{willLog.ToString().ToLower()}", outPut);
17521752
}
1753+
1754+
[Fact]
1755+
public void Log_WhenDuplicateKeysInState_LastValueWins()
1756+
{
1757+
// Arrange
1758+
var loggerName = Guid.NewGuid().ToString();
1759+
var service = Guid.NewGuid().ToString();
1760+
var logLevel = LogLevel.Information;
1761+
1762+
var configurations = Substitute.For<IPowertoolsConfigurations>();
1763+
configurations.Service.Returns(service);
1764+
configurations.LogLevel.Returns(logLevel.ToString());
1765+
configurations.LoggerOutputCase.Returns(LoggerOutputCase.PascalCase.ToString());
1766+
1767+
var systemWrapper = Substitute.For<IConsoleWrapper>();
1768+
1769+
var loggerConfiguration = new PowertoolsLoggerConfiguration
1770+
{
1771+
Service = service,
1772+
MinimumLogLevel = logLevel,
1773+
LoggerOutputCase = LoggerOutputCase.PascalCase,
1774+
LogOutput = systemWrapper
1775+
};
1776+
1777+
var provider = new PowertoolsLoggerProvider(loggerConfiguration, configurations);
1778+
var logger = provider.CreateLogger(loggerName);
1779+
1780+
// Create state with duplicate keys (simulating duplicate HTTP headers)
1781+
var stateWithDuplicates = new List<KeyValuePair<string, object>>
1782+
{
1783+
new("Content-Type", "application/json"),
1784+
new("Content-Type", "application/x-www-form-urlencoded"), // This should win
1785+
new("Accept", "text/html"),
1786+
new("Accept", "*/*") // This should win
1787+
};
1788+
1789+
// Act - This should not throw an exception
1790+
logger.Log(logLevel, new EventId(), stateWithDuplicates, null, (state, ex) => "Test message");
1791+
1792+
// Assert
1793+
systemWrapper.Received(1).WriteLine(Arg.Any<string>());
1794+
}
17531795

17541796
public void Dispose()
17551797
{

0 commit comments

Comments
 (0)