Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 9232e86

Browse files
committed
fix: handle duplicate keys in logger state to prevent ArgumentException
Replaces ToDictionary() with manual dictionary population to handle duplicate keys that can occur with HTTP headers (e.g., multiple Content-Type headers). The last value wins for duplicate keys, maintaining existing behavior. Performance improvements: - O(n) complexity instead of O(n log n) from GroupBy - Single pass enumeration - No LINQ overhead or intermediate collections Fixes issue where curl requests with duplicate headers caused: "An item with the same key has already been added. Key: Content-Type" Adds test case for duplicate key scenarios.
1 parent 2a131e3 commit 9232e86

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 (stateKeysisnull||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)

AltStyle によって変換されたページ (->オリジナル) /