Skip to main content

Logging

LoggingLayer reports every secret operation to an ISecretProviderLogger. The built-in LoggerSecretProviderLogger adapts that interface to Microsoft.Extensions.Logging, so secret operations appear in the same sinks as the rest of your application (console, Seq, Elastic, Loki, OpenTelemetry log export, and so on).

Logging lives in a layer, not in the providers. No provider constructor takes a logger, so all 14 provider packages keep their public API. The layer also sees pipeline behavior that a provider cannot see, such as retries.

Quick start​

using Microsoft.Extensions.Logging;
using SecretsManager;
using SecretsManager.Builder;

ILogger logger = loggerFactory.CreateLogger("SecretsManager");

await using var provider = new SecretProviderBuilder()
.WithFileSystem(options => options.BasePath = "/var/secrets")
.WithLogging(options =>
{
options.Logger = new LoggerSecretProviderLogger(logger);
options.OperationPrefix = "app";
})
.Build();

ASP.NET Core​

AddSecretsManager() registers an ISecretProviderLogger that writes through the application's ILoggerFactory (category SecretsManager). When no ILoggerFactory is registered, it falls back to a no-op logger. A logger that you register yourself is never replaced.

builder.Services.AddLogging();
builder.Services.AddSecretsManager();

builder.Services.AddSingleton<ISecretProvider>(sp =>
{
var secrets = sp.GetRequiredService<SecretProviderBuilder>();

return secrets
.WithFileSystem(options => options.BasePath = "/var/secrets")
.WithRetry()
.WithLogging(sp.GetRequiredService<ISecretProviderLogger>())
.Build();
});

Log levels​

The adapter honors the minimum level of your logging configuration. The layer asks IsEnabled first, so when a level is filtered out it builds no context and makes no logger call.

EventSecretProviderLogLevelMEL LogLevel
Operation startDebugDebug
Operation successDebugDebug
RetryWarningWarning
Operation failureErrorError

Log context​

Every event receives a SecretProviderLogContext.

FieldDescription
OperationOperation name, for example GetSecretAsync, with the optional OperationPrefix
KeySecret key
IsMetadataWriteWhether a write carries metadata
ProviderProvider name, for example vault. The builder sets it on every layer
OutcomeSuccess or Failure. It is null for the start event
TimestampWhen the operation started

The MEL adapter writes Operation, Key and Provider as structured properties, plus ElapsedMs for success and failure, and Attempt and DelayMs for retries.

Retries​

When a RetryLayer retries a read, the logger receives a LogRetry event with the next attempt number and the delay. Add WithLogging and WithRetry in either order. The builder shares the logging layer's logger with the retry layer. Set RetryLayerOptions.Logger to use a different one.

Failure isolation​

Logging is best-effort. A logger that throws, in any method including IsEnabled, cannot:

  • fail a secret operation,
  • turn a successful read into a failure,
  • hide the original exception of a failed operation.

The layer swallows logger exceptions.

Secrets and redaction​

  • SecretProviderLogContext has no value field, and secret values are never logged. Secret keys are logged.
  • The providers with a REST client (Aliyun KMS, Conjur, IBM Cloud Secrets Manager, OVH, Passbolt, Scaleway, Tencent Cloud and Vault) do not copy the response body into exception messages. Their error messages contain only the operation, HTTP status code and reason phrase, because backends can echo submitted values in error payloads.
  • If an exception from PutSecretAsync still contains the submitted value, the layer replaces it with [REDACTED] before it is logged. The caller still receives the original exception.

Exception messages that come from other sources, such as provider SDKs, are logged as they are. OtelTraceLayer also records exception messages in its spans. Treat your log and trace sinks as sensitive.

Cancellation​

When the caller cancels an operation through its CancellationToken, the layer does not log it as a failure. Other exceptions, including SecretNotFoundException, are logged at Error.

Custom loggers​

Implement ISecretProviderLogger to send events to another destination:

public interface ISecretProviderLogger
{
bool IsEnabled(SecretProviderLogLevel level);
void LogStart(SecretProviderLogContext context);
void LogSuccess(SecretProviderLogContext context, TimeSpan duration);
void LogFailure(SecretProviderLogContext context, Exception exception, TimeSpan duration);
void LogRetry(SecretProviderLogContext context, int attempt, TimeSpan delay);
}

IsEnabled and LogRetry have default implementations (true and no-op), so a logger that implements only LogStart, LogSuccess and LogFailure keeps working. NullSecretProviderLogger is the default and emits nothing.