Skip to content

Commit 3f1103c

Browse files
alexk-blackopsalexk-blackops
authored andcommitted
different changes
non-graceful shutdown logic; ability to pass multiple arguments to direct logger methods added; gathering app name from package.json changed; all auxiliary functions moved to helpers and error modules;
1 parent 6a660e5 commit 3f1103c

File tree

9 files changed

+267
-169
lines changed

9 files changed

+267
-169
lines changed

index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ module.exports = {
77
start: function (options) {
88
api.methods.identifyApp(options);
99
exception.exceptionHandler(null, options.exitOnError);
10+
exception.gracefulExitHandler();
1011
},
1112

1213
log: logger.methods.log,

lib/api.js

Lines changed: 21 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -1,54 +1,30 @@
11
var os = require('os'),
22

3-
send = require('./sender'),
3+
sender = require('./sender'),
44
logger = require('./logger'),
55
CONFIG = require('../config/config'),
66
exc = require('./exception'),
7+
helpers = require('./helpers'),
78

8-
pkginfo = require('pkginfo')(module, 'name'),
9-
10-
appname = module.exports.name, // name of the app from package.json
11-
lastApiError,
12-
13-
//getting all the headers for requests
14-
options = function options(path, body, proxy) {
15-
var result = {
16-
url: CONFIG.PROTOCOL + '://' + CONFIG.HOST + path,
17-
method: 'POST',
18-
json: true,
19-
headers: {
20-
'Content-Type': 'application/json',
21-
'X-Stackify-Key': CONFIG.APIKEY,
22-
'X-Stackify-PV': 'V1'
23-
},
24-
body: body
25-
};
26-
27-
if (proxy) {
28-
result.proxy = (proxy.slice(0,4) === 'http') ? proxy : 'http://' + proxy;
29-
CONFIG.PROXY = CONFIG.PROXY || proxy;
30-
}
31-
32-
return result;
9+
lastApiError;
3310

34-
};
3511
module.exports.lastApiError = lastApiError;
3612
module.exports.methods = {
3713

3814
identifyApp: function identifyApp(settings) {
39-
var body = {
15+
var appname = helpers.getAppName(),
16+
body = {
4017
DeviceName: os.hostname(),
4118
AppName: appname,
4219
ConfiguredAppName: appname,
4320
AppLocaton: process.env.PWD,
4421
ConfiguredEnvName: settings ? settings.env : (process.env.NODE_ENV || null)
4522
},
46-
opt = options(CONFIG.IDENTIFY_PATH, body, settings ? settings.proxy : undefined),
23+
opt = helpers.getOptions(CONFIG.IDENTIFY_PATH, body, settings ? settings.proxy : undefined),
4724
callback = function (data) {
4825
console.log('successfully identified');
4926

5027
CONFIG.APP_DETAILS = data;
51-
CONFIG.APIKEY = settings.apiKey;
5228

5329
//start sending logs unless it's not already being sent because of exception
5430

@@ -59,7 +35,7 @@ module.exports.methods = {
5935
},
6036
fail = function () {
6137
setTimeout(function () {
62-
send(opt, callback, fail);
38+
sender.send(opt, callback, fail);
6339
}, CONFIG.IDENTIFY_DELAY);
6440
};
6541

@@ -69,37 +45,25 @@ module.exports.methods = {
6945
}
7046

7147
if (settings.apiKey && typeof (settings.apiKey) === 'string') {
48+
CONFIG.APIKEY = settings.apiKey;
7249
opt.headers['X-Stackify-Key'] = settings.apiKey;
7350
CONFIG.APPNAME = appname;
7451
CONFIG.ENV = settings.env || process.env.NODE_ENV || null;
75-
send(opt, callback, fail);
52+
sender.send(opt, callback, fail);
7653
} else {
7754
throw new TypeError('You have to pass API key');
7855
}
7956

8057
},
58+
8159
/* posting logs to API.
8260
messages - array of messages,
8361
cb - callback function
8462
shutdown - optional parameter, indicating if we should retry request attempts when API is down
8563
*/
8664
postLogs: function postLogs(messages, cb, shutdown) {
8765
var delay = 0, // scheduled delay when postLogs call failed
88-
body = {
89-
CDID: CONFIG.APP_DETAILS.DeviceID,
90-
CDAppID: CONFIG.APP_DETAILS.DeviceAppID,
91-
AppNameID: CONFIG.APP_DETAILS.AppNameID,
92-
AppEnvID: CONFIG.APP_DETAILS.AppEnvID,
93-
EnvID: CONFIG.APP_DETAILS.EnvID,
94-
Env: CONFIG.APP_DETAILS.Env,
95-
ServerName: os.hostname(),
96-
AppName: appname,
97-
AppLoc: process.env.PWD,
98-
Logger: 'Node.js Stackify v.1.0',
99-
Platform: 'Node.js',
100-
Msgs : messages
101-
},
102-
opt = options(CONFIG.LOG_SAVE_PATH, body, CONFIG.PROXY),
66+
opt = helpers.getOptions(CONFIG.LOG_SAVE_PATH, helpers.getPostBody(messages), CONFIG.PROXY),
10367

10468
//retry the request if it failed
10569
fail = function (code) {
@@ -115,10 +79,18 @@ module.exports.methods = {
11579
}
11680

11781
setTimeout(function () {
118-
send(opt, cb, fail);
82+
sender.send(opt, cb, fail);
11983
}, delay);
12084
};
12185

122-
send(opt, cb, shutdown ? null : fail);
86+
sender.send(opt, cb, shutdown ? null : fail);
87+
},
88+
postLogsSync: function postLogsSync(messages) {
89+
var options = {
90+
url: CONFIG.PROTOCOL + '://' + CONFIG.HOST + CONFIG.LOG_SAVE_PATH,
91+
headers : helpers.getHeaders(),
92+
data: helpers.getPostBody(messages)
93+
};
94+
sender.sendSync(options);
12395
}
12496
};

lib/error.js

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,42 @@ var url = require('url'),
22
os = require('os'),
33

44
helpers = require('./helpers'),
5-
CONFIG = require('../config/config');
5+
CONFIG = require('../config/config'),
6+
7+
// hash that contains all the errors and their number logged during the current minute
8+
errorStorage = {};
69

710
module.exports = {
8-
/* getting source method and source method line of code*/
11+
/**
12+
* Check for duplicated error messages. If the same error message logged more than configurated limit in one minute
13+
* don't push it to the queue
14+
*/
15+
checkErrorLimitMessage : function checkErrorLimitMessage(ex) {
16+
var d = new Date(),
17+
min = d.getFullYear().toString() + d.getMonth().toString() + d.getDate().toString()
18+
+ d.getHours().toString() + d.getMinutes().toString(),
19+
key = ex.Error.Message + ex.Error.ErrorType + ex.Error.SourceMethod;
20+
21+
if (!ex) {
22+
return true;
23+
}
24+
25+
if (errorStorage[min]) {
26+
if (errorStorage[min][key]) {
27+
errorStorage[min][key] += 1;
28+
} else {
29+
errorStorage[min][key] = 1;
30+
}
31+
} else {
32+
errorStorage = {};
33+
errorStorage[min] = {};
34+
errorStorage[min][key] = 1;
35+
}
36+
37+
return errorStorage[min][key] < CONFIG.MSG.MAX_DUP_ERROR_PER_MINUTE;
38+
},
39+
40+
// getting source method and source method line of code
941
getStackTraceItem: function getStackTraceItem(err) {
1042
var trace = helpers.getTrace(err);
1143
return {

lib/exception.js

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,5 +53,39 @@ module.exports = {
5353
this.excCaught = true;
5454
logger.methods.sendException(err, req, cb);
5555
}
56-
}
56+
},
57+
// drain the queue and send the messages before server closes
58+
gracefulExitHandler : function gracefulExitHandler() {
59+
return (function() {
60+
61+
var drain = function drain() {
62+
if (logger.storage.length) {
63+
api.methods.postLogsSync(storage);
64+
}
65+
};
66+
67+
// Start reading from stdin so we don't exit instantly
68+
process.stdin.resume();
69+
70+
process.on('exit', function() {
71+
drain();
72+
});
73+
74+
// catch some signal events also
75+
process.on('SIGINT', function () {
76+
drain();
77+
process.exit(1);
78+
});
79+
80+
process.on('SIGTERM', function () {
81+
drain();
82+
process.exit(1);
83+
});
84+
85+
process.on('SIGHUP', function () {
86+
drain();
87+
process.exit(1);
88+
});
89+
}());
90+
},
5791
};

lib/helpers.js

Lines changed: 94 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,48 @@
11
var stackTrace = require('stack-trace'),
2-
util = require('util'),
32

4-
CONFIG = require('../config/config');
3+
path = require('path'),
4+
os = require('os'),
5+
6+
CONFIG = require('../config/config');
57

68
/*
79
*** Function for parsing meta objects attached to the message
810
*/
911

1012
module.exports.parseMeta = function parseMeta(meta, err) {
11-
var result,
12-
ex,
13-
key;
14-
if (Object.prototype.toString.call(meta) === '[object Object]') {
15-
for (key in meta) {
16-
if (meta[key] instanceof Error && err && !ex) {
17-
ex = meta[key];
18-
delete meta[key];
13+
var result = {
14+
'arguments': []
15+
},
16+
ex;
17+
18+
meta.forEach(function (elem) {
19+
var prop;
20+
if (Object.prototype.toString.call(elem) === '[object Object]') {
21+
for (prop in elem) {
22+
if (elem.hasOwnProperty(prop)) {
23+
if (elem[prop] instanceof Error && err && !ex) {
24+
ex = elem[prop];
25+
delete elem[prop];
26+
}
27+
}
1928
}
29+
if (Object.keys(elem).length) {
30+
result['arguments'].push(elem);
31+
}
32+
} else if (elem instanceof Error && err && !ex) {
33+
ex = elem;
34+
} else {
35+
result['arguments'].push(elem);
2036
}
21-
try {
22-
result = JSON.stringify(meta);
23-
24-
return {
25-
result: result,
26-
ex: ex
27-
};
28-
} catch (e) {
29-
throw new TypeError('Metadata should be valid JSON Object');
30-
}
31-
} else {
32-
throw new TypeError('Metadata should be valid JSON Object');
33-
}
37+
});
38+
39+
40+
result = result['arguments'].length ? JSON.stringify(result) : null;
41+
42+
return {
43+
result: result,
44+
ex: ex
45+
};
3446

3547
};
3648

@@ -82,7 +94,7 @@ module.exports.getTrace = function getTrace(err) {
8294
var trace = stackTrace.parse(err),
8395
result = [];
8496
/* remove methods of logger itself from the stack trace */
85-
trace.forEach(function (val, index, arr) {
97+
trace.forEach(function (val) {
8698
var method = val.methodName || (val.functionName || val.typeName) + '.<anonymous>';
8799

88100
if (val.fileName ? (val.fileName.search('lib/logger.js') < 0 || val.fileName.search('stackify') < 0) : true) {
@@ -103,4 +115,62 @@ module.exports.getTrace = function getTrace(err) {
103115
module.exports.getURL = function getURL(req) {
104116
var href = (req.connection.encrypted ? "https://" : "http://") + req.headers.host + req.url;
105117
return href;
118+
};
119+
120+
// read the name of the app from package.json file
121+
module.exports.getAppName = function getAppName() {
122+
var pjsonPath = path.join(process.env.PWD, 'package.json'),
123+
pjson;
124+
try {
125+
pjson = require(pjsonPath);
126+
} catch (err) {
127+
if (err.code === 'MODULE_NOT_FOUND') {
128+
return 'Unknown';
129+
}
130+
}
131+
return pjson.name;
132+
};
133+
134+
// getting post data for the request
135+
module.exports.getPostBody = function getPostBody(messages) {
136+
return {
137+
CDID: CONFIG.APP_DETAILS ? CONFIG.APP_DETAILS.DeviceID : null,
138+
CDAppID: CONFIG.APP_DETAILS ? CONFIG.APP_DETAILS.DeviceAppID : null,
139+
AppNameID: CONFIG.APP_DETAILS ? CONFIG.APP_DETAILS.AppNameID : null,
140+
AppEnvID: CONFIG.APP_DETAILS ? CONFIG.APP_DETAILS.AppEnvID : null,
141+
EnvID: CONFIG.APP_DETAILS ? CONFIG.APP_DETAILS.EnvID : null,
142+
Env: CONFIG.ENV,
143+
ServerName: os.hostname(),
144+
AppName: CONFIG.APPNAME,
145+
AppLoc: process.env.PWD,
146+
Logger: 'Node.js Stackify v.1.0',
147+
Platform: 'Node.js',
148+
Msgs : messages
149+
};
150+
};
151+
//getting all the options for requests
152+
module.exports.getOptions = function getOptions(path, body, proxy) {
153+
var result = {
154+
url: CONFIG.PROTOCOL + '://' + CONFIG.HOST + path,
155+
method: 'POST',
156+
json: true,
157+
headers: this.getHeaders(),
158+
body: body
159+
};
160+
161+
if (proxy) {
162+
result.proxy = (proxy.slice(0, 4) === 'http') ? proxy : 'http://' + proxy;
163+
CONFIG.PROXY = CONFIG.PROXY || proxy;
164+
}
165+
166+
return result;
167+
168+
};
169+
// getting request headers
170+
module.exports.getHeaders = function getHeaders() {
171+
return {
172+
'Content-Type': 'application/json',
173+
'X-Stackify-Key': CONFIG.APIKEY,
174+
'X-Stackify-PV': 'V1'
175+
};
106176
};

0 commit comments

Comments
 (0)