Files
cloudron-box/syslog.js
Girish Ramakrishnan 8c0c9981de remove usage of nsyslog-parser-2
this module is somehow parsing the syslog incorrectly causing
incorrect directories being created in the logs directory
(since appName got parsed incorrectly)
2024-09-10 13:09:43 +02:00

106 lines
3.4 KiB
JavaScript
Executable File

#!/usr/bin/env node
'use strict';
exports = module.exports = {
start,
stop
};
const debug = require('debug')('syslog:server'),
fs = require('fs'),
net = require('net'),
path = require('path'),
paths = require('./src/paths.js'),
util = require('util');
let gServer = null;
// https://docs.docker.com/engine/logging/drivers/syslog/
// example: <34>1 2023-09-07T14:33:22Z myhost myapp 1234 5678 [exampleSDID@32473 iut="3" eventSource="Application"] An example message
function parseRFC5424Message(message) {
const syslogRegex = /^<(\d+)>(\d+) (\S+) (\S+) (\S+) (\S+) (\S+) (?:\[(.*?)\])?(.*)$/;
const match = message.match(syslogRegex);
if (!match) return null;
const [, pri, version, timestamp, hostname, appName, procId, msgId, structuredData, msg] = match;
return {
pri: parseInt(pri, 10), // priority
version: parseInt(version, 10), // version
timestamp, // timestamp
hostname, // hostname
appName, // app name
procId, // process ID
msgId, // message ID
structuredData: structuredData ? structuredData : null, // structured data (if present)
msg: msg ? msg.trim() : null // message
};
}
async function start() {
debug('==========================================');
debug(' Cloudron Syslog Daemon ');
debug('==========================================');
gServer = net.createServer();
gServer.on('error', function (error) {
console.error(`server error: ${error}`);
});
gServer.on('connection', function (socket) {
socket.on('data', function (msg) {
const lines = msg.toString().split('\n'); // may be multiline data
for (const msg of lines) {
const info = parseRFC5424Message(msg);
if (!info || !info.appName) return debug('Ignore unknown app log:', msg);
// remove line breaks to avoid holes in the log file
// we do not ignore empty log lines, to allow gaps for potential ease of readability
const message = info.message.replace(/[\n\r]+/g, '');
const appLogDir = path.join(paths.LOG_DIR, info.appName);
try {
fs.mkdirSync(appLogDir, { recursive: true });
fs.appendFileSync(`${appLogDir}/app.log`, `${info.timestamp} ${message}\n}`);
} catch (error) {
debug(error);
}
}
});
socket.on('error', function (error) {
debug(`socket error: ${error}`);
});
});
await fs.promises.rm(paths.SYSLOG_SOCKET_FILE, { force: true });
await util.promisify(gServer.listen.bind(gServer))(paths.SYSLOG_SOCKET_FILE);
debug(`Listening on ${paths.SYSLOG_SOCKET_FILE}`);
}
async function stop() {
await fs.promises.rm(paths.SYSLOG_SOCKET_FILE, { force: true });
gServer.unref(); // TODO : cleanup client connections. otherwise server.close() won't return
gServer = null;
}
async function main() {
await start();
process.on('SIGTERM', async function () {
debug('Received SIGTERM. Shutting down.');
await stop();
setTimeout(process.exit.bind(process), 1000);
});
}
if (require.main === module) {
main();
}