nwinread 1.1.1 → 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.
- package/README.md +767 -134
- package/binding.gyp +30 -0
- package/build/binding.sln +6 -0
- package/build/eventlogasync.vcxproj +148 -0
- package/build/eventlogasync.vcxproj.filters +43 -0
- package/doc/ASYNC_STATUS.md +104 -0
- package/doc/COHERENCIA_APIS.md +154 -0
- package/doc/CORRECCION_NAPI.md +74 -0
- package/doc/CPU_EFFICIENCY_GUIDE.md +199 -0
- package/doc/PREBUILDS.md +180 -0
- package/doc/README_eventlogasync.md +134 -0
- package/doc/RESUMABLE_READER.md +250 -0
- package/doc/USAGE.md +527 -0
- package/index.js +206 -5
- package/native/eventlogasync.cc +687 -0
- package/package.json +33 -6
- package/prebuilds/metadata.json +24 -0
- package/prebuilds/win32-x64/eventlog.node +0 -0
- package/prebuilds/win32-x64/eventlogasync.node +0 -0
- package/prebuilds/win32-x64/meta.json +20 -0
- package/prebuilds/win32-x64/nwinread.node +0 -0
- package/scripts/generate-prebuilds-advanced.js +186 -0
- package/scripts/generate-prebuilds.js +86 -0
- package/scripts/prebuilds/win32-x64/meta.json +20 -0
- package/test/README.md +105 -0
- package/test/example_async.js +107 -0
- package/test/example_sync.js +76 -0
- package/test/test_beginning_mode.js +40 -0
- package/test/test_build_version.js +46 -0
- package/test/test_callback_simple.js +46 -0
- package/test/test_modes_comparison.js +74 -0
- package/test/test_watermark_realistic.js +75 -0
- package/test/test_watermark_specific.js +88 -0
- package/test/test_wrapper_vs_native.js +58 -0
- package/test/verify_sync_events.js +19 -0
- package/CHANGES.md +0 -120
- package/test.js +0 -34
- /package/{CONTRIBUTING.md → doc/CONTRIBUTING.md} +0 -0
- /package/{NAPI-SETUP.md → doc/NAPI-SETUP.md} +0 -0
package/README.md
CHANGED
|
@@ -1,202 +1,835 @@
|
|
|
1
1
|
# nwinread - Windows Event Log Reader
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
-
|
|
9
|
-
|
|
10
|
-
|
|
3
|
+
[](https://www.npmjs.com/package/nwinread)
|
|
4
|
+
[](https://www.npmjs.com/package/nwinread)
|
|
5
|
+
[](https://github.com/solzimer/nwinread/blob/main/LICENSE)
|
|
6
|
+
[](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
|
|
15
|
-
- **Node.js 16
|
|
16
|
-
- **
|
|
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
|
|
17
40
|
|
|
18
|
-
|
|
41
|
+
## Installation
|
|
19
42
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
| **18.x** | ✅ **LTS** | Instant (precompiled) |
|
|
24
|
-
| **20.x** | ✅ **LTS** | Instant (precompiled) |
|
|
25
|
-
| **22.x** | ✅ **Current** | Instant (precompiled) |
|
|
26
|
-
| 10-14 | ⚠️ **EOL** | Requires build tools |
|
|
43
|
+
```bash
|
|
44
|
+
npm install nwinread
|
|
45
|
+
```
|
|
27
46
|
|
|
28
|
-
**
|
|
29
|
-
- Visual Studio Build Tools or Visual Studio Community
|
|
30
|
-
- Python (for node-gyp)
|
|
47
|
+
✅ **Installs instantly** using precompiled binaries for Node.js 16+
|
|
31
48
|
|
|
32
|
-
##
|
|
49
|
+
## Quick Start
|
|
33
50
|
|
|
34
|
-
###
|
|
51
|
+
### Synchronous Reading (One-time queries)
|
|
35
52
|
|
|
36
|
-
```
|
|
37
|
-
|
|
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
|
+
});
|
|
38
69
|
```
|
|
39
70
|
|
|
40
|
-
|
|
41
|
-
**⚠️ For Node.js 10-14**: Automatically compiles from source (requires build tools)
|
|
71
|
+
### Asynchronous Monitoring (Real-time)
|
|
42
72
|
|
|
43
|
-
|
|
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
|
+
);
|
|
44
88
|
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
89
|
+
console.log(`✅ Monitoring started. Subscription ID: ${subscription.id}`);
|
|
90
|
+
|
|
91
|
+
// Stop monitoring after 30 seconds
|
|
92
|
+
setTimeout(() => {
|
|
93
|
+
subscription.unsubscribe();
|
|
94
|
+
console.log('✅ Monitoring stopped');
|
|
95
|
+
}, 30000);
|
|
96
|
+
```
|
|
97
|
+
|
|
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)`
|
|
111
|
+
|
|
112
|
+
```javascript
|
|
113
|
+
const eventLog = require('nwinread');
|
|
114
|
+
|
|
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
|
|
140
|
+
);
|
|
141
|
+
|
|
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
|
+
);
|
|
51
150
|
```
|
|
52
|
-
# Install dependencies
|
|
53
|
-
npm install
|
|
54
151
|
|
|
55
|
-
|
|
56
|
-
|
|
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
|
|
57
158
|
|
|
58
|
-
|
|
59
|
-
|
|
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)
|
|
181
|
+
);
|
|
182
|
+
|
|
183
|
+
console.log(`Subscription ID: ${subscription.id}`);
|
|
184
|
+
|
|
185
|
+
// Always cleanup when done
|
|
186
|
+
subscription.unsubscribe();
|
|
60
187
|
```
|
|
61
188
|
|
|
62
|
-
|
|
189
|
+
#### Helper Methods (Recommended)
|
|
190
|
+
|
|
191
|
+
**`subscribeFromEnd(channel, onEvent, onError, eventIds)`**
|
|
192
|
+
|
|
193
|
+
Monitor new events only (future events):
|
|
63
194
|
|
|
64
195
|
```javascript
|
|
65
|
-
const
|
|
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)`**
|
|
66
205
|
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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)
|
|
73
213
|
);
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**`subscribeFromWatermark(channel, watermark, onEvent, onError, eventIds)`**
|
|
74
217
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
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)
|
|
82
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
|
+
```
|
|
83
247
|
|
|
84
|
-
|
|
85
|
-
console.log(`Last Record ID: ${result.lastRecordId}`);
|
|
248
|
+
#### EventEmitter Pattern
|
|
86
249
|
|
|
87
|
-
|
|
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
|
+
);
|
|
88
292
|
|
|
89
|
-
//
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
293
|
+
// Keep monitoring until interrupted
|
|
294
|
+
process.on('SIGINT', () => {
|
|
295
|
+
console.log('\n🛑 Stopping system monitor...');
|
|
296
|
+
systemMonitor.unsubscribe();
|
|
297
|
+
process.exit(0);
|
|
94
298
|
});
|
|
95
299
|
```
|
|
96
300
|
|
|
97
|
-
|
|
301
|
+
### Application Error Monitoring
|
|
98
302
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
+
);
|
|
102
317
|
|
|
103
|
-
|
|
318
|
+
// Process for 1 minute then stop
|
|
319
|
+
setTimeout(() => {
|
|
320
|
+
appMonitor.unsubscribe();
|
|
321
|
+
console.log('✅ Application monitoring completed');
|
|
322
|
+
}, 60000);
|
|
323
|
+
```
|
|
104
324
|
|
|
105
|
-
###
|
|
325
|
+
### Resumable Processing with Watermarks
|
|
106
326
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
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
|
+
);
|
|
112
349
|
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
350
|
+
function processEvent(event) {
|
|
351
|
+
// Your event processing logic
|
|
352
|
+
// Database inserts, API calls, etc.
|
|
353
|
+
}
|
|
354
|
+
```
|
|
116
355
|
|
|
117
|
-
###
|
|
356
|
+
### Historical Analysis
|
|
118
357
|
|
|
119
358
|
```javascript
|
|
120
|
-
|
|
121
|
-
|
|
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
|
+
```
|
|
122
397
|
|
|
123
|
-
|
|
124
|
-
const serviceEvents = nwinread.readEvents('System', nwinread.START_MODE.BEGINNING, 0, 10, [7034, 7035, 7036, 7040, 7045]);
|
|
398
|
+
## Event Channels & Common Event IDs
|
|
125
399
|
|
|
126
|
-
|
|
127
|
-
const criticalEvents = nwinread.readEvents('System', nwinread.START_MODE.BEGINNING, 0, 10, [1000, 1001, 1002]);
|
|
400
|
+
### System Channel (No admin required)
|
|
128
401
|
|
|
129
|
-
|
|
130
|
-
const
|
|
402
|
+
```javascript
|
|
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
|
+
);
|
|
131
417
|
```
|
|
132
418
|
|
|
133
|
-
###
|
|
419
|
+
### Application Channel (No admin required)
|
|
134
420
|
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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
|
+
```
|
|
144
432
|
|
|
145
|
-
|
|
433
|
+
### Security Channel (Admin required)
|
|
146
434
|
|
|
147
|
-
|
|
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
|
+
```
|
|
148
450
|
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
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
|
+
);
|
|
471
|
+
```
|
|
472
|
+
|
|
473
|
+
### 🛡️ Reliable Processing
|
|
474
|
+
|
|
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
|
+
);
|
|
155
496
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
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
|
+
);
|
|
159
514
|
```
|
|
160
515
|
|
|
161
|
-
###
|
|
516
|
+
### 💾 Memory Management
|
|
162
517
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
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
|
+
);
|
|
167
534
|
|
|
168
|
-
|
|
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
|
|
169
545
|
|
|
170
|
-
|
|
546
|
+
The repository includes comprehensive examples in the `test/` directory:
|
|
171
547
|
|
|
172
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
|
|
173
559
|
npm test
|
|
174
560
|
```
|
|
175
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
|
+
|
|
176
744
|
## Troubleshooting
|
|
177
745
|
|
|
178
|
-
###
|
|
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
|
+
```
|
|
179
822
|
|
|
180
|
-
|
|
181
|
-
- ✅ Update to Node.js 16+ for precompiled binaries
|
|
182
|
-
- ✅ Check `npm ls nwinread` shows correct version
|
|
183
|
-
- ✅ Try `npm cache clean --force && npm install`
|
|
823
|
+
## Support
|
|
184
824
|
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
- ✅ Check administrator privileges if needed
|
|
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)
|
|
189
828
|
|
|
190
|
-
|
|
829
|
+
## Changelog
|
|
191
830
|
|
|
192
|
-
|
|
193
|
-
4. **Channel not found**: Verify that the channel name is correct
|
|
194
|
-
5. **Old Node.js**: Update to Node.js 16+ for best experience
|
|
831
|
+
See [GitHub Releases](https://github.com/solzimer/nwinread/releases) for version history and changes.
|
|
195
832
|
|
|
196
|
-
##
|
|
833
|
+
## License
|
|
197
834
|
|
|
198
|
-
-
|
|
199
|
-
- `Application`: Application events
|
|
200
|
-
- `Security`: Security events (requires admin permissions)
|
|
201
|
-
- `Setup`: Installation events
|
|
202
|
-
- `Microsoft-Windows-PowerShell/Operational`: PowerShell events
|
|
835
|
+
MIT License - see LICENSE file for details.
|