BizFirst Observe
Log Sanitization in BizFirstGO
BizFirstGO's INodeLogger interface wraps all structured logging with a built-in sanitization pass. Before any log message is forwarded to the OTel SDK, it passes through LogSanitizer.cs which applies regex patterns to mask recognized sensitive field patterns.
Built-In Sanitization Patterns
| Pattern Name | Matches | Replacement |
|---|---|---|
password | Any key named "password", "passwd", "pwd" (case-insensitive) | [REDACTED] |
api_key | Keys named "apiKey", "api_key", "x-api-key", "Authorization" | [REDACTED] |
token | Keys named "token", "accessToken", "refreshToken", "bearerToken" | [REDACTED] |
credit_card | 16-digit sequences matching Luhn algorithm (Visa, MC, Amex patterns) | [CC-REDACTED] |
ssn | US SSN format: ddd-dd-dddd | [SSN-REDACTED] |
email | Standard email format (configurable — disabled by default) | [EMAIL-REDACTED] |
connection_string | Strings containing "Server=", "Password=", "mongodb://", etc. | [CONNSTR-REDACTED] |
How INodeLogger Uses LogSanitizer
// ProcessEngine/Observability/LogSanitizer.cs (simplified)
public static class LogSanitizer
{
private static readonly List<SanitizationRule> Rules = new()
{
new SanitizationRule(
name: "password",
pattern: @"(?i)(""?password""?\s*[:=]\s*""?)([^""&\s,}]+)",
replacement: "$1[REDACTED]"
),
new SanitizationRule(
name: "credit_card",
pattern: @"\b(?:4[0-9]{12}(?:[0-9]{3})?|5[1-5][0-9]{14}|3[47][0-9]{13})\b",
replacement: "[CC-REDACTED]"
),
// ... more rules
};
public static string Sanitize(string message)
{
foreach (var rule in Rules)
message = Regex.Replace(message, rule.Pattern, rule.Replacement);
return message;
}
}
// ProcessEngine/Observability/NodeLogger.cs
public class NodeLogger : INodeLogger
{
public void LogInformation(string message, object? data = null)
{
var sanitizedMessage = LogSanitizer.Sanitize(message);
var sanitizedData = data != null ? SanitizeObject(data) : null;
_logger.LogInformation(sanitizedMessage, sanitizedData);
}
}
Adding Custom Sanitization Rules
// appsettings.json — extend built-in patterns with custom rules
{
"Observability": {
"LogSanitization": {
"CustomRules": [
{
"Name": "employee_id",
"Pattern": "EMP-[0-9]{6}",
"Replacement": "[EMP-ID-REDACTED]"
},
{
"Name": "iban",
"Pattern": "[A-Z]{2}[0-9]{2}[A-Z0-9]{11,30}",
"Replacement": "[IBAN-REDACTED]"
}
]
}
}
}
Verifying Sanitization Is Working
# After enabling sanitization, run a test workflow with known-sensitive input:
# Test input: { "password": "secret123", "cardNumber": "4111111111111111" }
# Query Loki for the execution log:
{job="processengine"} |= "test-sanitization-execution"
# Expected log output (sensitive values should be masked):
# {"executionId":"exec-test-123","level":"info","message":"Processing form input",
# "formData":{"password":"[REDACTED]","cardNumber":"[CC-REDACTED]"}}
# If you see raw values instead of [REDACTED]:
# 1. Verify LogSanitizer is registered in DI: services.AddLogSanitizer()
# 2. Verify the field name matches a sanitization rule
# 3. Check for regex pattern escaping issues
Sanitization Applies to Message Content, Not Labels
LogSanitizer processes log message content and structured property values. Loki labels (job, tenant_id, environment) are set at the OTel Collector level and do not pass through the sanitizer. For label hygiene (ensuring labels never contain PII), see the Label Hygiene page.