nwinread 1.1.0 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +779 -96
  2. package/binding.gyp +30 -0
  3. package/build/binding.sln +6 -0
  4. package/build/eventlogasync.vcxproj +148 -0
  5. package/build/eventlogasync.vcxproj.filters +43 -0
  6. package/doc/ASYNC_STATUS.md +104 -0
  7. package/doc/COHERENCIA_APIS.md +154 -0
  8. package/doc/CONTRIBUTING.md +64 -0
  9. package/doc/CORRECCION_NAPI.md +74 -0
  10. package/doc/CPU_EFFICIENCY_GUIDE.md +199 -0
  11. package/doc/NAPI-SETUP.md +294 -0
  12. package/doc/PREBUILDS.md +180 -0
  13. package/doc/README_eventlogasync.md +134 -0
  14. package/doc/RESUMABLE_READER.md +250 -0
  15. package/doc/USAGE.md +527 -0
  16. package/index.js +202 -25
  17. package/native/eventlogasync.cc +687 -0
  18. package/package.json +37 -9
  19. package/prebuilds/metadata.json +24 -0
  20. package/prebuilds/win32-x64/eventlog.node +0 -0
  21. package/prebuilds/win32-x64/eventlogasync.node +0 -0
  22. package/prebuilds/win32-x64/meta.json +20 -0
  23. package/prebuilds/win32-x64/nwinread.node +0 -0
  24. package/scripts/generate-prebuilds-advanced.js +186 -0
  25. package/scripts/generate-prebuilds.js +86 -0
  26. package/scripts/prebuilds/win32-x64/meta.json +20 -0
  27. package/scripts/verify-setup.js +2 -1
  28. package/test/README.md +105 -0
  29. package/test/example_async.js +107 -0
  30. package/test/example_sync.js +76 -0
  31. package/test/test_beginning_mode.js +40 -0
  32. package/test/test_build_version.js +46 -0
  33. package/test/test_callback_simple.js +46 -0
  34. package/test/test_modes_comparison.js +74 -0
  35. package/test/test_watermark_realistic.js +75 -0
  36. package/test/test_watermark_specific.js +88 -0
  37. package/test/test_wrapper_vs_native.js +58 -0
  38. package/test/verify_sync_events.js +19 -0
  39. package/CHANGES.md +0 -120
  40. package/NAPI-SETUP.md +0 -142
  41. package/test.js +0 -34
package/README.md CHANGED
@@ -1,152 +1,835 @@
1
1
  # nwinread - Windows Event Log Reader
2
2
 
3
- A native Node.js module for reading Windows event logs using the Windows Event Log API.
4
-
5
- **✨ Features:**
6
- - **Universal Binaries**: Built with N-API for compatibility across Node.js versions
7
- - **Precompiled Binaries**: No compilation needed on installation
8
- - **High Performance**: Native C++ implementation using Windows Event Log API
9
- - **Event Filtering**: Filter events by Event ID for efficient processing
10
- - **Multiple Read Modes**: Read from beginning, end, or specific position
3
+ [![npm version](https://img.shields.io/npm/v/nwinread.svg)](https://www.npmjs.com/package/nwinread)
4
+ [![npm downloads](https://img.shields.io/npm/dm/nwinread.svg)](https://www.npmjs.com/package/nwinread)
5
+ [![license](https://img.shields.io/npm/l/nwinread.svg)](https://github.com/solzimer/nwinread/blob/main/LICENSE)
6
+ [![Node.js version](https://img.shields.io/node/v/nwinread.svg)](https://www.npmjs.com/package/nwinread)
7
+
8
+ A high-performance native Node.js module for reading Windows event logs using the Windows Event Log API with both synchronous and asynchronous support.
9
+
10
+ ## Table of Contents
11
+
12
+ - [Features](#features)
13
+ - [Requirements](#requirements)
14
+ - [Installation](#installation)
15
+ - [Quick Start](#quick-start)
16
+ - [API Reference](#api-reference)
17
+ - [Common Use Cases](#common-use-cases)
18
+ - [Performance & Best Practices](#performance--best-practices)
19
+ - [Examples & Testing](#examples--testing)
20
+ - [Error Handling](#error-handling)
21
+ - [Troubleshooting](#troubleshooting)
22
+ - [Contributing](#contributing)
23
+ - [License](#license)
24
+
25
+ ## Features
26
+
27
+ - **đŸ”Ĩ Asynchronous Subscriptions**: Real-time event monitoring with zero polling
28
+ - **📚 Synchronous Reading**: One-time queries for batch processing
29
+ - **đŸŽ¯ Smart Positioning**: Read from beginning, end, or specific Record ID (watermarks)
30
+ - **⚡ Event Filtering**: Filter by Event IDs for efficient processing
31
+ - **📋 Bookmark Support**: Resume from exact positions using Windows Event Log bookmarks
32
+ - **đŸ›Ąī¸ Cross-Version Compatible**: Built with N-API for all Node.js versions 16+
33
+ - **🚀 Precompiled Binaries**: No compilation needed for standard installations
11
34
 
12
35
  ## Requirements
13
36
 
14
- - Windows Vista/7/8/10/11 or Windows Server 2008/2012/2016/2019/2022
15
- - Node.js (version 16 or higher)
16
-
17
- **Build requirements (only if compiling from source):**
18
- - Visual Studio Build Tools or Visual Studio Community
19
- - Python (for node-gyp)
37
+ - **Windows**: Vista/7/8/10/11 or Server 2008/2012/2016/2019/2022
38
+ - **Node.js**: 16+ (recommended for precompiled binaries)
39
+ - **Permissions**: Administrator privileges required for Security log only
20
40
 
21
41
  ## Installation
22
42
 
23
- ### From npm (recommended - includes precompiled binaries)
24
-
25
43
  ```bash
26
44
  npm install nwinread
27
45
  ```
28
46
 
29
- ### From source (for development)
47
+ ✅ **Installs instantly** using precompiled binaries for Node.js 16+
30
48
 
31
- ```bash
32
- git clone https://github.com/solzimer/nwinread.git
33
- cd nwinread
34
- npm install
35
- npm run build
49
+ ## Quick Start
50
+
51
+ ### Synchronous Reading (One-time queries)
52
+
53
+ ```javascript
54
+ const eventLog = require('nwinread');
55
+
56
+ // Read recent events
57
+ const recent = eventLog.readEvents(
58
+ "System", // Channel
59
+ eventLog.START_MODE.END, // From end
60
+ 0, // Watermark (ignored for END mode)
61
+ 10 // Max events
62
+ );
63
+
64
+ console.log(`Found ${recent.records.length} recent events`);
65
+ recent.records.forEach(event => {
66
+ console.log(`Record ID: ${event.recordId}`);
67
+ console.log(`Event XML: ${event.xml.substring(0, 100)}...`);
68
+ });
36
69
  ```
37
70
 
38
- ### Manual compilation
71
+ ### Asynchronous Monitoring (Real-time)
39
72
 
40
- ```bash
41
- # Install dependencies
42
- npm install
73
+ ```javascript
74
+ const eventLog = require('nwinread');
75
+
76
+ // Monitor new events in real-time
77
+ const subscription = eventLog.subscribeFromEnd(
78
+ 'Application',
79
+ (event) => {
80
+ console.log(`🆕 New event: ${event.recordId}`);
81
+ console.log(` XML: ${event.xml.substring(0, 150)}...`);
82
+ },
83
+ (error) => {
84
+ console.error(`❌ Error: ${error.message}`);
85
+ },
86
+ [1000, 1001] // Optional: filter by Event IDs
87
+ );
43
88
 
44
- # Build native module
45
- npm run build
89
+ console.log(`✅ Monitoring started. Subscription ID: ${subscription.id}`);
46
90
 
47
- # Or build precompiled binaries
48
- npm run prebuildify
91
+ // Stop monitoring after 30 seconds
92
+ setTimeout(() => {
93
+ subscription.unsubscribe();
94
+ console.log('✅ Monitoring stopped');
95
+ }, 30000);
49
96
  ```
50
97
 
51
- ## Usage
98
+ ## API Reference
99
+
100
+ ### Reading Modes
101
+
102
+ | Mode | Value | Description | Synchronous | Asynchronous |
103
+ |------|-------|-------------|-------------|--------------|
104
+ | **BEGINNING** | `0` | Start from oldest events | `readEvents()` | `subscribeFromBeginning()` |
105
+ | **END** | `1` | Start from newest/future events | `readEvents()` | `subscribeFromEnd()` |
106
+ | **WATERMARK** | `2` | Start from specific Record ID | `readEvents()` | `subscribeFromWatermark()` |
107
+
108
+ ### Synchronous API
109
+
110
+ #### `readEvents(channel, mode, watermark, maxEvents, eventIds)`
52
111
 
53
112
  ```javascript
54
- const nwinread = require('nwinread');
113
+ const eventLog = require('nwinread');
55
114
 
56
- // Read events from the end of the log (all events)
57
- const result = nwinread.readEvents(
58
- 'System', // Channel (System, Application, Security, etc.)
59
- nwinread.START_MODE.END, // Read mode
60
- 0, // Watermark (for WATERMARK mode)
61
- 10 // Maximum number of events
115
+ // Basic syntax
116
+ eventLog.readEvents(channel, mode, watermark, maxEvents, eventIds)
117
+
118
+ // Read from beginning
119
+ const oldest = eventLog.readEvents(
120
+ "Application",
121
+ eventLog.START_MODE.BEGINNING,
122
+ 0, // Watermark ignored for BEGINNING
123
+ 50 // Max events
124
+ );
125
+
126
+ // Read recent events
127
+ const recent = eventLog.readEvents(
128
+ "System",
129
+ eventLog.START_MODE.END,
130
+ 0, // Watermark ignored for END
131
+ 20
132
+ );
133
+
134
+ // Read from specific Record ID
135
+ const fromPoint = eventLog.readEvents(
136
+ "Application",
137
+ eventLog.START_MODE.WATERMARK,
138
+ 50000, // Start from Record ID 50000
139
+ 25
62
140
  );
63
141
 
64
- // Read events with specific ID filter
65
- const filteredResult = nwinread.readEvents(
66
- 'System', // Channel
67
- nwinread.START_MODE.BEGINNING, // Read mode
68
- 0, // Watermark
69
- 20, // Maximum number of events
70
- [7045, 7034, 7036] // Event IDs filter (optional)
142
+ // Read with Event ID filter
143
+ const filtered = eventLog.readEvents(
144
+ "System",
145
+ eventLog.START_MODE.END,
146
+ 0,
147
+ 30,
148
+ [1074, 6005, 6006] // Only these Event IDs
149
+ );
150
+ ```
151
+
152
+ **Parameters:**
153
+ - `channel` (string): Event log channel ("System", "Application", "Security", etc.)
154
+ - `mode` (number): Reading mode (0=BEGINNING, 1=END, 2=WATERMARK)
155
+ - `watermark` (number): Starting Record ID for WATERMARK mode (ignored for other modes)
156
+ - `maxEvents` (number): Maximum events to return
157
+ - `eventIds` (array, optional): Array of Event IDs to filter by
158
+
159
+ **Returns:** Object with `records` array and `lastRecordId`
160
+
161
+ ### Asynchronous API
162
+
163
+ #### `subscribe(channel, watermark, onEvent, onError, eventIds, mode)`
164
+
165
+ **Core subscription method with precise control:**
166
+
167
+ ```javascript
168
+ const subscription = eventLog.subscribe(
169
+ 'Application', // channel
170
+ 48000, // watermark (Record ID to start from)
171
+ (event) => { // onEvent callback
172
+ console.log(`Event: ${event.recordId}`);
173
+ // event.recordId - Record ID number
174
+ // event.xml - Full event XML
175
+ },
176
+ (error) => { // onError callback
177
+ console.log(`Error: ${error.message}`);
178
+ },
179
+ [1000, 1001], // eventIds filter (optional, null for all)
180
+ eventLog.START_MODE.WATERMARK // mode (0=BEGINNING, 1=END, 2=WATERMARK)
71
181
  );
72
182
 
73
- console.log(`Found ${result.records.length} events`);
74
- console.log(`Last Record ID: ${result.lastRecordId}`);
183
+ console.log(`Subscription ID: ${subscription.id}`);
184
+
185
+ // Always cleanup when done
186
+ subscription.unsubscribe();
187
+ ```
188
+
189
+ #### Helper Methods (Recommended)
190
+
191
+ **`subscribeFromEnd(channel, onEvent, onError, eventIds)`**
75
192
 
76
- console.log(`Filtered events: ${filteredResult.records.length}`);
193
+ Monitor new events only (future events):
77
194
 
78
- // Process events
79
- result.records.forEach((event, index) => {
80
- console.log(`Event ${index + 1}:`);
81
- console.log(` Record ID: ${event.recordId}`);
82
- console.log(` XML: ${event.xml.substring(0, 100)}...`);
195
+ ```javascript
196
+ const subscription = eventLog.subscribeFromEnd(
197
+ 'System',
198
+ (event) => console.log(`New: ${event.recordId}`),
199
+ (error) => console.error(error.message),
200
+ [1074, 6005] // Optional Event ID filter
201
+ );
202
+ ```
203
+
204
+ **`subscribeFromBeginning(channel, onEvent, onError, eventIds)`**
205
+
206
+ Process all historical events + monitor new events:
207
+
208
+ ```javascript
209
+ const subscription = eventLog.subscribeFromBeginning(
210
+ 'Application',
211
+ (event) => console.log(`Historical/New: ${event.recordId}`),
212
+ (error) => console.error(error.message)
213
+ );
214
+ ```
215
+
216
+ **`subscribeFromWatermark(channel, watermark, onEvent, onError, eventIds)`**
217
+
218
+ Resume from specific Record ID:
219
+
220
+ ```javascript
221
+ const subscription = eventLog.subscribeFromWatermark(
222
+ 'Application',
223
+ 50000, // Start from Record ID 50000
224
+ (event) => console.log(`From 50k: ${event.recordId}`),
225
+ (error) => console.error(error.message)
226
+ );
227
+ ```
228
+
229
+ #### EventLogSubscription Object
230
+
231
+ All subscription methods return an `EventLogSubscription` object:
232
+
233
+ ```javascript
234
+ const subscription = eventLog.subscribeFromEnd('System', onEvent, onError);
235
+
236
+ // Properties
237
+ console.log(subscription.id); // Subscription ID (number)
238
+ console.log(subscription.channel); // Channel name (string)
239
+ console.log(subscription.watermark); // Starting watermark (number)
240
+ console.log(subscription.mode); // Reading mode (number)
241
+
242
+ // Methods
243
+ subscription.isActive(); // Returns true if subscription is active
244
+ subscription.getLastRecordId(); // Get last processed Record ID
245
+ subscription.unsubscribe(); // Stop subscription and clean up
246
+ ```
247
+
248
+ #### EventEmitter Pattern
249
+
250
+ ```javascript
251
+ const emitter = eventLog.createEventEmitter('System', {
252
+ mode: eventLog.START_MODE.END,
253
+ watermark: 0,
254
+ eventIds: [1074, 6005]
255
+ });
256
+
257
+ emitter.on('event', (event) => {
258
+ console.log(`Event: ${event.recordId}`);
259
+ });
260
+
261
+ emitter.on('error', (error) => {
262
+ console.error(`Error: ${error.message}`);
263
+ });
264
+
265
+ // Cleanup
266
+ emitter.unsubscribe();
267
+ ```
268
+
269
+ ## Common Use Cases
270
+
271
+ ### Real-time System Monitoring
272
+
273
+ ```javascript
274
+ const eventLog = require('nwinread');
275
+
276
+ // Monitor system shutdown/startup events
277
+ const systemMonitor = eventLog.subscribeFromEnd("System",
278
+ (event) => {
279
+ // Extract Event ID from XML
280
+ const eventIdMatch = event.xml.match(/<EventID.*?>(\d+)<\/EventID>/);
281
+ const eventId = eventIdMatch ? eventIdMatch[1] : 'unknown';
282
+
283
+ if (eventId === '1074') {
284
+ console.log(`🔄 System shutdown initiated - Record ID: ${event.recordId}`);
285
+ } else if (eventId === '6005') {
286
+ console.log(`✅ Event Log service started - Record ID: ${event.recordId}`);
287
+ }
288
+ },
289
+ (error) => console.error(`❌ Monitor error: ${error.message}`),
290
+ [1074, 6005, 6006] // Filter critical system events
291
+ );
292
+
293
+ // Keep monitoring until interrupted
294
+ process.on('SIGINT', () => {
295
+ console.log('\n🛑 Stopping system monitor...');
296
+ systemMonitor.unsubscribe();
297
+ process.exit(0);
83
298
  });
84
299
  ```
85
300
 
86
- ## Read modes
301
+ ### Application Error Monitoring
302
+
303
+ ```javascript
304
+ // Monitor ALL application events (historical + new)
305
+ const appMonitor = eventLog.subscribeFromBeginning("Application",
306
+ (event) => {
307
+ // Look for error patterns in the XML
308
+ if (event.xml.includes('Error') || event.xml.includes('Exception')) {
309
+ console.log(`🚨 Application error detected:`);
310
+ console.log(` Record ID: ${event.recordId}`);
311
+ console.log(` Time: ${new Date().toISOString()}`);
312
+ console.log(` Preview: ${event.xml.substring(0, 200)}...`);
313
+ }
314
+ },
315
+ (error) => console.error(`❌ App monitor error: ${error.message}`)
316
+ );
317
+
318
+ // Process for 1 minute then stop
319
+ setTimeout(() => {
320
+ appMonitor.unsubscribe();
321
+ console.log('✅ Application monitoring completed');
322
+ }, 60000);
323
+ ```
324
+
325
+ ### Resumable Processing with Watermarks
87
326
 
88
- - `START_MODE.BEGINNING` (0): Read from the beginning of the log
89
- - `START_MODE.END` (1): Read from the end of the log
90
- - `START_MODE.WATERMARK` (2): Read from a specific Record ID
327
+ ```javascript
328
+ // Save last processed Record ID to resume later
329
+ let lastProcessedId = 48000; // Load from database/file in real usage
330
+
331
+ const processor = eventLog.subscribeFromWatermark("Application", lastProcessedId,
332
+ (event) => {
333
+ console.log(`📝 Processing event: ${event.recordId}`);
334
+
335
+ // Your business logic here
336
+ processEvent(event);
337
+
338
+ // Update watermark for next restart
339
+ lastProcessedId = event.recordId;
340
+ // Save to database/file in real usage
341
+
342
+ console.log(`✅ Processed ${event.recordId}, next watermark: ${lastProcessedId}`);
343
+ },
344
+ (error) => {
345
+ console.error(`❌ Processing error: ${error.message}`);
346
+ // Implement retry logic here
347
+ }
348
+ );
91
349
 
92
- ## API
350
+ function processEvent(event) {
351
+ // Your event processing logic
352
+ // Database inserts, API calls, etc.
353
+ }
354
+ ```
93
355
 
94
- ### readEvents(channel, mode, watermark, maxEvents, eventIds)
356
+ ### Historical Analysis
95
357
 
96
- - **channel** (string): Event channel name (e.g.: 'System', 'Application', 'Security')
97
- - **mode** (number): Read mode (use START_MODE constants)
98
- - **watermark** (number): Record ID to start from (only for WATERMARK mode)
99
- - **maxEvents** (number): Maximum number of events to read (1-10000)
100
- - **eventIds** (array|null, optional): Array of Event IDs to filter. If `null`, `undefined`, or empty array, no filter is applied
358
+ ```javascript
359
+ const eventLog = require('nwinread');
360
+
361
+ // Analyze large batch of historical events
362
+ function analyzeEvents() {
363
+ console.log('📊 Starting historical analysis...');
364
+
365
+ const events = eventLog.readEvents(
366
+ "System",
367
+ eventLog.START_MODE.BEGINNING,
368
+ 0,
369
+ 1000 // Analyze last 1000 events
370
+ );
371
+
372
+ let errorCount = 0;
373
+ let warningCount = 0;
374
+ let infoCount = 0;
375
+
376
+ events.records.forEach(event => {
377
+ // Simple level detection (Windows Event Levels: 1=Critical, 2=Error, 3=Warning, 4=Info)
378
+ if (event.xml.includes('Level>1<') || event.xml.includes('Level>2<')) {
379
+ errorCount++;
380
+ } else if (event.xml.includes('Level>3<')) {
381
+ warningCount++;
382
+ } else if (event.xml.includes('Level>4<')) {
383
+ infoCount++;
384
+ }
385
+ });
386
+
387
+ console.log(`📈 Analysis Results:`);
388
+ console.log(` Total events: ${events.records.length}`);
389
+ console.log(` Errors: ${errorCount}`);
390
+ console.log(` Warnings: ${warningCount}`);
391
+ console.log(` Information: ${infoCount}`);
392
+ console.log(` Last Record ID: ${events.lastRecordId}`);
393
+ }
394
+
395
+ analyzeEvents();
396
+ ```
101
397
 
102
- **Returns:** Object with properties:
103
- - `records`: Array of events with `xml` and `recordId` properties
104
- - `lastRecordId`: ID of the last processed record
398
+ ## Event Channels & Common Event IDs
105
399
 
106
- ### Event ID filtering examples
400
+ ### System Channel (No admin required)
107
401
 
108
402
  ```javascript
109
- // No filter - all events
110
- const allEvents = nwinread.readEvents('System', nwinread.START_MODE.BEGINNING, 0, 10);
403
+ const SYSTEM_EVENTS = {
404
+ SHUTDOWN: 1074, // System shutdown initiated
405
+ UNEXPECTED_SHUTDOWN: 41, // System rebooted unexpectedly
406
+ EVENTLOG_START: 6005, // Event Log service started
407
+ EVENTLOG_STOP: 6006, // Event Log service stopped
408
+ TIME_CHANGE: 4621, // System time was changed
409
+ POWER_SLEEP: 42, // System entering sleep
410
+ POWER_WAKE: 107 // System wake from sleep
411
+ };
412
+
413
+ // Monitor critical system events
414
+ const systemSub = eventLog.subscribeFromEnd("System", onEvent, onError,
415
+ [1074, 41, 6005, 6006]
416
+ );
417
+ ```
111
418
 
112
- // Filter only Windows service events
113
- const serviceEvents = nwinread.readEvents('System', nwinread.START_MODE.BEGINNING, 0, 10, [7034, 7035, 7036, 7040, 7045]);
419
+ ### Application Channel (No admin required)
114
420
 
115
- // Filter specific critical events
116
- const criticalEvents = nwinread.readEvents('System', nwinread.START_MODE.BEGINNING, 0, 10, [1000, 1001, 1002]);
421
+ ```javascript
422
+ const APPLICATION_EVENTS = {
423
+ ERROR: 1000, // Application error
424
+ WARNING: 1001, // Application warning
425
+ INFORMATION: 1002 // Application information
426
+ // Note: Most applications use custom Event IDs
427
+ };
428
+
429
+ // Monitor all application events
430
+ const appSub = eventLog.subscribeFromEnd("Application", onEvent, onError);
431
+ ```
117
432
 
118
- // Empty array = no filter (equivalent to null)
119
- const noFilter = nwinread.readEvents('System', nwinread.START_MODE.BEGINNING, 0, 10, []);
433
+ ### Security Channel (Admin required)
434
+
435
+ ```javascript
436
+ const SECURITY_EVENTS = {
437
+ LOGIN_SUCCESS: 4624, // Successful logon
438
+ LOGIN_FAILURE: 4625, // Failed logon
439
+ LOGOUT: 4634, // Account logged off
440
+ ACCOUNT_LOCKED: 4740, // Account lockout
441
+ PRIVILEGE_USE: 4673, // Privileged service called
442
+ OBJECT_ACCESS: 4656 // Handle to object requested
443
+ };
444
+
445
+ // Monitor security events (requires admin privileges)
446
+ const securitySub = eventLog.subscribeFromEnd("Security", onEvent, onError,
447
+ [4624, 4625, 4634]
448
+ );
449
+ ```
450
+
451
+ ## Performance & Best Practices
452
+
453
+ ### ⚡ High Performance Tips
454
+
455
+ ```javascript
456
+ // 1. Use Event ID filters to reduce processing
457
+ const filtered = eventLog.subscribeFromEnd("System", onEvent, onError, [1074, 6005]);
458
+
459
+ // 2. Limit synchronous batch sizes
460
+ const limited = eventLog.readEvents("Application", eventLog.START_MODE.END, 0, 100);
461
+
462
+ // 3. Process events immediately, don't accumulate
463
+ const efficient = eventLog.subscribeFromEnd("Application",
464
+ (event) => {
465
+ // Process immediately
466
+ sendToDatabase(event);
467
+ // Don't store in arrays/collections
468
+ },
469
+ onError
470
+ );
120
471
  ```
121
472
 
122
- ### Common Event IDs
473
+ ### đŸ›Ąī¸ Reliable Processing
123
474
 
124
- - **7034**: Service crashed
125
- - **7035**: Service start/stop control sent
126
- - **7036**: Service started/stopped
127
- - **7040**: Service startup type changed
128
- - **7045**: New service installed
129
- - **1000**: Application error
130
- - **1001**: Application hang
131
- - **4624**: Successful account logon (Security log)
132
- - **4625**: Failed account logon (Security log)
475
+ ```javascript
476
+ // 1. Always handle errors gracefully
477
+ const reliable = eventLog.subscribeFromEnd("Application",
478
+ (event) => {
479
+ try {
480
+ processEvent(event);
481
+ } catch (error) {
482
+ console.error(`Event processing error: ${error.message}`);
483
+ // Log error but continue processing other events
484
+ }
485
+ },
486
+ (error) => {
487
+ console.error(`Subscription error: ${error.message}`);
488
+
489
+ // Implement automatic recovery
490
+ setTimeout(() => {
491
+ console.log('🔄 Attempting to restart subscription...');
492
+ createNewSubscription();
493
+ }, 5000);
494
+ }
495
+ );
133
496
 
134
- ## Testing
497
+ // 2. Clean shutdown handling
498
+ process.on('SIGINT', () => {
499
+ console.log('🛑 Gracefully shutting down...');
500
+ reliable.unsubscribe();
501
+ process.exit(0);
502
+ });
503
+
504
+ // 3. Save watermarks for recovery
505
+ let lastProcessedId = loadWatermarkFromFile();
506
+ const persistent = eventLog.subscribeFromWatermark("Application", lastProcessedId,
507
+ (event) => {
508
+ processEvent(event);
509
+ lastProcessedId = event.recordId;
510
+ saveWatermarkToFile(lastProcessedId); // Persist progress
511
+ },
512
+ onError
513
+ );
514
+ ```
515
+
516
+ ### 💾 Memory Management
517
+
518
+ ```javascript
519
+ // 1. Unsubscribe when done
520
+ const subscription = eventLog.subscribeFromEnd("System", onEvent, onError);
521
+
522
+ // Always cleanup
523
+ setTimeout(() => {
524
+ subscription.unsubscribe(); // Frees native resources
525
+ }, 30000);
526
+
527
+ // 2. Process events in streams, not collections
528
+ // ❌ Don't accumulate events
529
+ const events = [];
530
+ const badSub = eventLog.subscribeFromEnd("Application",
531
+ (event) => events.push(event), // Memory leak!
532
+ onError
533
+ );
534
+
535
+ // ✅ Process immediately
536
+ const goodSub = eventLog.subscribeFromEnd("Application",
537
+ (event) => {
538
+ processEventImmediately(event); // No accumulation
539
+ },
540
+ onError
541
+ );
542
+ ```
543
+
544
+ ## Examples & Testing
545
+
546
+ The repository includes comprehensive examples in the `test/` directory:
135
547
 
136
548
  ```bash
549
+ # Test synchronous reading
550
+ node test/example_sync.js
551
+
552
+ # Test asynchronous monitoring
553
+ node test/example_async.js
554
+
555
+ # Test watermark functionality
556
+ node test/test_watermark_realistic.js
557
+
558
+ # Test all reading modes
137
559
  npm test
138
560
  ```
139
561
 
562
+ ### Example Files
563
+
564
+ - `test/example_sync.js` - Synchronous reading examples
565
+ - `test/example_async.js` - Asynchronous monitoring examples
566
+ - `test/test_watermark_realistic.js` - Watermark/bookmark functionality
567
+ - `test/test_watermark_realistic.js` - Find valid Record IDs for testing
568
+
569
+ ## Error Handling
570
+
571
+ ### Common Errors
572
+
573
+ ```javascript
574
+ // Channel not found
575
+ try {
576
+ const sub = eventLog.subscribeFromEnd("InvalidChannel", onEvent, onError);
577
+ } catch (error) {
578
+ if (error.code === 15007) {
579
+ console.log('❌ Channel not found - check channel name');
580
+ }
581
+ }
582
+
583
+ // Permission denied (for Security channel)
584
+ try {
585
+ const sub = eventLog.subscribeFromEnd("Security", onEvent, onError);
586
+ } catch (error) {
587
+ if (error.message.includes('access')) {
588
+ console.log('❌ Admin privileges required for Security log');
589
+ }
590
+ }
591
+
592
+ // Invalid Record ID
593
+ try {
594
+ const sub = eventLog.subscribeFromWatermark("Application", 999999999, onEvent, onError);
595
+ } catch (error) {
596
+ if (error.code === 15027) {
597
+ console.log('❌ Invalid Record ID - may be beyond available range');
598
+ }
599
+ }
600
+ ```
601
+
602
+ ### Error Codes
603
+
604
+ | Code | Meaning | Solution |
605
+ |------|---------|----------|
606
+ | 15007 | Channel not found | Check channel name ("System", "Application", etc.) |
607
+ | 15027 | Invalid query/Record ID | Use valid Record ID within available range |
608
+ | 87 | Invalid parameter | Check parameter types and values |
609
+ | 5 | Access denied | Run as administrator for Security log |
610
+
611
+ ## Advanced Usage
612
+
613
+ ### Custom Event Processing Pipeline
614
+
615
+ ```javascript
616
+ const eventLog = require('nwinread');
617
+
618
+ class EventProcessor {
619
+ constructor() {
620
+ this.stats = { processed: 0, errors: 0 };
621
+ this.subscription = null;
622
+ }
623
+
624
+ start() {
625
+ this.subscription = eventLog.subscribeFromEnd("Application",
626
+ (event) => this.processEvent(event),
627
+ (error) => this.handleError(error),
628
+ [1000, 1001, 1002] // Filter application events
629
+ );
630
+
631
+ console.log(`✅ Event processor started. Subscription: ${this.subscription.id}`);
632
+ }
633
+
634
+ processEvent(event) {
635
+ try {
636
+ // Parse event XML
637
+ const data = this.parseEventData(event);
638
+
639
+ // Business logic
640
+ this.handleApplicationEvent(data);
641
+
642
+ this.stats.processed++;
643
+
644
+ // Log progress
645
+ if (this.stats.processed % 100 === 0) {
646
+ console.log(`📊 Processed ${this.stats.processed} events`);
647
+ }
648
+
649
+ } catch (error) {
650
+ console.error(`❌ Processing error: ${error.message}`);
651
+ this.stats.errors++;
652
+ }
653
+ }
654
+
655
+ parseEventData(event) {
656
+ // Extract structured data from event XML
657
+ const eventIdMatch = event.xml.match(/<EventID.*?>(\d+)<\/EventID>/);
658
+ const timeMatch = event.xml.match(/<TimeCreated SystemTime='([^']+)'/);
659
+
660
+ return {
661
+ recordId: event.recordId,
662
+ eventId: eventIdMatch ? parseInt(eventIdMatch[1]) : null,
663
+ timestamp: timeMatch ? new Date(timeMatch[1]) : null,
664
+ xml: event.xml
665
+ };
666
+ }
667
+
668
+ handleApplicationEvent(data) {
669
+ switch(data.eventId) {
670
+ case 1000:
671
+ this.handleErrorEvent(data);
672
+ break;
673
+ case 1001:
674
+ this.handleWarningEvent(data);
675
+ break;
676
+ case 1002:
677
+ this.handleInfoEvent(data);
678
+ break;
679
+ default:
680
+ this.handleGenericEvent(data);
681
+ }
682
+ }
683
+
684
+ handleErrorEvent(data) {
685
+ console.log(`🚨 Application Error - Record ${data.recordId} at ${data.timestamp}`);
686
+ // Send alert, log to database, etc.
687
+ }
688
+
689
+ handleWarningEvent(data) {
690
+ console.log(`âš ī¸ Application Warning - Record ${data.recordId}`);
691
+ // Log to monitoring system
692
+ }
693
+
694
+ handleInfoEvent(data) {
695
+ console.log(`â„šī¸ Application Info - Record ${data.recordId}`);
696
+ }
697
+
698
+ handleGenericEvent(data) {
699
+ console.log(`📄 Generic event ${data.eventId} - Record ${data.recordId}`);
700
+ }
701
+
702
+ handleError(error) {
703
+ console.error(`❌ Subscription error: ${error.message}`);
704
+ this.stats.errors++;
705
+
706
+ // Implement reconnection logic
707
+ setTimeout(() => {
708
+ console.log('🔄 Attempting to restart...');
709
+ this.start();
710
+ }, 5000);
711
+ }
712
+
713
+ getStats() {
714
+ return {
715
+ ...this.stats,
716
+ isActive: this.subscription && this.subscription.isActive()
717
+ };
718
+ }
719
+
720
+ stop() {
721
+ if (this.subscription) {
722
+ this.subscription.unsubscribe();
723
+ console.log(`✅ Event processor stopped. Final stats:`, this.getStats());
724
+ }
725
+ }
726
+ }
727
+
728
+ // Usage
729
+ const processor = new EventProcessor();
730
+ processor.start();
731
+
732
+ // Stop after 60 seconds
733
+ setTimeout(() => {
734
+ processor.stop();
735
+ }, 60000);
736
+
737
+ // Graceful shutdown
738
+ process.on('SIGINT', () => {
739
+ processor.stop();
740
+ process.exit(0);
741
+ });
742
+ ```
743
+
140
744
  ## Troubleshooting
141
745
 
142
- 1. **Compilation error**: Make sure you have Visual Studio Build Tools installed
143
- 2. **Permission error**: Some logs require administrator privileges
144
- 3. **Channel not found**: Verify that the channel name is correct
746
+ ### Installation Issues
747
+
748
+ ```bash
749
+ # For Node.js 16+
750
+ npm install nwinread # Should install instantly
751
+
752
+ # If compilation is needed (Node.js < 16)
753
+ npm install --build-from-source
754
+
755
+ # Force rebuild if having issues
756
+ npm rebuild nwinread
757
+ ```
758
+
759
+ ### Runtime Issues
760
+
761
+ 1. **"Channel not found" error**
762
+ - Verify channel name: "System", "Application", "Security"
763
+ - Check Windows Event Viewer to confirm channel exists
764
+
765
+ 2. **"Access denied" for Security log**
766
+ - Run as Administrator: `Run as administrator` in Command Prompt
767
+ - Only Security log requires admin privileges
768
+
769
+ 3. **No events received in subscription**
770
+ - Check if events exist: use `readEvents()` first
771
+ - Verify Event ID filters are correct
772
+ - For Security channel: ensure admin privileges
773
+
774
+ 4. **"Invalid Record ID" for watermarks**
775
+ - Use `readEvents()` to find valid Record ID range
776
+ - Record IDs are not sequential and vary by channel
777
+
778
+ ### Debugging
779
+
780
+ ```javascript
781
+ // Enable debugging to see internal operations
782
+ const eventLog = require('nwinread');
783
+
784
+ // Test with small batch first
785
+ const test = eventLog.readEvents("Application", eventLog.START_MODE.END, 0, 5);
786
+ console.log(`Test result: ${test.records.length} events found`);
787
+
788
+ if (test.records.length > 0) {
789
+ console.log(`Record ID range: ${test.records[0].recordId} to ${test.lastRecordId}`);
790
+
791
+ // Now try subscription with known good Record ID
792
+ const sub = eventLog.subscribeFromWatermark("Application",
793
+ test.records[0].recordId,
794
+ (event) => console.log(`✅ Event: ${event.recordId}`),
795
+ (error) => console.log(`❌ Error: ${error.message}`)
796
+ );
797
+ }
798
+ ```
799
+
800
+ ## Contributing
801
+
802
+ This is a Windows-specific native module. Development requires:
803
+
804
+ - Windows development environment
805
+ - Visual Studio Build Tools
806
+ - Node.js 16+
807
+ - Windows Event Log for testing
808
+
809
+ ```bash
810
+ # Development setup
811
+ git clone https://github.com/solzimer/nwinread.git
812
+ cd nwinread
813
+ npm install
814
+ npm run rebuild
815
+
816
+ # Run tests
817
+ npm test
818
+
819
+ # Run examples
820
+ npm run examples
821
+ ```
822
+
823
+ ## Support
824
+
825
+ - 🐛 **Bug Reports**: [GitHub Issues](https://github.com/solzimer/nwinread/issues)
826
+ - 📖 **Documentation**: This README and inline code examples
827
+ - 💡 **Feature Requests**: [GitHub Issues](https://github.com/solzimer/nwinread/issues)
828
+
829
+ ## Changelog
830
+
831
+ See [GitHub Releases](https://github.com/solzimer/nwinread/releases) for version history and changes.
145
832
 
146
- ## Common channels
833
+ ## License
147
834
 
148
- - `System`: System events
149
- - `Application`: Application events
150
- - `Security`: Security events (requires admin permissions)
151
- - `Setup`: Installation events
152
- - `Microsoft-Windows-PowerShell/Operational`: PowerShell events
835
+ MIT License - see LICENSE file for details.