Skip to content

Commit 1d9594e

Browse files
authored
feat(logging): implement thread-safe logging with per-thread scope storage (#1078)
1 parent cb67126 commit 1d9594e

File tree

26 files changed

+1004
-48
lines changed

26 files changed

+1004
-48
lines changed

libraries/AWS.Lambda.Powertools.sln

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,6 +125,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.Kafka
125125
EndProject
126126
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.ModuleInitializer.Tests", "tests\AWS.Lambda.Powertools.ModuleInitializer.Tests\AWS.Lambda.Powertools.ModuleInitializer.Tests.csproj", "{E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B}"
127127
EndProject
128+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AWS.Lambda.Powertools.ConcurrencyTests", "tests\AWS.Lambda.Powertools.ConcurrencyTests\AWS.Lambda.Powertools.ConcurrencyTests.csproj", "{D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}"
129+
EndProject
128130

129131
Global
130132
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -704,6 +706,18 @@ Global
704706
{E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B}.Release|x86.ActiveCfg = Release|Any CPU
705707
{E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B}.Release|x86.Build.0 = Release|Any CPU
706708

709+
{D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
710+
{D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Debug|Any CPU.Build.0 = Debug|Any CPU
711+
{D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Debug|x64.ActiveCfg = Debug|Any CPU
712+
{D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Debug|x64.Build.0 = Debug|Any CPU
713+
{D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Debug|x86.ActiveCfg = Debug|Any CPU
714+
{D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Debug|x86.Build.0 = Debug|Any CPU
715+
{D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Release|Any CPU.ActiveCfg = Release|Any CPU
716+
{D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Release|Any CPU.Build.0 = Release|Any CPU
717+
{D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Release|x64.ActiveCfg = Release|Any CPU
718+
{D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Release|x64.Build.0 = Release|Any CPU
719+
{D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Release|x86.ActiveCfg = Release|Any CPU
720+
{D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5}.Release|x86.Build.0 = Release|Any CPU
707721
EndGlobalSection
708722

709723
GlobalSection(NestedProjects) = preSolution
@@ -764,5 +778,6 @@ Global
764778
{B640DB80-C982-407B-A2EC-CD29AC77DDB8} = {73C9B1E5-3893-47E8-B373-17E5F5D7E6F5}
765779
{E1F2A3B4-C5D6-7E8F-9A0B-1C2D3E4F5A6B} = {1CFF5568-8486-475F-81F6-06105C437528}
766780

781+
{D2951A1A-D0EF-4CA4-AB4D-5ABAEFD164F5} = {1CFF5568-8486-475F-81F6-06105C437528}
767782
EndGlobalSection
768783
EndGlobal

libraries/src/AWS.Lambda.Powertools.Common/Core/PowertoolsConfigurations.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System.Globalization;
2+
using Amazon.Lambda.Core;
23
using AWS.Lambda.Powertools.Common.Core;
34

45
namespace AWS.Lambda.Powertools.Common;
@@ -167,7 +168,7 @@ public bool GetEnvironmentVariableOrDefault(string variable, bool defaultValue)
167168
/// </summary>
168169
/// <value>The X-Ray trace identifier.</value>
169170
public string XRayTraceId =>
170-
GetEnvironmentVariable(Constants.XrayTraceIdEnv);
171+
LambdaTraceProvider.CurrentTraceId;
171172

172173
/// <summary>
173174
/// Gets a value indicating whether this instance is Lambda.

libraries/src/AWS.Lambda.Powertools.Logging/InternalsVisibleTo.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,5 @@
1515

1616
using System.Runtime.CompilerServices;
1717

18-
[assembly: InternalsVisibleTo("AWS.Lambda.Powertools.Logging.Tests")]
18+
[assembly: InternalsVisibleTo("AWS.Lambda.Powertools.Logging.Tests")]
19+
[assembly: InternalsVisibleTo("AWS.Lambda.Powertools.ConcurrencyTests")]

libraries/src/AWS.Lambda.Powertools.Logging/Logger.Scope.cs

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
using System;
2+
using System.Collections.Concurrent;
23
using System.Collections.Generic;
34
using System.Linq;
5+
using System.Threading;
46
using AWS.Lambda.Powertools.Logging.Internal;
57
using AWS.Lambda.Powertools.Logging.Internal.Helpers;
68

@@ -9,10 +11,25 @@ namespace AWS.Lambda.Powertools.Logging;
911
public static partial class Logger
1012
{
1113
/// <summary>
12-
/// Gets the scope.
14+
/// Thread-safe dictionary for per-thread scope storage.
15+
/// Uses ManagedThreadId as key to ensure isolation when Lambda processes
16+
/// multiple concurrent requests (AWS_LAMBDA_MAX_CONCURRENCY > 1).
17+
/// </summary>
18+
private static readonly ConcurrentDictionary<int, Dictionary<string, object>> _threadScopes = new();
19+
20+
/// <summary>
21+
/// Gets the scope for the current thread.
22+
/// Creates a new dictionary if one doesn't exist for this thread.
1323
/// </summary>
1424
/// <value>The scope.</value>
15-
private static IDictionary<string, object> Scope { get; } = new Dictionary<string, object>(StringComparer.Ordinal);
25+
private static IDictionary<string, object> Scope
26+
{
27+
get
28+
{
29+
var threadId = Environment.CurrentManagedThreadId;
30+
return _threadScopes.GetOrAdd(threadId, _ => new Dictionary<string, object>(StringComparer.Ordinal));
31+
}
32+
}
1633

1734
/// <summary>
1835
/// Gets the correlation identifier from the log context.
@@ -70,7 +87,7 @@ public static void AppendKeys(IEnumerable<KeyValuePair<string, string>> keys)
7087
/// Remove additional keys from the log context.
7188
/// </summary>
7289
/// <param name="keys">The list of keys.</param>
73-
public static void RemoveKeys(params string[] keys)
90+
public static void RemoveKeys(params string[] keys)
7491
{
7592
if (keys == null) return;
7693
foreach (var key in keys)
@@ -88,11 +105,15 @@ public static IEnumerable<KeyValuePair<string, object>> GetAllKeys()
88105
}
89106

90107
/// <summary>
91-
/// Removes all additional keys from the log context.
108+
/// Removes all additional keys from the log context for the current thread.
92109
/// </summary>
93110
internal static void RemoveAllKeys()
94111
{
95-
Scope.Clear();
112+
var threadId = Environment.CurrentManagedThreadId;
113+
if (_threadScopes.TryGetValue(threadId, out var scope))
114+
{
115+
scope.Clear();
116+
}
96117
}
97118

98119
/// <summary>

libraries/src/AWS.Lambda.Powertools.Parameters/AWS.Lambda.Powertools.Parameters.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
<PackageReference Include="AWSSDK.SecretsManager" />
2121
<PackageReference Include="AWSSDK.SimpleSystemsManagement" />
2222
<PackageReference Include="Microsoft.Extensions.Configuration" />
23+
<PackageReference Include="Amazon.Lambda.Core"/>
2324
<ProjectReference Include="..\AWS.Lambda.Powertools.Common\AWS.Lambda.Powertools.Common.csproj" Condition="'$(Configuration)'=='Debug'"/>
2425
</ItemGroup>
2526

libraries/src/AWS.Lambda.Powertools.Tracing/AWS.Lambda.Powertools.Tracing.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
<PackageReference Include="AWSXRayRecorder.Core" />
1717
<PackageReference Include="AWSXRayRecorder.Handlers.AwsSdk" />
1818
<PackageReference Include="Amazon.Lambda.Serialization.SystemTextJson" />
19+
<PackageReference Include="Amazon.Lambda.Core"/>
1920
<ProjectReference Include="..\AWS.Lambda.Powertools.Common\AWS.Lambda.Powertools.Common.csproj" Condition="'$(Configuration)'=='Debug'"/>
2021
</ItemGroup>
2122

libraries/src/Directory.Packages.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
<PackageVersion Include="Amazon.Lambda.Serialization.SystemTextJson" Version="2.4.4" />
88
<PackageVersion Include="Apache.Avro" Version="1.12.0" />
99
<PackageVersion Include="AspectInjector" Version="2.8.1" />
10-
<PackageVersion Include="Amazon.Lambda.Core" Version="2.7.1" />
10+
<PackageVersion Include="Amazon.Lambda.Core" Version="2.8.0" />
1111
<PackageVersion Include="AWSSDK.AppConfig" Version="4.0.2.2" />
1212
<PackageVersion Include="AWSSDK.AppConfigData" Version="4.0.1.2" />
1313
<PackageVersion Include="AWSSDK.BedrockAgentRuntime" Version="4.0.2.2" />
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<LangVersion>default</LangVersion>
5+
<ImplicitUsings>enable</ImplicitUsings>
6+
<Nullable>enable</Nullable>
7+
<AssemblyName>AWS.Lambda.Powertools.ConcurrencyTests</AssemblyName>
8+
<RootNamespace>AWS.Lambda.Powertools.ConcurrencyTests</RootNamespace>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<!-- Package versions are Centrally managed in Directory.Packages.props file -->
13+
<!-- More info https://learn.microsoft.com/en-us/nuget/consume-packages/central-package-management -->
14+
<PackageReference Include="Microsoft.NET.Test.Sdk" />
15+
<PackageReference Include="xunit" />
16+
<PackageReference Include="xunit.runner.visualstudio">
17+
<PrivateAssets>all</PrivateAssets>
18+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
19+
</PackageReference>
20+
<PackageReference Include="coverlet.collector">
21+
<PrivateAssets>all</PrivateAssets>
22+
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
23+
</PackageReference>
24+
</ItemGroup>
25+
26+
<ItemGroup>
27+
<ProjectReference Include="..\..\src\AWS.Lambda.Powertools.Logging\AWS.Lambda.Powertools.Logging.csproj" />
28+
<ProjectReference Include="..\..\src\AWS.Lambda.Powertools.Metrics\AWS.Lambda.Powertools.Metrics.csproj" />
29+
</ItemGroup>
30+
31+
</Project>

0 commit comments

Comments
 (0)