From 705dafa4e482e718909f7ffee63e3666bcc5cf51 Mon Sep 17 00:00:00 2001 From: Klemek Date: Sat, 22 Jun 2019 10:02:25 +0200 Subject: [PATCH] Error log option --- .gitignore | 3 ++- README.md | 2 ++ src/app.js | 14 ++++++++++---- src/config.default.json | 1 + test/app.test.js | 41 ++++++++++++++++++++++++++++++++++++++++- test/test_utils.js | 13 +++++++++++-- 6 files changed, 66 insertions(+), 8 deletions(-) diff --git a/.gitignore b/.gitignore index 606eb9f..54ecd89 100644 --- a/.gitignore +++ b/.gitignore @@ -4,4 +4,5 @@ /config.example.json /data /test_data -/git_secret \ No newline at end of file +/access.log +/error.log \ No newline at end of file diff --git a/README.md b/README.md index 73a3e88..f32b751 100644 --- a/README.md +++ b/README.md @@ -198,6 +198,8 @@ Any URL like `/year/month/day/anything/` will redirect to this article (and link the Express view engine used to render pages from templates * `access_log` (default: access.log) log file where to save access requests (empty to disable) +* `error_log` (default: error.log) + log file where to save all server errors (empty to disable) * `modules` * `rss` (default: true) activate the RSS endpoint and its features diff --git a/src/app.js b/src/app.js index 1e9d38a..08b72a0 100644 --- a/src/app.js +++ b/src/app.js @@ -102,7 +102,7 @@ module.exports = (config) => { const end = res.end; res.end = (chunk, encoding) => { fs.appendFile(config['access_log'], - res.statusCode + ' ' + req.method + ' ' + req.url + ' ' + new Date().toUTCString() + ' ' + (req.ips.join(' ') || req.ip) + '\n', + `${res.statusCode} ${req.method} ${req.url} ${new Date().toUTCString()} ${req.ips.join(' ') || req.ip}\n`, {encoding: 'UTF-8'}, () => { res.end = end; res.end(chunk, encoding); @@ -235,10 +235,16 @@ module.exports = (config) => { res.status(400).send('bad request'); }); + //log all server errors app.use((err, req, res, next) => { - console.log(cons.error, `error when handling ${req.path} request : ${err}`); - console.error(err.stack); - next(err); + console.log(cons.error, `error when handling ${req.method} ${req.path} request : ${err}`); + if (!config['error_log']) + next(err); + fs.appendFile(config['error_log'], + `500 ${req.method} ${req.url} ${new Date().toUTCString()} ${req.ips.join(' ') || req.ip}\n${err.stack}\n`, + {encoding: 'UTF-8'}, () => { + next(err); + }); }); // must be use in a server.js to start the server diff --git a/src/config.default.json b/src/config.default.json index 7c9c6b4..5f39d6e 100644 --- a/src/config.default.json +++ b/src/config.default.json @@ -3,6 +3,7 @@ "data_dir": "data", "view_engine": "ejs", "access_log": "access.log", + "error_log": "error.log", "modules": { "rss": true, "webhook": true, diff --git a/test/app.test.js b/test/app.test.js index 7b5d6d1..bea34ec 100644 --- a/test/app.test.js +++ b/test/app.test.js @@ -21,6 +21,7 @@ config['rss']['endpoint'] = '/rsstest'; config['rss']['length'] = 2; config['webhook']['endpoint'] = '/webhooktest'; config['access_log'] = path.join(dataDir, 'access.log'); +config['error_log'] = path.join(dataDir, 'error.log'); const app = require('../src/app')(config); @@ -36,7 +37,16 @@ afterAll(() => { } }); -describe('Test logging', () => { +describe('Test request logging', () => { + test('test no log', (done) => { + const tmp = config['access_log']; + config['access_log'] = ''; + request(app).get('/rsstest').then(() => { + config['access_log'] = tmp; + expect(fs.existsSync(path.join(dataDir, 'access.log'))).toBe(false); + done(); + }); + }); test('test get 200', (done) => { request(app).get('/rsstest').then(() => { fs.readFile(path.join(dataDir, 'access.log'), {encoding: 'UTF-8'}, (err, data) => { @@ -69,6 +79,35 @@ describe('Test logging', () => { }); }); +describe('Test error logging', () => { + test('test no log', (done) => { + const tmp = config['home']['hidden']; + config['home']['hidden'] = null; + const tmp2 = config['errpr_log']; + config['error_log'] = ''; + request(app).get('/somefile.txt').then(() => { + config['home']['hidden'] = tmp; + config['error_log'] = tmp2; + expect(fs.existsSync(path.join(dataDir, 'error.log'))).toBe(false); + done(); + }); + }); + test('test null error ', (done) => { + const tmp = config['home']['hidden']; + config['home']['hidden'] = null; + request(app).get('/somefile.txt').then(() => { + config['home']['hidden'] = tmp; + fs.readFile(path.join(dataDir, 'error.log'), {encoding: 'UTF-8'}, (err, data) => { + expect(err).toBeNull(); + const start = data.split('\n').slice(0, 2).join('\n'); + const expected = '500 GET /somefile.txt ' + new Date().toUTCString() + ' ::ffff:127.0.0.1\nTypeError: Cannot read property \'includes\' of null'; + expect(start).toBe(expected); + done(); + }); + }); + }); +}); + describe('Test root path', () => { test('404 no index no error', (done) => { request(app).get('/').then((response) => { diff --git a/test/test_utils.js b/test/test_utils.js index 6b509c5..8c250f5 100644 --- a/test/test_utils.js +++ b/test/test_utils.js @@ -4,12 +4,21 @@ const path = require('path'); const deleteFolderSync = (dir) => { if (!fs.existsSync(dir)) return; - fs.readdirSync(dir, {withFileTypes: true}).forEach((item) => { + let items; + const deleteItem = (item) => { if (item.isDirectory()) deleteFolderSync(path.join(dir, item.name)); else fs.unlinkSync(path.join(dir, item.name)); - }); + }; + do { + items = fs.readdirSync(dir, {withFileTypes: true}); + try { + items.forEach(deleteItem); + } catch (e) { + console.error(e); + } + } while (items.length > 0); fs.rmdirSync(dir); };