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

nwinread

v1.2.0

Published

High-performance native Node.js module for reading Windows event logs with real-time monitoring and watermark support

Readme

nwinread - Windows Event Log Reader

npm version npm downloads license Node.js version

A high-performance native Node.js module for reading Windows event logs using the Windows Event Log API with both synchronous and asynchronous support.

Table of Contents

Features

  • 🔥 Asynchronous Subscriptions: Real-time event monitoring with zero polling
  • 📚 Synchronous Reading: One-time queries for batch processing
  • 🎯 Smart Positioning: Read from beginning, end, or specific Record ID (watermarks)
  • ⚡ Event Filtering: Filter by Event IDs for efficient processing
  • 📋 Bookmark Support: Resume from exact positions using Windows Event Log bookmarks
  • 🛡️ Cross-Version Compatible: Built with N-API for all Node.js versions 16+
  • 🚀 Precompiled Binaries: No compilation needed for standard installations

Requirements

  • Windows: Vista/7/8/10/11 or Server 2008/2012/2016/2019/2022
  • Node.js: 16+ (recommended for precompiled binaries)
  • Permissions: Administrator privileges required for Security log only

Installation

npm install nwinread

Installs instantly using precompiled binaries for Node.js 16+

Quick Start

Synchronous Reading (One-time queries)

const eventLog = require('nwinread');

// Read recent events
const recent = eventLog.readEvents(
    "System",                    // Channel
    eventLog.START_MODE.END,     // From end
    0,                           // Watermark (ignored for END mode)
    10                           // Max events
);

console.log(`Found ${recent.records.length} recent events`);
recent.records.forEach(event => {
    console.log(`Record ID: ${event.recordId}`);
    console.log(`Event XML: ${event.xml.substring(0, 100)}...`);
});

Asynchronous Monitoring (Real-time)

const eventLog = require('nwinread');

// Monitor new events in real-time
const subscription = eventLog.subscribeFromEnd(
    'Application',
    (event) => {
        console.log(`🆕 New event: ${event.recordId}`);
        console.log(`   XML: ${event.xml.substring(0, 150)}...`);
    },
    (error) => {
        console.error(`❌ Error: ${error.message}`);
    },
    [1000, 1001]  // Optional: filter by Event IDs
);

console.log(`✅ Monitoring started. Subscription ID: ${subscription.id}`);

// Stop monitoring after 30 seconds
setTimeout(() => {
    subscription.unsubscribe();
    console.log('✅ Monitoring stopped');
}, 30000);

API Reference

Reading Modes

| Mode | Value | Description | Synchronous | Asynchronous | |------|-------|-------------|-------------|--------------| | BEGINNING | 0 | Start from oldest events | readEvents() | subscribeFromBeginning() | | END | 1 | Start from newest/future events | readEvents() | subscribeFromEnd() | | WATERMARK | 2 | Start from specific Record ID | readEvents() | subscribeFromWatermark() |

Synchronous API

readEvents(channel, mode, watermark, maxEvents, eventIds)

const eventLog = require('nwinread');

// Basic syntax
eventLog.readEvents(channel, mode, watermark, maxEvents, eventIds)

// Read from beginning
const oldest = eventLog.readEvents(
    "Application", 
    eventLog.START_MODE.BEGINNING, 
    0,      // Watermark ignored for BEGINNING
    50      // Max events
);

// Read recent events
const recent = eventLog.readEvents(
    "System", 
    eventLog.START_MODE.END, 
    0,      // Watermark ignored for END
    20
);

// Read from specific Record ID
const fromPoint = eventLog.readEvents(
    "Application", 
    eventLog.START_MODE.WATERMARK, 
    50000,  // Start from Record ID 50000
    25
);

// Read with Event ID filter
const filtered = eventLog.readEvents(
    "System", 
    eventLog.START_MODE.END, 
    0, 
    30,
    [1074, 6005, 6006]  // Only these Event IDs
);

Parameters:

  • channel (string): Event log channel ("System", "Application", "Security", etc.)
  • mode (number): Reading mode (0=BEGINNING, 1=END, 2=WATERMARK)
  • watermark (number): Starting Record ID for WATERMARK mode (ignored for other modes)
  • maxEvents (number): Maximum events to return
  • eventIds (array, optional): Array of Event IDs to filter by

Returns: Object with records array and lastRecordId

Asynchronous API

subscribe(channel, watermark, onEvent, onError, eventIds, mode)

Core subscription method with precise control:

const subscription = eventLog.subscribe(
    'Application',                    // channel
    48000,                           // watermark (Record ID to start from)
    (event) => {                     // onEvent callback
        console.log(`Event: ${event.recordId}`);
        // event.recordId - Record ID number
        // event.xml - Full event XML
    },
    (error) => {                     // onError callback  
        console.log(`Error: ${error.message}`);
    },
    [1000, 1001],                   // eventIds filter (optional, null for all)
    eventLog.START_MODE.WATERMARK   // mode (0=BEGINNING, 1=END, 2=WATERMARK)
);

console.log(`Subscription ID: ${subscription.id}`);

// Always cleanup when done
subscription.unsubscribe();

Helper Methods (Recommended)

subscribeFromEnd(channel, onEvent, onError, eventIds)

Monitor new events only (future events):

const subscription = eventLog.subscribeFromEnd(
    'System',
    (event) => console.log(`New: ${event.recordId}`),
    (error) => console.error(error.message),
    [1074, 6005]  // Optional Event ID filter
);

subscribeFromBeginning(channel, onEvent, onError, eventIds)

Process all historical events + monitor new events:

const subscription = eventLog.subscribeFromBeginning(
    'Application',
    (event) => console.log(`Historical/New: ${event.recordId}`),
    (error) => console.error(error.message)
);

subscribeFromWatermark(channel, watermark, onEvent, onError, eventIds)

Resume from specific Record ID:

const subscription = eventLog.subscribeFromWatermark(
    'Application',
    50000,  // Start from Record ID 50000
    (event) => console.log(`From 50k: ${event.recordId}`),
    (error) => console.error(error.message)
);

EventLogSubscription Object

All subscription methods return an EventLogSubscription object:

const subscription = eventLog.subscribeFromEnd('System', onEvent, onError);

// Properties
console.log(subscription.id);       // Subscription ID (number)
console.log(subscription.channel);  // Channel name (string)  
console.log(subscription.watermark); // Starting watermark (number)
console.log(subscription.mode);     // Reading mode (number)

// Methods
subscription.isActive();             // Returns true if subscription is active
subscription.getLastRecordId();     // Get last processed Record ID
subscription.unsubscribe();         // Stop subscription and clean up

EventEmitter Pattern

const emitter = eventLog.createEventEmitter('System', {
    mode: eventLog.START_MODE.END,
    watermark: 0,
    eventIds: [1074, 6005]
});

emitter.on('event', (event) => {
    console.log(`Event: ${event.recordId}`);
});

emitter.on('error', (error) => {
    console.error(`Error: ${error.message}`);
});

// Cleanup
emitter.unsubscribe();

Common Use Cases

Real-time System Monitoring

const eventLog = require('nwinread');

// Monitor system shutdown/startup events
const systemMonitor = eventLog.subscribeFromEnd("System",
    (event) => {
        // Extract Event ID from XML
        const eventIdMatch = event.xml.match(/<EventID.*?>(\d+)<\/EventID>/);
        const eventId = eventIdMatch ? eventIdMatch[1] : 'unknown';
        
        if (eventId === '1074') {
            console.log(`🔄 System shutdown initiated - Record ID: ${event.recordId}`);
        } else if (eventId === '6005') {
            console.log(`✅ Event Log service started - Record ID: ${event.recordId}`);
        }
    },
    (error) => console.error(`❌ Monitor error: ${error.message}`),
    [1074, 6005, 6006]  // Filter critical system events
);

// Keep monitoring until interrupted
process.on('SIGINT', () => {
    console.log('\n🛑 Stopping system monitor...');
    systemMonitor.unsubscribe();
    process.exit(0);
});

Application Error Monitoring

// Monitor ALL application events (historical + new)
const appMonitor = eventLog.subscribeFromBeginning("Application",
    (event) => {
        // Look for error patterns in the XML
        if (event.xml.includes('Error') || event.xml.includes('Exception')) {
            console.log(`🚨 Application error detected:`);
            console.log(`   Record ID: ${event.recordId}`);
            console.log(`   Time: ${new Date().toISOString()}`);
            console.log(`   Preview: ${event.xml.substring(0, 200)}...`);
        }
    },
    (error) => console.error(`❌ App monitor error: ${error.message}`)
);

// Process for 1 minute then stop
setTimeout(() => {
    appMonitor.unsubscribe();
    console.log('✅ Application monitoring completed');
}, 60000);

Resumable Processing with Watermarks

// Save last processed Record ID to resume later
let lastProcessedId = 48000; // Load from database/file in real usage

const processor = eventLog.subscribeFromWatermark("Application", lastProcessedId,
    (event) => {
        console.log(`📝 Processing event: ${event.recordId}`);
        
        // Your business logic here
        processEvent(event);
        
        // Update watermark for next restart
        lastProcessedId = event.recordId;
        // Save to database/file in real usage
        
        console.log(`✅ Processed ${event.recordId}, next watermark: ${lastProcessedId}`);
    },
    (error) => {
        console.error(`❌ Processing error: ${error.message}`);
        // Implement retry logic here
    }
);

function processEvent(event) {
    // Your event processing logic
    // Database inserts, API calls, etc.
}

Historical Analysis

const eventLog = require('nwinread');

// Analyze large batch of historical events
function analyzeEvents() {
    console.log('📊 Starting historical analysis...');
    
    const events = eventLog.readEvents(
        "System",
        eventLog.START_MODE.BEGINNING, 
        0,
        1000  // Analyze last 1000 events
    );
    
    let errorCount = 0;
    let warningCount = 0;
    let infoCount = 0;
    
    events.records.forEach(event => {
        // Simple level detection (Windows Event Levels: 1=Critical, 2=Error, 3=Warning, 4=Info)
        if (event.xml.includes('Level>1<') || event.xml.includes('Level>2<')) {
            errorCount++;
        } else if (event.xml.includes('Level>3<')) {
            warningCount++;
        } else if (event.xml.includes('Level>4<')) {
            infoCount++;
        }
    });
    
    console.log(`📈 Analysis Results:`);
    console.log(`   Total events: ${events.records.length}`);
    console.log(`   Errors: ${errorCount}`);
    console.log(`   Warnings: ${warningCount}`);  
    console.log(`   Information: ${infoCount}`);
    console.log(`   Last Record ID: ${events.lastRecordId}`);
}

analyzeEvents();

Event Channels & Common Event IDs

System Channel (No admin required)

const SYSTEM_EVENTS = {
    SHUTDOWN: 1074,              // System shutdown initiated
    UNEXPECTED_SHUTDOWN: 41,     // System rebooted unexpectedly
    EVENTLOG_START: 6005,        // Event Log service started
    EVENTLOG_STOP: 6006,         // Event Log service stopped  
    TIME_CHANGE: 4621,           // System time was changed
    POWER_SLEEP: 42,             // System entering sleep
    POWER_WAKE: 107              // System wake from sleep
};

// Monitor critical system events
const systemSub = eventLog.subscribeFromEnd("System", onEvent, onError, 
    [1074, 41, 6005, 6006]
);

Application Channel (No admin required)

const APPLICATION_EVENTS = {
    ERROR: 1000,                 // Application error
    WARNING: 1001,               // Application warning
    INFORMATION: 1002            // Application information
    // Note: Most applications use custom Event IDs
};

// Monitor all application events
const appSub = eventLog.subscribeFromEnd("Application", onEvent, onError);

Security Channel (Admin required)

const SECURITY_EVENTS = {
    LOGIN_SUCCESS: 4624,         // Successful logon
    LOGIN_FAILURE: 4625,         // Failed logon  
    LOGOUT: 4634,                // Account logged off
    ACCOUNT_LOCKED: 4740,        // Account lockout
    PRIVILEGE_USE: 4673,         // Privileged service called
    OBJECT_ACCESS: 4656          // Handle to object requested
};

// Monitor security events (requires admin privileges)
const securitySub = eventLog.subscribeFromEnd("Security", onEvent, onError,
    [4624, 4625, 4634]
);

Performance & Best Practices

⚡ High Performance Tips

// 1. Use Event ID filters to reduce processing
const filtered = eventLog.subscribeFromEnd("System", onEvent, onError, [1074, 6005]);

// 2. Limit synchronous batch sizes  
const limited = eventLog.readEvents("Application", eventLog.START_MODE.END, 0, 100);

// 3. Process events immediately, don't accumulate
const efficient = eventLog.subscribeFromEnd("Application", 
    (event) => {
        // Process immediately
        sendToDatabase(event);
        // Don't store in arrays/collections
    }, 
    onError
);

🛡️ Reliable Processing

// 1. Always handle errors gracefully
const reliable = eventLog.subscribeFromEnd("Application",
    (event) => {
        try {
            processEvent(event);
        } catch (error) {
            console.error(`Event processing error: ${error.message}`);
            // Log error but continue processing other events
        }
    },
    (error) => {
        console.error(`Subscription error: ${error.message}`);
        
        // Implement automatic recovery
        setTimeout(() => {
            console.log('🔄 Attempting to restart subscription...');
            createNewSubscription();
        }, 5000);
    }
);

// 2. Clean shutdown handling
process.on('SIGINT', () => {
    console.log('🛑 Gracefully shutting down...');
    reliable.unsubscribe();
    process.exit(0);
});

// 3. Save watermarks for recovery
let lastProcessedId = loadWatermarkFromFile();
const persistent = eventLog.subscribeFromWatermark("Application", lastProcessedId,
    (event) => {
        processEvent(event);
        lastProcessedId = event.recordId;
        saveWatermarkToFile(lastProcessedId);  // Persist progress
    },
    onError
);

💾 Memory Management

// 1. Unsubscribe when done
const subscription = eventLog.subscribeFromEnd("System", onEvent, onError);

// Always cleanup
setTimeout(() => {
    subscription.unsubscribe();  // Frees native resources
}, 30000);

// 2. Process events in streams, not collections
// ❌ Don't accumulate events
const events = [];
const badSub = eventLog.subscribeFromEnd("Application", 
    (event) => events.push(event),  // Memory leak!
    onError
);

// ✅ Process immediately
const goodSub = eventLog.subscribeFromEnd("Application",
    (event) => {
        processEventImmediately(event);  // No accumulation
    },
    onError
);

Examples & Testing

The repository includes comprehensive examples in the test/ directory:

# Test synchronous reading
node test/example_sync.js

# Test asynchronous monitoring  
node test/example_async.js

# Test watermark functionality
node test/test_watermark_realistic.js

# Test all reading modes
npm test

Example Files

  • test/example_sync.js - Synchronous reading examples
  • test/example_async.js - Asynchronous monitoring examples
  • test/test_watermark_realistic.js - Watermark/bookmark functionality
  • test/test_watermark_realistic.js - Find valid Record IDs for testing

Error Handling

Common Errors

// Channel not found
try {
    const sub = eventLog.subscribeFromEnd("InvalidChannel", onEvent, onError);
} catch (error) {
    if (error.code === 15007) {
        console.log('❌ Channel not found - check channel name');
    }
}

// Permission denied (for Security channel)
try {
    const sub = eventLog.subscribeFromEnd("Security", onEvent, onError);
} catch (error) {
    if (error.message.includes('access')) {
        console.log('❌ Admin privileges required for Security log');
    }
}

// Invalid Record ID
try {
    const sub = eventLog.subscribeFromWatermark("Application", 999999999, onEvent, onError);
} catch (error) {
    if (error.code === 15027) {
        console.log('❌ Invalid Record ID - may be beyond available range');
    }
}

Error Codes

| Code | Meaning | Solution | |------|---------|----------| | 15007 | Channel not found | Check channel name ("System", "Application", etc.) | | 15027 | Invalid query/Record ID | Use valid Record ID within available range | | 87 | Invalid parameter | Check parameter types and values | | 5 | Access denied | Run as administrator for Security log |

Advanced Usage

Custom Event Processing Pipeline

const eventLog = require('nwinread');

class EventProcessor {
    constructor() {
        this.stats = { processed: 0, errors: 0 };
        this.subscription = null;
    }
    
    start() {
        this.subscription = eventLog.subscribeFromEnd("Application",
            (event) => this.processEvent(event),
            (error) => this.handleError(error),
            [1000, 1001, 1002]  // Filter application events
        );
        
        console.log(`✅ Event processor started. Subscription: ${this.subscription.id}`);
    }
    
    processEvent(event) {
        try {
            // Parse event XML
            const data = this.parseEventData(event);
            
            // Business logic
            this.handleApplicationEvent(data);
            
            this.stats.processed++;
            
            // Log progress
            if (this.stats.processed % 100 === 0) {
                console.log(`📊 Processed ${this.stats.processed} events`);
            }
            
        } catch (error) {
            console.error(`❌ Processing error: ${error.message}`);
            this.stats.errors++;
        }
    }
    
    parseEventData(event) {
        // Extract structured data from event XML
        const eventIdMatch = event.xml.match(/<EventID.*?>(\d+)<\/EventID>/);
        const timeMatch = event.xml.match(/<TimeCreated SystemTime='([^']+)'/);
        
        return {
            recordId: event.recordId,
            eventId: eventIdMatch ? parseInt(eventIdMatch[1]) : null,
            timestamp: timeMatch ? new Date(timeMatch[1]) : null,
            xml: event.xml
        };
    }
    
    handleApplicationEvent(data) {
        switch(data.eventId) {
            case 1000:
                this.handleErrorEvent(data);
                break;
            case 1001:
                this.handleWarningEvent(data);
                break;
            case 1002:
                this.handleInfoEvent(data);
                break;
            default:
                this.handleGenericEvent(data);
        }
    }
    
    handleErrorEvent(data) {
        console.log(`🚨 Application Error - Record ${data.recordId} at ${data.timestamp}`);
        // Send alert, log to database, etc.
    }
    
    handleWarningEvent(data) {
        console.log(`⚠️  Application Warning - Record ${data.recordId}`);
        // Log to monitoring system
    }
    
    handleInfoEvent(data) {
        console.log(`ℹ️  Application Info - Record ${data.recordId}`);
    }
    
    handleGenericEvent(data) {
        console.log(`📄 Generic event ${data.eventId} - Record ${data.recordId}`);
    }
    
    handleError(error) {
        console.error(`❌ Subscription error: ${error.message}`);
        this.stats.errors++;
        
        // Implement reconnection logic
        setTimeout(() => {
            console.log('🔄 Attempting to restart...');
            this.start();
        }, 5000);
    }
    
    getStats() {
        return {
            ...this.stats,
            isActive: this.subscription && this.subscription.isActive()
        };
    }
    
    stop() {
        if (this.subscription) {
            this.subscription.unsubscribe();
            console.log(`✅ Event processor stopped. Final stats:`, this.getStats());
        }
    }
}

// Usage
const processor = new EventProcessor();
processor.start();

// Stop after 60 seconds
setTimeout(() => {
    processor.stop();
}, 60000);

// Graceful shutdown
process.on('SIGINT', () => {
    processor.stop();  
    process.exit(0);
});

Troubleshooting

Installation Issues

# For Node.js 16+
npm install nwinread  # Should install instantly

# If compilation is needed (Node.js < 16)
npm install --build-from-source

# Force rebuild if having issues
npm rebuild nwinread

Runtime Issues

  1. "Channel not found" error

    • Verify channel name: "System", "Application", "Security"
    • Check Windows Event Viewer to confirm channel exists
  2. "Access denied" for Security log

    • Run as Administrator: Run as administrator in Command Prompt
    • Only Security log requires admin privileges
  3. No events received in subscription

    • Check if events exist: use readEvents() first
    • Verify Event ID filters are correct
    • For Security channel: ensure admin privileges
  4. "Invalid Record ID" for watermarks

    • Use readEvents() to find valid Record ID range
    • Record IDs are not sequential and vary by channel

Debugging

// Enable debugging to see internal operations
const eventLog = require('nwinread');

// Test with small batch first
const test = eventLog.readEvents("Application", eventLog.START_MODE.END, 0, 5);
console.log(`Test result: ${test.records.length} events found`);

if (test.records.length > 0) {
    console.log(`Record ID range: ${test.records[0].recordId} to ${test.lastRecordId}`);
    
    // Now try subscription with known good Record ID
    const sub = eventLog.subscribeFromWatermark("Application", 
        test.records[0].recordId,
        (event) => console.log(`✅ Event: ${event.recordId}`),
        (error) => console.log(`❌ Error: ${error.message}`)
    );
}

Contributing

This is a Windows-specific native module. Development requires:

  • Windows development environment
  • Visual Studio Build Tools
  • Node.js 16+
  • Windows Event Log for testing
# Development setup
git clone https://github.com/solzimer/nwinread.git
cd nwinread
npm install
npm run rebuild

# Run tests
npm test

# Run examples  
npm run examples

Support

Changelog

See GitHub Releases for version history and changes.

License

MIT License - see LICENSE file for details.