diff --git a/README.md b/README.md index 4554e25..48d8903 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,26 @@ SyslogProtocol.js SyslogProtocol.js is a Syslog ([RFC 3164][rfc3164]) format parser that supports high-precision timestamps ([RFC 3339][rfc3339], [ISO 8601][iso8601]). + +Given a Syslog message with RFC 3164 timestamp: +``` +<38>Jun 18 15:20:30 server sshd[42]: Accepted publickey for user +``` + +It'll return the following object (with `time` being an instance of `Date`, and the year being 'this year'): +```javascript +{ facility: "auth", + facilityCode: 4, + severity: "info", + severityCode: 6, + time: new Date("2014-06-18T15:20:30.0Z"), + host: "server", + process: "sshd", + pid: 42, + message: "Accepted publickey for user" } +``` + + Given a Syslog message with a high-precision timestamp: ``` <38>1987-06-18T15:20:30.337Z server sshd[42]: Accepted publickey for user @@ -26,14 +46,14 @@ It'll return the following object (with `time` being an instance of `Date`): message: "Accepted publickey for user" } ``` + [rfc3164]: https://tools.ietf.org/html/rfc3164 [rfc3339]: https://tools.ietf.org/html/rfc3339 [iso8601]: https://en.wikipedia.org/wiki/ISO_8601 -Ironically, SyslogProtocol.js does *not* support plain [RFC 3164's +SyslogProtocol.js does now support plain [RFC 3164's timestamps](https://tools.ietf.org/html/rfc3164#section-4.1.2), which are in -who-knows-what time zone and lack a year part. If you can, don't use them. If -you're really keen on them, please let me know and I'll see about implementing. +who-knows-what time zone and lack a year part. If you can, don't use them. ### Tour - Supports [RFC 3164](rfc3164) with high-precision timestamps ([RFC diff --git a/index.js b/index.js index 5376f3f..95fbd03 100644 --- a/index.js +++ b/index.js @@ -1,40 +1,39 @@ -exports.parse = function(line) { +exports.parse = function (line) { var matches = RFC3164_WITH_ISO8601_TIME.exec(line) if (!matches) return null - var priority = Number(matches[1]) - var facility = priority >> 3 - var severity = priority & 7 + var priority = Number(matches[1]), + facility = priority >> 3, + severity = priority & 7, + struct = { + // RFC 3164 and RFC 5424 use the term "Numerical Code" for the integer + // values of facilities and severities. + facility: FACILITY[facility], + facilityCode: facility, + severity: SEVERITY[severity], + severityCode: severity, - var struct = { - // RFC 3164 and RFC 5424 use the term "Numerical Code" for the integer - // values of facilities and severities. - facility: FACILITY[facility], - facilityCode: facility, - severity: SEVERITY[severity], - severityCode: severity, + // While RFC 3164 and RFC 5424 both call it TIMESTAMP, an instantiated + // field would no longer be a stamp, but a time. + time: parseTime(matches[2]), - // While RFC 3164 and RFC 5424 both call it TIMESTAMP, an instantiated - // field would no longer be a stamp, but a time. - time: new Date(matches[2]), + // While RFC 3164 and RFC 5424 call it HOSTNAME, both allow IP addresses + // to be used. Host is a more accurate name in that case. + host: matches[3], - // While RFC 3164 and RFC 5424 call it HOSTNAME, both allow IP addresses - // to be used. Host is a more accurate name in that case. - host: matches[3], + // While RFC 3164 calls the identifier a TAG, it also mentions it being + // used as process information: + // http://tools.ietf.org/html/rfc3164#section-5.3 + // RFC 5424 calls it an APP-NAME field. + // + // For consistency with PID, use "process". + process: matches[4], - // While RFC 3164 calls the identifier a TAG, it also mentions it being - // used as process information: - // http://tools.ietf.org/html/rfc3164#section-5.3 - // RFC 5424 calls it an APP-NAME field. - // - // For consistency with PID, use "process". - process: matches[4], - - // While RFC 3164 calls it content, RFC 5424 calls it message. - // http://tools.ietf.org/html/rfc5424#appendix-A.1 - // For consistency, stick to "message". - message: matches[6] - } + // While RFC 3164 calls it content, RFC 5424 calls it message. + // http://tools.ietf.org/html/rfc5424#appendix-A.1 + // For consistency, stick to "message". + message: matches[6] + } // Both RFC 3164 and talk about the value in brackets as PID. // http://tools.ietf.org/html/rfc3164#section-5.3 @@ -47,13 +46,17 @@ exports.parse = function(line) { return struct } -var RFC3164_WITH_ISO8601_TIME = new RegExp("^" - + "<(\\d+)>" // Priority - + "(\\d+-\\d+-\\d+T\\S+)" // Time - + " (\\S+)" // Host - + " (?:(\\S+?)(?:\\[([^\\]]*)\\])?)" // Tag/Process + PID - + "(?: |: ?)(.*)" // Message -) +var RFC3164_Time_WithoutGroups = "(?:[A-Z][a-z]{2} [ 1-3][0-9] [0-2][0-9]:[0-5][0-9]:[0-5][0-9])", + RFC3164_Time_WithGroups = "(?:([A-Z][a-z]{2}) ([ 1-3][0-9]) ([0-2][0-9]):([0-5][0-9]):([0-5][0-9]))", + RFC3164_WITH_ISO8601_TIME = new RegExp("^" + + "<(\\d+)>" // Priority + + "(" + RFC3164_Time_WithoutGroups // RFC 3164 Time + + "|(?:\\d+-\\d+-\\d+T\\S+))" // RFC 3339 & ISO 8601 Time + + " (\\S+)" // Host + + " (?:(\\S+?)(?:\\[([^\\]]*)\\])?)" // Tag/Process + PID + + "(?: |: ?)([\\s\\S]*)" // Message + ), + RFC3164_TIME = new RegExp("^" + RFC3164_Time_WithGroups + "$") // Facility names are set to match common naming. var FACILITY = exports.FACILITY = [ @@ -96,3 +99,26 @@ var SEVERITY = exports.SEVERITY = [ ] var NUMBER = /^\d+$/ + +function parseTime (time){ + var matches = RFC3164_TIME.exec(time) + if (!matches) return new Date(time) + + var now = new Date(), + months = { + "Jan" : 0, + "Feb" : 1, + "Mar" : 2, + "Apr" : 3, + "May" : 4, + "Jun" : 5, + "Jul" : 6, + "Aug" : 7, + "Sep" : 8, + "Oct" : 9, + "Nov" : 10, + "Dec" : 11 + } + + return new Date(now.getFullYear(), months[matches[1]], matches[2].trim(), matches[3], matches[4], matches[5], 0) +} \ No newline at end of file diff --git a/test/index_test.js b/test/index_test.js index 492ec12..cda5260 100644 --- a/test/index_test.js +++ b/test/index_test.js @@ -7,7 +7,7 @@ describe("SyslogProtocol", function() { demand(parse("<15>")).be.null() }) - describe("given RFC 3164 with ISO 8601 timestamp", function() { + describe("given RFC 3164 inc. ISO 8601 timestamp", function() { ;[ "kern", "user", @@ -68,14 +68,27 @@ describe("SyslogProtocol", function() { }) }) + it("must parse timestamp in RFC 3164", function() { + var msg = "<15>Jun 18 15:20:30 server user: Test 123" + var now = new Date() + var syslog = parse(msg) + syslog.time.must.eql(new Date(now.getFullYear(), 5, 18, 15, 20, 30, 0)) + }) + + it("must parse timestamp in RFC 3164 with single digit date", function() { + var msg = "<15>Jun 8 15:20:30 server user: Test 123" + var now = new Date() + var syslog = parse(msg) + syslog.time.must.eql(new Date(now.getFullYear(), 5, 8, 15, 20, 30, 0)) + }) - it("must parse timestamp given UTC offset", function() { + it("must parse timestamp in ISO 8601 given UTC offset", function() { var msg = "<15>1987-06-18T15:20:30.337Z server user: Test 123" var syslog = parse(msg) syslog.time.must.eql(new Date(Date.UTC(1987, 5, 18, 15, 20, 30, 337))) }) - it("must parse timestamp given time offset", function() { + it("must parse timestamp in ISO 8601 given time offset", function() { var msg = "<15>1987-06-18T18:20:30.337+03:00 server user: Test 123" var syslog = parse(msg) syslog.time.must.eql(new Date(Date.UTC(1987, 5, 18, 15, 20, 30, 337)))