Privacy-first logging utility that masks PII and PHI data before logs are written
npm install logveilPrivacy-first logging utility that masks PII and PHI data before logs are written.
LogVeil is a TypeScript library that provides automatic detection and masking of sensitive data in your application logs. It integrates seamlessly with popular logging frameworks like Winston while maintaining zero runtime dependencies for the core masking engine.
⨠Automatic PII/PHI Detection - Detects emails, phone numbers, SSNs, patient IDs, and more
š Multiple Masking Strategies - Partial, full, hash, or remove sensitive fields
š Environment-Aware - Different masking levels for dev, staging, and production
šÆ Deep Object Traversal - Handles nested objects and arrays automatically
š”ļø Immutable - Never mutates your original log objects
š Logger-Agnostic Core - Works with any logging library
š¦ Zero Dependencies - Core engine has no external dependencies
šØ Fully Typed - Complete TypeScript support
``bash`
npm install logveil
For Winston integration:
`bash`
npm install logveil winston
`typescript
import { mask } from "logveil";
const data = {
email: "john@gmail.com",
phone: "+919999999999",
message: "Hello world"
};
const masked = mask(data);
console.log(masked);
// { email: '
`
`typescript
import winston from "winston";
import { createMaskedWinstonLogger } from "logveil";
const baseLogger = winston.createLogger({
transports: [new winston.transports.Console()]
});
const logger = createMaskedWinstonLogger({
logger: baseLogger,
env: "production",
piiFields: ["email", "phone", "ssn"],
phiFields: ["patientId", "diagnosis", "medication"]
});
logger.info("User created", {
email: "john@gmail.com",
phone: "+919999999999",
patientId: "PAT-12345",
name: "John Doe" // Not masked
});
// Output:
// {
// level: 'info',
// message: 'User created',
// email: '
// phone: '**',
// patientId: '
// name: 'John Doe'
// }
`
LogVeil supports four masking strategies:
Shows part of the data (useful for development):
`typescript`
email: 'john@gmail.com' ā 'jo**@gmail.com'
phone: '+919999999999' ā '**9999'
Replaces with asterisks:
`typescript`
email: 'john@gmail.com' ā '**'
Creates a SHA-256 hash:
`typescript`
email: 'john@gmail.com' ā '
Deletes the field entirely:
`typescript`
{ email: 'john@gmail.com', name: 'John' } ā { name: 'John' }
LogVeil can detect and mask PII/PHI patterns within string values - not just field names!
`typescript`
logger.info("User created", {
description: "User email is testing@gmail.com and phone is +919999999999"
});
The description field isn't sensitive, but the value contains PII!
`typescript
import { createMasker } from "logveil";
const masker = createMasker({
env: "development",
maskStringValues: true // Enable value-level masking (default: true)
});
const data = {
description: "Contact at john@gmail.com or call +919999999999",
notes: "SSN: 123-45-6789, Card: 4532-1234-5678-9010"
};
const result = masker.mask(data);
console.log(result.masked);
`
Output (development mode):
`typescript`
{
description: 'Contact at jo*@gmail.com or call *9999',
notes: 'SSN: --, Card: --*-9010'
}
Output (production mode):
`typescript`
{
description: 'Contact at
notes: 'SSN:
}
- ā
Email addresses (test@example.com)+1-555-1234
- ā
Phone numbers (, 919999999999)123-45-6789
- ā
SSN ()4532-1234-5678-9010
- ā
Credit card numbers ()
`typescript`
const masker = createMasker({
maskStringValues: false // Only field-level masking
});
Different environments can use different strategies:
`typescript
import { createMaskedWinstonLogger } from "logveil";
const logger = createMaskedWinstonLogger({
logger: baseLogger,
env: process.env.NODE_ENV as "development" | "staging" | "production",
piiFields: ["email", "phone"]
// Default mappings:
// development ā partial
// staging ā full
// production ā hash
});
`
Custom environment mappings:
`typescript`
const logger = createMaskedWinstonLogger({
logger: baseLogger,
env: "production",
piiFields: ["email"],
piiEnvironmentMapping: {
development: "partial",
staging: "hash",
production: "remove"
}
});
`typescript
import { Masker } from "logveil";
const masker = new Masker({
env: "production",
piiFields: ["email", "phone", "ssn", /.password./i],
phiFields: ["patientId", "diagnosis", "medicalRecordNumber"],
detectPII: true, // Auto-detect common PII patterns
hashAlgorithm: "sha256",
preserveStructure: true, // Keep removed fields with '
// Custom per-field rules
maskingRules: [
{
field: "creditCard",
strategy: "partial",
customMask: (value) => {
// Show last 4 digits only
const cleaned = value.replace(/\D/g, "");
return "* * " + cleaned.slice(-4);
}
}
]
});
const result = masker.mask(data);
console.log(result.masked);
console.log(result.fieldsProcessed); // Number of fields masked
console.log(result.detectedFields); // Details of detected fields
`
Use RegExp for flexible field matching:
`typescript`
const masker = new Masker({
piiFields: [
"email",
/.password./i, // Matches: password, userPassword, PASSWORD
/^api[_-]?key$/i // Matches: apiKey, api_key, API-KEY
]
});
Add your own regex patterns for domain-specific sensitive data:
`typescript
import { Masker, CustomPattern } from "logveil";
const customPatterns: CustomPattern[] = [
{
name: "employeeId",
pattern: /^EMP-[A-Z]{2}-\d{4}$/i,
fieldType: "pii",
maskingStrategy: "partial",
description: "Company employee ID format: EMP-XX-1234"
},
{
name: "projectCode",
pattern: /^PROJ-[A-Z]{3}-\d{3}$/i,
fieldType: "custom",
maskingStrategy: "hash"
},
{
name: "customerAccount",
pattern: /^ACC-\d{8}$/i,
fieldType: "pii",
customMask: (value: string) => ACC-*${value.slice(-3)},
description: "Customer account: ACC-12345678"
}
];
const masker = new Masker({
env: "production",
customPatterns,
detectPII: true
});
// Add patterns at runtime
masker.addCustomPattern({
name: "sessionToken",
pattern: /^SESSION-[A-F0-9]{40}$/i,
fieldType: "custom",
maskingStrategy: "remove"
});
const data = {
employee: "EMP-US-1234",
account: "ACC-87654321",
session: "SESSION-A1B2C3D4E5F6789012345678901234567890ABCD"
};
const result = masker.mask(data);
// {
// employee: 'EM**-1234',
// account: 'ACC-*321',
// session: '
// }
`
#### Custom Pattern Options
- name: Unique identifier for the patternpattern
- : RegExp to match field valuesfieldType
- : 'pii', 'phi', or 'custom'maskingStrategy
- : Strategy to apply (optional)customMask
- : Custom masking function (optional)description
- : Documentation (optional)
#### Managing Custom Patterns
`typescript
// Add pattern
masker.addCustomPattern(pattern);
// Remove pattern
masker.removeCustomPattern("employeeId");
// Get all patterns
const patterns = masker.getCustomPatterns();
// Clear all patterns
masker.clearCustomPatterns();
`
LogVeil automatically detects common patterns:
- Email addresses
- Phone numbers (international formats)
- SSNs (123-45-6789)
- Credit cards (1234-5678-9012-3456)
- IPv4 addresses
- Patient IDs (PAT-12345, PT-123)
- Medical record numbers (MRN-12345)
- Health plan IDs (HP-12345)
- email, emailAddress, phone, phoneNumberssn
- , socialSecurityNumber, passwordpatientId
- , diagnosis, medicationcreditCard
- , apiKey, token
`typescript
import { createMasker } from "logveil";
const masker = createMasker({
maskingRules: [
{
field: "creditCard",
strategy: "partial",
customMask: (value) => {
const last4 = value.slice(-4);
return * * ${last4};`
}
},
{
field: "dateOfBirth",
strategy: "partial",
customMask: (value) => {
// Show only year
return value.split("-")[0] + "--";
}
}
]
});
LogVeil automatically handles nested structures:
`typescript
const data = {
user: {
profile: {
email: "john@gmail.com",
phone: "+919999999999"
},
preferences: {
theme: "dark"
}
},
contacts: [
{ email: "alice@example.com", name: "Alice" },
{ email: "bob@example.com", name: "Bob" }
]
};
const masked = mask(data, {
piiFields: ["email", "phone"]
});
// All email fields are masked, regardless of depth
`
Use as a Winston format for more control:
`typescript
import winston from "winston";
import { createMaskingFormat } from "logveil";
const logger = winston.createLogger({
format: winston.format.combine(
winston.format.timestamp(),
createMaskingFormat({
env: "production",
piiFields: ["email", "phone"]
}) as any,
winston.format.json()
),
transports: [new winston.transports.Console()]
});
`
Quick one-off masking function.
Parameters:
- data: any - Data to maskconfig?: MaskingConfig
- - Optional configuration
Returns: Masked data
---
Create a reusable masker instance.
Parameters:
- config?: MaskingConfig - Configuration options
Returns: Masker instance
---
Create a Winston logger with automatic masking.
Parameters:
- options.logger - Winston logger instanceoptions.env?
- - Environment ('development' | 'staging' | 'production')options.piiFields?
- - Array of PII field names/patternsoptions.phiFields?
- - Array of PHI field names/patternsoptions.maskingRules?
- - Custom masking rulesoptions.detectPII?
- - Enable auto-detection (default: true)
Returns: Masked logger instance
---
`typescript`
interface MaskingConfig {
env?: "development" | "staging" | "production";
piiFields?: Array
phiFields?: Array
maskingRules?: FieldMaskingRule[];
piiEnvironmentMapping?: EnvironmentMaskingConfig;
phiEnvironmentMapping?: EnvironmentMaskingConfig;
detectPII?: boolean;
hashAlgorithm?: string;
preserveStructure?: boolean;
}
`typescript
import { createMaskedWinstonLogger } from "logveil";
import winston from "winston";
const logger = createMaskedWinstonLogger({
logger: winston.createLogger({
transports: [new winston.transports.File({ filename: "app.log" })]
}),
env: "production",
phiFields: ["patientId", "medicalRecordNumber", "diagnosis", "medication", "labResults"],
piiFields: ["email", "phone", "ssn", "address"]
});
logger.info("Patient record accessed", {
patientId: "PAT-12345",
diagnosis: "Hypertension",
email: "patient@email.com",
accessedBy: "Dr. Smith" // Not masked
});
`
`typescript
import { createMasker } from "logveil";
const masker = createMasker({
env: "production",
piiFields: ["email", "phone", "address"],
maskingRules: [
{
field: "creditCard",
strategy: "partial",
customMask: (value) => {
const last4 = value.slice(-4);
return *--*-${last4};
}
}
]
});
app.post("/checkout", (req, res) => {
const result = masker.mask(req.body);
logger.info("Checkout initiated", result.masked);
});
`
`typescript
const logger = createMaskedWinstonLogger({
logger: baseLogger,
env: "development",
piiFields: ["email", "phone"],
piiEnvironmentMapping: {
development: "partial" // Show partial data in dev for debugging
}
});
logger.debug("User login", {
email: "developer@company.com", // ā 'de**@company.com'
loginTime: new Date()
});
`
1. Configure fields explicitly - Don't rely solely on auto-detection
2. Use environment variables - env: process.env.NODE_ENV`
3. Test masking rules - Verify sensitive data is properly masked
4. Review logs regularly - Ensure no PII/PHI leaks through
5. Use hash in production - Irreversible masking for production logs
6. Combine with access controls - Logging is one layer of security
MIT
Contributions are welcome! Please open an issue or submit a pull request.
For issues and questions, please open a GitHub issue.