Compare commits
5 Commits
syslog-v1.
...
syslog-v1.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2f2a832db1 | ||
|
|
a0d9f7fe75 | ||
|
|
af89e04c89 | ||
|
|
123a11ee9d | ||
|
|
3a22716df6 |
44
index.js
44
index.js
@@ -2,20 +2,17 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
var dgram = require('dgram'),
|
||||
fs = require('fs'),
|
||||
path = require('path'),
|
||||
mkdirp = require('mkdirp'),
|
||||
parser = require('nsyslog-parser');
|
||||
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 LOG_FILE_FOLDER = argv.logdir;
|
||||
const LOG_FILE_NAME = 'app.log';
|
||||
const PORT = argv.port;
|
||||
const options = {
|
||||
logFolder: argv.logdir,
|
||||
port: argv.port
|
||||
};
|
||||
|
||||
if (argv.version) {
|
||||
console.log('1.0.1');
|
||||
@@ -27,34 +24,13 @@ console.log('==========================================');
|
||||
console.log(' Cloudron Syslog Daemon ');
|
||||
console.log('==========================================');
|
||||
console.log();
|
||||
console.log(' Log Folder: ', LOG_FILE_FOLDER);
|
||||
console.log(' UDP Port: ', PORT);
|
||||
console.log(' Log Folder: ', options.logFolder);
|
||||
console.log(' UDP Port: ', options.port);
|
||||
console.log();
|
||||
console.log('==========================================');
|
||||
console.log();
|
||||
|
||||
var server = dgram.createSocket('udp4');
|
||||
|
||||
server.on('error', function (error) {
|
||||
console.error('Error:', error);
|
||||
}).on('listening', function () {
|
||||
server.start(options, function (error) {
|
||||
if (error) return console.error(error);
|
||||
console.log('Listening...');
|
||||
}).on('message', function (msg, rinfo) {
|
||||
var 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(LOG_FILE_FOLDER, info.appName);
|
||||
const fileName = path.join(filePath, LOG_FILE_NAME);
|
||||
|
||||
try {
|
||||
mkdirp.sync(filePath);
|
||||
fs.appendFileSync(fileName, info.ts.toISOString() + ' ' + message + '\n');
|
||||
} catch (error) {
|
||||
console.error(error);
|
||||
}
|
||||
}).bind(PORT);
|
||||
});
|
||||
|
||||
1812
package-lock.json
generated
1812
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
14
package.json
14
package.json
@@ -1,10 +1,10 @@
|
||||
{
|
||||
"name": "cloudron-syslog",
|
||||
"version": "1.0.2",
|
||||
"version": "1.1.0",
|
||||
"description": "Cloudron Syslog Daemon listening on port 2514",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "echo \"Error: no test specified\" && exit 1"
|
||||
"test": "./node_modules/.bin/mocha ./test.js"
|
||||
},
|
||||
"bin": {
|
||||
"cloudron-syslog": "./index.js"
|
||||
@@ -16,8 +16,12 @@
|
||||
"author": "Cloudron Developers",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"mkdirp": "^0.5.1",
|
||||
"nsyslog-parser": "^0.8.1",
|
||||
"yargs": "^11.0.0"
|
||||
"nsyslog-parser": "^0.9.6",
|
||||
"yargs": "^17.3.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"expect.js": "^0.3.1",
|
||||
"grepit": "^1.0.0",
|
||||
"mocha": "^9.1.3"
|
||||
}
|
||||
}
|
||||
|
||||
61
server.js
Normal file
61
server.js
Normal 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
test.js
Normal file
89
test.js
Normal 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);
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user