
# windows-eventlog-reader

**NOTE** This module has not proved useful to myself or my colleagues who
it was written for, so I have decided to no longer maintain this project.
If you would like to take over maintenance of this project please let me
know via <stephen.vickers.sv@gmail.com>.  For the record, the following
Microsoft Windows bug is the core reason I am no longer maintaining this
project - this bug still seems to affect the Windows platforms I am
targetting - <https://support.microsoft.com/en-us/kb/177199>.

This modules provides [Node.js][nodejs] programs the ability to read events
from a Windows event log.

This module is installed using [node package manager (npm)][npm]:

    # This module contains C++ source code which will be compiled
    # during installation using node-gyp.  A suitable build chain
    # must be configured before installation.
    
    npm install windows-eventlog-reader

It is loaded using the `require()` function:

    var eventlog = require ("windows-eventlog-reader");

Individual logs can then be opened and tail'ed:

    var reader = eventlog.createReader ("Application");
    
    function feedCb (event) {
        console.dir (event);
    }
    
    function doneCb (error) {
        if (error)
            console.error (error.toString ());
    }
    
    reader.readAll (1, feedCb, doneCb);
    
    reader.tail (1, function (error, event) {
        if (error)
            console.error (error.toString ());
        else
            console.dir (event);
    });

[nodejs]: http://nodejs.org "Node.js"
[npm]: https://npmjs.org/ "npm"

# Non-Blocking Operations

Windows event logs are typically stored in files on a hard disk.  Opening and
reading an event log involves reading these files from disk, and the Windows
API to do so operates in a blocking mode, i.e. each read request will block
the calling program from performing other operations.  In the context of
[Node.js][nodejs] this will introduce a pause of an undefined amount of time
into the [Node.js][nodejs] event loop.

**NOTE** Although event log contents may come directly from the operating
systems file/page cache, the blocking I/O side effect is still passed on to
the calling program, i.e. something in the call chain may decide to block and
a calling [Node.js][nodejs] program cannot do anything about it.  Also, to
obtain formatted messages for each event log event, one or more DLL's need to
be loaded directly from disk, which involves more blocking I/O.

This module utilises the `libuv` library to integrate into the
[Node.js][nodejs] event loop - this library is also used by [Node.js][nodejs].
The `libuv` function `uv_queue_work()` is used to queue operations to run in
the background.  As a result this module will not block the [Node.js][nodejs]
event loop when blocking operations are performed.

There is one exception to this claim.  The `close()` method exposed by
this module is performed within the [Node.js][nodejs] event loop.  This
function will call the Win32 API function `CloseEventLog()`, which might
block.  However, during development this function always seemed to return
pretty much instantly.

[nodejs]: http://nodejs.org "Node.js"

# Event Objects

This module uses JavaScript objects to represent events, for example:

    {
        sourceName: 'gupdate',
        computerName: 'as00041.uk.internal',
        recordNumber: 6330,
        timeGenerated: 1372576636,
        timeWritten: 1372576636,
        eventId: 5104,
        eventType: 'Information',
        eventCategory: 0,
        message: 'Service started'
    }

Each event object will have following parameters:

 * `sourceName` - String specifying the event source for the event, e.g.
   `MSSQLServer`
 * `computerName` - String specifying the computer on which the event was
   generated, e.g. `sql-server.test.com`
 * `recordNumber` - Identifies the event in this event log
 * `timeGenerated` - Instance of the JavaScript `Date` class specifying the
   date and time at which the event was generated
 * `timeWritten` - Instance of the JavaScript `Date` class specifying the
   date and time at which the event was actually written to the event log
 * `eventId` - Number specifying the event identifier, this is specifiec to
   the event source
 * `eventType` - String specifying event type, this can be one of `Error`,
   `Warning`, `Information`, `AuditSuccess`, `AuditFailure` or `Unknown`
 * `eventCategory` - Number specifying the event category, this is specific
   to the event source and is not defined by this module
 * `message` - String containing the formatted event message

Event identifiers, i.e. the `eventId` attribute, contain multiple sub-fields.
Refer to Win32 API documentation for details on each field.  To obtain the
event code field, as it is displayed in the Windows event log viewer, bitwise
`&` the `eventId` attribute with `0xffff`:

    var eventCode = event.eventId & 0xffff;

# Error Handling

Each operation exposed by this module typically requires a mandatory callback
function which will be called when an operation completes.

Callback functions are typically provided an `error` argument, and almost all
errors are instances of the `Error` class.

In the event a Windows event log is cleared while a reader is reading from it
the Win32 API will return the error code `ERROR_EVENTLOG_FILE_CHANGED`.  In
this case the `error` argument will be an instance of the
`eventlog.EventLogClearedError` class.

This type of error will typically only occur for operations that involve a
read from the event log, e.g. `read()` or `tail()`.

If this error is experienced, the event log can be closed, re-opened, and
the original request re-submitted:

    var offset = 1;

    function cb (error) {
        if (error) {
            // When an error occurs this operation will complete, and in the
            // case of an event log being cleared we want to re-open it and
            // re-submit our original request
            if (error instanceof eventlog.EventLogClearedError) {
                reader.close ();
                reader.open (function (error) {
                    if (error)
                        console.error (error.toString ());
                    else
                        reader.tail (offset, cb);
                });
            } else {
                console.error (error.toString ());
            }
        } else {
            offset = event.recordNumber;
            console.dir (event);
        }
    }

    reader.tail (offset, cb);

# Using This Module

Event log readers are represented by an instance of the `Reader` class.  This
module exports the `createReader()` function which is used to create instances
of the `Reader` class.

Readers can be used to read all or part of an event log, and to tail the event
log and notify users of new events.

## eventlog.createReader (name)

The `createReader()` function instantiates and returns an instance of the
`Reader` class:
    
    var reader = eventlog.createReader ("Application");

The `name` parameter is the name of the event log to create a reader for.

An exception will be thrown if the reader could not be created.  The error
will be an instance of the `Error` class.

After calling this function and creating a reader the underlying Windows event
log will not have been opened.  The `open()` method must be called on the
reader for the underlying Windows event log to be opened.

**NOTE** When the `open()` method is called this module will use the Win32 API
`OpenEventLog()` function.  The documentation for this function states that if
the event log could not be found then the `Application` event log will be
opened instead.

## reader.on ("close", callback)

The `close` event is emitted by the reader when the underlying event log
handle is closed.

No arguments are passed to the callback.

The following example prints a message to the console when the reader is
closed:

    reader.on ("close", function () {
        console.log ("event log closed");
    });

## reader.open (callback)

The `open()` method opens the underlying Windows event log represented by the
reader.

The `callback` function is called once the underlying Windows event log has
been opened.  The following arguments will be passed to the `callback`
function:

 * `error` - Instance of the `Error` class or a sub-class, or `null` if no
   error occurred

The following example opens the `Application` Windows event log:

    reader.open (function (error) {
        if (error)
            console.error (error.toString ());
    });

## reader.close ()

The `close()` method closes the underlying Windows event log represented by
the reader.

**NOTE** This function will call the Win32 API function `CloseEventLog()`,
which might block. However, during development this function always seemed to
return pretty much instantly.

The following example opens the `Application` Windows event log:

    reader.open (function (error) {
        if (error)
            console.error (error.toString ());
        else
            reader.close ();
    });

## reader.read (offset, feedCallback, doneCallback)

The `read()` method reads zero or more events from the underlying Windows
event log.

The `offset` parameter specifies at what record number to start reading from
(note that the event with this record number will also be returned if it
exists).

This method will not call a single callback once some events have been read.
Instead the `feedCallback` function will be called for each event read.  The
following arguments will be passed to the `feedCallback` function:

 * `event` - An object describing the event (see the "Event Objects" section
   for more information on event objects)

Once the read has completed, or an error has occurred, the `doneCallback`
function will be called.  The following arguments will be passed to the
`doneCallback` function:

 * `error` - Instance of the `Error` class or a sub-class, or `null` if no
   error occurred

If the `feedCallback` function returns a `true` value when called no more
events will be read or passed to the `feedCallback` function, and the
`doneCallback` will be called.

Once the `doneCallback` function has been called the read is complete and
the `feedCallback` function will no longer be called.

The following example reads a number of events starting at offset 1,000:

    function feedCb (event) {
        console.dir (event);
    }

    function doneCb (error) {
        if (error)
            console.error (error.toString ());
    }

    var reader = eventlog.createReader (name);
    var offset = 1000;

    reader.open (function (error) {
        if (error) {
            console.error (error.toString ());
        } else {
            reader.read (offset, feedCb, doneCb);
        }
    });

## reader.readAll (offset, feedCallback, doneCallback)

The `readAll()` method reads zero or more events from the underlying Windows
event log until no more events are available.

The `offset` parameter specifies at what record number to start reading from
(note that the event with this record number will also be returned if it
exists).

This method will not call a single callback once all events have been read.
Instead the `feedCallback` function will be called for each event read.  The
following arguments will be passed to the `feedCallback` function:

 * `event` - An object describing the event (see the "Event Objects" section
   for more information on event objects)

Once the read has completed, or an error has occurred, the `doneCallback`
function will be called.  The following arguments will be passed to the
`doneCallback` function:

 * `error` - Instance of the `Error` class or a sub-class, or `null` if no
   error occurred

If the `feedCallback` function returns a `true` value when called no more
events will be read or passed to the `feedCallback` function, and the
`doneCallback` will be called.

Once the `doneCallback` function has been called all available events will
have been read and the `feedCallback` function will no longer be called.

The following example reads all events starting at offset 2,000:

    function feedCb (event) {
        console.dir (event);
    }

    function doneCb (error) {
        if (error)
            console.error (error.toString ());
    }

    var reader = eventlog.createReader (name);
    var offset = 2000;

    reader.open (function (error) {
        if (error) {
            console.error (error.toString ());
        } else {
            reader.readAll (offset, feedCb, doneCb);
        }
    });

## reader.tail (offset, [interval], callback)

The `tail()` method periodically reads zero or more events from the underlying
Windows event log as they become available.

The `offset` parameter specifies at what record number to start reading from
(note that the event with this record number will also be returned if it
exists).  The optional `interval` parameter is the number of milliseconds
between each read call, and defaults to `3000`.

The `callback` function will be called as each event is read from the event
log, or if an error occurs.  The following arguments will be passed to the
`callback` function:

 * `error` - Instance of the `Error` class or a sub-class, or `null` if no
   error occurred
 * `event` - An object describing the event (see the "Event Objects" section
   for more information on event objects)

**NOTE** If an error occurs the `callback` function will be called with the
first `error` argument defined, and after the `callback` function returns the
tail operation will be stopped.

If the `callback` function returns a `true` value when called no more events
will be read, and the `callback` function will not be called again.

The following example reads all events starting at offset 6,000, and then
tails the Windows event log, calling the `cb` function as and when events
become available:

    function cb (error, event) {
        if (error)
            console.error (error.toString ());
        else
            console.dir (event);
    }

    var reader = eventlog.createReader (name);
    var offset = 6000;

    reader.open (function (error) {
        if (error) {
            console.error (error.toString ());
        } else {
            reader.tail (offset, cb);
        }
    });

# Example Programs

Example programs are included under the modules `example` directory.

# Bugs & Known Issues

None, yet!

Bug reports should be sent to <stephen.vickers.sv@gmail.com>.

# Changes

## Version 1.0.0 - 30/06/2013

 * Initial release

## Version 1.0.1 - 01/07/2013

 * Add "Error Handling" section to the README.md file

## Version 1.0.2 - 01/07/2013

 * Feed callback functions can return `true` to indicate an operation should
   stop feeding events
 * Silence some compilation warnings
 * Throw an exception when the event log name provided is not valid

## Version 1.0.3 - 11/07/2013

 * Added `eventId` attribute to the event object
 * Added missing documentation for the `close()` method to the README.md file

## Version 1.0.4 - 15/08/2013

 * Address various issues reported by cppcheck

## Version 1.0.5 - 15/12/2014

 * Not initializing event log handle results in random invalid handle messages

## Version 1.1.0 - 09/10/2015

 * Add note to the README.md that the project is no longer maintained

# Roadmap

Suggestions and requirements should be sent to <stephen.vickers.sv@gmail.com>.

# License

This program is free software: you can redistribute it and/or modify it under
the terms of the GNU General Public License as published by the Free Software
Foundation, either version 3 of the License, or (at your option) any later
version.

This program is distributed in the hope that it will be useful, but WITHOUT
ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
FOR A PARTICULAR PURPOSE.  See the GNU General Public License for more
details.

You should have received a copy of the GNU General Public License along with
this program.  If not, see
[http://www.gnu.org/licenses](http://www.gnu.org/licenses).

# Author

Stephen Vickers <stephen.vickers.sv@gmail.com>
