From 62d52ba43090b72d25a8dba2aa0e89b1c0df16d3 Mon Sep 17 00:00:00 2001 From: Sebastian Lagemann Date: Thu, 9 Mar 2023 13:22:04 +0100 Subject: [PATCH 1/3] migrated to jest from nodeunit for unit tests added addContext() support to log message for better convenience in user/context specific logging patterns fixed package.json node engine requirement to >=6.4.0 (ES6 support) --- README.md | 4 +- __tests__/basic-tests.js | 120 +++++++++++++++++++++++++++++++++++ classes/TcpConnectionPool.js | 6 +- index.js | 21 ++++-- package.json | 15 +++-- tests/test.js | 54 ---------------- 6 files changed, 152 insertions(+), 68 deletions(-) create mode 100644 __tests__/basic-tests.js delete mode 100644 tests/test.js diff --git a/README.md b/README.md index 81e133c..d609a35 100644 --- a/README.md +++ b/README.md @@ -51,6 +51,8 @@ Plain javascript }); var log = log4js.getLogger('tests'); + // adding context and add it as additional field to each log entry + log.addContext('customer', {id: 123, name: 'John Doe'}); log.error('hello hello'); ``` @@ -59,4 +61,4 @@ Plain javascript ## License -[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2FAigent%2Flog4js-logstash-tcp.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2FAigent%2Flog4js-logstash-tcp?ref=badge_large) \ No newline at end of file +[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2FAigent%2Flog4js-logstash-tcp.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2FAigent%2Flog4js-logstash-tcp?ref=badge_large) diff --git a/__tests__/basic-tests.js b/__tests__/basic-tests.js new file mode 100644 index 0000000..0da3213 --- /dev/null +++ b/__tests__/basic-tests.js @@ -0,0 +1,120 @@ +const net = require('net'); +const log4jsAppender = require('../index.js'); +const layouts = require('log4js/lib/layouts.js'); + +const context = {}; +beforeEach(() => { + return new Promise((resolve) => { + context.config = { + "category": "TEST", + "type": "log4js-logstashTCP", + "host": "localhost", + "port": 5050, + "fields": { + "instance": "MyAwsInstance", + "source": "myApp", + "environment": "development" + } + }; + + context.testServer = net.createServer({ + }); + context.testServer.on('listening', function () { + resolve(); + }); + context.testServer.listen(context.config.port, context.config.host); + }) +}) +afterEach(() => { + return new Promise((resolve) => { + context.testServer.close(); + context.testServer.on('close', function () { + delete context.testServer; + resolve(); + }); + // context.testServer.forceClose(); + }) +}) +describe('check basic usage', () => { + "use strict"; + + it('testing logger setup, configuration and sending log message', (done) => { + const message = 'Im awesome'; + let log = null; + + try { + log = log4jsAppender.configure(context.config, layouts); + } catch (e) { + return fail('configure shouldn\'t throw an error'); + } + + context.testServer.on('connection', function (con) { + con.on('data', function (data) { + expect(JSON.parse(data.toString())['message']).toBe(message); + log.shutdown(() => { + done(); + }) + }); + }); + log({startTime: new Date(), level: {levelStr: 'FOO'}, categoryName: 'BAR', data: [message]}); + }) + + it('testing logger setup, configuration and sending log message with context', (done) => { + const message = 'Im awesome'; + let log = null; + + try { + log = log4jsAppender.configure(context.config, layouts); + } catch (e) { + return fail('configure shouldn\'t throw an error'); + } + + context.testServer.on('connection', function (con) { + con.on('data', function (data) { + const parsedData = JSON.parse(data.toString()) + expect(parsedData['message']).toBe(message); + expect(parsedData['testContextField']).toBe('test context field value') + log.shutdown(() => { + done(); + }) + }); + }); + log({ + startTime: new Date(), + level: {levelStr: 'FOO'}, + categoryName: 'BAR', + data: [message], + context: { + testContextField: "test context field value" + }}); + }) + it('testing logger setup, configuration and sending log message with context but gets overwritten by log message', (done) => { + const message = 'Im awesome'; + let log = null; + + try { + log = log4jsAppender.configure(context.config, layouts); + } catch (e) { + return fail('configure shouldn\'t throw an error'); + } + + context.testServer.on('connection', function (con) { + con.on('data', function (data) { + const parsedData = JSON.parse(data.toString()); + expect(parsedData['message']).toBe(message); + expect(parsedData['testContextField']).toBe('context value overwritten'); + log.shutdown(() => { + done(); + }) + }); + }); + log({ + startTime: new Date(), + level: {levelStr: 'FOO'}, + categoryName: 'BAR', + data: [message, {testContextField: "context value overwritten"}], + context: { + testContextField: "test context field value" + }}); + }) +}) diff --git a/classes/TcpConnectionPool.js b/classes/TcpConnectionPool.js index 25ace01..9a5540a 100644 --- a/classes/TcpConnectionPool.js +++ b/classes/TcpConnectionPool.js @@ -100,7 +100,11 @@ class TcpConnectionPool { _createIndexForTcpConnection() { return Math.floor( Math.random() * this.config.maxTcpConnections) % this.config.maxTcpConnections; } - + close() { + for(let key in this.tcpConnections) { + this.tcpConnections[key].destroy(); + } + } } module.exports.TcpConnectionPool = TcpConnectionPool; diff --git a/index.js b/index.js index 188cc37..cd371f0 100644 --- a/index.js +++ b/index.js @@ -1,7 +1,5 @@ 'use strict'; -const util = require('util'); - const TcpConnectionPool = require("./classes/TcpConnectionPool").TcpConnectionPool; const tcpConnectionPool = new TcpConnectionPool(); @@ -34,11 +32,7 @@ function logstashTCP(config, layout) { return true; } - if ((!logUnderFields) && (argsValue === 'direct')) { - return true; - } - - return false; + return (!logUnderFields) && (argsValue === 'direct'); } function log(loggingEvent) { @@ -64,6 +58,18 @@ function logstashTCP(config, layout) { Object.keys(config.fields).forEach((key) => { fields[key] = typeof config.fields[key] === 'function' ? config.fields[key](loggingEvent) : config.fields[key]; }); + // adding context to log messages as separate fields added by logger.addContext(key, value) + if(loggingEvent.context) { + if(loggingEvent.data.length === 1 || !loggingEvent.data[1]) { + loggingEvent.data[1] = {} + } + if(typeof loggingEvent.data[1] === 'object') { + loggingEvent.data[1] = { + ...loggingEvent.context, + ...loggingEvent.data[1] + } + } + } /* eslint no-prototype-builtins:1,no-restricted-syntax:[1, "ForInStatement"] */ if (loggingEvent.data.length > 1) { @@ -96,6 +102,7 @@ function logstashTCP(config, layout) { } log.shutdown = function (cb) { + tcpConnectionPool.close(); cb(); }; diff --git a/package.json b/package.json index eb8988e..1ec0a22 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "log4js-logstash-tcp", - "version": "2.0.3", + "version": "2.0.4", "author": "Aigent B.V.", "description": "This is a copy of the logstashUDP appender but instead sending via UDP send via TCP to avoid the maximum 64k bytes message size with the logstashUDP appender.", "keywords": [ @@ -12,6 +12,11 @@ { "name": "Sebastian Lagemann", "email": "sebastian@aigent.com" + }, + { + "name": "Sebastian Lagemann", + "email": "sebastian@slab.rocks", + "url": "https://slab.rocks" } ], "licenses": [ @@ -25,14 +30,14 @@ "url": "https://github.com/Aigent/log4js-logstash-tcp.git" }, "devDependencies": { - "log4js": "^2.4.1", - "nodeunit": "^0.11.2" + "jest": "^29.5.0", + "log4js": "^6.9.1" }, "scripts": { - "test": "nodeunit tests" + "test": "jest --coverage --detectOpenHandles" }, "engines": { - "node": "0.10.x" + "node": ">=6.4.0" }, "main": "./index.js" } diff --git a/tests/test.js b/tests/test.js deleted file mode 100644 index ab1015a..0000000 --- a/tests/test.js +++ /dev/null @@ -1,54 +0,0 @@ -var net = require('net'), - log4jsAppender = require('../index.js'), - layouts = require('log4js/lib/layouts.js'); - -module.exports = { - setUp: function (callback) { - "use strict"; - var self = this; - this.config = { - "category": "TEST", - "type": "log4js-logstashTCP", - "host": "localhost", - "port": 5050, - "fields": { - "instance": "MyAwsInstance", - "source": "myApp", - "environment": "development" - } - }; - - this.testServer = net.createServer(); - this.testServer.on('listening', function () { - callback(); - }); - this.testServer.listen(this.config.port, this.config.host); - }, - tearDown: function (callback) { - "use strict"; - var self = this; - this.testServer.on('close', function () { - callback(); - }); - this.testServer.close(); - }, - 'Check basic usage': function (test) { - "use strict"; - test.expect(2); - var self = this, - message = 'Im awesome', - log; - - test.doesNotThrow(function () { - log = log4jsAppender.configure(self.config, layouts); - }, 'Error', 'Configure shouldn\'t throw an error'); - - this.testServer.on('connection', function (con) { - con.on('data', function (data) { - test.strictEqual(JSON.parse(data.toString())['message'], message, 'data should be message!'); - test.done(); - }); - }); - log({ startTime: new Date(), level: {levelStr: 'FOO'}, categoryName: 'BAR', data: [message] }); - } -}; From 23c05e40cf30046120754fd29cf613ef53c6745a Mon Sep 17 00:00:00 2001 From: Lam Wei Li Date: Fri, 21 Apr 2023 14:04:38 +0800 Subject: [PATCH 2/3] fix: flush before shutdown --- classes/TcpConnectionPool.js | 31 +++++++++++++++++++++++++------ index.js | 3 +-- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/classes/TcpConnectionPool.js b/classes/TcpConnectionPool.js index 9a5540a..f055c4b 100644 --- a/classes/TcpConnectionPool.js +++ b/classes/TcpConnectionPool.js @@ -54,9 +54,14 @@ class TcpConnectionWrapper extends EventEmitter { } - destroy() { - this.connection.end(); - this.connection.destroy(); + destroy(cb) { + const self = this; + this.connection.end(() => { + self.connection.destroy(); + if (typeof cb === "function") { + cb(); + } + }); this.emit("destroy"); } @@ -100,9 +105,23 @@ class TcpConnectionPool { _createIndexForTcpConnection() { return Math.floor( Math.random() * this.config.maxTcpConnections) % this.config.maxTcpConnections; } - close() { - for(let key in this.tcpConnections) { - this.tcpConnections[key].destroy(); + + close(cb) { + const connections = []; + Object.keys(this.tcpConnections).forEach((index) => { + connections.push(this.tcpConnections[index]); + }); + + let completed = 0; + const complete = () => { + if (++completed >= connections.length) { + if (typeof cb === "function") { + cb(); + } + } + }; + for (const tcpConnectionWrapper of connections) { + tcpConnectionWrapper.destroy(complete); } } } diff --git a/index.js b/index.js index cd371f0..1494c37 100644 --- a/index.js +++ b/index.js @@ -102,8 +102,7 @@ function logstashTCP(config, layout) { } log.shutdown = function (cb) { - tcpConnectionPool.close(); - cb(); + tcpConnectionPool.close(cb); }; return log; From 06ec449aaff2dec15a07f3cbcd6d1fcbd4d67a3a Mon Sep 17 00:00:00 2001 From: Sebastian Lagemann Date: Fri, 21 Apr 2023 10:11:21 +0200 Subject: [PATCH 3/3] fixing FOSSA & Travis integration change Github repo in package.json since previous repository seems to be unattended pushing version to 2.0.5 --- README.md | 6 +++--- package.json | 10 +++++++--- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index d609a35..24a95f6 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ log4js-logstash-tcp =============== -[![Build Status](https://travis-ci.org/Aigent/log4js-logstash-tcp.svg?branch=master)](https://travis-ci.org/Aigent/log4js-logstash-tcp) -[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2FAigent%2Flog4js-logstash-tcp.svg?type=shield)](https://app.fossa.io/projects/git%2Bgithub.com%2FAigent%2Flog4js-logstash-tcp?ref=badge_shield) +[![Build Status](https://app.travis-ci.com/Degola/log4js-logstash-tcp.svg?branch=master)](https://app.travis-ci.com/Degola/log4js-logstash-tcp) +[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FAigent%2Flog4js-logstash-tcp.svg?type=shield)](https://app.fossa.com/projects/git%2Bgithub.com%2FAigent%2Flog4js-logstash-tcp?ref=badge_shield) This is a copy of the logstashUDP appender but instead sending via UDP send via TCP to avoid the maximum 64k bytes message size with the logstashUDP appender @@ -61,4 +61,4 @@ Plain javascript ## License -[![FOSSA Status](https://app.fossa.io/api/projects/git%2Bgithub.com%2FAigent%2Flog4js-logstash-tcp.svg?type=large)](https://app.fossa.io/projects/git%2Bgithub.com%2FAigent%2Flog4js-logstash-tcp?ref=badge_large) +[![FOSSA Status](https://app.fossa.com/api/projects/git%2Bgithub.com%2FAigent%2Flog4js-logstash-tcp.svg?type=large)](https://app.fossa.com/projects/git%2Bgithub.com%2FAigent%2Flog4js-logstash-tcp?ref=badge_large) diff --git a/package.json b/package.json index 1ec0a22..670c069 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "log4js-logstash-tcp", - "version": "2.0.4", - "author": "Aigent B.V.", + "version": "2.0.5", + "author": "Sebastian Lagemann (https://slab.rocks)", "description": "This is a copy of the logstashUDP appender but instead sending via UDP send via TCP to avoid the maximum 64k bytes message size with the logstashUDP appender.", "keywords": [ "logstash", @@ -17,6 +17,10 @@ "name": "Sebastian Lagemann", "email": "sebastian@slab.rocks", "url": "https://slab.rocks" + }, + { + "name": "Lam Wei Li", + "url": "https://github.com/lamweili" } ], "licenses": [ @@ -27,7 +31,7 @@ ], "repository": { "type": "git", - "url": "https://github.com/Aigent/log4js-logstash-tcp.git" + "url": "https://github.com/Degola/log4js-logstash-tcp" }, "devDependencies": { "jest": "^29.5.0",