logzio-nodejs
Advanced tools
Comparing version 0.4.16 to 1.0.1
module.exports = { | ||
"env": { | ||
"node": true, | ||
"es6": true, | ||
}, | ||
"extends": "eslint:recommended", | ||
"parserOptions": { | ||
"ecmaVersion": 5 | ||
}, | ||
"extends": "airbnb-base", | ||
"rules": { | ||
@@ -25,4 +23,6 @@ "indent": [ | ||
"always" | ||
] | ||
], | ||
"no-param-reassign": 1, | ||
"no-underscore-dangle": 0 | ||
} | ||
}; |
@@ -1,303 +0,319 @@ | ||
var request = require('request'); | ||
var stringifySafe = require('json-stringify-safe'); | ||
var _assign = require('lodash.assign'); | ||
var dgram = require('dgram'); | ||
var zlib = require('zlib'); | ||
const request = require('request-promise'); | ||
const stringifySafe = require('json-stringify-safe'); | ||
const assign = require('lodash.assign'); | ||
const dgram = require('dgram'); | ||
const zlib = require('zlib'); | ||
exports.version = require('../package.json').version; | ||
var LogzioLogger = function (options) { | ||
if (!options || !options.token) { | ||
throw new Error('You are required to supply a token for logging.'); | ||
const jsonToString = (json) => { | ||
try { | ||
return JSON.stringify(json); | ||
} catch (ex) { | ||
return stringifySafe(json, null, null, () => { }); | ||
} | ||
}; | ||
this.token = options.token; | ||
this.host = options.host || 'listener.logz.io'; | ||
this.userAgent = 'Logzio-Logger NodeJS'; | ||
this.type = options.type || 'nodejs'; | ||
this.sendIntervalMs = options.sendIntervalMs || 10 * 1000; | ||
this.bufferSize = options.bufferSize || 100; | ||
this.debug = options.debug || false; | ||
this.numberOfRetries = options.numberOfRetries || 3; | ||
this.timer = null; | ||
this.closed = false; | ||
this.supressErrors = options.supressErrors || false; | ||
this.addTimestampWithNanoSecs = options.addTimestampWithNanoSecs || false; | ||
this.compress = options.compress || false; | ||
this.internalLogger = options.internalLogger || console; | ||
const messagesToBody = messages => messages.reduce((body, msg) => `${body}${jsonToString(msg)}\n`, ''); | ||
var protocolToPortMap = { | ||
'udp': 5050, | ||
'http': 8070, | ||
'https': 8071 | ||
}; | ||
this.protocol = options.protocol || 'http'; | ||
if (!protocolToPortMap.hasOwnProperty(this.protocol)) { | ||
throw new Error('Invalid protocol defined. Valid options are : ' + JSON.stringify(Object.keys(protocolToPortMap))); | ||
} | ||
this.port = options.port || protocolToPortMap[this.protocol]; | ||
const UNAVAILABLE_CODES = ['ETIMEDOUT', 'ECONNRESET', 'ESOCKETTIMEDOUT', 'ECONNABORTED']; | ||
if (this.protocol === 'udp') { | ||
this.udpClient = dgram.createSocket('udp4'); | ||
} | ||
const zlibPromised = body => new Promise(((resolve, reject) => { | ||
zlib.gzip(body, (err, res) => { | ||
if (err) return reject(err); | ||
return resolve(res); | ||
}); | ||
})); | ||
/* | ||
Callback method executed on each bulk of messages sent to logzio. | ||
If the bulk failed, it will be called: callback(exception), otherwise upon | ||
success it will called as callback() | ||
*/ | ||
this.callback = options.callback || this._defaultCallback; | ||
const protocolToPortMap = { | ||
udp: 5050, | ||
http: 8070, | ||
https: 8071, | ||
}; | ||
/* | ||
* the read/write/connection timeout in milliseconds of the outgoing HTTP request | ||
*/ | ||
this.timeout = options.timeout; | ||
const USER_AGENT = 'Logzio-Logger NodeJS'; | ||
// build the url for logging | ||
this.url = this.protocol + '://' + this.host + ':' + this.port + '?token=' + this.token; | ||
class LogzioLogger { | ||
constructor({ | ||
token, | ||
host = 'listener.logz.io', | ||
type = 'nodejs', | ||
sendIntervalMs = 10 * 1000, | ||
bufferSize = 100, | ||
debug = false, | ||
numberOfRetries = 3, | ||
supressErrors = false, | ||
addTimestampWithNanoSecs = false, | ||
compress = false, | ||
internalLogger = console, | ||
protocol = 'http', | ||
port, | ||
timeout, | ||
sleepUntilNextRetry = 2 * 1000, | ||
callback = this._defaultCallback, | ||
extraFields = {}, | ||
}) { | ||
if (!token) { | ||
throw new Error('You are required to supply a token for logging.'); | ||
} | ||
this.messages = []; | ||
this.bulkId = 1; | ||
this.extraFields = options.extraFields || {}; | ||
}; | ||
this.token = token; | ||
this.host = host; | ||
this.type = type; | ||
this.sendIntervalMs = sendIntervalMs; | ||
this.bufferSize = bufferSize; | ||
this.debug = debug; | ||
this.numberOfRetries = numberOfRetries; | ||
this.supressErrors = supressErrors; | ||
this.addTimestampWithNanoSecs = addTimestampWithNanoSecs; | ||
this.compress = compress; | ||
this.internalLogger = internalLogger; | ||
this.sleepUntilNextRetry = sleepUntilNextRetry; | ||
exports.createLogger = function (options) { | ||
var l = new LogzioLogger(options); | ||
l._timerSend(); | ||
return l; | ||
}; | ||
this.timer = null; | ||
this.closed = false; | ||
var jsonToString = exports.jsonToString = function (json) { | ||
try { | ||
return JSON.stringify(json); | ||
} catch (ex) { | ||
return stringifySafe(json, null, null, function () {}); | ||
} | ||
}; | ||
this.protocol = protocol; | ||
this._setProtocol(port); | ||
LogzioLogger.prototype._defaultCallback = function (err) { | ||
if (err && !this.supressErrors) { | ||
this.internalLogger.log('logzio-logger error: ' + err, err); | ||
} | ||
}; | ||
/* | ||
Callback method executed on each bulk of messages sent to logzio. | ||
If the bulk failed, it will be called: callback(exception), otherwise upon | ||
success it will called as callback() | ||
*/ | ||
this.callback = callback; | ||
LogzioLogger.prototype.sendAndClose = function (callback) { | ||
this.callback = callback || this._defaultCallback; | ||
this._debug('Sending last messages and closing...'); | ||
this._popMsgsAndSend(); | ||
clearTimeout(this.timer); | ||
/* | ||
* the read/write/connection timeout in milliseconds of the outgoing HTTP request | ||
*/ | ||
this.timeout = timeout; | ||
if (this.protocol === 'udp') { | ||
this.udpClient.close(); | ||
} | ||
}; | ||
// build the url for logging | ||
this.url = `${this.protocol}://${this.host}:${this.port}?token=${this.token}`; | ||
LogzioLogger.prototype._timerSend = function () { | ||
if (this.messages.length > 0) { | ||
this._debug('Woke up and saw ' + this.messages.length + ' messages to send. Sending now...'); | ||
this._popMsgsAndSend(); | ||
this.messages = []; | ||
this.bulkId = 1; | ||
this.extraFields = extraFields; | ||
} | ||
var self = this; | ||
this.timer = setTimeout(function () { | ||
self._timerSend(); | ||
}, this.sendIntervalMs); | ||
}; | ||
_setProtocol(port) { | ||
if (!protocolToPortMap[this.protocol]) { | ||
throw new Error(`Invalid protocol defined. Valid options are : ${JSON.stringify(Object.keys(protocolToPortMap))}`); | ||
} | ||
this.port = port || protocolToPortMap[this.protocol]; | ||
LogzioLogger.prototype._sendMessagesUDP = function () { | ||
var messagesLength = this.messages.length; | ||
var self = this; | ||
if (this.protocol === 'udp') { | ||
this.udpClient = dgram.createSocket('udp4'); | ||
} | ||
} | ||
var udpSentCallback = function (err, bytes) { | ||
if (err) { | ||
self._debug('Error while sending udp packets. err = ' + err); | ||
self.callback(new Error('Failed to send udp log message. err = ' + err)); | ||
_defaultCallback(err) { | ||
if (err && !this.supressErrors) { | ||
this.internalLogger.log(`logzio-logger error: ${err}`, err); | ||
} | ||
}; | ||
for (var i = 0; i < messagesLength; i++) { | ||
var msg = this.messages[i]; | ||
msg.token = this.token; | ||
var buff = new Buffer(stringifySafe(msg)); | ||
self._debug('Starting to send messages via udp.'); | ||
this.udpClient.send(buff, 0, buff.length, this.port, this.host, udpSentCallback); | ||
} | ||
}; | ||
LogzioLogger.prototype.close = function () { | ||
// clearing the timer allows the node event loop to quit when needed | ||
clearTimeout(this.timer); | ||
// send pending messages, if any | ||
if (this.messages.length > 0) { | ||
this._debug('Closing, purging messages.'); | ||
sendAndClose(callback) { | ||
this.callback = callback || this._defaultCallback; | ||
this._debug('Sending last messages and closing...'); | ||
this._popMsgsAndSend(); | ||
} | ||
clearTimeout(this.timer); | ||
if (this.protocol === 'udp') { | ||
this.udpClient.close(); | ||
if (this.protocol === 'udp') { | ||
this.udpClient.close(); | ||
} | ||
} | ||
// no more logging allowed | ||
this.closed = true; | ||
}; | ||
_timerSend() { | ||
if (this.messages.length > 0) { | ||
this._debug(`Woke up and saw ${this.messages.length} messages to send. Sending now...`); | ||
this._popMsgsAndSend(); | ||
} | ||
/** | ||
* Attach a timestamp to the log record. If @timestamp already exists, use it. Else, use current time. | ||
* The same goes for @timestamp_nano | ||
* @param msg - The message (Object) to append the timestamp to. | ||
* @private | ||
*/ | ||
LogzioLogger.prototype._addTimestamp = function (msg) { | ||
var now = (new Date()).toISOString(); | ||
msg['@timestamp'] = msg['@timestamp'] || now; | ||
if (this.addTimestampWithNanoSecs) { | ||
var time = process.hrtime(); | ||
msg['@timestamp_nano'] = msg['@timestamp_nano'] || [now, time[0].toString(), time[1].toString()].join('-'); | ||
this.timer = setTimeout(() => { | ||
this._timerSend(); | ||
}, this.sendIntervalMs); | ||
} | ||
}; | ||
LogzioLogger.prototype.log = function (msg) { | ||
if (this.closed === true) { | ||
throw new Error('Logging into a logger that has been closed!'); | ||
} | ||
if (typeof msg === 'string') { | ||
msg = { | ||
message: msg | ||
_sendMessagesUDP() { | ||
const udpSentCallback = (err) => { | ||
if (err) { | ||
this._debug(`Error while sending udp packets. err = ${err}`); | ||
this.callback(new Error(`Failed to send udp log message. err = ${err}`)); | ||
} | ||
}; | ||
} | ||
msg = _assign(msg, this.extraFields); | ||
if (!msg.type) { | ||
msg.type = this.type; | ||
} | ||
this._addTimestamp(msg); | ||
this.messages.push(msg); | ||
if (this.messages.length >= this.bufferSize) { | ||
this._debug('Buffer is full - sending bulk'); | ||
this._popMsgsAndSend(); | ||
this.messages.forEach((message) => { | ||
const msg = message; | ||
msg.token = this.token; | ||
const buff = new Buffer(stringifySafe(msg)); | ||
this._debug('Starting to send messages via udp.'); | ||
this.udpClient.send(buff, 0, buff.length, this.port, this.host, udpSentCallback); | ||
}); | ||
} | ||
}; | ||
LogzioLogger.prototype._popMsgsAndSend = function () { | ||
close() { | ||
// clearing the timer allows the node event loop to quit when needed | ||
clearTimeout(this.timer); | ||
if (this.protocol === 'udp') { | ||
this._debug('Sending messages via udp'); | ||
this._sendMessagesUDP(); | ||
} else { | ||
var bulk = this._createBulk(this.messages); | ||
this._debug('Sending bulk #' + bulk.id); | ||
this._send(bulk); | ||
} | ||
// send pending messages, if any | ||
if (this.messages.length > 0) { | ||
this._debug('Closing, purging messages.'); | ||
this._popMsgsAndSend(); | ||
} | ||
this.messages = []; | ||
}; | ||
if (this.protocol === 'udp') { | ||
this.udpClient.close(); | ||
} | ||
LogzioLogger.prototype._createBulk = function (msgs) { | ||
var bulk = {}; | ||
// creates a new copy of the array. Objects references are copied (no deep copy) | ||
bulk.msgs = msgs.slice(); | ||
bulk.attemptNumber = 1; | ||
bulk.sleepUntilNextRetry = 2 * 1000; | ||
bulk.id = this.bulkId++; | ||
// no more logging allowed | ||
this.closed = true; | ||
} | ||
return bulk; | ||
}; | ||
/** | ||
* Attach a timestamp to the log record. | ||
* If @timestamp already exists, use it. Else, use current time. | ||
* The same goes for @timestamp_nano | ||
* @param msg - The message (Object) to append the timestamp to. | ||
* @private | ||
*/ | ||
_addTimestamp(msg) { | ||
const now = (new Date()).toISOString(); | ||
msg['@timestamp'] = msg['@timestamp'] || now; | ||
LogzioLogger.prototype._messagesToBody = function (msgs) { | ||
var body = ''; | ||
for (var i = 0; i < msgs.length; i++) { | ||
body = body + jsonToString(msgs[i]) + '\n'; | ||
if (this.addTimestampWithNanoSecs) { | ||
const time = process.hrtime(); | ||
msg['@timestamp_nano'] = msg['@timestamp_nano'] || [now, time[0].toString(), time[1].toString()].join('-'); | ||
} | ||
} | ||
return body; | ||
}; | ||
LogzioLogger.prototype._debug = function (msg) { | ||
if (this.debug) this.internalLogger.log('logzio-nodejs: ' + msg); | ||
}; | ||
log(msg) { | ||
if (this.closed === true) { | ||
throw new Error('Logging into a logger that has been closed!'); | ||
} | ||
if (typeof msg === 'string') { | ||
msg = { | ||
message: msg, | ||
}; | ||
} | ||
msg = assign(msg, this.extraFields); | ||
if (!msg.type) { | ||
msg.type = this.type; | ||
} | ||
this._addTimestamp(msg); | ||
LogzioLogger.prototype._send = function (bulk) { | ||
var self = this; | ||
var body = self._messagesToBody(bulk.msgs); | ||
var options = { | ||
uri: self.url, | ||
headers: { | ||
'host': self.host, | ||
'accept': '*/*', | ||
'user-agent': self.userAgent, | ||
'content-type': 'text/plain', | ||
this.messages.push(msg); | ||
if (this.messages.length >= this.bufferSize) { | ||
this._debug('Buffer is full - sending bulk'); | ||
this._popMsgsAndSend(); | ||
} | ||
}; | ||
} | ||
if (typeof self.timeout !== 'undefined') { | ||
options.timeout = self.timeout; | ||
_popMsgsAndSend() { | ||
if (this.protocol === 'udp') { | ||
this._debug('Sending messages via udp'); | ||
this._sendMessagesUDP(); | ||
} else { | ||
const bulk = this._createBulk(this.messages); | ||
this._debug(`Sending bulk #${bulk.id}`); | ||
this._send(bulk); | ||
} | ||
this.messages = []; | ||
} | ||
var zlibPromised = function (body) { | ||
return new Promise(function (resolve, reject) { | ||
zlib.gzip(body, function (err, res) { | ||
if (err) return reject(err); | ||
resolve(res); | ||
}); | ||
}); | ||
}; | ||
_createBulk(msgs) { | ||
const bulk = {}; | ||
// creates a new copy of the array. Objects references are copied (no deep copy) | ||
bulk.msgs = msgs.slice(); | ||
bulk.attemptNumber = 1; | ||
bulk.sleepUntilNextRetry = this.sleepUntilNextRetry; | ||
bulk.id = this.bulkId; // TODO test | ||
this.bulkId += 1; | ||
return Promise.resolve() | ||
.then(function () { | ||
if (self.compress) { | ||
options.headers['content-encoding'] = 'gzip'; | ||
return zlibPromised(body); | ||
} | ||
return body; | ||
}) | ||
.then(function (body) { | ||
options.body = body; | ||
self._tryToSend(options, bulk); | ||
}); | ||
}; | ||
return bulk; | ||
} | ||
LogzioLogger.prototype._tryToSend = function (options, bulk) { | ||
var callback = this.callback; | ||
var self = this; | ||
_debug(msg) { | ||
if (this.debug) this.internalLogger.log(`logzio-nodejs: ${msg}`); | ||
} | ||
function tryAgainIn(sleepTimeMs) { | ||
self._debug('Bulk #' + bulk.id + ' - Trying again in ' + sleepTimeMs + '[ms], attempt no. ' + bulk.attemptNumber); | ||
setTimeout(function () { | ||
self._send(bulk); | ||
_tryAgainIn(sleepTimeMs, bulk) { | ||
this._debug(`Bulk #${bulk.id} - Trying again in ${sleepTimeMs}[ms], attempt no. ${bulk.attemptNumber}`); | ||
setTimeout(() => { | ||
this._send(bulk); | ||
}, sleepTimeMs); | ||
} | ||
try { | ||
request.post(options, function (err, res, body) { | ||
if (err) { | ||
_send(bulk) { | ||
const body = messagesToBody(bulk.msgs); | ||
const options = { | ||
uri: this.url, | ||
headers: { | ||
host: this.host, | ||
accept: '*/*', | ||
'user-agent': USER_AGENT, | ||
'content-type': 'text/plain', | ||
}, | ||
}; | ||
if (typeof this.timeout !== 'undefined') { | ||
options.timeout = this.timeout; | ||
} | ||
return Promise.resolve() | ||
.then(() => { | ||
if (this.compress) { | ||
options.headers['content-encoding'] = 'gzip'; | ||
return zlibPromised(body); | ||
} | ||
return body; | ||
}) | ||
.then((finalBody) => { | ||
options.body = finalBody; | ||
this._tryToSend(options, bulk); | ||
}); | ||
} | ||
_tryToSend(options, bulk) { | ||
return request.post(options) | ||
.then(() => { | ||
this._debug(`Bulk #${bulk.id} - sent successfully`); | ||
this.callback(); | ||
}) | ||
.catch((err) => { | ||
// In rare cases server is busy | ||
if (err.code === 'ETIMEDOUT' || err.code === 'ECONNRESET' || err.code === 'ESOCKETTIMEDOUT' || err.code === 'ECONNABORTED') { | ||
if (bulk.attemptNumber >= self.numberOfRetries) { | ||
callback(new Error('Failed after ' + bulk.attemptNumber + ' retries on error = ' + err, err)); | ||
} else { | ||
self._debug('Bulk #' + bulk.id + ' - failed on error: ' + err); | ||
var sleepTimeMs = bulk.sleepUntilNextRetry; | ||
bulk.sleepUntilNextRetry = bulk.sleepUntilNextRetry * 2; | ||
bulk.attemptNumber++; | ||
tryAgainIn(sleepTimeMs); | ||
const errorCode = err.cause.code; | ||
if (UNAVAILABLE_CODES.includes(errorCode)) { | ||
if (bulk.attemptNumber >= this.numberOfRetries) { | ||
return this.callback(new Error(`Failed after ${bulk.attemptNumber} retries on error = ${err}`)); | ||
} | ||
} else { | ||
callback(err); | ||
this._debug(`Bulk #${bulk.id} - failed on error: ${err}`); | ||
const sleepTimeMs = bulk.sleepUntilNextRetry; | ||
bulk.sleepUntilNextRetry *= 2; | ||
bulk.attemptNumber += 1; | ||
return this._tryAgainIn(sleepTimeMs, bulk); | ||
} | ||
} else { | ||
var responseCode = res.statusCode.toString(); | ||
if (responseCode !== '200') { | ||
callback(new Error('There was a problem with the request.\nResponse: ' + responseCode + ': ' + body.toString())); | ||
} else { | ||
self._debug('Bulk #' + bulk.id + ' - sent successfully'); | ||
callback(); | ||
if (err.statusCode !== 200) { | ||
return this.callback(new Error(`There was a problem with the request.\nResponse: ${err.statusCode}: ${err.message}`)); | ||
} | ||
} | ||
}); | ||
} catch (ex) { | ||
callback(ex); | ||
return this.callback(err); | ||
}); | ||
} | ||
}; | ||
} | ||
const createLogger = (options) => { | ||
const l = new LogzioLogger(options); | ||
l._timerSend(); | ||
return l; | ||
}; | ||
module.exports = { | ||
jsonToString, | ||
createLogger, | ||
}; |
{ | ||
"name": "logzio-nodejs", | ||
"description": "A nodejs implementation for sending logs to Logz.IO cloud service", | ||
"version": "0.4.16", | ||
"version": "1.0.1", | ||
"author": "Gilly Barr <gilly@logz.io>", | ||
@@ -39,3 +39,4 @@ "contributors": [ | ||
"moment": "^2.22.1", | ||
"request": "^2.87.0" | ||
"request": "^2.88.0", | ||
"request-promise": "^4.2.2" | ||
}, | ||
@@ -45,16 +46,21 @@ "devDependencies": { | ||
"async": "1.4.2", | ||
"eslint": "^5.8.0", | ||
"eslint": "^5.9.0", | ||
"eslint-config-airbnb-base": "^13.1.0", | ||
"eslint-plugin-import": "^2.14.0", | ||
"mocha": "^5.2.0", | ||
"jest": "^23.6.0", | ||
"nock": "^10.0.2", | ||
"should": "^7.1.0", | ||
"sinon": "^1.17.1" | ||
"sinon": "^7.1.1" | ||
}, | ||
"main": "./lib/logzio-nodejs", | ||
"engines": { | ||
"node": ">= 0.8.0" | ||
"node": ">= 6.0.0" | ||
}, | ||
"license": "(Apache-2.0)", | ||
"scripts": { | ||
"test": "node_modules/.bin/mocha" | ||
"test": "jest", | ||
"detached": "jest --detectOpenHandles", | ||
"watch": "jest --watch" | ||
} | ||
} |
@@ -60,2 +60,8 @@ ![Build Status](https://travis-ci.org/logzio/logzio-nodejs.svg?branch=master) | ||
## Update log | ||
**1.0.1** | ||
- ES6 | ||
- support node greater than node 6 | ||
- added gzip compress option | ||
- added internal logger option | ||
**0.4.14** | ||
@@ -62,0 +68,0 @@ - UDP callback bug fix + tests |
module.exports = { | ||
"env": { | ||
"mocha": true, | ||
"jest": true, | ||
}, | ||
}; |
Sorry, the diff of this file is not supported yet
Deprecated
MaintenanceThe maintainer of the package marked it as deprecated. This could indicate that a single version should not be used, or that the package is no longer maintained and any new vulnerabilities will not be fixed.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
39
0
1
122
1
278599
5
10
1775
+ Addedrequest-promise@^4.2.2
+ Addedbluebird@3.7.2(transitive)
+ Addedlodash@4.17.21(transitive)
+ Addedrequest-promise@4.2.6(transitive)
+ Addedrequest-promise-core@1.1.4(transitive)
+ Addedstealthy-require@1.1.1(transitive)
Updatedrequest@^2.88.0