diff --git a/docs/core/logging.md b/docs/core/logging.md
index 9ea38fb4..c3932e2a 100644
--- a/docs/core/logging.md
+++ b/docs/core/logging.md
@@ -446,7 +446,7 @@ When debugging in non-production environments, you can instruct Logger to log th
parameter or via `POWERTOOLS_LOGGER_LOG_EVENT` environment variable.
!!! warning
-Log event is disabled by default to prevent sensitive info being logged.
+ Log event is disabled by default to prevent sensitive info being logged.
=== "Function.cs"
@@ -471,8 +471,8 @@ You can set a Correlation ID using `CorrelationIdPath` parameter by passing
a [JSON Pointer expression](https://datatracker.ietf.org/doc/html/draft-ietf-appsawg-json-pointer-03){target="_blank"}.
!!! Attention
-The JSON Pointer expression is `case sensitive`. In the bellow example `/headers/my_request_id_header` would work but
-`/Headers/my_request_id_header` would not find the element.
+ The JSON Pointer expression is `case sensitive`. In the bellow example `/headers/my_request_id_header` would work but
+ `/Headers/my_request_id_header` would not find the element.
=== "Function.cs"
@@ -577,8 +577,8 @@ for known event sources, where either a request ID or X-Ray Trace ID are present
## Appending additional keys
!!! info "Custom keys are persisted across warm invocations"
-Always set additional keys as part of your handler to ensure they have the latest value, or explicitly clear them with [
-`ClearState=true`](#clearing-all-state).
+ Always set additional keys as part of your handler to ensure they have the latest value, or explicitly clear them with [
+ `ClearState=true`](#clearing-all-state).
You can append your own keys to your existing logs via `AppendKey`. Typically this value would be passed into the
function via the event. Appended keys are added to all subsequent log entries in the current execution from the point
@@ -683,7 +683,7 @@ It accepts any dictionary, and all keyword arguments will be added as part of th
log statement.
!!! info
-Any keyword argument added using extra keys will not be persisted for subsequent messages.
+ Any keyword argument added using extra keys will not be persisted for subsequent messages.
=== "Function.cs"
@@ -782,8 +782,8 @@ You can dynamically set a percentage of your logs to **DEBUG** level via env var
via `SamplingRate` parameter on attribute.
!!! info
-Configuration on environment variable is given precedence over sampling rate configuration on attribute, provided it's
-in valid value range.
+ Configuration on environment variable is given precedence over sampling rate configuration on attribute, provided it's
+ in valid value range.
=== "Sampling via attribute parameter"
@@ -915,8 +915,8 @@ We support the following log levels:
### Using AWS Lambda Advanced Logging Controls (ALC)
!!! question "When is it useful?"
-When you want to set a logging policy to drop informational or verbose logs for one or all AWS Lambda functions,
-regardless of runtime and logger used.
+ When you want to set a logging policy to drop informational or verbose logs for one or all AWS Lambda functions,
+ regardless of runtime and logger used.
With [AWS Lambda Advanced Logging Controls (ALC)](https://docs.aws.amazon.com/lambda/latest/dg/monitoring-cloudwatchlogs.html#monitoring-cloudwatchlogs-advanced)
{target="_blank"}, you can enforce a minimum log level that Lambda will accept from your application code.
@@ -924,9 +924,9 @@ With [AWS Lambda Advanced Logging Controls (ALC)](https://docs.aws.amazon.com/la
When enabled, you should keep `Logger` and ALC log level in sync to avoid data loss.
!!! warning "When using AWS Lambda Advanced Logging Controls (ALC)"
-- When Powertools Logger output is set to `PascalCase` **`Level`** property name will be replaced by **`LogLevel`** as
- a property name.
-- ALC takes precedence over **`POWERTOOLS_LOG_LEVEL`** and when setting it in code using **`[Logging(LogLevel = )]`**
+ - When Powertools Logger output is set to `PascalCase` **`Level`** property name will be replaced by **`LogLevel`** as
+ a property name.
+ - ALC takes precedence over **`POWERTOOLS_LOG_LEVEL`** and when setting it in code using **`[Logging(LogLevel = )]`**
Here's a sequence diagram to demonstrate how ALC will drop both `Information` and `Debug` logs emitted from `Logger`,
when ALC log level is stricter than `Logger`.
@@ -985,8 +985,8 @@ customize the serialization of Powertools Logger.
This is useful when you want to change the casing of the property names or use a different naming convention.
!!! info
-If you want to preserve the original casing of the property names (keys), you can set the `DictionaryKeyPolicy` to
-`null`.
+ If you want to preserve the original casing of the property names (keys), you can set the `DictionaryKeyPolicy` to
+ `null`.
```csharp
builder.Logging.AddPowertoolsLogger(options =>
@@ -999,6 +999,19 @@ builder.Logging.AddPowertoolsLogger(options =>
});
```
+!!! warning
+ When using `builder.Logging.AddPowertoolsLogger` method it will use any already configured logging providers (file loggers, database loggers, third-party providers).
+
+ If you want to use Powertools Logger as the only logging provider, you should call `builder.Logging.ClearProviders()` before adding Powertools Logger or the new method override
+
+ ```csharp
+ builder.Logging.AddPowertoolsLogger(config =>
+ {
+ config.Service = "TestService";
+ config.LoggerOutputCase = LoggerOutputCase.PascalCase;
+ }, clearExistingProviders: true);
+ ```
+
### Custom Log formatter (Bring Your Own Formatter)
You can customize the structure (keys and values) of your log entries by implementing a custom log formatter and
diff --git a/libraries/src/AWS.Lambda.Powertools.Logging/PowertoolsLoggingBuilderExtensions.cs b/libraries/src/AWS.Lambda.Powertools.Logging/PowertoolsLoggingBuilderExtensions.cs
index 651e4f25..73046197 100644
--- a/libraries/src/AWS.Lambda.Powertools.Logging/PowertoolsLoggingBuilderExtensions.cs
+++ b/libraries/src/AWS.Lambda.Powertools.Logging/PowertoolsLoggingBuilderExtensions.cs
@@ -56,6 +56,7 @@ internal static PowertoolsLoggerConfiguration GetCurrentConfiguration()
/// Adds the Powertools logger to the logging builder with default configuration.
///
/// The logging builder to configure.
+ /// Opt-in to clear providers for Powertools-only output
/// The logging builder for further configuration.
///
/// This method registers the Powertools logger with default settings. The logger will output
@@ -78,14 +79,24 @@ internal static PowertoolsLoggerConfiguration GetCurrentConfiguration()
///
///
public static ILoggingBuilder AddPowertoolsLogger(
- this ILoggingBuilder builder)
+ this ILoggingBuilder builder,
+ bool clearExistingProviders = false)
{
+ if (clearExistingProviders)
+ {
+ builder.ClearProviders();
+ }
+
builder.AddConfiguration();
-
+
builder.Services.TryAddSingleton();
builder.Services.TryAddSingleton(sp =>
new PowertoolsConfigurations(sp.GetRequiredService()));
+ // automatically register ILogger
+ builder.Services.TryAddSingleton(provider =>
+ provider.GetRequiredService().CreatePowertoolsLogger());
+
builder.Services.TryAddEnumerable(
ServiceDescriptor.Singleton(provider =>
{
@@ -111,6 +122,7 @@ public static ILoggingBuilder AddPowertoolsLogger(
///
/// The logging builder to configure.
///
+ /// Opt-in to clear providers for Powertools-only output
/// The logging builder for further configuration.
///
/// This method registers the Powertools logger with default settings. The logger will output
@@ -155,10 +167,11 @@ public static ILoggingBuilder AddPowertoolsLogger(
///
public static ILoggingBuilder AddPowertoolsLogger(
this ILoggingBuilder builder,
- Action configure)
+ Action configure,
+ bool clearExistingProviders = false)
{
// Add configuration
- builder.AddPowertoolsLogger();
+ builder.AddPowertoolsLogger(clearExistingProviders);
// Create initial configuration
var options = new PowertoolsLoggerConfiguration();
diff --git a/libraries/tests/AWS.Lambda.Powertools.Logging.Tests/PowertoolsLoggerExtensionsTests.cs b/libraries/tests/AWS.Lambda.Powertools.Logging.Tests/PowertoolsLoggerExtensionsTests.cs
new file mode 100644
index 00000000..8e1ea3c6
--- /dev/null
+++ b/libraries/tests/AWS.Lambda.Powertools.Logging.Tests/PowertoolsLoggerExtensionsTests.cs
@@ -0,0 +1,72 @@
+using System;
+using System.Linq;
+using AWS.Lambda.Powertools.Logging.Internal;
+using Microsoft.Extensions.DependencyInjection;
+using Microsoft.Extensions.Logging;
+using Xunit;
+
+namespace AWS.Lambda.Powertools.Logging.Tests;
+
+public class PowertoolsLoggerExtensionsTests
+{
+ [Fact]
+ public void AddPowertoolsLogger_WithClearExistingProviders_False_KeepsExistingProviders()
+ {
+ // Arrange
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddLogging(builder =>
+ {
+ // Add a mock existing provider first
+ builder.Services.AddSingleton();
+
+ // Act
+ builder.AddPowertoolsLogger(clearExistingProviders: false);
+ });
+
+ var serviceProvider = serviceCollection.BuildServiceProvider();
+ var loggerProviders = serviceProvider.GetServices();
+
+ // Assert
+ var collection = loggerProviders as ILoggerProvider[] ?? loggerProviders.ToArray();
+ Assert.Contains(collection, p => p is MockLoggerProvider);
+ Assert.Contains(collection, p => p is PowertoolsLoggerProvider);
+ Assert.True(collection.Count() >= 2); // Should have both providers
+ }
+
+ [Fact]
+ public void AddPowertoolsLogger_WithClearExistingProviders_True_RemovesExistingProviders()
+ {
+ // Arrange
+ var serviceCollection = new ServiceCollection();
+ serviceCollection.AddLogging(builder =>
+ {
+ // Add a mock existing provider first
+ builder.Services.AddSingleton();
+
+ // Act
+ builder.AddPowertoolsLogger(clearExistingProviders: true);
+ });
+
+ var serviceProvider = serviceCollection.BuildServiceProvider();
+ var loggerProviders = serviceProvider.GetServices();
+
+ // Assert
+ var collection = loggerProviders as ILoggerProvider[] ?? loggerProviders.ToArray();
+ Assert.DoesNotContain(collection, p => p is MockLoggerProvider);
+ Assert.Contains(collection, p => p is PowertoolsLoggerProvider);
+ Assert.Single(collection); // Should only have Powertools provider
+ }
+
+ private class MockLoggerProvider : ILoggerProvider
+ {
+ public ILogger CreateLogger(string categoryName) => new MockLogger();
+ public void Dispose() { }
+ }
+
+ private class MockLogger : ILogger
+ {
+ public IDisposable BeginScope(TState state) => null;
+ public bool IsEnabled(LogLevel logLevel) => true;
+ public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) { }
+ }
+}
\ No newline at end of file