npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@rajatparihar26/e2ee-logger

v1.0.3

Published

[![npm version](https://badge.fury.io/js/%40rajat12826%2Flogger.svg)](https://www.npmjs.com/package/rajat12826/logger) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT) [![TypeScript](https://img.s

Readme

🔐 Logger

npm version License: MIT TypeScript

Production-grade end-to-end encrypted logging library for Node.js applications.

Your logs are encrypted before they leave your application. Your backend is blind to the content—only you can decrypt them.


✨ Features

  • 🔒 End-to-End Encryption - AES-256-GCM encryption with authentication tags
  • 📦 Automatic Batching - Configurable batch size and intervals
  • 🔄 Smart Retry Logic - Exponential backoff for failed requests
  • Type-Safe - Full TypeScript support with comprehensive types
  • 🎯 Zero Heavy Dependencies - Only axios, zod, and dotenv
  • 🚀 Production Ready - Extensive error handling and validation
  • 📊 Statistics Tracking - Monitor sent, failed, and retried logs
  • 🛡️ Input Validation - Comprehensive validation with helpful error messages
  • 🌐 Framework Agnostic - Works with Express, NestJS, Fastify, etc.

📦 Installation

npm install @rajat12826/logger

Or with yarn:

yarn add @rajat12826/logger

🚀 Quick Start

Step 1: Generate a Master Key

First, generate a cryptographically secure master key. This is the most important step!

import { generateMasterKey } from '@rajat12826/logger';

const masterKey = generateMasterKey();
console.log('Master Key:', masterKey);
// Output: Master Key: a1b2c3d4e5f6... (64 characters)

⚠️ CRITICAL SECURITY NOTICE:

  • Store this key in your .env file as MASTER_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/api

Step 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 failures

Metadata 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(): number

Statistics 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 string

Error: 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

  1. Check if the bug is already reported in GitHub Issues
  2. Create a new issue with:
    • Clear title and description
    • Steps to reproduce
    • Expected vs actual behavior
    • Environment details (Node version, OS, etc.)

Feature Requests

  1. Open an issue with the enhancement label
  2. Describe the feature and use case
  3. Provide examples if possible

Pull Requests

  1. Fork the repository
  2. Create a feature branch: git checkout -b feature/amazing-feature
  3. Make your changes
  4. Add tests for new functionality
  5. Ensure all tests pass: npm test
  6. Commit: git commit -m 'Add amazing feature'
  7. Push: git push origin feature/amazing-feature
  8. 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

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:

  1. Generate a new key
  2. Update your application config
  3. 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!


⬆ Back to Top