Redaction ​
Logly.zig provides automatic sensitive data masking to help maintain security and compliance. Redact passwords, API keys, credit card numbers, and other PII from log messages.
Overview ​
The Redactor module enables you to:
- Automatically mask sensitive data in log messages
- Define custom patterns for different data types
- Use pattern matching: exact, prefix, suffix, contains, regex
- Apply different redaction types: full, partial, hashed
- Use pre-built presets for common compliance scenarios
Basic Usage ​
zig
const std = @import("std");
const logly = @import("logly");
const Redactor = logly.Redactor;
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Create a redactor
var redactor = Redactor.init(allocator);
defer redactor.deinit();
// Add pattern for sensitive data
// Parameters: name, pattern_type, pattern, replacement
try redactor.addPattern(
"password",
.contains,
"password=secret",
"[REDACTED]",
);
// Redact a message
const message = "User login password=secret success";
const redacted = try redactor.redact(message);
defer allocator.free(redacted);
std.debug.print("{s}\n", .{redacted});
// Output: User login [REDACTED] success
}Pattern Types ​
Contains Match ​
Match any message containing a substring:
zig
try redactor.addPattern("api_key", .contains, "api_key=", "[API_KEY_HIDDEN]");Prefix Match ​
Match patterns starting with a specific string:
zig
try redactor.addPattern("bearer_token", .prefix, "Bearer ", "[TOKEN] ");Suffix Match ​
Match patterns ending with a specific string:
zig
try redactor.addPattern("company_email", .suffix, "@company.com", "@[REDACTED]");Exact Match ​
Match exact strings:
zig
try redactor.addPattern("secret_key", .exact, "supersecretkey123", "[HIDDEN]");Regex Match ​
Use regex-like patterns for complex matching: Supports: * (any chars), + (one or more), . (single char), \d (digit), \w (word char), \s (whitespace)
zig
// Credit card numbers
try redactor.addPattern("credit_card", .regex,
"\\d\\d\\d\\d-\\d\\d\\d\\d-\\d\\d\\d\\d-\\d\\d\\d\\d",
"****-****-****-****");
// Social Security Numbers
try redactor.addPattern("ssn", .regex,
"\\d\\d\\d-\\d\\d-\\d\\d\\d\\d",
"***-**-****");Field-Based Redaction ​
Add sensitive fields with different redaction types:
zig
var redactor = Redactor.init(allocator);
defer redactor.deinit();
// Full redaction - replaces with "[REDACTED]"
try redactor.addField("password", .full);
try redactor.addField("secret", .full);
// Partial end - shows first 4 chars only
try redactor.addField("api_key", .partial_end);
// Partial start - shows last 4 chars only
try redactor.addField("email", .partial_start);
// Mask middle - shows first 3 and last 3 chars
try redactor.addField("credit_card", .mask_middle);
// Hash - replaces with SHA256 hash prefix
try redactor.addField("patient_id", .hash);Redaction Types ​
| Type | Input | Output |
|---|---|---|
.full | secret123 | [REDACTED] |
.partial_start | 1234567890 | ******7890 |
.partial_end | 1234567890 | 1234****** |
.mask_middle | 1234567890 | 123****890 |
.hash | sensitive | [HASH:a1b2c3d4...] |
Redaction Presets ​
Logly provides pre-built redactor configurations for compliance scenarios:
zig
const RedactionPresets = logly.RedactionPresets;
// Common sensitive data: password, secret, api_key, token, credit_card, ssn, email
var common = try RedactionPresets.common(allocator);
defer common.deinit();
// PCI-DSS compliance: pan, cvv, pin, card_number, expiry
var pci = try RedactionPresets.pciDss(allocator);
defer pci.deinit();
// HIPAA compliance: patient_id, ssn, dob, address, phone, email, medical_record
var hipaa = try RedactionPresets.hipaa(allocator);
defer hipaa.deinit();Production Example ​
zig
const std = @import("std");
const logly = @import("logly");
const Redactor = logly.Redactor;
const RedactionPresets = logly.RedactionPresets;
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
// Use common preset as a base
var redactor = try RedactionPresets.common(allocator);
defer redactor.deinit();
// Add custom patterns
try redactor.addPattern("bearer_token", .prefix, "Bearer ", "[TOKEN] ");
try redactor.addPattern("credit_card", .regex,
"\\d\\d\\d\\d-\\d\\d\\d\\d-\\d\\d\\d\\d-\\d\\d\\d\\d",
"****-****-****-****");
// Redact messages before logging
const message = "Processing payment for card 4532-1234-5678-9012";
const redacted = try redactor.redact(message);
defer allocator.free(redacted);
std.debug.print("{s}\n", .{redacted});
// Output: Processing payment for card ****-****-****-****
}Checking Field Redaction ​
zig
var redactor = Redactor.init(allocator);
defer redactor.deinit();
try redactor.addField("password", .full);
try redactor.addField("api_key", .partial_end);
// Check if a field should be redacted
if (redactor.getFieldRedaction("password")) |redaction_type| {
const value = "mysecretpassword";
const redacted_value = try redaction_type.apply(allocator, value);
defer allocator.free(redacted_value);
// redacted_value is "[REDACTED]"
}Compliance Considerations ​
Redaction helps with compliance requirements like:
- GDPR: Mask personal data in logs
- PCI-DSS: Redact credit card numbers (use
RedactionPresets.pciDss) - HIPAA: Mask health information (use
RedactionPresets.hipaa) - SOC 2: Protect sensitive data in audit logs
Best Practices ​
- Redact before logging: Process messages before they reach sinks
- Test your patterns: Verify patterns match what you expect
- Don't over-redact: Avoid patterns that mask useful debugging info
- Use presets: Start with
RedactionPresetsfor compliance scenarios - Document patterns: Keep a list of what data types are being redacted
- Performance: Complex regex patterns may impact performance
Global Configuration ​
Configure redaction globally through Config.RedactionConfig:
zig
var config = logly.Config.default();
config.redaction = .{
.enabled = true,
.replacement = "[REDACTED]",
.default_type = .full,
.mask_char = '*',
.partial_start_chars = 4,
.partial_end_chars = 4,
.case_insensitive = true,
.audit_redactions = true,
.compliance_preset = .gdpr,
};
const logger = try logly.Logger.initWithConfig(allocator, config);RedactionConfig Options ​
| Option | Type | Default | Description |
|---|---|---|---|
enabled | bool | false | Enable redaction system |
replacement | []const u8 | "[REDACTED]" | Default replacement text |
default_type | RedactionType | .full | Default redaction type |
enable_regex | bool | false | Enable regex patterns |
hash_algorithm | HashAlgorithm | .sha256 | Hash algorithm for .hash type |
partial_start_chars | u8 | 4 | Characters to reveal at start |
partial_end_chars | u8 | 4 | Characters to reveal at end |
mask_char | u8 | '*' | Mask character |
case_insensitive | bool | true | Case-insensitive field matching |
audit_redactions | bool | false | Log when redaction applied |
compliance_preset | ?CompliancePreset | null | Use compliance preset |
Configuration Presets ​
zig
// PCI-DSS compliance
config.redaction = logly.Config.RedactionConfig.pciDss();
// HIPAA compliance
config.redaction = logly.Config.RedactionConfig.hipaa();
// GDPR compliance
config.redaction = logly.Config.RedactionConfig.gdpr();
// Maximum security
config.redaction = logly.Config.RedactionConfig.strict();See Also ​
- Filtering - Rule-based log filtering
- Configuration - Global configuration options
- JSON Logging - Structured JSON output
New Compliance Presets (v0.0.9) ​
zig
const RedactionPresets = logly.RedactionPresets;
// GDPR compliance preset
var gdpr = try RedactionPresets.gdpr(allocator);
defer gdpr.deinit();
// API secrets preset (API keys, tokens, secrets)
var api = try RedactionPresets.apiSecrets(allocator);
defer api.deinit();
// Financial data preset (accounts, routing numbers)
var fin = try RedactionPresets.financial(allocator);
defer fin.deinit();Aliases ​
| Alias | Method |
|---|---|
addRule | addPattern |
field | addField |
sensitiveField | addField |
mask | redact |
sanitize | redact |
statistics | getStats |
