Skip to content

Commit 7df214f

Browse files
committed
fix(logging): Improve log sampling documentation and add manual RefreshSampleRateCalculation
1 parent 5b52bad commit 7df214f

File tree

8 files changed

+527
-75
lines changed

8 files changed

+527
-75
lines changed

docs/core/logging.md

Lines changed: 33 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -778,12 +778,16 @@ custom keys can be persisted across invocations. If you want all custom keys to
778778

779779
## Sampling debug logs
780780

781-
You can dynamically set a percentage of your logs to **DEBUG** level via env var `POWERTOOLS_LOGGER_SAMPLE_RATE` or
782-
via `SamplingRate` parameter on attribute.
781+
Use sampling when you want to dynamically change your log level to **DEBUG** based on a **percentage of the Lambda function invocations**.
783782

784-
!!! info
785-
Configuration on environment variable is given precedence over sampling rate configuration on attribute, provided it's
786-
in valid value range.
783+
You can use values ranging from `0.0` to `1` (100%) when setting `POWERTOOLS_LOGGER_SAMPLE_RATE` env var, or `SamplingRate` parameter in Logger.
784+
785+
???+ tip "Tip: When is this useful?"
786+
Log sampling allows you to capture debug information for a fraction of your requests, helping you diagnose rare or intermittent issues without increasing the overall verbosity of your logs.
787+
788+
Example: Imagine an e-commerce checkout process where you want to understand rare payment gateway errors. With 10% sampling, you'll log detailed information for a small subset of transactions, making troubleshooting easier without generating excessive logs.
789+
790+
The sampling decision happens automatically with each invocation when using `Logger` decorator. When not using the decorator, you're in charge of refreshing it via `RefreshSampleRateCalculation` method. Skipping both may lead to unexpected sampling results.
787791

788792
=== "Sampling via attribute parameter"
789793

@@ -802,6 +806,30 @@ via `SamplingRate` parameter on attribute.
802806
}
803807
```
804808

809+
=== "Sampling Logger.Configure"
810+
811+
```c# hl_lines="5-10 16"
812+
public class Function
813+
{
814+
public Function()
815+
{
816+
Logger.Configure(options =>
817+
{
818+
options.MinimumLogLevel = LogLevel.Information;
819+
options.LoggerOutputCase = LoggerOutputCase.CamelCase;
820+
options.SamplingRate = 0.1; // 10% sampling
821+
});
822+
}
823+
824+
public async Task<APIGatewayProxyResponse> FunctionHandler
825+
(APIGatewayProxyRequest apigProxyEvent, ILambdaContext context)
826+
{
827+
Logger.RefreshSampleRateCalculation();
828+
...
829+
}
830+
}
831+
```
832+
805833
=== "Sampling via environment variable"
806834

807835
```yaml hl_lines="8"

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

Lines changed: 23 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,10 @@ public PowertoolsLoggerProvider(
2424
{
2525
_powertoolsConfigurations = powertoolsConfigurations;
2626
_currentConfig = config;
27-
27+
2828
// Set execution environment
2929
_powertoolsConfigurations.SetExecutionEnvironment(this);
30-
30+
3131
// Apply environment configurations if available
3232
ConfigureFromEnvironment();
3333
}
@@ -60,36 +60,37 @@ public void ConfigureFromEnvironment()
6060

6161
var minLogLevel = lambdaLogLevelEnabled ? lambdaLogLevel : logLevel;
6262
var effectiveLogLevel = minLogLevel != LogLevel.None ? minLogLevel : LoggingConstants.DefaultLogLevel;
63-
63+
6464
// Only set InitialLogLevel if it hasn't been explicitly configured
6565
if (_currentConfig.InitialLogLevel == LogLevel.Information)
6666
{
6767
_currentConfig.InitialLogLevel = effectiveLogLevel;
6868
}
69+
6970
_currentConfig.MinimumLogLevel = effectiveLogLevel;
70-
71+
7172
_currentConfig.XRayTraceId = _powertoolsConfigurations.XRayTraceId;
7273
_currentConfig.LogEvent = _powertoolsConfigurations.LoggerLogEvent;
73-
74+
7475
// Configure the log level key based on output case
7576
_currentConfig.LogLevelKey = _powertoolsConfigurations.LambdaLogLevelEnabled() &&
7677
_currentConfig.LoggerOutputCase == LoggerOutputCase.PascalCase
7778
? "LogLevel"
7879
: LoggingConstants.KeyLogLevel;
79-
80+
8081
ProcessSamplingRate(_currentConfig, _powertoolsConfigurations);
8182
_environmentConfigured = true;
8283
}
83-
84+
8485
/// <summary>
8586
/// Process sampling rate configuration
8687
/// </summary>
8788
private void ProcessSamplingRate(PowertoolsLoggerConfiguration config, IPowertoolsConfigurations configurations)
8889
{
89-
var samplingRate = config.SamplingRate > 0
90-
? config.SamplingRate
90+
var samplingRate = config.SamplingRate > 0
91+
? config.SamplingRate
9192
: configurations.LoggerSampleRate;
92-
93+
9394
samplingRate = ValidateSamplingRate(samplingRate, config);
9495
config.SamplingRate = samplingRate;
9596
}
@@ -122,20 +123,29 @@ public virtual ILogger CreateLogger(string categoryName)
122123
}
123124

124125
internal PowertoolsLoggerConfiguration GetCurrentConfig() => _currentConfig;
125-
126+
126127
public void UpdateConfiguration(PowertoolsLoggerConfiguration config)
127128
{
128129
_currentConfig = config;
129-
130+
130131
// Apply environment configurations if available
131132
if (_powertoolsConfigurations != null && !_environmentConfigured)
132133
{
133134
ConfigureFromEnvironment();
134135
}
135136
}
136137

138+
/// <summary>
139+
/// Refresh the sampling calculation and update the minimum log level if needed
140+
/// </summary>
141+
/// <returns>True if debug sampling was enabled, false otherwise</returns>
142+
internal bool RefreshSampleRateCalculation()
143+
{
144+
return _currentConfig.RefreshSampleRateCalculation();
145+
}
146+
137147
public virtual void Dispose()
138148
{
139149
_loggers.Clear();
140150
}
141-
}
151+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
namespace AWS.Lambda.Powertools.Logging;
2+
3+
public static partial class Logger
4+
{
5+
/// <summary>
6+
/// Refresh the sampling calculation and update the minimum log level if needed
7+
/// </summary>
8+
/// <returns>True if debug sampling was enabled, false otherwise</returns>
9+
public static bool RefreshSampleRateCalculation()
10+
{
11+
return _config.RefreshSampleRateCalculation();
12+
}
13+
}

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

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ namespace AWS.Lambda.Powertools.Logging;
1212
/// </summary>
1313
public static partial class Logger
1414
{
15+
private static PowertoolsLoggerConfiguration _config;
1516
private static ILogger _loggerInstance;
1617
private static readonly object Lock = new object();
1718

@@ -58,9 +59,9 @@ public static void Configure(Action<PowertoolsLoggerConfiguration> configure)
5859
{
5960
lock (Lock)
6061
{
61-
var config = new PowertoolsLoggerConfiguration();
62-
configure(config);
63-
_loggerInstance = LoggerFactoryHelper.CreateAndConfigureFactory(config).CreatePowertoolsLogger();
62+
_config = new PowertoolsLoggerConfiguration();
63+
configure(_config);
64+
_loggerInstance = LoggerFactoryHelper.CreateAndConfigureFactory(_config).CreatePowertoolsLogger();
6465
}
6566
}
6667

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

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -324,22 +324,31 @@ internal virtual double GetRandom()
324324
{
325325
return GetSafeRandom();
326326
}
327-
327+
328+
/// <summary>
329+
/// Refresh the sampling calculation and update the minimum log level if needed
330+
/// </summary>
331+
/// <returns>True if debug sampling was enabled, false otherwise</returns>
328332
internal bool RefreshSampleRateCalculation()
329333
{
330334
return RefreshSampleRateCalculation(out _);
331335
}
332336

337+
/// <summary>
338+
/// Refresh the sampling calculation and update the minimum log level if needed
339+
/// </summary>
340+
/// <param name="samplerValue"></param>
341+
/// <returns>True if debug sampling was enabled, false otherwise</returns>
333342
internal bool RefreshSampleRateCalculation(out double samplerValue)
334343
{
335344
samplerValue = 0.0;
336-
345+
337346
if (SamplingRate <= 0)
338347
return false;
339348

340349
// Increment counter at the beginning for proper cold start protection
341350
SamplingRefreshCount++;
342-
351+
343352
// Skip first call for cold start protection
344353
if (SamplingRefreshCount == 1)
345354
{
@@ -357,10 +366,11 @@ internal bool RefreshSampleRateCalculation(out double samplerValue)
357366
{
358367
MinimumLogLevel = InitialLogLevel;
359368
}
360-
369+
361370
return shouldEnableDebugSampling;
362371
}
363372

373+
364374
internal bool ShouldEnableDebugSampling()
365375
{
366376
return ShouldEnableDebugSampling(out _);
@@ -370,11 +380,11 @@ internal bool ShouldEnableDebugSampling(out double samplerValue)
370380
{
371381
samplerValue = 0.0;
372382
if (SamplingRate <= 0) return false;
373-
383+
374384
samplerValue = GetRandom();
375385
return samplerValue <= SamplingRate;
376386
}
377-
387+
378388
internal static double GetSafeRandom()
379389
{
380390
var randomGenerator = RandomNumberGenerator.Create();
@@ -383,4 +393,4 @@ internal static double GetSafeRandom()
383393
uint randomUInt = BitConverter.ToUInt32(data, 0);
384394
return (double)randomUInt / uint.MaxValue;
385395
}
386-
}
396+
}

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

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -257,4 +257,13 @@ public static void ClearBuffer(this ILogger logger)
257257
// Direct call to the buffer manager to avoid any recursion
258258
LogBufferManager.ClearCurrentBuffer();
259259
}
260+
261+
/// <summary>
262+
/// Refresh the sampling calculation and update the minimum log level if needed
263+
/// </summary>
264+
/// <returns></returns>
265+
public static bool RefreshSampleRateCalculation(this ILogger logger)
266+
{
267+
return Logger.RefreshSampleRateCalculation();
268+
}
260269
}

0 commit comments

Comments
 (0)