Skip to content

Commit 4958dd9

Browse files
author
Lucas Thomas
committed
SF-3577 Express Middleware failing on second request.
1 parent fa55f39 commit 4958dd9

File tree

7 files changed

+373
-122
lines changed

7 files changed

+373
-122
lines changed

lib/api.js

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,14 @@ module.exports.methods = {
1919
DeviceName: os.hostname(),
2020
AppName: appname,
2121
ConfiguredAppName: appname,
22-
AppLocaton: process.env.PWD
22+
AppLocation: process.env.PWD
2323
},
2424
callback = function (data) {
2525
debug.write('Successfully identified');
2626

2727
CONFIG.APP_DETAILS = data;
2828

29-
//start sending logs unless it's not already being sent because of exception
30-
31-
if (!exc.excCaught) {
32-
logger.methods.start();
33-
}
29+
logger.methods.start();
3430

3531
},
3632
fail = function () {
@@ -56,7 +52,6 @@ module.exports.methods = {
5652
}
5753

5854
options = helpers.getOptions(CONFIG.IDENTIFY_PATH, body, settings ? settings.proxy : undefined);
59-
options.headers['X-Stackify-Key'] = settings.apiKey;
6055

6156
debug.write('Identifying the app');
6257
sender.send(options, callback, fail);
@@ -82,12 +77,12 @@ module.exports.methods = {
8277
var sinceFirstError;
8378
debug.write('Sending logs failed');
8479
if (code === 401) {
85-
delay = CONFIG.DELAY.FIVE_MINUTES;
80+
delay = CONFIG.DELAY.FIVE_MINUTES_DELAY;
8681
lastApiError = new Date().getTime();
8782
} else {
8883
lastApiError = lastApiError || new Date().getTime();
8984
sinceFirstError = new Date().getTime() - lastApiError;
90-
delay = Math.min(Math.max(sinceFirstError, CONFIG.DELAY.ONE_SECOND), CONFIG.DELAY.ONE_MINUTE);
85+
delay = Math.min(Math.max(sinceFirstError, CONFIG.DELAY.ONE_SECOND_DELAY), CONFIG.DELAY.ONE_MINUTE_DELAY);
9186
}
9287

9388
setTimeout(function () {

lib/exception.js

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -43,10 +43,7 @@ module.exports = {
4343
return next();
4444
}
4545

46-
if (!this.excCaught) {
47-
this.excCaught = true;
48-
logger.methods.sendException(err, req, cb);
49-
}
46+
logger.methods.sendException(err, req, cb);
5047
},
5148
// drain the queue and send the messages before server closes
5249
gracefulExitHandler : function gracefulExitHandler() {

lib/logger.js

Lines changed: 155 additions & 107 deletions
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,37 @@ var api = require('./api'),
1717
params.unshift(level);
1818
return this.log.apply(this, params);
1919
},
20+
// Determine whether or not an API key has been specified. If not, then APP_DETAILS will not exist.
21+
appDetailsExist,
22+
hasAppDetails = function () {
23+
if (appDetailsExist === undefined) {
24+
appDetailsExist = undefined !== CONFIG.APP_DETAILS;
25+
26+
if (!appDetailsExist) {
27+
debug.write('*** A valid Stackify API key has not been specified ***');
28+
}
29+
}
30+
31+
return appDetailsExist;
32+
},
2033

2134
/* handler for sending logs, accepts callback function and boolean variable
2235
indicating if we should run process.exit() after the callback
2336
*/
2437
sendLogs = function (cb, shutdown) {
38+
39+
// If Stackify Application details have not been received, skip logging.
40+
if (!hasAppDetails()) {
41+
cb && cb();
42+
43+
return;
44+
}
45+
2546
var length = storage.length, // number of the messages in the iteration
2647
chunk = Math.min(length, CONFIG.MSG.MAX_BATCH_SIZE),
2748
data = storage.slice(0, chunk),
2849

29-
/* if request is succesful remove messages from the queue,
50+
/* if request is successful remove messages from the queue,
3051
send another batch (if there are enough messages in the queue)
3152
*/
3253
success = function (response) {
@@ -41,11 +62,11 @@ var api = require('./api'),
4162
api.methods.postLogs(data, success, shutdown);
4263
} else {
4364
// full batch is sent, set timeout for the next request
65+
sendingLogsInProgress = false;
4466
if (cb) {
4567
// if exception is caught run next middleware or check if we need to shutdown the server
4668
cb();
4769
} else {
48-
sendingLogsInProgress = false;
4970
timeout = setTimeout(sendLogs, delay);
5071
}
5172
}
@@ -61,135 +82,162 @@ var api = require('./api'),
6182

6283
if (data.length && CONFIG.APP_DETAILS) {
6384
// queue has messages and the app is authorized - set the flag, post data
64-
api.methods.postLogs(data, success, shutdown);
6585
sendingLogsInProgress = true;
86+
api.methods.postLogs(data, success, shutdown);
6687
} else {
6788
// queue is empty or app is not authorized, set timeout for the next request, switch the flag
68-
timeout = setTimeout(sendLogs, delay);
6989
sendingLogsInProgress = false;
90+
timeout = setTimeout(sendLogs, delay);
7091
}
7192
};
7293

73-
module.exports.storage = storage;
74-
75-
module.exports.methods = {
76-
// create the message object, push it to the queue if the queue cap isn't exceeded
77-
push : function push(level, msg, meta, req, err) {
78-
var stackError = err || new Error(),
79-
getStack = error.getStackTraceItem(stackError, err),
80-
messageObject = {
81-
Msg: msg,
82-
Level: level.toUpperCase(),
83-
EpochMs: Date.now(),
84-
SrcMethod: getStack.SrcMethod,
85-
SrcLine: getStack.SrcLine
86-
},
87-
data;
88-
89-
// handle properly metadata object and create exception object if needed
90-
if (meta.length) {
91-
if (level.toLowerCase() === 'error') {
92-
data = helpers.parseMeta(meta, true);
93-
// if duplicate errors cap per minute isn't exceeded and error object is passed, create an exception
94-
if (data.ex) {
95-
messageObject.Ex = error.formatEx(data.ex, req);
96-
if (!error.checkErrorLimitMessage(messageObject.Ex)) {
97-
delete messageObject.Ex;
98-
}
99-
}
100-
} else {
101-
data = helpers.parseMeta(meta);
102-
}
94+
//module.exports.storage = storage;
10395

104-
if (data.result) {
105-
messageObject.Data = data.result;
106-
}
107-
}
96+
module.exports = {
10897

109-
// if error object isn't passed with message & duplicate errors cap per minute isn't exceeded, create a string exception
110-
if (level.toLowerCase() === 'error' && !messageObject.Ex) {
111-
messageObject.Ex = error.formatEx(stackError, null, msg);
98+
size: function() {
99+
return storage ? storage.length : 0;
100+
},
112101

113-
if (!error.checkErrorLimitMessage(messageObject.Ex)) {
114-
delete messageObject.Ex;
115-
}
102+
get: function(index) {
103+
if (index >= 0 && storage && storage.length) {
104+
return storage[index];
116105
}
117106

118-
storage.push(messageObject);
107+
return null;
108+
},
109+
110+
hasAppDetails: hasAppDetails,
119111

120-
// remove the earliest message from the queue if message cap is exceeded
121-
if (storage.length === CONFIG.MSG.QUEUE_CAP) {
122-
storage.shift();
112+
/**
113+
* A function to clear the logs.
114+
*/
115+
flushLogs: function () {
116+
if (storage && storage.length) {
117+
storage.length = 0;
123118
}
124119
},
125120

126-
// start sending logs after IdentifyApp call is done
127-
start: function start() {
128-
timeout = setTimeout(sendLogs, delay);
129-
},
121+
methods: {
122+
// create the message object, push it to the queue if the queue cap isn't exceeded
123+
push: function push(level, msg, meta, req, err) {
124+
var stackError = err || new Error(),
125+
getStack = error.getStackTraceItem(stackError, err),
126+
messageObject = {
127+
Msg: msg,
128+
Level: level.toUpperCase(),
129+
EpochMs: Date.now(),
130+
SrcMethod: getStack.SrcMethod,
131+
SrcLine: getStack.SrcLine
132+
},
133+
data;
134+
135+
// handle properly metadata object and create exception object if needed
136+
if (meta.length) {
137+
if (level.toLowerCase() === 'error') {
138+
data = helpers.parseMeta(meta, true);
139+
// if duplicate errors cap per minute isn't exceeded and error object is passed, create an exception
140+
if (data.ex) {
141+
messageObject.Ex = error.formatEx(data.ex, req);
142+
if (!error.checkErrorLimitMessage(messageObject.Ex)) {
143+
delete messageObject.Ex;
144+
}
145+
}
146+
} else {
147+
data = helpers.parseMeta(meta);
148+
}
130149

131-
// reset current delay schedule, push exception to the queue, send data and run the callback
132-
sendException: function sendException(err, req, cb) {
133-
// check if messages are being sent right now
134-
var check = function check() {
150+
if (data.result) {
151+
messageObject.Data = data.result;
152+
}
153+
}
135154

136-
if (sendingLogsInProgress) {
137-
setTimeout(check, 500);
138-
} else {
139-
sendLogs(cb, true);
155+
// if error object isn't passed with message & duplicate errors cap per minute isn't exceeded, create a string exception
156+
if (level.toLowerCase() === 'error' && !messageObject.Ex) {
157+
messageObject.Ex = error.formatEx(stackError, null, msg);
158+
159+
if (!error.checkErrorLimitMessage(messageObject.Ex)) {
160+
delete messageObject.Ex;
161+
}
140162
}
141-
};
142163

143-
debug.write('Exception caught');
164+
storage.push(messageObject);
144165

145-
clearTimeout(timeout);
146-
this.push('error', err.message, [err], req, err);
147-
check();
148-
},
149-
// drain the queue and send the messages before server closes
150-
drain : function drain() {
151-
if (storage.length) {
152-
api.methods.postLogsSync(storage);
153-
} else {
154-
debug.close();
155-
}
156-
},
166+
// remove the earliest message from the queue if message cap is exceeded
167+
if (storage.length === CONFIG.MSG.QUEUE_CAP) {
168+
storage.shift();
169+
}
170+
},
157171

158-
// basic logging method
159-
log: function log(level, msg) {
160-
var meta = Array.prototype.slice.call(arguments, 2);
161-
var levels = ['error', 'debug', 'warn', 'info', 'trace'];
172+
// start sending logs after IdentifyApp call is done
173+
start: function start() {
174+
appDetailsExist = undefined; // Reset to allow for logging after receiving successful application details.
175+
timeout = setTimeout(sendLogs, delay);
176+
},
162177

163-
// check the message level
164-
if (levels.indexOf(level.toLowerCase()) < 0) {
165-
debug.write(level + ' level doesn\'t exist');
166-
throw new TypeError(level + ' level doesn\'t exist');
167-
}
168-
// check the message itself
169-
if (typeof msg !== 'string') {
170-
debug.write('Message must be a string');
171-
throw new TypeError('Message must be a string');
172-
}
173-
this.push(level, msg, meta);
174-
},
178+
// reset current delay schedule, push exception to the queue, send data and run the callback
179+
sendException: function sendException(err, req, cb) {
180+
// check if messages are being sent right now
181+
var check = function check() {
175182

176-
/*
177-
Shortcut functions for logging message of certain level. Every function takes the same params as log function except the level.
178-
*/
183+
if (sendingLogsInProgress) {
184+
setTimeout(check, CONFIG.DELAY.ONE_SECOND_DELAY);
185+
} else {
186+
sendLogs(cb, true);
187+
}
188+
};
179189

180-
trace: function trace() {
181-
getShortcut.call(this, arguments, 'trace');
182-
},
183-
debug: function debug() {
184-
getShortcut.call(this, arguments, 'debug');
185-
},
186-
info: function info() {
187-
getShortcut.call(this, arguments, 'info');
188-
},
189-
warn: function warn() {
190-
getShortcut.call(this, arguments, 'warn');
191-
},
192-
error: function error() {
193-
getShortcut.call(this, arguments, 'error');
190+
debug.write('Exception caught');
191+
192+
clearTimeout(timeout);
193+
this.push('error', err.message, [err], req, err);
194+
check();
195+
},
196+
// drain the queue and send the messages before server closes
197+
drain: function drain() {
198+
if (storage.length) {
199+
api.methods.postLogsSync(storage);
200+
} else {
201+
debug.close();
202+
}
203+
},
204+
205+
// basic logging method
206+
log: function log(level, msg) {
207+
var meta = Array.prototype.slice.call(arguments, 2);
208+
var levels = ['error', 'debug', 'warn', 'info', 'trace'];
209+
210+
// check the message level
211+
if (levels.indexOf(level.toLowerCase()) < 0) {
212+
debug.write(level + ' level doesn\'t exist');
213+
throw new TypeError(level + ' level doesn\'t exist');
214+
}
215+
// check the message itself
216+
if (typeof msg !== 'string') {
217+
debug.write('Message must be a string');
218+
throw new TypeError('Message must be a string');
219+
}
220+
this.push(level, msg, meta);
221+
},
222+
223+
/*
224+
Shortcut functions for logging message of certain level. Every function takes the same params as log function except the level.
225+
*/
226+
227+
trace: function trace() {
228+
getShortcut.call(this, arguments, 'trace');
229+
},
230+
debug: function debug() {
231+
getShortcut.call(this, arguments, 'debug');
232+
},
233+
info: function info() {
234+
getShortcut.call(this, arguments, 'info');
235+
},
236+
warn: function warn() {
237+
getShortcut.call(this, arguments, 'warn');
238+
},
239+
error: function error() {
240+
getShortcut.call(this, arguments, 'error');
241+
}
194242
}
195243
};

lib/sender.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ var http = require('http'),
1818
module.exports.send = function send(options, cb, fail) {
1919
var callback = function (error, response, body) {
2020
if (!error) {
21-
debug.writeResponse(response, exc.excCaught);
21+
debug.writeResponse(response);
2222
if (response.statusCode === 200) {
2323
if (cb) {
2424
cb(body);

package.json

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,5 +31,12 @@
3131
},
3232
3333
"_shasum": "33730db585d53b43988b359145d76629e7ec35f1",
34-
"_from": "stackify-logger@"
34+
"_from": "stackify-logger@",
35+
"devDependencies": {
36+
"express": "^4.13.3",
37+
"mocha": "^2.3.2",
38+
"should": "^7.1.0",
39+
"sinon": "^1.16.1",
40+
"supertest": "^1.1.0"
41+
}
3542
}

0 commit comments

Comments
 (0)