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.
- package/README.md +779 -96
- 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/CONTRIBUTING.md +64 -0
- package/doc/CORRECCION_NAPI.md +74 -0
- package/doc/CPU_EFFICIENCY_GUIDE.md +199 -0
- package/doc/NAPI-SETUP.md +294 -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 +202 -25
- package/native/eventlogasync.cc +687 -0
- package/package.json +37 -9
- 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/scripts/verify-setup.js +2 -1
- 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/NAPI-SETUP.md +0 -142
- package/test.js +0 -34
package/doc/USAGE.md
ADDED
|
@@ -0,0 +1,527 @@
|
|
|
1
|
+
# nwinread - Native Windows Event Log Reader
|
|
2
|
+
|
|
3
|
+
Lector nativo de Event Log de Windows para Node.js con soporte para lectura síncrona (polling) y asíncrona (suscripciones).
|
|
4
|
+
|
|
5
|
+
## ✨ Novedades v1.1.1
|
|
6
|
+
|
|
7
|
+
- **API Coherente**: Modos de lectura consistentes entre sync y async
|
|
8
|
+
- **Nuevos Métodos**: `subscribeFromBeginning()`, `subscribeFromEnd()`, `subscribeFromWatermark()`
|
|
9
|
+
- **Improved EventEmitter**: Soporte para modos en `createEventEmitter()`
|
|
10
|
+
- **Compatibilidad 100%**: Código existente sigue funcionando sin cambios
|
|
11
|
+
|
|
12
|
+
Ver [COHERENCIA_APIS.md](COHERENCIA_APIS.md) para detalles completos.
|
|
13
|
+
|
|
14
|
+
## Instalación y Compilación
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
# Instalar dependencias
|
|
18
|
+
npm install
|
|
19
|
+
|
|
20
|
+
# Compilar módulos nativos
|
|
21
|
+
npm run rebuild
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Uso Básico
|
|
25
|
+
|
|
26
|
+
### API Síncrona (Polling)
|
|
27
|
+
|
|
28
|
+
```javascript
|
|
29
|
+
const eventLog = require('nwinread');
|
|
30
|
+
|
|
31
|
+
// Leer eventos más recientes
|
|
32
|
+
const result = eventLog.readEvents(
|
|
33
|
+
"System", // Canal
|
|
34
|
+
eventLog.START_MODE.END, // Desde el final
|
|
35
|
+
0, // Watermark
|
|
36
|
+
100, // Máximo eventos
|
|
37
|
+
[1074, 1076] // Solo shutdown/restart
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
console.log(`Eventos: ${result.records.length}`);
|
|
41
|
+
console.log(`Último ID: ${result.lastRecordId}`);
|
|
42
|
+
|
|
43
|
+
result.records.forEach(event => {
|
|
44
|
+
console.log(`ID: ${event.recordId}, XML: ${event.xml}`);
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
### API Asíncrona (Suscripciones)
|
|
49
|
+
|
|
50
|
+
```javascript
|
|
51
|
+
const eventLog = require('nwinread');
|
|
52
|
+
|
|
53
|
+
// Crear suscripción con callbacks
|
|
54
|
+
const subscription = eventLog.subscribe(
|
|
55
|
+
"System", // Canal
|
|
56
|
+
0, // Watermark (0 = solo futuros)
|
|
57
|
+
(event) => { // Callback de eventos
|
|
58
|
+
console.log(`Evento: ${event.recordId}`);
|
|
59
|
+
console.log(`XML: ${event.xml}`);
|
|
60
|
+
},
|
|
61
|
+
(error) => { // Callback de errores
|
|
62
|
+
console.error(`Error: ${error.message}`);
|
|
63
|
+
},
|
|
64
|
+
[6005, 6006] // Solo eventos de inicio/fin de sesión
|
|
65
|
+
);
|
|
66
|
+
|
|
67
|
+
// Gestionar la suscripción
|
|
68
|
+
console.log(`ID: ${subscription.id}`);
|
|
69
|
+
console.log(`Último Record ID: ${subscription.getLastRecordId()}`);
|
|
70
|
+
|
|
71
|
+
// Cancelar cuando termine
|
|
72
|
+
subscription.unsubscribe();
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
### API EventEmitter (Conveniente)
|
|
76
|
+
|
|
77
|
+
```javascript
|
|
78
|
+
const eventLog = require('nwinread');
|
|
79
|
+
|
|
80
|
+
// Crear EventEmitter
|
|
81
|
+
const emitter = eventLog.createEventEmitter('Application', {
|
|
82
|
+
watermark: 0,
|
|
83
|
+
eventIds: [1000, 1001, 1002]
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
// Escuchar eventos
|
|
87
|
+
emitter.on('event', (event) => {
|
|
88
|
+
console.log(`App Event: ${event.recordId}`);
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
emitter.on('error', (error) => {
|
|
92
|
+
console.error(`Error: ${error.message}`);
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
// Limpiar
|
|
96
|
+
emitter.unsubscribe();
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## API Coherente (Modos Síncronos/Asíncronos)
|
|
100
|
+
|
|
101
|
+
New! APIs que mantienen coherencia entre operaciones síncronas y asíncronas:
|
|
102
|
+
|
|
103
|
+
### Métodos Helper Específicos
|
|
104
|
+
|
|
105
|
+
```javascript
|
|
106
|
+
const eventLog = require('nwinread');
|
|
107
|
+
|
|
108
|
+
// 1. Procesar TODOS los eventos (equivale a mode=BEGINNING)
|
|
109
|
+
const allEvents = eventLog.subscribeFromBeginning("System",
|
|
110
|
+
(event) => console.log(`Historical: ${event.recordId}`),
|
|
111
|
+
(error) => console.error(error.message)
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
// 2. Solo eventos FUTUROS (equivale a mode=END)
|
|
115
|
+
const futureEvents = eventLog.subscribeFromEnd("Security",
|
|
116
|
+
(event) => console.log(`New: ${event.recordId}`),
|
|
117
|
+
(error) => console.error(error.message)
|
|
118
|
+
);
|
|
119
|
+
|
|
120
|
+
// 3. Desde un PUNTO ESPECÍFICO (equivale a mode=WATERMARK)
|
|
121
|
+
const fromPoint = eventLog.subscribeFromWatermark("Application", 50000,
|
|
122
|
+
(event) => console.log(`From 50k: ${event.recordId}`),
|
|
123
|
+
(error) => console.error(error.message)
|
|
124
|
+
);
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
### subscribe() con Opciones de Modo
|
|
128
|
+
|
|
129
|
+
```javascript
|
|
130
|
+
// Nuevo formato con modo explícito
|
|
131
|
+
const subscription = eventLog.subscribe("System", {
|
|
132
|
+
mode: eventLog.START_MODE.BEGINNING, // BEGINNING, END, o WATERMARK
|
|
133
|
+
watermark: 12345, // Para WATERMARK mode
|
|
134
|
+
eventIds: [1074, 6005] // Filtros opcionales
|
|
135
|
+
}, onEvent, onError);
|
|
136
|
+
|
|
137
|
+
// Formato anterior sigue funcionando (compatibilidad total)
|
|
138
|
+
const legacy = eventLog.subscribe("System", 0, onEvent, onError, [1074]);
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### EventEmitter con Modos
|
|
142
|
+
|
|
143
|
+
```javascript
|
|
144
|
+
// EventEmitter que procesa todos los eventos históricos
|
|
145
|
+
const historicalEmitter = eventLog.createEventEmitter('Application', {
|
|
146
|
+
mode: eventLog.START_MODE.BEGINNING,
|
|
147
|
+
eventIds: [1000, 1001]
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
// EventEmitter solo para eventos futuros con filtros
|
|
151
|
+
const futureEmitter = eventLog.createEventEmitter('Security', {
|
|
152
|
+
mode: eventLog.START_MODE.END,
|
|
153
|
+
eventIds: [4624, 4625, 4634] // Login/logout events
|
|
154
|
+
});
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## API Referencia
|
|
158
|
+
|
|
159
|
+
### `readEvents(channel, mode, watermark, maxEvents, eventIds)`
|
|
160
|
+
|
|
161
|
+
Lectura síncrona de eventos del Event Log.
|
|
162
|
+
|
|
163
|
+
- **channel** `string` - Canal del Event Log ("System", "Application", "Security", etc.)
|
|
164
|
+
- **mode** `number` - Modo de lectura (START_MODE.BEGINNING, END, WATERMARK)
|
|
165
|
+
- **watermark** `number` - Record ID de inicio para modo WATERMARK
|
|
166
|
+
- **maxEvents** `number` - Número máximo de eventos a retornar (1-10000)
|
|
167
|
+
- **eventIds** `Array<number>` - Array opcional de Event IDs para filtrar
|
|
168
|
+
|
|
169
|
+
**Retorna:** `Object`
|
|
170
|
+
```javascript
|
|
171
|
+
{
|
|
172
|
+
records: [
|
|
173
|
+
{ recordId: number, xml: string },
|
|
174
|
+
...
|
|
175
|
+
],
|
|
176
|
+
lastRecordId: number
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
### `subscribe(channel, watermark, onEvent, onError, eventIds)`
|
|
181
|
+
|
|
182
|
+
Crear suscripción asíncrona a eventos en tiempo real.
|
|
183
|
+
|
|
184
|
+
- **channel** `string` - Canal del Event Log
|
|
185
|
+
- **watermark** `number` - Record ID de inicio (ver [Configuración de Offset](#configuración-de-offset))
|
|
186
|
+
- **onEvent** `Function` - Callback `(event) => {}` llamado por cada evento
|
|
187
|
+
- **onError** `Function` - Callback `(error) => {}` llamado en errores
|
|
188
|
+
- **eventIds** `Array<number>` - Array opcional de Event IDs para filtrar
|
|
189
|
+
|
|
190
|
+
**Retorna:** `EventLogSubscription`
|
|
191
|
+
|
|
192
|
+
### `createEventEmitter(channel, options)`
|
|
193
|
+
|
|
194
|
+
Crear EventEmitter para suscripción conveniente.
|
|
195
|
+
|
|
196
|
+
- **channel** `string` - Canal del Event Log
|
|
197
|
+
- **options** `Object` - Opciones de configuración
|
|
198
|
+
- **watermark** `number` - Record ID de inicio (default: 0)
|
|
199
|
+
- **eventIds** `Array<number>` - Event IDs para filtrar (default: null)
|
|
200
|
+
|
|
201
|
+
**Retorna:** `EventEmitter` que emite eventos 'event' y 'error'
|
|
202
|
+
|
|
203
|
+
### Clase `EventLogSubscription`
|
|
204
|
+
|
|
205
|
+
#### Métodos
|
|
206
|
+
|
|
207
|
+
- **`getLastRecordId()`** - Obtiene el último Record ID procesado
|
|
208
|
+
- **`unsubscribe()`** - Cancela la suscripción
|
|
209
|
+
- **`isActive()`** - Verifica si la suscripción está activa
|
|
210
|
+
|
|
211
|
+
#### Propiedades
|
|
212
|
+
|
|
213
|
+
- **`id`** - ID único de la suscripción
|
|
214
|
+
- **`channel`** - Canal al que está suscrito
|
|
215
|
+
- **`watermark`** - Watermark inicial
|
|
216
|
+
|
|
217
|
+
## Constantes
|
|
218
|
+
|
|
219
|
+
### `START_MODE`
|
|
220
|
+
|
|
221
|
+
```javascript
|
|
222
|
+
{
|
|
223
|
+
BEGINNING: 0, // Leer desde el inicio del log
|
|
224
|
+
END: 1, // Leer desde el final del log
|
|
225
|
+
WATERMARK: 2 // Leer desde un Record ID específico
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
## Canales Comunes
|
|
230
|
+
|
|
231
|
+
- **"System"** - Eventos del sistema (inicio, apagado, errores hardware)
|
|
232
|
+
- **"Application"** - Eventos de aplicaciones
|
|
233
|
+
- **"Security"** - Eventos de seguridad y auditoría
|
|
234
|
+
- **"Setup"** - Eventos de instalación
|
|
235
|
+
- **"Microsoft-Windows-*"** - Canales específicos de Windows
|
|
236
|
+
|
|
237
|
+
## Event IDs Comunes
|
|
238
|
+
|
|
239
|
+
### Sistema
|
|
240
|
+
- **1074** - Shutdown del sistema
|
|
241
|
+
- **1076** - Motivo de último shutdown
|
|
242
|
+
- **6005** - Inicio del Event Log Service
|
|
243
|
+
- **6006** - Parada del Event Log Service
|
|
244
|
+
- **6008** - Shutdown inesperado anterior
|
|
245
|
+
|
|
246
|
+
### Seguridad
|
|
247
|
+
- **4624** - Logon exitoso
|
|
248
|
+
- **4625** - Logon fallido
|
|
249
|
+
- **4634** - Logoff de cuenta
|
|
250
|
+
|
|
251
|
+
## Ejemplos Avanzados
|
|
252
|
+
|
|
253
|
+
### Monitoreo de Seguridad
|
|
254
|
+
|
|
255
|
+
```javascript
|
|
256
|
+
const eventLog = require('nwinread');
|
|
257
|
+
|
|
258
|
+
const securityMonitor = eventLog.createEventEmitter('Security', {
|
|
259
|
+
eventIds: [4624, 4625, 4634] // Logon/Logoff events
|
|
260
|
+
});
|
|
261
|
+
|
|
262
|
+
securityMonitor.on('event', (event) => {
|
|
263
|
+
// Parsear XML del evento para obtener detalles
|
|
264
|
+
const xml = event.xml;
|
|
265
|
+
|
|
266
|
+
if (xml.includes('4625')) {
|
|
267
|
+
console.log('⚠️ Intento de login fallido detectado');
|
|
268
|
+
} else if (xml.includes('4624')) {
|
|
269
|
+
console.log('✅ Login exitoso');
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### Sistema de Alarmas
|
|
275
|
+
|
|
276
|
+
```javascript
|
|
277
|
+
const eventLog = require('nwinread');
|
|
278
|
+
|
|
279
|
+
class SystemAlerts {
|
|
280
|
+
constructor() {
|
|
281
|
+
this.subscription = eventLog.subscribe(
|
|
282
|
+
"System",
|
|
283
|
+
0,
|
|
284
|
+
this.handleEvent.bind(this),
|
|
285
|
+
this.handleError.bind(this),
|
|
286
|
+
[1074, 1076, 6008] // Shutdown y errores
|
|
287
|
+
);
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
handleEvent(event) {
|
|
291
|
+
const xml = event.xml;
|
|
292
|
+
|
|
293
|
+
if (xml.includes('1074')) {
|
|
294
|
+
this.sendAlert('Sistema apagándose', event);
|
|
295
|
+
} else if (xml.includes('6008')) {
|
|
296
|
+
this.sendAlert('Shutdown inesperado detectado', event);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
handleError(error) {
|
|
301
|
+
console.error(`Sistema de alertas error: ${error.message}`);
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
sendAlert(message, event) {
|
|
305
|
+
console.log(`🚨 ALERTA: ${message}`);
|
|
306
|
+
console.log(` Record ID: ${event.recordId}`);
|
|
307
|
+
console.log(` Timestamp: ${new Date().toISOString()}`);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
stop() {
|
|
311
|
+
return this.subscription.unsubscribe();
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
const alerts = new SystemAlerts();
|
|
316
|
+
```
|
|
317
|
+
|
|
318
|
+
## Manejo de Errores
|
|
319
|
+
|
|
320
|
+
### Errores Comunes
|
|
321
|
+
|
|
322
|
+
- **Access Denied** - Permisos insuficientes para leer el canal
|
|
323
|
+
- **Channel Not Found** - El canal especificado no existe
|
|
324
|
+
- **Invalid Arguments** - Parámetros incorrectos
|
|
325
|
+
|
|
326
|
+
### Códigos de Error Windows
|
|
327
|
+
|
|
328
|
+
Los errores retornan objetos con:
|
|
329
|
+
```javascript
|
|
330
|
+
{
|
|
331
|
+
message: "Descripción del error",
|
|
332
|
+
code: 123 // Código de error de Windows
|
|
333
|
+
}
|
|
334
|
+
```
|
|
335
|
+
|
|
336
|
+
## Rendimiento y Best Practices
|
|
337
|
+
|
|
338
|
+
### Para API Síncrona
|
|
339
|
+
- Use para consultas puntuales o análisis histórico
|
|
340
|
+
- Limite maxEvents para evitar uso excesivo de memoria
|
|
341
|
+
- Use filtros eventIds para mejorar rendimiento
|
|
342
|
+
|
|
343
|
+
### Para API Asíncrona
|
|
344
|
+
- Ideal para monitoreo en tiempo real
|
|
345
|
+
- Menor uso de CPU que polling
|
|
346
|
+
- Maneje adecuadamente cleanup en cierre de aplicación
|
|
347
|
+
- Use filtros para reducir ruido
|
|
348
|
+
|
|
349
|
+
### Cleanup Apropiado
|
|
350
|
+
|
|
351
|
+
```javascript
|
|
352
|
+
// Manejar señales de terminación
|
|
353
|
+
process.on('SIGINT', cleanup);
|
|
354
|
+
process.on('SIGTERM', cleanup);
|
|
355
|
+
|
|
356
|
+
function cleanup() {
|
|
357
|
+
if (subscription && subscription.isActive()) {
|
|
358
|
+
subscription.unsubscribe();
|
|
359
|
+
}
|
|
360
|
+
process.exit(0);
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
## Configuración de Offset
|
|
365
|
+
|
|
366
|
+
El parámetro `watermark` (offset de inicio) permite controlar desde qué punto iniciar la lectura de eventos. Es especialmente útil para recuperación tras interrupciones o análisis histórico selectivo.
|
|
367
|
+
|
|
368
|
+
### Valores de Watermark
|
|
369
|
+
|
|
370
|
+
| Valor | Comportamiento | Caso de Uso |
|
|
371
|
+
|-------|---------------|-------------|
|
|
372
|
+
| `0` | Solo eventos futuros | Monitoreo en tiempo real |
|
|
373
|
+
| `N > 0` | Desde Record ID específico | Recuperación desde punto conocido |
|
|
374
|
+
| `-1` | Todos los eventos disponibles | Análisis histórico completo |
|
|
375
|
+
|
|
376
|
+
### Ejemplos de Uso
|
|
377
|
+
|
|
378
|
+
#### 1. Monitoreo en Tiempo Real
|
|
379
|
+
```javascript
|
|
380
|
+
// Solo eventos que ocurran después de la suscripción
|
|
381
|
+
const subscription = eventLog.subscribe(
|
|
382
|
+
"System",
|
|
383
|
+
0, // Solo eventos futuros
|
|
384
|
+
onEvent,
|
|
385
|
+
onError
|
|
386
|
+
);
|
|
387
|
+
```
|
|
388
|
+
|
|
389
|
+
#### 2. Recuperación desde Punto Específico
|
|
390
|
+
```javascript
|
|
391
|
+
// Continuar desde un Record ID conocido
|
|
392
|
+
const subscription = eventLog.subscribe(
|
|
393
|
+
"Application",
|
|
394
|
+
5000, // Desde Record ID 5000 en adelante
|
|
395
|
+
onEvent,
|
|
396
|
+
onError
|
|
397
|
+
);
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
#### 3. Análisis Histórico Completo
|
|
401
|
+
```javascript
|
|
402
|
+
// Procesar todos los eventos disponibles
|
|
403
|
+
const subscription = eventLog.subscribe(
|
|
404
|
+
"Security",
|
|
405
|
+
-1, // Todos los eventos del log
|
|
406
|
+
onEvent,
|
|
407
|
+
onError
|
|
408
|
+
);
|
|
409
|
+
```
|
|
410
|
+
|
|
411
|
+
#### 4. Offset Dinámico por Fecha
|
|
412
|
+
```javascript
|
|
413
|
+
function getOffsetByDate(channel, targetDate) {
|
|
414
|
+
const events = eventLog.read(channel, "recent", 0, 100);
|
|
415
|
+
let closestRecordId = 0;
|
|
416
|
+
let closestDiff = Infinity;
|
|
417
|
+
|
|
418
|
+
for (const event of events) {
|
|
419
|
+
const eventTime = new Date(event.timestamp);
|
|
420
|
+
const timeDiff = Math.abs(eventTime.getTime() - targetDate.getTime());
|
|
421
|
+
|
|
422
|
+
if (timeDiff < closestDiff) {
|
|
423
|
+
closestDiff = timeDiff;
|
|
424
|
+
closestRecordId = event.recordId;
|
|
425
|
+
}
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
return closestRecordId;
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
// Suscribirse desde hace 1 hora
|
|
432
|
+
const oneHourAgo = new Date(Date.now() - 60 * 60 * 1000);
|
|
433
|
+
const offset = getOffsetByDate("System", oneHourAgo);
|
|
434
|
+
|
|
435
|
+
const subscription = eventLog.subscribe("System", offset, onEvent, onError);
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
#### 5. Estado de Suscripción con Offset
|
|
439
|
+
```javascript
|
|
440
|
+
const subscription = eventLog.subscribe("Application", 1000, onEvent, onError);
|
|
441
|
+
|
|
442
|
+
console.log(`Iniciado desde Record ID: ${subscription.getStartWatermark()}`);
|
|
443
|
+
console.log(`Canal: ${subscription.channel}`);
|
|
444
|
+
console.log(`Activa: ${subscription.isActive()}`);
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
### EventEmitter con Offset
|
|
448
|
+
|
|
449
|
+
```javascript
|
|
450
|
+
const emitter = eventLog.createEventEmitter('System', {
|
|
451
|
+
watermark: 2500, // Desde Record ID 2500
|
|
452
|
+
eventIds: [1074, 6008] // Con filtro de eventos
|
|
453
|
+
});
|
|
454
|
+
|
|
455
|
+
emitter.on('event', (event) => {
|
|
456
|
+
console.log(`Record: ${event.recordId}, desde offset 2500`);
|
|
457
|
+
});
|
|
458
|
+
```
|
|
459
|
+
|
|
460
|
+
### Mejores Prácticas para Offsets
|
|
461
|
+
|
|
462
|
+
1. **Persistir último Record ID**: Guarde el último Record ID procesado para recuperación
|
|
463
|
+
2. **Validar disponibilidad**: Los logs rotan, verifique que el offset sigue disponible
|
|
464
|
+
3. **Fallback a 0**: Si el offset específico falla, use 0 como fallback
|
|
465
|
+
4. **Monitorear gaps**: Detecte saltos en Record IDs que indican rotación de logs
|
|
466
|
+
|
|
467
|
+
```javascript
|
|
468
|
+
class PersistentEventReader {
|
|
469
|
+
constructor(channel, stateFile = './last-record-id.txt') {
|
|
470
|
+
this.channel = channel;
|
|
471
|
+
this.stateFile = stateFile;
|
|
472
|
+
this.lastRecordId = this.loadState();
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
start() {
|
|
476
|
+
this.subscription = eventLog.subscribe(
|
|
477
|
+
this.channel,
|
|
478
|
+
this.lastRecordId,
|
|
479
|
+
(event) => {
|
|
480
|
+
this.handleEvent(event);
|
|
481
|
+
this.saveState(event.recordId);
|
|
482
|
+
},
|
|
483
|
+
this.handleError.bind(this)
|
|
484
|
+
);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
handleEvent(event) {
|
|
488
|
+
console.log(`Procesando Record ID: ${event.recordId}`);
|
|
489
|
+
// Procesar evento...
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
loadState() {
|
|
493
|
+
try {
|
|
494
|
+
const fs = require('fs');
|
|
495
|
+
const content = fs.readFileSync(this.stateFile, 'utf8');
|
|
496
|
+
return parseInt(content.trim()) + 1; // Siguiente al último procesado
|
|
497
|
+
} catch {
|
|
498
|
+
return 0; // Primera ejecución, solo eventos futuros
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
|
|
502
|
+
saveState(recordId) {
|
|
503
|
+
const fs = require('fs');
|
|
504
|
+
fs.writeFileSync(this.stateFile, recordId.toString());
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
```
|
|
508
|
+
|
|
509
|
+
## Requisitos del Sistema
|
|
510
|
+
|
|
511
|
+
- Windows Vista o superior
|
|
512
|
+
- Permisos de lectura al Event Log
|
|
513
|
+
- Node.js 14+ con N-API support
|
|
514
|
+
- Visual Studio Build Tools para compilación
|
|
515
|
+
|
|
516
|
+
## Troubleshooting
|
|
517
|
+
|
|
518
|
+
### Error: "Module not found"
|
|
519
|
+
```bash
|
|
520
|
+
npm run rebuild
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
### Error: "Access denied"
|
|
524
|
+
Ejecutar con permisos elevados o verificar permisos de usuario.
|
|
525
|
+
|
|
526
|
+
### Error de compilación
|
|
527
|
+
Verificar que Visual Studio Build Tools esté instalado.
|