Merge remote-tracking branch 'syslog/master'

Merge cloudron-syslog code here
This commit is contained in:
Girish Ramakrishnan
2023-04-14 19:17:36 +02:00
7 changed files with 1865 additions and 0 deletions
+1
View File
@@ -0,0 +1 @@
node_modules/
+9
View File
@@ -0,0 +1,9 @@
{
"node": true,
"browser": true,
"unused": true,
"multistr": true,
"globalstrict": true,
"predef": [ "angular", "$" ],
"esnext": true
}
+36
View File
@@ -0,0 +1,36 @@
#!/usr/bin/env node
'use strict';
const server = require('./server.js');
const argv = require('yargs')
.default('port', 2514, 'The port to listen on') // syslog is 514 so we prefix with 2
.default('logdir', '/tmp/logs', 'The root log directory')
.argv;
const options = {
logFolder: argv.logdir,
port: argv.port
};
if (argv.version) {
console.log('1.0.1');
process.exit(0);
}
console.log();
console.log('==========================================');
console.log(' Cloudron Syslog Daemon ');
console.log('==========================================');
console.log();
console.log(' Log Folder: ', options.logFolder);
console.log(' UDP Port: ', options.port);
console.log();
console.log('==========================================');
console.log();
server.start(options, function (error) {
if (error) return console.error(error);
console.log('Listening...');
});
+1642
View File
File diff suppressed because it is too large Load Diff
+27
View File
@@ -0,0 +1,27 @@
{
"name": "cloudron-syslog",
"version": "1.1.0",
"description": "Cloudron Syslog Daemon listening on port 2514",
"main": "index.js",
"scripts": {
"test": "./node_modules/.bin/mocha ./test.js"
},
"bin": {
"cloudron-syslog": "./index.js"
},
"repository": {
"type": "git",
"url": "https://git.cloudron.io/cloudron/cloudron-syslog.git"
},
"author": "Cloudron Developers",
"license": "MIT",
"dependencies": {
"nsyslog-parser": "^0.9.6",
"yargs": "^17.3.1"
},
"devDependencies": {
"expect.js": "^0.3.1",
"grepit": "^1.0.0",
"mocha": "^9.1.3"
}
}
+61
View File
@@ -0,0 +1,61 @@
'use strict';
exports = module.exports = {
start,
stop
};
const LOG_FILENAME = 'app.log';
const assert = require('assert'),
dgram = require('dgram'),
fs = require('fs'),
path = require('path'),
parser = require('nsyslog-parser');
let server = null;
function start(options, callback) {
assert.strictEqual(typeof options, 'object');
assert.strictEqual(typeof options.port, 'number');
assert.strictEqual(typeof options.logFolder, 'string');
assert.strictEqual(typeof callback, 'function');
server = dgram.createSocket('udp4');
server.on('error', function (error) {
callback(error);
}).on('listening', function () {
callback();
}).on('message', function (msg /*, rinfo */) {
const info = parser(msg.toString());
if (!info || !info.appName) return console.log('Ignore unknown app log:', msg.toString());
// 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/g, '');
const filePath = path.join(options.logFolder, info.appName);
const fileName = path.join(filePath, LOG_FILENAME);
try {
fs.mkdirSync(filePath, { recursive: true });
fs.appendFileSync(fileName, info.ts.toISOString() + ' ' + message + '\n');
} catch (error) {
console.error(error);
}
}).bind(options.port);
}
function stop(callback) {
assert.strictEqual(typeof callback, 'function');
if (!server) return callback();
server.close();
server = null;
callback();
}
+89
View File
@@ -0,0 +1,89 @@
#!/usr/bin/env node
/* global it:false */
/* global describe:false */
/* global after:false */
'use strict';
const dgram = require('dgram'),
expect = require('expect.js'),
fs = require('fs'),
grepit = require('grepit'),
path = require('path'),
os = require('os'),
server = require('./server.js');
const PORT = 5678;
const LOG_FOLDER = path.join(os.tmpdir(), '/cloudron-syslog-test/');
function sendMessage(message, callback) {
const client = dgram.createSocket('udp4');
client.send(message, PORT, 'localhost', function (error) {
if (error) return callback(error);
client.close();
callback();
});
}
function verifyMessage(pattern, fileName, callback) {
// give the server some time to write to disk
setTimeout(function () {
const found = grepit(pattern, path.join(LOG_FOLDER, fileName));
if (found.length === 0) return callback('not found');
callback();
}, 250);
}
describe('Daemon', function () {
this.timeout(5000);
after(function (done) {
fs.rmSync(LOG_FOLDER, { recursive: true, force: true });
server.stop(done);
});
it('can start', function (done) {
server.start({ port: PORT, logFolder: LOG_FOLDER }, function (error) {
expect(error).to.not.be.ok();
done();
});
});
it('handle good message', function (done) {
// IETF (RFC 5424) message, with structured data and chained hostnames
const ietfLine = '<110>1 2009-05-03T14:00:39.529966+02:00 host.example.org/relay.example.org testapp 2138 - [exampleSDID@32473 iut="3" eventSource="Application" eventID="1011"][exampleSDID@32474 iut="4" eventSource="Application" eventID="1012"][ssign VER="0111" RSID="1" SG="0" SPRI="0" GBC="2" FMN="1" CNT="7" HB="K6wzcombEvKJ+UTMcn9bPryAeaU= zrkDcIeaDluypaPCY8WWzwHpPok= zgrWOdpx16ADc7UmckyIFY53icE= XfopJ+S8/hODapiBBCgVQaLqBKg= J67gKMFl/OauTC20ibbydwIlJC8= M5GziVgB6KPY3ERU1HXdSi2vtdw= Wxd/lU7uG/ipEYT9xeqnsfohyH0=" SIGN="AKBbX4J7QkrwuwdbV7Taujk2lvOf8gCgC62We1QYfnrNHz7FzAvdySuMyfM="] BOMAn application event log entry';
sendMessage(ietfLine, function (error) {
expect(error).to.not.be.ok();
verifyMessage(/An application event log entry/, 'testapp/app.log', done);
});
});
it('ignores invalid message', function (done) {
const invalidLine = 'foobar';
sendMessage(invalidLine, function (error) {
expect(error).to.not.be.ok();
verifyMessage(/foobar/, 'testapp/app.log', function (error) {
expect(error).to.be.ok();
done();
});
});
});
it('can handle message with :', function (done) {
// this is what we see from docker syslog
const message = '<30>1 2018-06-24T22:22:53Z my.test.com testapp 26599 testapp - This: contains two : colons';
sendMessage(message, function (error) {
expect(error).to.not.be.ok();
verifyMessage(/This: contains two : colons/, 'testapp/app.log', done);
});
});
});