@rajatparihar26/e2ee-logger
v1.0.3
Published
[](https://www.npmjs.com/package/rajat12826/logger) [](https://opensource.org/licenses/MIT) [;
console.log('Master Key:', masterKey);
// Output: Master Key: a1b2c3d4e5f6... (64 characters)⚠️ CRITICAL SECURITY NOTICE:
- Store this key in your
.envfile asMASTER_KEY=your_generated_key_here - NEVER commit this key to version control
- NEVER share this key publicly
- Losing this key means you cannot decrypt your logs
- Keep a secure backup of this key
Add to your .env file:
MASTER_KEY=your_64_character_hex_key_here
API_KEY=your_api_authentication_key
LOG_API_URL=https://your-backend.com/apiStep 2: Initialize the Logger
import { E2EELogger } from '@rajat12826/logger';
const logger = new E2EELogger({
apiUrl: process.env.LOG_API_URL!,
apiKey: process.env.API_KEY!,
masterKey: process.env.MASTER_KEY!,
projectId: 'my-app',
enableConsole: true, // Optional: also log to console for development
});Step 3: Start Logging
// Simple logs
logger.info('Application started successfully');
logger.warn('Low disk space detected');
logger.error('Failed to connect to database');
// Logs with rich metadata
logger.info('User logged in', {
userId: '12345',
email: '[email protected]',
loginMethod: 'oauth',
ipAddress: '192.168.1.1',
});
logger.error('Payment processing failed', {
userId: '12345',
amount: 99.99,
currency: 'USD',
errorCode: 'CARD_DECLINED',
paymentProvider: 'stripe',
});
logger.critical('Service outage detected', {
service: 'database',
affectedUsers: 1523,
startTime: new Date().toISOString(),
});Step 4: Graceful Shutdown
Always flush logs before your application exits:
// Flush all pending logs
await logger.flush();
// Clean up resources
await logger.destroy();📖 Complete API Reference
E2EELogger Class
Constructor Configuration
interface LoggerConfig {
// Required fields
apiUrl: string; // Your backend API endpoint
apiKey: string; // API authentication key
masterKey: string; // 64-char hex string from generateMasterKey()
projectId: string; // Identifier for your application
// Optional fields
enableConsole?: boolean; // Also log to console (default: false)
batchSize?: number; // Logs per batch (default: 10, max: 100)
batchInterval?: number; // Batch interval in ms (default: 5000)
maxRetries?: number; // Max retry attempts (default: 3, max: 10)
retryDelay?: number; // Initial retry delay in ms (default: 1000)
timeout?: number; // HTTP timeout in ms (default: 5000)
onError?: (error: Error) => void; // Custom error handler
}Logging Methods
// Log levels (in order of severity)
logger.debug(message: string, metadata?: LogMetadata): void // Detailed debugging info
logger.info(message: string, metadata?: LogMetadata): void // General information
logger.warn(message: string, metadata?: LogMetadata): void // Warning messages
logger.error(message: string, metadata?: LogMetadata): void // Error messages
logger.critical(message: string, metadata?: LogMetadata): void // Critical failuresMetadata Structure:
interface LogMetadata {
[key: string]: string | number | boolean | null | undefined | LogMetadata;
}Metadata can contain nested objects:
logger.info('Complex operation completed', {
user: {
id: '123',
role: 'admin',
},
operation: {
type: 'data_export',
recordsProcessed: 50000,
duration: 12500,
},
success: true,
});Utility Methods
// Manually flush all queued logs
await logger.flush(): Promise<void>
// Gracefully shutdown the logger (flushes and cleans up)
await logger.destroy(): Promise<void>
// Get transport statistics
logger.getStats(): TransportStats
// Get current queue size
logger.getQueueSize(): numberStatistics Object
interface TransportStats {
sent: number; // Total logs successfully sent
failed: number; // Total logs that failed to send
retried: number; // Total retry attempts
lastError?: Error; // Most recent error (if any)
}🔐 Security Architecture
How End-to-End Encryption Works
┌─────────────────────────────────────────────────────────────────┐
│ YOUR APPLICATION │
│ │
│ 1. Log created: "User login failed" │
│ 2. Encrypted with Master Key (AES-256-GCM) │
│ 3. Result: "a8f3b2c1d4e5..." (ciphertext) │
│ │
└────────────────────────┬────────────────────────────────────────┘
│
│ HTTPS (Encrypted ciphertext)
▼
┌─────────────────────────────────────────────────────────────────┐
│ YOUR BACKEND (BLIND) │
│ │
│ • Receives: "a8f3b2c1d4e5..." (cannot read it) │
│ • Stores in database as-is │
│ • Forwards to dashboard via WebSocket │
│ │
└────────────────────────┬────────────────────────────────────────┘
│
│ WebSocket (Still encrypted)
▼
┌─────────────────────────────────────────────────────────────────┐
│ DASHBOARD (DECRYPTS) │
│ │
│ 1. Receives: "a8f3b2c1d4e5..." (ciphertext) │
│ 2. User enters Master Key in browser │
│ 3. Decrypts: "User login failed" (readable!) │
│ │
└─────────────────────────────────────────────────────────────────┘What Gets Encrypted vs. What Stays Plain
| Field | Encrypted? | Why? | |-------|-----------|------| | Log Message | ✅ Yes | Contains sensitive information | | Metadata | ✅ Yes | May contain user data, IPs, etc. | | Log Level | ❌ No | Needed for filtering (DEBUG, INFO, etc.) | | Timestamp | ❌ No | Needed for chronological sorting | | Project ID | ❌ No | Needed for routing to correct project |
Encryption Specifications
- Algorithm: AES-256-GCM (Galois/Counter Mode)
- Key Size: 256 bits (32 bytes)
- Initialization Vector (IV): 128 bits (16 bytes), randomly generated per message
- Authentication Tag: 128 bits (16 bytes) for integrity verification
- Key Derivation: Uses native Node.js
crypto.randomBytes()for secure key generation
Security Guarantees
✅ Confidentiality: Only someone with the master key can read the logs
✅ Integrity: Authentication tags prevent tampering
✅ Authenticity: Each log is cryptographically verified
✅ Forward Secrecy: Each log uses a unique IV
❌ Access Control: Not provided (implement in your backend)
❌ Key Rotation: Not supported yet (roadmap feature)
💡 Real-World Usage Examples
Example 1: Express.js HTTP Request Logging
import express from 'express';
import { E2EELogger } from '@rajat12826/logger';
const app = express();
const logger = new E2EELogger({
apiUrl: process.env.LOG_API_URL!,
apiKey: process.env.API_KEY!,
masterKey: process.env.MASTER_KEY!,
projectId: 'express-api',
});
// Middleware to log all HTTP requests
app.use((req, res, next) => {
const startTime = Date.now();
// Log when response finishes
res.on('finish', () => {
const duration = Date.now() - startTime;
const level = res.statusCode >= 500 ? 'error' :
res.statusCode >= 400 ? 'warn' : 'info';
logger[level]('HTTP Request', {
method: req.method,
path: req.path,
statusCode: res.statusCode,
duration,
userAgent: req.get('user-agent'),
ip: req.ip,
query: req.query,
});
});
next();
});
// Graceful shutdown
process.on('SIGTERM', async () => {
await logger.destroy();
process.exit(0);
});
app.listen(3000);Example 2: NestJS Dependency Injection
// logger.service.ts
import { Injectable, OnModuleDestroy } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { E2EELogger } from '@rajat12826/logger';
@Injectable()
export class LoggerService implements OnModuleDestroy {
private logger: E2EELogger;
constructor(private configService: ConfigService) {
this.logger = new E2EELogger({
apiUrl: this.configService.get('LOG_API_URL')!,
apiKey: this.configService.get('API_KEY')!,
masterKey: this.configService.get('MASTER_KEY')!,
projectId: 'nestjs-app',
enableConsole: this.configService.get('NODE_ENV') === 'development',
});
}
async onModuleDestroy() {
await this.logger.destroy();
}
logUserAction(userId: string, action: string, metadata?: any) {
this.logger.info(`User ${action}`, {
userId,
action,
timestamp: new Date().toISOString(),
...metadata,
});
}
logError(context: string, error: Error, metadata?: any) {
this.logger.error(`Error in ${context}`, {
context,
errorMessage: error.message,
errorStack: error.stack,
...metadata,
});
}
}
// Usage in a controller
import { Controller, Post, Body } from '@nestjs/common';
import { LoggerService } from './logger.service';
@Controller('users')
export class UsersController {
constructor(private loggerService: LoggerService) {}
@Post('login')
async login(@Body() credentials: any) {
try {
// Login logic...
this.loggerService.logUserAction(
credentials.email,
'login',
{ loginMethod: 'password', success: true }
);
return { success: true };
} catch (error) {
this.loggerService.logError('login', error, {
email: credentials.email,
});
throw error;
}
}
}Example 3: Database Query Logging
import { E2EELogger } from '@rajat12826/logger';
import { Pool } from 'pg';
const logger = new E2EELogger({ /* config */ });
const pool = new Pool({ /* db config */ });
// Wrapper function for logging database queries
async function loggedQuery<T>(
queryText: string,
params?: any[]
): Promise<T> {
const startTime = Date.now();
try {
const result = await pool.query(queryText, params);
const duration = Date.now() - startTime;
logger.debug('Database query executed', {
query: queryText,
paramCount: params?.length || 0,
rowCount: result.rowCount,
duration,
});
return result.rows as T;
} catch (error) {
const duration = Date.now() - startTime;
logger.error('Database query failed', {
query: queryText,
paramCount: params?.length || 0,
duration,
error: error.message,
code: error.code,
});
throw error;
}
}
// Usage
const users = await loggedQuery('SELECT * FROM users WHERE active = $1', [true]);Example 4: Error Boundary with Detailed Logging
import { E2EELogger } from '@rajat12826/logger';
const logger = new E2EELogger({ /* config */ });
// Global error handler
class ErrorHandler {
static async handle(error: Error, context?: any) {
const errorDetails = {
message: error.message,
stack: error.stack,
name: error.name,
context,
timestamp: new Date().toISOString(),
environment: process.env.NODE_ENV,
nodeVersion: process.version,
platform: process.platform,
};
// Log based on error type
if (error.name === 'ValidationError') {
logger.warn('Validation failed', errorDetails);
} else if (error.name === 'AuthenticationError') {
logger.error('Authentication failed', errorDetails);
} else {
logger.critical('Unhandled error', errorDetails);
}
// Also send to external monitoring (e.g., Sentry)
// Sentry.captureException(error);
}
}
// Async wrapper with error handling
async function withErrorLogging<T>(
fn: () => Promise<T>,
context: string
): Promise<T> {
try {
return await fn();
} catch (error) {
await ErrorHandler.handle(error as Error, { context });
throw error;
}
}
// Usage
await withErrorLogging(
async () => {
await processPayment(orderId);
},
'payment-processing'
);Example 5: Background Job Monitoring
import { E2EELogger } from '@rajat12826/logger';
import cron from 'node-cron';
const logger = new E2EELogger({ /* config */ });
// Monitor a scheduled job
function monitoredJob(name: string, fn: () => Promise<void>) {
return async () => {
const jobId = `${name}-${Date.now()}`;
const startTime = Date.now();
logger.info('Job started', {
jobId,
jobName: name,
startTime: new Date().toISOString(),
});
try {
await fn();
const duration = Date.now() - startTime;
logger.info('Job completed successfully', {
jobId,
jobName: name,
duration,
status: 'success',
});
} catch (error) {
const duration = Date.now() - startTime;
logger.error('Job failed', {
jobId,
jobName: name,
duration,
status: 'failed',
error: error.message,
stack: error.stack,
});
}
};
}
// Schedule jobs with monitoring
cron.schedule('0 * * * *', monitoredJob('hourly-sync', async () => {
// Sync data every hour
await syncDataFromAPI();
}));
cron.schedule('0 0 * * *', monitoredJob('daily-cleanup', async () => {
// Clean up old records daily
await cleanupOldRecords();
}));Example 6: Performance Monitoring
import { E2EELogger } from '@rajat12826/logger';
const logger = new E2EELogger({ /* config */ });
class PerformanceMonitor {
private metrics: Map<string, number[]> = new Map();
track(operation: string, duration: number) {
if (!this.metrics.has(operation)) {
this.metrics.set(operation, []);
}
this.metrics.get(operation)!.push(duration);
}
async flush() {
for (const [operation, durations] of this.metrics.entries()) {
const avg = durations.reduce((a, b) => a + b, 0) / durations.length;
const max = Math.max(...durations);
const min = Math.min(...durations);
logger.info('Performance metrics', {
operation,
count: durations.length,
avgDuration: avg,
maxDuration: max,
minDuration: min,
});
}
this.metrics.clear();
}
}
const monitor = new PerformanceMonitor();
// Track function performance
async function tracked<T>(
name: string,
fn: () => Promise<T>
): Promise<T> {
const start = Date.now();
try {
return await fn();
} finally {
monitor.track(name, Date.now() - start);
}
}
// Flush metrics every 5 minutes
setInterval(() => monitor.flush(), 5 * 60 * 1000);
// Usage
const data = await tracked('fetch-user-data', async () => {
return await db.users.findMany();
});Example 7: Custom Error Handler with Alerting
import { E2EELogger } from '@rajat12826/logger';
const logger = new E2EELogger({
apiUrl: process.env.LOG_API_URL!,
apiKey: process.env.API_KEY!,
masterKey: process.env.MASTER_KEY!,
projectId: 'production-app',
// Custom error handler
onError: async (error) => {
console.error('[Logger Error]', error.message);
// Send alert to Slack/Discord/Email
await sendAlert({
title: 'Logger Error',
message: error.message,
severity: 'high',
timestamp: new Date().toISOString(),
});
// Fallback: write to local file
const fs = require('fs');
fs.appendFileSync(
'logger-errors.log',
`${new Date().toISOString()} - ${error.message}\n`
);
},
});🔧 Configuration Examples
Development Environment
const logger = new E2EELogger({
apiUrl: 'http://localhost:3000/api',
apiKey: 'dev-key',
masterKey: process.env.MASTER_KEY!,
projectId: 'my-app-dev',
// Development settings
enableConsole: true, // See logs in terminal
batchSize: 1, // Send immediately (no batching)
batchInterval: 100, // Very low latency
maxRetries: 1, // Don't retry much
timeout: 3000, // Short timeout
});Production Environment
const logger = new E2EELogger({
apiUrl: process.env.LOG_API_URL!,
apiKey: process.env.API_KEY!,
masterKey: process.env.MASTER_KEY!,
projectId: 'my-app-prod',
// Production settings
enableConsole: false, // Don't spam console
batchSize: 50, // Efficient batching
batchInterval: 10000, // Flush every 10 seconds
maxRetries: 5, // Resilient retry strategy
retryDelay: 2000, // Wait 2s before first retry
timeout: 15000, // Longer timeout for reliability
});High-Traffic Environment
const logger = new E2EELogger({
apiUrl: process.env.LOG_API_URL!,
apiKey: process.env.API_KEY!,
masterKey: process.env.MASTER_KEY!,
projectId: 'high-traffic-app',
// High-throughput settings
enableConsole: false,
batchSize: 100, // Maximum batch size
batchInterval: 5000, // Frequent flushes
maxRetries: 3,
timeout: 20000,
});🧪 Testing Your Integration
Local Testing Script
Create a file test-logger.js:
import { E2EELogger, generateMasterKey } from '@rajat12826/logger';
const masterKey = generateMasterKey();
console.log('Generated Master Key:', masterKey);
const logger = new E2EELogger({
apiUrl: 'http://localhost:3000/api',
apiKey: 'test-key',
masterKey,
projectId: 'test',
enableConsole: true,
});
// Test all log levels
logger.debug('Debug message', { userId: '123' });
logger.info('Info message', { action: 'test' });
logger.warn('Warning message', { memory: '90%' });
logger.error('Error message', { code: 500 });
logger.critical('Critical message', { service: 'down' });
// Test complex metadata
logger.info('Complex log', {
user: {
id: '123',
email: '[email protected]',
preferences: {
theme: 'dark',
notifications: true,
},
},
metrics: {
responseTime: 1234,
requestCount: 42,
},
});
// Wait and shutdown
setTimeout(async () => {
console.log('Flushing logs...');
await logger.flush();
console.log('Statistics:', logger.getStats());
console.log('Queue size:', logger.getQueueSize());
await logger.destroy();
console.log('Done!');
}, 3000);Run it:
node test-logger.js📊 Monitoring and Statistics
Real-time Statistics
// Monitor logger health every 30 seconds
setInterval(() => {
const stats = logger.getStats();
console.log('Logger Health Check:', {
sent: stats.sent,
failed: stats.failed,
retried: stats.retried,
queueSize: logger.getQueueSize(),
successRate: stats.sent / (stats.sent + stats.failed) * 100,
});
// Alert if failure rate is high
if (stats.failed > stats.sent * 0.1) {
console.warn('HIGH FAILURE RATE DETECTED!');
// Send alert to monitoring system
}
}, 30000);Export Metrics to Prometheus
import { register, Counter, Gauge } from 'prom-client';
const logsSent = new Counter({
name: 'e2ee_logger_logs_sent_total',
help: 'Total number of logs sent',
});
const logsFailed = new Counter({
name: 'e2ee_logger_logs_failed_total',
help: 'Total number of failed logs',
});
const queueSize = new Gauge({
name: 'e2ee_logger_queue_size',
help: 'Current queue size',
});
// Update metrics periodically
setInterval(() => {
const stats = logger.getStats();
logsSent.inc(stats.sent);
logsFailed.inc(stats.failed);
queueSize.set(logger.getQueueSize());
}, 10000);
// Expose metrics endpoint
app.get('/metrics', async (req, res) => {
res.set('Content-Type', register.contentType);
res.end(await register.metrics());
});🚨 Error Handling & Troubleshooting
Common Errors
1. Configuration Errors
// ❌ Wrong
const logger = new E2EELogger({
apiUrl: 'not-a-valid-url',
// Missing required fields
});
// ✅ Correct
const logger = new E2EELogger({
apiUrl: 'https://api.example.com',
apiKey: process.env.API_KEY!,
masterKey: process.env.MASTER_KEY!,
projectId: 'my-app',
});Error: ConfigurationError: Invalid API URL
Solution: Ensure all required fields are provided and valid.
2. Invalid Master Key
// ❌ Wrong
masterKey: 'short-key'
// ✅ Correct
masterKey: generateMasterKey() // Returns 64-char hex stringError: ValidationError: Master key must be a 64-character hex string
Solution: Use generateMasterKey() to create a valid key.
3. Network Errors
const logger = new E2EELogger({
// ... config
maxRetries: 5, // Increase retries
retryDelay: 2000, // Wait before retry
timeout: 15000, // Longer timeout
onError: (error) => {
if (error.name === 'TransportError') {
console.error('Network issue:', error.message);
// Implement fallback (write to file, etc.)
}
},
});4. Message Too Large
// ❌ Wrong
logger.info('x'.repeat(20000)); // > 10KB
// ✅ Correct
const message = longMessage.substring(0, 1000);
logger.info(message);Error: ValidationError: Log message exceeds maximum length
Solution: Keep messages under 10,000 characters.
Debugging
Enable console logging during development:
const logger = new E2EELogger({
// ... config
enableConsole: true, // See logs in real-time
onError: (error) => {
console.error('Logger Error:', {
name: error.name,
message: error.message,
stack: error.stack,
});
},
});🤝 Contributing
We welcome contributions! Here's how you can help:
Reporting Bugs
- Check if the bug is already reported in GitHub Issues
- Create a new issue with:
- Clear title and description
- Steps to reproduce
- Expected vs actual behavior
- Environment details (Node version, OS, etc.)
Feature Requests
- Open an issue with the
enhancementlabel - Describe the feature and use case
- Provide examples if possible
Pull Requests
- Fork the repository
- Create a feature branch:
git checkout -b feature/amazing-feature - Make your changes
- Add tests for new functionality
- Ensure all tests pass:
npm test - Commit:
git commit -m 'Add amazing feature' - Push:
git push origin feature/amazing-feature - Open a Pull Request
Development Setup
# Clone your fork
git clone https://github.com/rajat1826/logger.git
cd logger
# Install dependencies
npm install
# Run tests
npm test
# Run tests in watch mode
npm run test:watch
# Check code coverage
npm run test:coverage
# Lint code
npm run lint
# Build
npm run build📄 License
This project is licensed under the MIT License - see the LICENSE file for details.
MIT License
Copyright (c) 2024 [Your Name]
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.🙏 Acknowledgments
- Inspired by WhatsApp's end-to-end encryption architecture
- Built with TypeScript for type safety
- Uses Zod for runtime validation
- Encryption powered by Node.js crypto module
- Compatible with NestJS, Express, Fastify, and other frameworks
📞 Support & Community
Get Help
- 📧 Email: [email protected]
- 🐛 Issues: GitHub Issues
- 📖 Documentation: Full Docs
- 💡 Stack Overflow: Tag your questions with
logger
Stay Updated
- ⭐ Star the repo on GitHub
- 👁️ Watch for updates
- 🔔 Subscribe to release notifications
🗺️ Roadmap
Version 1.x (Current)
- [x] End-to-end encryption (AES-256-GCM)
- [x] Automatic batching
- [x] Retry logic with exponential backoff
- [x] TypeScript support
- [x] Input validation
- [x] Statistics tracking
Version 2.0 (Planned)
- [ ] Browser Support - Use WebCrypto API for client-side encryption
- [ ] Log Compression - Reduce bandwidth using gzip/brotli
- [ ] Sampling - Configurable log sampling for high-volume apps
- [ ] Key Rotation - Support for rotating master keys
- [ ] Multiple Transports - Support file, S3, Kinesis, etc.
- [ ] Structured Logging - JSON schema validation
- [ ] Log Levels Filtering - Filter by log level before sending
Version 3.0 (Future)
- [ ] Searchable Encryption - Search logs without decrypting
- [ ] Multi-tenancy - Support multiple projects with one instance
- [ ] Prometheus Integration - Built-in metrics export
- [ ] Log Aggregation - Built-in log correlation and tracing
- [ ] Cloud Provider SDKs - Direct integration with AWS CloudWatch, GCP Logging, etc.
📈 Performance Benchmarks
Tested on: Acer Predator (I5, 16GB RAM), Node.js v20.x
| Operation | Time | Throughput | |-----------|------|------------| | Encrypt single log | ~0.5ms | 2,000 logs/sec | | Batch (10 logs) | ~3ms | 3,333 logs/sec | | Network round-trip | ~50ms | 200 req/sec | | Memory per 1000 logs | ~2MB | - |
Notes:
- Batching significantly improves throughput
- Network latency is the main bottleneck
- Memory usage is minimal due to streaming batches
🔐 Security Best Practices
Do's ✅
- ✅ Store master key in environment variables
- ✅ Use different master keys for dev/staging/prod
- ✅ Rotate master keys periodically (when v2.0 supports it)
- ✅ Enable HTTPS for your backend API
- ✅ Implement authentication on your backend
- ✅ Keep master key backups in a secure location
- ✅ Audit who has access to master keys
- ✅ Use strong API keys (min 32 characters)
Don'ts ❌
- ❌ Never commit master keys to Git
- ❌ Never log the master key (even in debug mode)
- ❌ Never share master keys via email/Slack
- ❌ Never use the same key across environments
- ❌ Never hardcode keys in source code
- ❌ Never store keys in frontend code
- ❌ Never send master keys over unencrypted channels
Key Management Checklist
# Generate a new key
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
# Store in .env
echo "MASTER_KEY=your_generated_key" >> .env
# Ensure .env is in .gitignore
echo ".env" >> .gitignore
# For team sharing, use a secrets manager:
# - AWS Secrets Manager
# - HashiCorp Vault
# - 1Password Secrets Automation
# - Azure Key Vault📚 Additional Resources
Tutorials
Videos
Blog Posts
❓ FAQ
Q: Can I use this in the browser?
A: Not yet. Version 2.0 will support browser environments using the WebCrypto API.
Q: What happens if I lose my master key?
A: Your logs cannot be decrypted. There is no recovery mechanism. Always keep secure backups.
Q: Can the backend read my logs?
A: No. The backend only stores encrypted ciphertext. Only someone with your master key can decrypt them.
Q: How do I rotate my master key?
A: Key rotation is not yet supported. It's planned for version 2.0. For now, you would need to:
- Generate a new key
- Update your application config
- Old logs remain encrypted with the old key
Q: What's the performance overhead?
A: Encryption adds ~0.5ms per log. Batching amortizes this cost. For most applications, this is negligible.
Q: Can I disable encryption?
A: No. This library is purpose-built for E2EE. Use a standard logger like Winston or Pino if you don't need encryption.
Q: Is this GDPR compliant?
A: Yes, but you are responsible for:
- Implementing data retention policies
- Providing data export functionality
- Handling deletion requests
- Documenting your data processing
Q: Can I use this with serverless (AWS Lambda)?
A: Yes! Make sure to call await logger.flush() before your function returns.
export const handler = async (event) => {
logger.info('Lambda invoked');
// ... your code
await logger.flush(); // Important!
return { statusCode: 200 };
};Q: Does this support structured logging?
A: Yes! Use the metadata parameter:
logger.info('Order placed', {
orderId: '12345',
userId: 'abc',
amount: 99.99,
items: ['item1', 'item2'],
});🎯 Use Cases
1. Healthcare Applications (HIPAA Compliance)
logger.info('Patient record accessed', {
patientId: 'P12345',
accessedBy: 'Dr. Smith',
recordType: 'medical-history',
reason: 'routine-checkup',
});2. Financial Services (PCI-DSS)
logger.info('Payment processed', {
transactionId: 'TX789',
lastFourDigits: '4242',
amount: 1999.99,
merchantId: 'M123',
});3. SaaS Platforms (User Privacy)
logger.info('User activity', {
userId: 'U456',
action: 'file-upload',
fileSize: 1024000,
fileName: 'document.pdf',
});4. Government Systems (Classified Data)
logger.critical('Security breach attempt', {
sourceIp: '192.168.1.1',
targetSystem: 'database',
attackType: 'sql-injection',
blocked: true,
});Built with ❤️ for privacy-conscious developers
Version: 1.0.0
Last Updated: February 2026
Maintainer: Rajat Parihar
🌟 Show Your Support
If this library helped you, please:
- ⭐ Star this repository
- 🐦 Tweet about it
- 📝 Write a blog post
- 💬 Share with your team
Every bit of support helps us make this library better!
