Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add RFC 3164 timestamp support #1

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 23 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
100 changes: 63 additions & 37 deletions index.js
Original file line number Diff line number Diff line change
@@ -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 <syslog.h> talk about the value in brackets as PID.
// http://tools.ietf.org/html/rfc3164#section-5.3
Expand All @@ -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 <syslog.h> naming.
var FACILITY = exports.FACILITY = [
Expand Down Expand Up @@ -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)
}
19 changes: 16 additions & 3 deletions test/index_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -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)))
Expand Down