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';
|
'use strict';
|
||||||
|
|
||||||
var dgram = require('dgram'),
|
const server = require('./server.js');
|
||||||
fs = require('fs'),
|
|
||||||
path = require('path'),
|
|
||||||
mkdirp = require('mkdirp'),
|
|
||||||
parser = require('nsyslog-parser');
|
|
||||||
|
|
||||||
const argv = require('yargs')
|
const argv = require('yargs')
|
||||||
.default('port', 2514, 'The port to listen on') // syslog is 514 so we prefix with 2
|
.default('port', 2514, 'The port to listen on') // syslog is 514 so we prefix with 2
|
||||||
.default('logdir', '/tmp/logs', 'The root log directory')
|
.default('logdir', '/tmp/logs', 'The root log directory')
|
||||||
.argv;
|
.argv;
|
||||||
|
|
||||||
const LOG_FILE_FOLDER = argv.logdir;
|
const options = {
|
||||||
const LOG_FILE_NAME = 'app.log';
|
logFolder: argv.logdir,
|
||||||
const PORT = argv.port;
|
port: argv.port
|
||||||
|
};
|
||||||
|
|
||||||
if (argv.version) {
|
if (argv.version) {
|
||||||
console.log('1.0.1');
|
console.log('1.0.1');
|
||||||
@@ -27,34 +24,13 @@ console.log('==========================================');
|
|||||||
console.log(' Cloudron Syslog Daemon ');
|
console.log(' Cloudron Syslog Daemon ');
|
||||||
console.log('==========================================');
|
console.log('==========================================');
|
||||||
console.log();
|
console.log();
|
||||||
console.log(' Log Folder: ', LOG_FILE_FOLDER);
|
console.log(' Log Folder: ', options.logFolder);
|
||||||
console.log(' UDP Port: ', PORT);
|
console.log(' UDP Port: ', options.port);
|
||||||
console.log();
|
console.log();
|
||||||
console.log('==========================================');
|
console.log('==========================================');
|
||||||
console.log();
|
console.log();
|
||||||
|
|
||||||
var server = dgram.createSocket('udp4');
|
server.start(options, function (error) {
|
||||||
|
if (error) return console.error(error);
|
||||||
server.on('error', function (error) {
|
|
||||||
console.error('Error:', error);
|
|
||||||
}).on('listening', function () {
|
|
||||||
console.log('Listening...');
|
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);
|
|
||||||
|
|||||||
1798
package-lock.json
generated
1798
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",
|
"name": "cloudron-syslog",
|
||||||
"version": "1.0.2",
|
"version": "1.1.0",
|
||||||
"description": "Cloudron Syslog Daemon listening on port 2514",
|
"description": "Cloudron Syslog Daemon listening on port 2514",
|
||||||
"main": "index.js",
|
"main": "index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "echo \"Error: no test specified\" && exit 1"
|
"test": "./node_modules/.bin/mocha ./test.js"
|
||||||
},
|
},
|
||||||
"bin": {
|
"bin": {
|
||||||
"cloudron-syslog": "./index.js"
|
"cloudron-syslog": "./index.js"
|
||||||
@@ -16,8 +16,12 @@
|
|||||||
"author": "Cloudron Developers",
|
"author": "Cloudron Developers",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"mkdirp": "^0.5.1",
|
"nsyslog-parser": "^0.9.6",
|
||||||
"nsyslog-parser": "^0.8.1",
|
"yargs": "^17.3.1"
|
||||||
"yargs": "^11.0.0"
|
},
|
||||||
|
"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