soajs.controller 4.3.11 → 4.3.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/logOnce.js ADDED
@@ -0,0 +1,45 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * @license
5
+ * Copyright SOAJS All Rights Reserved.
6
+ *
7
+ * Use of this source code is governed by an Apache license that can be
8
+ * found in the LICENSE file at the root of this repository
9
+ */
10
+
11
+ const reported = new Set();
12
+
13
+ /**
14
+ * Log a message once per distinct key.
15
+ *
16
+ * Static misconfiguration is discovered inside the request path, so reporting it
17
+ * normally would repeat on every request for the lifetime of the process. The
18
+ * condition cannot resolve itself without a registry change, so the first report
19
+ * carries all the information the rest would.
20
+ *
21
+ * @param log the soajs logger
22
+ * @param level one of the logger levels, ex: error
23
+ * @param key identifies the condition, repeats with the same key are dropped
24
+ * @param message
25
+ */
26
+ function logOnce(log, level, key, message) {
27
+ if (reported.has(key)) {
28
+ return false;
29
+ }
30
+ if (!log || typeof log[level] !== "function") {
31
+ return false;
32
+ }
33
+ reported.add(key);
34
+ log[level](message);
35
+ return true;
36
+ }
37
+
38
+ /**
39
+ * Forget what has been reported. Used by the unit tests.
40
+ */
41
+ function resetLogOnce() {
42
+ reported.clear();
43
+ }
44
+
45
+ module.exports = { logOnce, resetLogOnce };
@@ -145,7 +145,7 @@ let awareness_healthCheck = function (core, log) {
145
145
 
146
146
  let stopLoggingAfter = registry.serviceConfig.awareness.maxLogCount;
147
147
  if (statusObj.downCount <= stopLoggingAfter) {
148
- log.warn("Self Awareness health check for service [" + sObj.name + "] for host [" + sObj.host + "] is NOT healthy");
148
+ log.error("Self Awareness health check for service [" + sObj.name + "] for host [" + sObj.host + "] is NOT healthy");
149
149
  }
150
150
  if (serviceAwarenessObj[sObj.name].healthy[sObj.version].indexOf(sObj.host) !== -1) {
151
151
  //TODO: if we guarantee uniqueness we will not need the for loop
@@ -192,7 +192,7 @@ let awareness_healthCheck = function (core, log) {
192
192
  }
193
193
  }, function (err) {
194
194
  if (err) {
195
- log.warn('Unable to build awareness structure for services: ' + err);
195
+ log.error('Unable to build awareness structure for services: ' + err);
196
196
  }
197
197
  });
198
198
  setTimeout(() => {
@@ -26,7 +26,7 @@ let fetchControllerHosts = function (core, log, next) {
26
26
  registry = registryModule.get();
27
27
  registryModule.loadOtherEnvControllerHosts(gatewayServiceName, function (error, hosts) {
28
28
  if (error) {
29
- log.warn("Failed to load controller hosts. reusing from previous load. Reason: " + error.message);
29
+ log.error("Failed to load controller hosts. reusing from previous load. Reason: " + error.message);
30
30
  } else {
31
31
  controllerHosts = hosts;
32
32
  }
@@ -107,7 +107,7 @@ let awareness_healthCheck = function (core, log) {
107
107
  // });
108
108
  }, function (err) {
109
109
  if (err) {
110
- log.warn('Unable to build awareness ENV structure for controllers: ' + err);
110
+ log.error('Unable to build awareness ENV structure for controllers: ' + err);
111
111
  }
112
112
  });
113
113
  }
package/mw/cache/index.js CHANGED
@@ -12,6 +12,7 @@ const crypto = require('crypto');
12
12
  const get = (p, o) => p.reduce((xs, x) => (xs && xs[x]) ? xs[x] : null, o);
13
13
  const memory = require('./model/memory.js');
14
14
  const mongo = require('./model/mongo.js');
15
+ const { logOnce } = require('../../lib/logOnce.js');
15
16
 
16
17
  let model = { memory, mongo };
17
18
 
@@ -102,7 +103,8 @@ module.exports = function (configuration) {
102
103
 
103
104
  const modelType = cacheConfig.model || 'memory';
104
105
  if (!model[modelType]) {
105
- configuration.log.warn('Cache: Unknown model [' + modelType + ']. It can only be [memory || mongo]');
106
+ logOnce(configuration.log, "error", "cache_model_" + modelType,
107
+ 'Cache: Unknown model [' + modelType + ']. It can only be [memory || mongo]. Caching is off.');
106
108
  return next();
107
109
  }
108
110
 
@@ -119,7 +121,8 @@ module.exports = function (configuration) {
119
121
  let scope = apiConfig.scope;
120
122
  if (scope !== 'tenant' && scope !== 'tenant_user') {
121
123
  if (scope) {
122
- configuration.log.warn('Cache: Unknown scope [' + scope + '] for API [' + method + ' ' + apiPath + ']. It can only be [tenant || tenant_user]. Falling back to default.');
124
+ logOnce(configuration.log, "error", "cache_scope_" + scope + "_" + method + "_" + apiPath,
125
+ 'Cache: Unknown scope [' + scope + '] for API [' + method + ' ' + apiPath + ']. It can only be [tenant || tenant_user]. Falling back to default.');
123
126
  }
124
127
  scope = isAPIPublic ? 'tenant' : 'tenant_user';
125
128
  }
@@ -22,10 +22,18 @@ let model = {
22
22
  if (!model.mongo) {
23
23
  model.mongo = new Mongo(dbConfiguration);
24
24
  model.mongo.createIndex(cache_store, { 'l1': 1, 'l2': 1 }, { unique: true }, (err, index) => {
25
- log.debug("Cache index: " + index + " created with error: " + err);
25
+ if (err) {
26
+ log.error("Cache index creation failed: " + err.message);
27
+ } else {
28
+ log.debug("Cache index created: " + index);
29
+ }
26
30
  });
27
31
  model.mongo.createIndex(cache_store, { 'expiresAt': 1 }, { expireAfterSeconds: 0 }, (err, index) => {
28
- log.debug("Cache TTL index: " + index + " created with error: " + err);
32
+ if (err) {
33
+ log.error("Cache TTL index creation failed: " + err.message);
34
+ } else {
35
+ log.debug("Cache TTL index created: " + index);
36
+ }
29
37
  });
30
38
  }
31
39
  },
@@ -50,7 +50,7 @@ module.exports = (configuration) => {
50
50
  //create proxy info object before calling extractbuildparams
51
51
  let proxy = false;
52
52
  if (serviceInfo[1] === 'proxy' && serviceInfo[2] === 'redirect') {
53
- req.soajs.log.error("Route: [/proxy/redirect] is deprecated. You should use [/soajs/proxy].");
53
+ req.soajs.log.info("Route: [/proxy/redirect] is deprecated. You should use [/soajs/proxy].");
54
54
  proxy = true;
55
55
  } else if (serviceInfo[1] === 'soajs' && serviceInfo[2] === 'proxy') {
56
56
  proxy = true;
@@ -81,7 +81,8 @@ function flushMonitorBuffer() {
81
81
  }
82
82
  })
83
83
  .catch((error) => {
84
- req.soajs.log.debug('Monitor buffer flush failed: ' + error.message);
84
+ //NOTE: the buffer was already cleared, these items are lost
85
+ req.soajs.log.error('Monitor buffer flush failed, ' + itemsToSend.length + ' items lost: ' + error.message);
85
86
  });
86
87
  });
87
88
  }
@@ -11,6 +11,7 @@
11
11
  const get = (p, o) => p.reduce((xs, x) => (xs && xs[x]) ? xs[x] : null, o);
12
12
  const memory = require('./model/memory.js');
13
13
  const mongo = require('./model/mongo.js');
14
+ const { logOnce } = require('../../lib/logOnce.js');
14
15
 
15
16
  let model = { memory, mongo };
16
17
 
@@ -119,7 +120,8 @@ module.exports = function (configuration) {
119
120
 
120
121
  const modelType = idempotencyConfig.model || 'memory';
121
122
  if (!model[modelType]) {
122
- configuration.log.warn('Idempotency: Unknown model [' + modelType + ']. It can only be [memory || mongo]');
123
+ logOnce(configuration.log, "error", "idempotency_model_" + modelType,
124
+ 'Idempotency: Unknown model [' + modelType + ']. It can only be [memory || mongo]. Idempotency is off.');
123
125
  return next();
124
126
  }
125
127
 
@@ -22,10 +22,18 @@ let model = {
22
22
  if (!model.mongo) {
23
23
  model.mongo = new Mongo(dbConfiguration);
24
24
  model.mongo.createIndex(idempotency_store, { 'l1': 1, 'l2': 1 }, { unique: true }, (err, index) => {
25
- log.debug("Idempotency index: " + index + " created with error: " + err);
25
+ if (err) {
26
+ log.error("Idempotency index creation failed: " + err.message);
27
+ } else {
28
+ log.debug("Idempotency index created: " + index);
29
+ }
26
30
  });
27
31
  model.mongo.createIndex(idempotency_store, { 'expiresAt': 1 }, { expireAfterSeconds: 0 }, (err, index) => {
28
- log.debug("Idempotency TTL index: " + index + " created with error: " + err);
32
+ if (err) {
33
+ log.error("Idempotency TTL index creation failed: " + err.message);
34
+ } else {
35
+ log.debug("Idempotency TTL index created: " + index);
36
+ }
29
37
  });
30
38
  }
31
39
  },
@@ -48,7 +48,7 @@ module.exports = () => {
48
48
  if (obj.service_v) {
49
49
  let san_v = coreLibs.version.sanitize(obj.service_v);
50
50
  if (!aclObj[san_v]) {
51
- obj.req.soajs.log.error("Problem accessing service [" + obj.req.soajs.controller.serviceParams.service_n + "] & version [" + obj.req.soajs.controller.serviceParams.service_v + "]");
51
+ //NOTE: the error context already reports the service and the version
52
52
  return cb(154, null);
53
53
  }
54
54
  aclObj = aclObj[san_v];
package/mw/mt/index.js CHANGED
@@ -48,7 +48,7 @@ module.exports = (configuration) => {
48
48
  serviceInfo = req.soajs.controller.serviceParams.registry.versions[req.soajs.controller.serviceParams.version];
49
49
  }
50
50
  if (!serviceInfo) {
51
- req.soajs.log.error("Problem accessing service [" + req.soajs.controller.serviceParams.name + "], API [" + req.soajs.controller.serviceParams.path + "] & version [" + req.soajs.controller.serviceParams.version + "]");
51
+ //NOTE: the error context already reports the service, the api and the version
52
52
  return next(133);
53
53
  }
54
54
  let oauth = true;
@@ -160,12 +160,16 @@ module.exports = (configuration) => {
160
160
  //if this is controller route: /key/permission/get, ignore async waterfall response
161
161
  if (url_keyACL) {
162
162
  if (!req.soajs.uracDriver) {
163
- if (err && err.message) {
164
- req.soajs.log.error(err.message);
165
- }
166
163
  if (err && err.name === "OAuth2Error") {
164
+ //NOTE: the code and the message travel with the error, the
165
+ // error context reports both with full attribution
167
166
  return next(err);
168
167
  }
168
+ //NOTE: the cause is replaced by 158 below, so it is only
169
+ // recoverable from here
170
+ if (err && err.message) {
171
+ req.soajs.log.error(err.message);
172
+ }
169
173
  //doesn't work if you are not logged in
170
174
  return next(158);
171
175
  } else {
@@ -180,7 +184,8 @@ module.exports = (configuration) => {
180
184
  }
181
185
 
182
186
  if (err) {
183
- req.soajs.log.error("Problem accessing service [" + req.soajs.controller.serviceParams.name + "], API [" + req.soajs.controller.serviceParams.path + "] & version [" + req.soajs.controller.serviceParams.version + "]");
187
+ //NOTE: the error context already reports the service, the api and
188
+ // the version alongside the code and the message
184
189
  return next(err);
185
190
  } else {
186
191
  let serviceName = data.req.soajs.controller.serviceParams.name;
@@ -330,7 +335,9 @@ module.exports = (configuration) => {
330
335
  return callback();
331
336
  });
332
337
  } else {
333
- req.soajs.log.debug(serviceName + " interConnect failed for [" + item.name + "@" + item.version + "]");
338
+ //NOTE: the peer is dropped from the injected address book, the calling
339
+ // service will behave as if it was never configured
340
+ req.soajs.log.error(serviceName + " interConnect failed for [" + item.name + "@" + item.version + "], peer dropped from awareness");
334
341
  return callback();
335
342
  }
336
343
  });
package/mw/mt/utils.js CHANGED
@@ -190,8 +190,7 @@ let utils = {
190
190
  let block = getCachedNetmask(addr);
191
191
  return block && block.contains(ip);
192
192
  } catch (err) {
193
- obj.req.soajs.log.error('IP whitelist security configuration failed: ', addr);
194
- obj.req.soajs.log.error(err);
193
+ obj.req.soajs.log.error('IP whitelist security configuration failed for [' + addr + ']: ' + (err.message || err));
195
194
  }
196
195
  return false;
197
196
  }));
@@ -320,8 +319,7 @@ let utils = {
320
319
  let block = getCachedNetmask(addr);
321
320
  return block && block.contains(ip);
322
321
  } catch (err) {
323
- obj.req.soajs.log.error('Geographic security configuration failed: ', addr);
324
- obj.req.soajs.log.error(err.message);
322
+ obj.req.soajs.log.error('Geographic security configuration failed for [' + addr + ']: ' + (err.message || err));
325
323
  }
326
324
  return false;
327
325
  }));
@@ -12,6 +12,7 @@
12
12
  const get = (p, o) => p.reduce((xs, x) => (xs && xs[x]) ? xs[x] : null, o);
13
13
  const memory = require('./model/memory.js');
14
14
  const mongo = require('./model/mongo.js');
15
+ const { logOnce } = require('../../lib/logOnce.js');
15
16
 
16
17
  let model = {
17
18
  memory, mongo
@@ -175,7 +176,8 @@ module.exports = function (configuration) {
175
176
  if (trafficModel) {
176
177
  throttling.model = trafficModel;
177
178
  if (!model[throttling.model]) {
178
- req.soajs.log.warn('Throttling:', 'Unkown model [' + throttling.model + ']. It can only be [memory || mongo]');
179
+ logOnce(req.soajs.log, "error", "throttling_model_" + throttling.model,
180
+ 'Throttling: Unknown model [' + throttling.model + ']. It can only be [memory || mongo]. Throttling is off.');
179
181
  return next();
180
182
  }
181
183
  }
@@ -23,7 +23,11 @@ let model = {
23
23
  if (!model.mongo) {
24
24
  model.mongo = new Mongo(dbConfiguration);
25
25
  model.mongo.createIndex(throttling_monitor, { 'l1': 1, 'l2': 1 }, {}, (err, index) => {
26
- log.debug("Index: " + index + " created with error: " + err);
26
+ if (err) {
27
+ log.error("Throttling index creation failed: " + err.message);
28
+ } else {
29
+ log.debug("Throttling index created: " + index);
30
+ }
27
31
  });
28
32
  }
29
33
  },
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "soajs.controller",
3
3
  "description": "soajs multi tenant API gateway",
4
- "version": "4.3.11",
4
+ "version": "4.3.13",
5
5
  "author": {
6
6
  "name": "soajs team",
7
7
  "email": "team@soajs.org"
@@ -74,7 +74,7 @@ Controller.prototype.init = function (callback) {
74
74
  let log = core.getLogger(_self.soajs.param.init.serviceName, registry.serviceConfig.logger);
75
75
  if (service.fetched) {
76
76
  if (!service.fetched.result) {
77
- log.warn("Unable to find the service host ip. The service will NOT be registered for awareness.");
77
+ log.error("Unable to find the service host ip. The service will NOT be registered for awareness.");
78
78
  log.info("IPs found: ", service.fetched.extra.ips);
79
79
  log.warn("The default service IP has been used [" + service.ip + "]");
80
80
  } else {
@@ -316,7 +316,7 @@ Controller.prototype.start = function (registry, log, service, server, serverMai
316
316
  });
317
317
  });
318
318
  } else {
319
- log.warn("Unable to register host IP [" + service.ip + "] for service [" + _self.soajs.param.init.serviceName + "@" + _self.soajs.param.init.serviceVersion + "]");
319
+ log.error("Unable to register host IP [" + service.ip + "] for service [" + _self.soajs.param.init.serviceName + "@" + _self.soajs.param.init.serviceVersion + "]");
320
320
  }
321
321
  });
322
322
  }
@@ -145,7 +145,7 @@ let reloadRegistry = (parsedUrl, core, log, param, serviceIp, cb) => {
145
145
  }, function (err, reg) {
146
146
  let response = maintenanceResponse(parsedUrl, param);
147
147
  if (err) {
148
- log.warn("Failed to load registry. reusing from previous load. Reason: " + err.message);
148
+ log.error("Failed to load registry. reusing from previous load. Reason: " + err.message);
149
149
  } else {
150
150
  response.result = true;
151
151
  response.data = cloneAndFilterRegistry(reg);
@@ -236,7 +236,7 @@ let Maintenance = (core, log, param, serviceIp, regEnvironment, awareness_mw, so
236
236
  infoObj = body;
237
237
  }
238
238
  if (!soajsLib.version.validate(infoObj.version)) {
239
- log.warn("Failed to register service for [" + infoObj.name + "@" + infoObj.version + "] version should be of format [1.1]");
239
+ log.error("Failed to register service for [" + infoObj.name + "@" + infoObj.version + "] version should be of format [1.1]");
240
240
  // res.writeHead(200, {'Content-Type': 'application/json'});
241
241
  return res.end(JSON.stringify(response));
242
242
  }
@@ -287,7 +287,7 @@ let Maintenance = (core, log, param, serviceIp, regEnvironment, awareness_mw, so
287
287
  response.result = true;
288
288
  response.data = data;
289
289
  } else {
290
- log.warn("Failed to register service for [" + infoObj.name + "] " + err.message);
290
+ log.error("Failed to register service for [" + infoObj.name + "] " + err.message);
291
291
  }
292
292
  return res.end(JSON.stringify(response));
293
293
  });
@@ -10,6 +10,7 @@ describe("Starting Gateway Unit test", () => {
10
10
  require("./lib/apiPathParam2apiRegExp.js");
11
11
  require("./lib/http.js");
12
12
  require("./lib/ip.js");
13
+ require("./lib/logOnce.js");
13
14
  require("./lib/param.js");
14
15
  require("./lib/parseURL.js");
15
16
 
@@ -0,0 +1,81 @@
1
+ "use strict";
2
+
3
+ /**
4
+ * @license
5
+ * Copyright SOAJS All Rights Reserved.
6
+ *
7
+ * Use of this source code is governed by an Apache license that can be
8
+ * found in the LICENSE file at the root of this repository
9
+ */
10
+
11
+ const assert = require('assert');
12
+ const helper = require("../../helper.js");
13
+
14
+ let logOnceLib = helper.requireModule('./lib/logOnce.js');
15
+ const { logOnce, resetLogOnce } = logOnceLib;
16
+
17
+ describe("Unit test for: lib - logOnce", () => {
18
+
19
+ let emitted = [];
20
+ let log = {
21
+ error: (input) => {
22
+ emitted.push({ "level": "error", "input": input });
23
+ },
24
+ warn: (input) => {
25
+ emitted.push({ "level": "warn", "input": input });
26
+ }
27
+ };
28
+
29
+ beforeEach(() => {
30
+ emitted = [];
31
+ resetLogOnce();
32
+ });
33
+
34
+ it("emits the first time", (done) => {
35
+ let did = logOnce(log, "error", "k1", "boom");
36
+ assert.strictEqual(did, true);
37
+ assert.strictEqual(emitted.length, 1);
38
+ assert.strictEqual(emitted[0].level, "error");
39
+ assert.strictEqual(emitted[0].input, "boom");
40
+ done();
41
+ });
42
+
43
+ it("drops repeats of the same key", (done) => {
44
+ for (let i = 0; i < 100; i++) {
45
+ logOnce(log, "error", "k1", "boom");
46
+ }
47
+ assert.strictEqual(emitted.length, 1);
48
+ done();
49
+ });
50
+
51
+ it("keeps distinct keys separate", (done) => {
52
+ logOnce(log, "error", "k1", "one");
53
+ logOnce(log, "error", "k2", "two");
54
+ logOnce(log, "error", "k1", "one again");
55
+ assert.strictEqual(emitted.length, 2);
56
+ assert.strictEqual(emitted[0].input, "one");
57
+ assert.strictEqual(emitted[1].input, "two");
58
+ done();
59
+ });
60
+
61
+ it("honours the requested level", (done) => {
62
+ logOnce(log, "warn", "k3", "careful");
63
+ assert.strictEqual(emitted[0].level, "warn");
64
+ done();
65
+ });
66
+
67
+ it("does not throw on a missing logger or unknown level", (done) => {
68
+ assert.strictEqual(logOnce(null, "error", "k4", "x"), false);
69
+ assert.strictEqual(logOnce(log, "nosuchlevel", "k5", "x"), false);
70
+ assert.strictEqual(emitted.length, 0);
71
+ done();
72
+ });
73
+
74
+ it("does not consume the key when it could not emit", (done) => {
75
+ logOnce(null, "error", "k6", "x");
76
+ let did = logOnce(log, "error", "k6", "now it works");
77
+ assert.strictEqual(did, true);
78
+ assert.strictEqual(emitted.length, 1);
79
+ done();
80
+ });
81
+ });
@@ -109,6 +109,56 @@ describe("Testing utilities", () => {
109
109
  });
110
110
  });
111
111
 
112
+ it("logErrors - no longer duplicates what the error context reports", (done) => {
113
+ // the full chain: logErrors -> controllerClientErrorHandler -> controllerErrorHandler
114
+ let r = makeReq(true);
115
+ let oauthErr = new Error("The access token provided is invalid.");
116
+ oauthErr.name = "OAuth2Error";
117
+ oauthErr.code = 401;
118
+
119
+ utils.logErrors(oauthErr, r, res, (e1) => {
120
+ utils.controllerClientErrorHandler(e1, r, res, (e2) => {
121
+ utils.controllerErrorHandler(e2, r, res, null);
122
+ });
123
+ });
124
+
125
+ assert.strictEqual(logged.error.length, 1, "expected exactly one error line for one failure");
126
+ let ctx = JSON.parse(logged.error[0]);
127
+ assert.strictEqual(ctx.code, 401);
128
+ assert.strictEqual(ctx.msg, "The access token provided is invalid.");
129
+ done();
130
+ });
131
+
132
+ it("logErrors - keeps the underlying cause when it becomes a generic 164", (done) => {
133
+ // an error with neither code nor msg is replaced by 164, so the context line
134
+ // cannot report the original. logErrors must still record it.
135
+ let r = makeReq(true);
136
+ utils.logErrors({ "message": "socket hang up" }, r, res, (e1) => {
137
+ utils.controllerClientErrorHandler(e1, r, res, (e2) => {
138
+ utils.controllerErrorHandler(e2, r, res, null);
139
+ });
140
+ });
141
+
142
+ assert.strictEqual(logged.error.length, 2);
143
+ assert.strictEqual(logged.error[0], "socket hang up", "underlying cause must survive");
144
+ let ctx = JSON.parse(logged.error[1]);
145
+ assert.strictEqual(ctx.code, 164);
146
+ done();
147
+ });
148
+
149
+ it("errorContext - falls back to service_n and service_v before gotoService resolves them", (done) => {
150
+ // key and keyACL run before gotoService, so serviceParams carries only the
151
+ // raw service_n / service_v at that point
152
+ let r = makeReq(true);
153
+ r.soajs.controller.serviceParams = { "service_n": "urac", "service_v": "3" };
154
+ utils.controllerErrorHandler(154, r, res, null);
155
+
156
+ let ctx = JSON.parse(logged.error[0]);
157
+ assert.strictEqual(ctx.service, "urac");
158
+ assert.strictEqual(ctx.version, "3");
159
+ done();
160
+ });
161
+
112
162
  it("logErrors - string error", (done) => {
113
163
  utils.logErrors("error", req, res, (error) => {
114
164
  done();
@@ -51,8 +51,10 @@ function errorContext(err, req) {
51
51
  "code": code,
52
52
  "msg": msg,
53
53
  "method": req.method,
54
+ //NOTE: name and version are resolved by gotoService, the service_n and service_v
55
+ // fallbacks cover the middleware that runs before it, ex: key and keyACL
54
56
  "service": serviceParams.name || serviceParams.service_n || null,
55
- "version": serviceParams.version || null,
57
+ "version": serviceParams.version || serviceParams.service_v || null,
56
58
  "api": (serviceParams.parsedUrl && serviceParams.parsedUrl.pathname) || null,
57
59
  "url": redactUrl(req.url),
58
60
  "ip": req.getClientIP ? req.getClientIP() : null,
@@ -86,13 +88,15 @@ function errorContext(err, req) {
86
88
  * @param next
87
89
  */
88
90
  function logErrors(err, req, res, next) {
91
+ //NOTE: every error routed here reaches controllerErrorHandler, which logs a
92
+ // structured context carrying the code and the message. The cases below
93
+ // only log what that context cannot reproduce, which is the underlying
94
+ // cause of an error that gets replaced by the generic 164.
89
95
  if (typeof err === "number") {
90
- req.soajs.log.error(core.error.generate(err).message);
91
96
  return next(err);
92
97
  }
93
98
  if (typeof err === "object") {
94
99
  if (err.code && err.message) {
95
- req.soajs.log.error(err.message);
96
100
  if (err.name === "OAuth2Error") {
97
101
  return next({ "code": err.code, "status": err.code, "msg": err.message });
98
102
  } else {
@@ -100,15 +104,12 @@ function logErrors(err, req, res, next) {
100
104
  }
101
105
  } else if (err.code && err.msg) {
102
106
  err.message = err.msg;
103
- req.soajs.log.error(err.message);
104
107
  return next(err);
105
108
  } else {
106
109
  req.soajs.log.error(err.message || err);
107
- req.soajs.log.error(core.error.generate(164).message);
108
110
  }
109
111
  } else {
110
112
  req.soajs.log.error(err);
111
- req.soajs.log.error(core.error.generate(164).message);
112
113
  }
113
114
 
114
115
  return next(core.error.getError(164));