syslog-appender-pro
Version:
Syslog Appender for TCP/TLS/UDP connections
435 lines (424 loc) • 9.23 kB
JavaScript
const { hostname } = require('os');
const tcp = require('net').createConnection;
const tls = require('tls').connect;
const udp = require('dgram').createSocket;
// REFERENCES
// https://tools.ietf.org/html/rfc5424
// https://tools.ietf.org/html/rfc5425
// https://tools.ietf.org/html/rfc5426
// https://tools.ietf.org/html/rfc6587
const FACILITIES = {
LOCAL0: 16,
LOCAL1: 17,
LOCAL2: 18,
LOCAL3: 19,
LOCAL4: 20,
LOCAL5: 21,
LOCAL6: 22,
LOCAL7: 23,
}
exports.FACILITIES = FACILITIES;
const SEVERITIES = {
ALERT: 1,
CRITICAL: 2,
DEBUG: 7,
EMERGENCY: 0,
ERROR: 3,
INFORMATIONAL: 6,
NOTICE: 5,
WARNING: 4,
}
exports.SEVERITIES = SEVERITIES;
const isObject = (value) => {
return typeof value === 'object' && Array.isArray(value) === false
}
const parseStructuredData = (structuredData) => {
let result = ''
for (const id in structuredData) {
if (isObject(structuredData[id]) === true) {
result += `[${id}`
for (const param in structuredData[id]) {
result += ` ${param}="${structuredData[id][param]}"`
}
result += ']'
}
}
return result.length === 0 ? '-' : result
}
const rfc5424 = ({ appName, eol, facility, hostname, message, msgId, procId, severity, structuredData, timestamp }) => {
if (typeof facility === 'string') {
facility = FACILITIES[facility.toUpperCase()] ? FACILITIES[facility.toUpperCase()] : FACILITIES.LOCAL0
}
if (typeof severity === 'string') {
severity = SEVERITIES[severity.toUpperCase()] ? SEVERITIES[severity.toUpperCase()] : SEVERITIES.DEBUG
}
if (isObject(structuredData) === true) {
structuredData = parseStructuredData(structuredData)
}
const priority = Math.imul(facility, 8) + severity
return Buffer.from(`<${priority}>1 ${timestamp} ${hostname} ${appName} ${procId} ${msgId} ${structuredData} ${message}${eol}`)
}
exports.rfc5424 = rfc5424;
// sync -> async
const rfc5425 = ({
defaultAppName,
defaultEol,
defaultFacility,
defaultHostname,
defaultMsgId,
defaultProcId,
defaultSeverity,
defaultStructuredData,
ca,
cert,
checkServerIdentity,
family,
host,
key,
port,
}) => {
return ({
appName = defaultAppName,
eol = defaultEol,
facility = defaultFacility,
hostname = defaultHostname,
message,
msgId = defaultMsgId,
procId = defaultProcId,
severity = defaultSeverity,
structuredData = defaultStructuredData,
timestamp = new Date().toISOString(),
}) => {
return new Promise((resolve, reject) => {
const options = {
ca,
cert,
family,
host,
key,
port,
}
if (typeof checkServerIdentity === 'function') {
options.checkServerIdentity = checkServerIdentity
}
const socket = tls(options)
socket.once('error', reject)
socket.once('end', resolve)
socket.once('connect', () => {
socket.end(
rfc5424({
appName,
eol,
facility,
hostname,
message,
msgId,
procId,
severity,
structuredData,
timestamp,
})
)
})
})
}
}
exports.rfc5425 = rfc5425;
// async -> sync
const rfc5426 = ({
defaultAppName,
defaultEol,
defaultFacility,
defaultHostname,
defaultMsgId,
defaultProcId,
defaultSeverity,
defaultStructuredData,
family,
host,
port,
}) => {
return new Promise((resolve, reject) => {
const options = {
0: {
type: 'udp6',
},
4: {
type: 'udp4',
},
6: {
ipv6Only: true,
type: 'udp6',
},
}
const socket = udp(options[family])
socket.connect(port, host)
socket.once('error', reject)
socket.once('connect', () => {
resolve(
({
appName = defaultAppName,
eol = defaultEol,
facility = defaultFacility,
hostname = defaultHostname,
message,
msgId = defaultMsgId,
procId = defaultProcId,
severity = defaultSeverity,
structuredData = defaultStructuredData,
timestamp = new Date().toISOString(),
}) => {
socket.send(
rfc5424({
appName,
eol,
facility,
hostname,
message,
msgId,
procId,
severity,
structuredData,
timestamp,
})
)
}
)
})
})
}
exports.rfc5426 = rfc5426;
// sync -> async
const rfc6587 = ({
defaultAppName,
defaultEol,
defaultFacility,
defaultHostname,
defaultMsgId,
defaultProcId,
defaultSeverity,
defaultStructuredData,
family,
host,
port,
}) => {
return ({
appName = defaultAppName,
eol = defaultEol,
facility = defaultFacility,
hostname = defaultHostname,
message,
msgId = defaultMsgId,
procId = defaultProcId,
severity = defaultSeverity,
structuredData = defaultStructuredData,
timestamp = new Date().toISOString(),
}) => {
return new Promise((resolve, reject) => {
const socket = tcp({
family,
host,
port,
})
socket.once('error', reject)
socket.once('end', resolve)
socket.once('connect', () => {
socket.end(
rfc5424({
appName,
eol,
facility,
hostname,
message,
msgId,
procId,
severity,
structuredData,
timestamp,
})
)
})
})
}
}
exports.rfc6587 = rfc6587;
const syslog = ({
ca,
cert,
checkServerIdentity,
defaultAppName = process.argv[process.argv.length - 1],
defaultEol = '\0',
defaultFacility = FACILITIES.LOCAL0,
defaultHostname = hostname(),
defaultMsgId = '-',
defaultProcId = process.pid,
defaultSeverity = SEVERITIES.DEBUG,
defaultStructuredData = '-',
host = 'localhost',
port = 514,
key,
protocol = 'tcp',
}) => {
switch (protocol) {
case 'tls': {
return rfc5425({
ca,
cert,
checkServerIdentity,
defaultAppName,
defaultEol,
defaultFacility,
defaultHostname,
defaultMsgId,
defaultProcId,
defaultSeverity,
defaultStructuredData,
family: 0,
host,
hostname,
key,
port,
})
}
case 'tls4': {
return rfc5425({
ca,
cert,
checkServerIdentity,
defaultAppName,
defaultEol,
defaultFacility,
defaultHostname,
defaultMsgId,
defaultProcId,
defaultSeverity,
defaultStructuredData,
family: 4,
host,
hostname,
key,
port,
})
}
case 'tls6': {
return rfc5425({
ca,
cert,
checkServerIdentity,
defaultAppName,
defaultEol,
defaultFacility,
defaultHostname,
defaultMsgId,
defaultProcId,
defaultSeverity,
defaultStructuredData,
family: 6,
host,
hostname,
key,
port,
})
}
case 'tcp': {
return rfc6587({
defaultAppName,
defaultEol,
defaultFacility,
defaultHostname,
defaultMsgId,
defaultProcId,
defaultSeverity,
defaultStructuredData,
family: 0,
host,
hostname,
port,
})
}
case 'tcp4': {
return rfc6587({
defaultAppName,
defaultEol,
defaultFacility,
defaultHostname,
defaultMsgId,
defaultProcId,
defaultSeverity,
defaultStructuredData,
family: 4,
host,
hostname,
port,
})
}
case 'tcp6': {
return rfc6587({
defaultAppName,
defaultEol,
defaultFacility,
defaultHostname,
defaultMsgId,
defaultProcId,
defaultSeverity,
defaultStructuredData,
family: 6,
host,
hostname,
port,
})
}
case 'udp': {
return rfc5426({
defaultAppName,
defaultEol,
defaultFacility,
defaultHostname,
defaultMsgId,
defaultProcId,
defaultSeverity,
defaultStructuredData,
family: 0,
host,
hostname,
port,
})
}
case 'udp4': {
return rfc5426({
defaultAppName,
defaultEol,
defaultFacility,
defaultHostname,
defaultMsgId,
defaultProcId,
defaultSeverity,
defaultStructuredData,
family: 4,
host,
hostname,
port,
})
}
case 'udp6': {
return rfc5426({
defaultAppName,
defaultEol,
defaultFacility,
defaultHostname,
defaultMsgId,
defaultProcId,
defaultSeverity,
defaultStructuredData,
family: 6,
host,
hostname,
port,
})
}
default: {
throw Error('Unsupported protocol')
}
}
}
exports.syslog = syslog;