Skip to content

Privacy and Security

Production log streams inevitably capture sensitive data — passwords embedded in connection strings, credit card numbers passed as query parameters, or personal identifiers logged during business operations. Pragmatic.Logging provides a layered defense that detects, redacts, and audits sensitive data before it reaches any output provider, so your logs stay compliant without requiring manual review of every log statement.


Accidental exposure of API keys, JWT tokens, or private keys in logs can lead to credential compromise. The SecretDetector scans log messages and property values against a curated library of pre-compiled regex patterns, assigning a confidence score and severity level to each detection.

The SecretPatterns class organizes detection patterns into six categories.

CategoryExamplesSeverity
ApiKeysAWS Access Key (AKIA...), GitHub PAT (ghp_...), Stripe Key (sk_live_...), Google API Key (AIza...)Critical
TokensJWT (eyJ...), Bearer tokens, OAuth access/refresh tokens, session tokensHigh
CryptoRSA/EC/PGP private keys, PKCS#8 keys, X.509 certificates, Base64 keysCritical
DatabaseSQL Server Password=, MongoDB URI with credentials, Redis password, generic DB URLsCritical
CloudAzure Storage Account Key, GCP Service Account Key, DigitalOcean PATCritical
ApplicationEncryption keys, hash salts, ASP.NET Machine Keys, GUIDsMedium

Each detection carries a confidence score between 0.0 and 1.0. The SecretDetectionOptions.MinimumConfidenceForRedaction threshold (default: 0.7) controls which detections trigger automatic redaction. Lower the threshold in development to catch test secrets; raise it in high-performance scenarios to reduce false positives.

var options = SecretDetectionOptions.CreateDefault();
options.MinimumConfidenceForRedaction = 0.7; // Production default
options.PrecompilePatterns = true; // Compile regex at startup
options.RegexTimeoutSeconds = 2.0; // Safety timeout per pattern
options.MaxPatternsPerScan = 50; // Limit patterns for performance
PresetConfidencePatternsUse case
CreateDefault()0.750 maxProduction balanced
CreateForDevelopment()0.5UnlimitedCatch test secrets
CreateForHighPerformance()0.920 maxMinimal overhead
CreateForSecurity()0.3UnlimitedMaximum detection

When a secret is detected, the SecretRedactionStyle controls how it is replaced.

StyleOutputDescription
Placeholder[API_KEY_REDACTED]Descriptive placeholder with secret type
PreserveLength********************************Asterisks matching original length
PreserveStructureeyJ***.***.***Keeps format markers (JWT segments)
Minimal[REDACTED]Simple constant replacement

For scenarios where you want only the highest-priority detections, SecretPatterns.GetCriticalPatterns() returns the subset that should never appear in logs: private keys, cloud credentials, database passwords, and JWT tokens.


Beyond secret detection, business applications need to redact personally identifiable information (PII), financial data, and health records based on property names and message content patterns. The IDataRedactor interface and its implementation PragmaticDataRedactor provide this capability.

The redactor evaluates each log property in three steps:

  1. Explicit flag — Properties marked with PropertyCharacteristics.Redacted are always redacted.
  2. Name matching — The property name is checked against a HashSet<string> of known sensitive names (case-insensitive) and an array of compiled regex patterns.
  3. Message scanning — The log message text is scanned against message redaction patterns (emails, credit cards, SSNs, phone numbers).
public interface IDataRedactor
{
bool ShouldRedact(string propertyName, PropertyCharacteristics characteristics);
Dictionary<string, object?> RedactProperties(Dictionary<string, object?> properties);
Dictionary<string, object?> RedactProperties(
Dictionary<string, object?> properties,
LogLevel logLevel, string categoryName,
string? userId = null, string? correlationId = null);
string RedactMessage(string message);
}
var redactorConfig = PragmaticDataRedactorConfiguration.CreateDefault();
// Add application-specific sensitive properties
redactorConfig.SensitivePropertyNames = new[]
{
"password", "token", "apiKey", "secret",
"creditCard", "ssn", "email", "phoneNumber"
};
// Regex patterns for property names
redactorConfig.PropertyNamePatterns = new[]
{
@"(?i).*password.*",
@"(?i).*secret.*",
@"(?i).*token.*"
};
// Patterns for message content redaction
redactorConfig.MessageRedactionPatterns = new[]
{
@"\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Z|a-z]{2,}\b", // Email
@"\b(?:\d{4}[-\s]?){3}\d{4}\b", // Credit card
@"\b\d{3}-?\d{2}-?\d{4}\b" // SSN
};
// Redaction behavior
redactorConfig.RedactionPlaceholder = "[REDACTED]";
redactorConfig.PreserveLengths = false;
redactorConfig.PreserveJsonStructure = true;
redactorConfig.EnableDeepRedaction = false;
var redactor = new PragmaticDataRedactor(redactorConfig);

The RedactionMode enum on PrivacyConfiguration controls the aggressiveness of automatic redaction.

ModeBehavior
NoneNo automatic redaction
ConservativeOnly explicitly marked sensitive data
StandardCommon sensitive patterns (passwords, tokens, emails)
AggressiveGDPR-compliant with extensive PII patterns
CustomUser-defined patterns only

When PreserveJsonStructure is enabled, the redactor replaces values while keeping the JSON shape intact. Strings become [REDACTED], numbers become 0, booleans become false, and objects become {"redacted": true}. This allows downstream parsers to process the log entry without schema errors.


Different industries require different redaction rules. Pragmatic.Logging provides pre-built compliance templates that configure the correct sensitive property lists, message patterns, redaction placeholders, audit requirements, and data retention periods for each standard.

// Apply a single compliance standard
var gdprConfig = ComplianceTemplates.CreateGdprCompliant();
var hipaaConfig = ComplianceTemplates.CreateHipaaCompliant();
var pciConfig = ComplianceTemplates.CreatePciDssCompliant();
// Multi-compliance (uses the most restrictive settings)
var multiConfig = ComplianceTemplates.CreateMultiCompliance(
includeGdpr: true,
includeHipaa: true,
includePciDss: false);
StandardPlaceholderDeep RedactionAudit TrailRetentionConsent Required
GDPR[GDPR-REDACTED]YesYes2 yearsYes
HIPAA[PHI-REDACTED]YesYes6 yearsYes
PCI-DSS[CARD-DATA-REDACTED]YesYes1 yearNo
CCPA[REDACTED]YesYes90 daysYes
SOX[REDACTED]YesYes7 yearsNo
Production Default[REDACTED]NoNo90 daysNo
Development[DEV-REDACTED]NoNo7 daysNo

GDPR — Names, addresses, email, phone, date of birth, IP addresses, location data, nationality, passport/license numbers, user IDs, customer/client references.

HIPAA — SSN, medical record numbers, patient IDs, health plan numbers, diagnosis, treatment, medication, dates of birth, and all GDPR personal data.

PCI-DSS — Credit card numbers (PAN), CVV/CVC, expiration dates, magnetic stripe data, cardholder names, payment methods.


Compliance frameworks require proof that sensitive data was handled correctly. The PragmaticAuditService records every redaction event, sensitive data access, and compliance violation into a durable audit store with batch processing and configurable flush intervals.

The audit service implements IHostedService and runs as a background service. It queues audit entries in a ConcurrentQueue and flushes them to storage in batches.

// Recording a redaction event (called automatically by PragmaticDataRedactor)
auditService.RecordRedaction(
LogLevel.Information,
categoryName: "OrderService",
propertyName: "CreditCardNumber",
originalLength: 16,
redactionReason: RedactionReason.ComplianceRequirement,
complianceStandard: ComplianceStandard.PciDss,
userId: "admin-42",
correlationId: "corr-abc-123");
// Recording sensitive data access
auditService.RecordSensitiveDataAccess(
userId: "support-agent-7",
dataType: "CustomerPII",
accessReason: "Support ticket #12345",
correlationId: "corr-def-456",
complianceStandard: ComplianceStandard.Gdpr);
// Recording a compliance violation (forces immediate flush)
auditService.RecordComplianceViolation(
violationType: "UnencryptedPII",
description: "Customer email logged without redaction in PaymentService",
complianceStandard: ComplianceStandard.Gdpr,
severity: AuditSeverity.High);
StorageClassPersistenceBest For
MemoryMemoryAuditStorageIn-processTesting, development
File SystemFileSystemAuditStorageDiskSingle-server deployments

Audit policies control which events are recorded and how entries are transformed before storage. The IAuditPolicy interface defines ShouldAudit(context), ShouldFlushImmediately(entry), and TransformEntry(entry).

PolicyDescription
DefaultRecords all redaction and access events
GDPRRecords everything, forces immediate flush for violations
HighPerformanceOnly records high-severity events
DevelopmentMinimal auditing for fast iteration
// Time-range query
var entries = await auditService.GetAuditEntriesAsync(
from: DateTime.UtcNow.AddDays(-7),
until: DateTime.UtcNow);
// Structured query with builder
var result = await auditService.QueryAsync(
new AuditQueryBuilder()
.WithEventType(AuditEventType.DataRedaction)
.WithComplianceStandard(ComplianceStandard.Gdpr)
.WithDateRange(DateTime.UtcNow.AddDays(-30), DateTime.UtcNow));
// Statistics
var stats = await auditService.GetStatisticsAsync(
from: DateTime.UtcNow.AddDays(-30),
until: DateTime.UtcNow);
// Export for compliance reporting
var csv = await auditService.ExportAuditAsync(
from: DateTime.UtcNow.AddDays(-90),
until: DateTime.UtcNow,
format: AuditExportFormat.Csv);
OptionTypeDefaultDescription
BatchSizeint100Entries per flush batch
FlushIntervalTimeSpan30sTimer-based flush interval
MaxQueueSizeint10000Maximum queued entries
EnableCompressionbooltrueCompress stored entries
EnableDeduplicationboolfalseDeduplicate similar entries

Privacy and security features can be configured through code, appsettings.json, or a combination of both.

services.AddPragmaticLogging(logging =>
{
logging.AddConsole(config =>
{
config.Privacy.EnableRedaction = true;
config.Privacy.RedactionMode = RedactionMode.Standard;
config.Privacy.ComplianceStandard = ComplianceStandard.Gdpr;
config.Privacy.SensitivePropertyNames = new[]
{
"password", "token", "email", "ssn"
};
config.Privacy.EnableAuditTrail = true;
config.Privacy.DataRetentionPeriod = TimeSpan.FromDays(730);
});
});
{
"PragmaticLogging": {
"Privacy": {
"EnableRedaction": true,
"EnableSecretDetection": true,
"ComplianceStandard": "GDPR",
"SecretDetection": {
"MinimumSecretLength": 8,
"MaximumSecretLength": 2048,
"PrecompilePatterns": true,
"MinimumConfidenceForRedaction": 0.7,
"RedactionStyle": "Placeholder",
"EnableAuditTrail": true,
"MaxPatternsPerScan": 50
},
"DataRedaction": {
"RedactionPlaceholder": "[REDACTED]",
"PreserveLengths": false,
"PreserveJsonStructure": true,
"SensitivePropertyNames": [
"password", "token", "apiKey", "secret",
"email", "ssn", "creditCard"
],
"PropertyNamePatterns": [
".*[Pp]assword.*",
".*[Ss]ecret.*",
".*[Tt]oken.*"
],
"MessageRedactionPatterns": [
"\\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Z|a-z]{2,}\\b"
]
}
},
"Audit": {
"Enabled": true,
"BatchSize": 100,
"FlushIntervalSeconds": 30,
"StorageType": "FileSystem",
"PolicyType": "GDPR",
"StorageOptions": {
"BasePath": "logs/audit"
}
}
}
}

Each provider can have its own PrivacyConfiguration, so you can apply aggressive redaction to file logs while keeping full detail in the memory provider for debugging.

services.AddPragmaticLogging(logging =>
{
// Production file: aggressive redaction
logging.AddFile("logs/app.log", config =>
{
config.Privacy.EnableRedaction = true;
config.Privacy.RedactionMode = RedactionMode.Aggressive;
});
// Debug memory: no redaction for troubleshooting
logging.AddProvider<PragmaticMemoryProvider>(_ =>
{
var config = PragmaticMemoryConfiguration.ForTesting();
config.Privacy.EnableRedaction = false;
return new PragmaticMemoryProvider("Memory", config);
});
});

The following diagram shows how a log entry flows through the privacy pipeline before reaching any output provider.

Log Entry
|
v
[IsEnabled check] --no--> (dropped)
|
yes
v
[Advanced filter chain] --filtered--> (dropped, counter incremented)
|
pass
v
[Context enrichment] -- adds CorrelationId, UserId, etc.
|
v
[Structured property filter] -- applies ContextFilterMode
|
v
[Data redaction]
|-- SecretDetector scans message + property values
|-- PragmaticDataRedactor checks property names + patterns
|-- Audit trail records each redaction event
|
v
[WriteLogCore] -- provider-specific output

This pipeline runs inside PragmaticLoggerProviderBase.WriteLog(), so every provider — built-in or custom — automatically benefits from the full privacy stack.