diff --git a/components/api-server/src/application.js b/components/api-server/src/application.js index a84612a14..062a86b17 100644 --- a/components/api-server/src/application.js +++ b/components/api-server/src/application.js @@ -56,6 +56,7 @@ const userLocalDirectory = require('business').users.userLocalDirectory; const { Extension, ExtensionLoader } = require('utils').extension; const { getAPIVersion } = require('middleware/src/project_version'); +const { pubsub } = require('messages'); const { tracingMiddleware } = require('tracing'); logger.debug('Loading app'); diff --git a/components/api-server/src/expressApp.js b/components/api-server/src/expressApp.js index a76a131a0..add8322f4 100644 --- a/components/api-server/src/expressApp.js +++ b/components/api-server/src/expressApp.js @@ -18,6 +18,8 @@ const { getAPIVersion } = require('middleware/src/project_version'); const { getConfig } = require('@pryv/boiler'); +const { initExpressTracer } = require('tracing'); + // ------------------------------------------------------------ express app init // Creates and returns an express application with a standard set of middleware. @@ -27,6 +29,9 @@ async function expressAppInit(logging) { const version = await getAPIVersion(); const config = await getConfig(); const app = express(); // register common middleware + + initExpressTracer(app); + const commonHeadersMiddleware = await middleware.commonHeaders(); const requestTraceMiddleware = middleware.requestTrace(app, logging); diff --git a/components/api-server/src/routes/auth/register.js b/components/api-server/src/routes/auth/register.js index 6d667aa7e..48420ddfd 100644 --- a/components/api-server/src/routes/auth/register.js +++ b/components/api-server/src/routes/auth/register.js @@ -47,13 +47,13 @@ module.exports = function (expressApp: express$Application, app: Application) { expressApp.get(path.join(regPath, '/:username/check_username'), setMinimalMethodContext, setMethodId('auth.usernameCheck'), - (req: express$Request, res, next) => { + function apiCall(req: express$Request, res, next) { api.call(req.context, req.params, methodCallback(res, next, 200)); }); expressApp.get(path.join(regPath, '/:email/check_email'), setMinimalMethodContext, setMethodId('auth.emailCheck'), - (req: express$Request, res, next) => { + function apiCall(req: express$Request, res, next) { api.call(req.context, req.params, methodCallback(res, next, 200)); }); expressApp.post(path.join(regPath, '/username/check'), (req: express$Request, res, next) => { diff --git a/components/api-server/src/routes/events.js b/components/api-server/src/routes/events.js index 17db34c6c..5667683e6 100644 --- a/components/api-server/src/routes/events.js +++ b/components/api-server/src/routes/events.js @@ -35,7 +35,7 @@ module.exports = async function(expressApp: express$Application, app: Applicatio expressApp.get(Paths.Events + '/', setMethodId('events.get'), loadAccessMiddleware, - function (req: express$Request, res, next) { + function coerce(req: express$Request, res, next) { const params = _.extend({}, req.query); tryCoerceStringValues(params, { fromTime: 'number', @@ -56,7 +56,7 @@ module.exports = async function(expressApp: express$Application, app: Applicatio expressApp.get(Paths.Events + '/:id', setMethodId('events.getOne'), loadAccessMiddleware, - function (req: express$Request, res, next) { + function coerce(req: express$Request, res, next) { const params = _.extend({id: req.params.id}, req.query); tryCoerceStringValues(params, { includeHistory: 'boolean' @@ -126,7 +126,7 @@ module.exports = async function(expressApp: express$Application, app: Applicatio setMethodId('events.create'), loadAccessMiddleware, hasFileUpload, - function (req: express$Request, res, next) { + function apiCall(req: express$Request, res, next) { const params = req.body; if (req.files) { params.files = req.files; @@ -144,12 +144,12 @@ module.exports = async function(expressApp: express$Application, app: Applicatio expressApp.put(Paths.Events + '/:id', setMethodId('events.update'), loadAccessMiddleware, - function (req: express$Request, res, next) { + function apiCall(req: express$Request, res, next) { api.call(req.context, { id: req.params.id, update: req.body }, methodCallback(res, next, 200)); }); expressApp.post(Paths.Events + '/stop', - function (req: express$Request, res, next) { + function apiCall(req: express$Request, res, next) { return next(errors.goneResource()); }); @@ -158,7 +158,7 @@ module.exports = async function(expressApp: express$Application, app: Applicatio setMethodId('events.update'), loadAccessMiddleware, hasFileUpload, - function (req: express$Request, res, next) { + function apiCall(req: express$Request, res, next) { const params = { id: req.params.id, update: {} @@ -174,14 +174,14 @@ module.exports = async function(expressApp: express$Application, app: Applicatio expressApp.delete(Paths.Events + '/:id', setMethodId('events.delete'), loadAccessMiddleware, - function (req: express$Request, res, next) { + function apiCall(req: express$Request, res, next) { api.call(req.context, {id: req.params.id}, methodCallback(res, next, 200)); }); expressApp.delete(Paths.Events + '/:id/:fileId', setMethodId('events.deleteAttachment'), loadAccessMiddleware, - function (req: express$Request, res, next) { + function apiCall(req: express$Request, res, next) { api.call(req.context, {id: req.params.id, fileId: req.params.fileId}, methodCallback(res, next, 200)); }); diff --git a/components/api-server/src/routes/methodCallback.js b/components/api-server/src/routes/methodCallback.js index 862166548..eb4d1b992 100644 --- a/components/api-server/src/routes/methodCallback.js +++ b/components/api-server/src/routes/methodCallback.js @@ -18,7 +18,7 @@ import type Result from '../Result'; * @returns {Function} */ module.exports = function (res: express$Response, next: express$NextFunction, successCode: number) { - return function (err: ?Error, result: ?Result) { + return function methodCallBack(err: ?Error, result: ?Result) { if (err != null) { return next(err); diff --git a/components/api-server/src/routes/streams.js b/components/api-server/src/routes/streams.js index e9f6b38ef..7602f09dd 100644 --- a/components/api-server/src/routes/streams.js +++ b/components/api-server/src/routes/streams.js @@ -24,7 +24,7 @@ module.exports = function (expressApp: express$Application, app: Application) { expressApp.get(Paths.Streams, loadAccessMiddleware, setMethodId('streams.get'), - function (req: express$Request, res, next) { + function coerce (req: express$Request, res, next) { const params = _.extend({}, req.query); tryCoerceStringValues(params, { includeDeletionsSince: 'number' diff --git a/components/middleware/src/commonHeaders.js b/components/middleware/src/commonHeaders.js index 524b63b4b..c41d7841b 100644 --- a/components/middleware/src/commonHeaders.js +++ b/components/middleware/src/commonHeaders.js @@ -11,9 +11,9 @@ const { getAPIVersion } = require('middleware/src/project_version'); // Middleware to handle OPTIONS requests and to add CORS headers to all other // requests. -module.exports = async function (): Promise { +module.exports = async function (): Promise { const version = await getAPIVersion(); - return function (req: express$Request, res: express$Response, next: express$NextFunction) { + return function commonHeaders(req: express$Request, res: express$Response, next: express$NextFunction) { // allow cross-domain requests (CORS) res.header('Access-Control-Allow-Origin', req.headers.origin || '*'); // * diff --git a/components/middleware/src/contentType.js b/components/middleware/src/contentType.js index 189541351..13948beca 100644 --- a/components/middleware/src/contentType.js +++ b/components/middleware/src/contentType.js @@ -17,7 +17,7 @@ var errors = require('errors').factory; function checkContentType(/* arguments */) { var acceptedTypes = arguments, count = acceptedTypes.length; - return function (req, res, next) { + return function checkContentType(req, res, next) { if (count < 1) { return next(); } var contentType = req.headers['content-type']; diff --git a/components/middleware/src/initContext.js b/components/middleware/src/initContext.js index 6b32df2d3..333945500 100644 --- a/components/middleware/src/initContext.js +++ b/components/middleware/src/initContext.js @@ -21,7 +21,7 @@ import type { StorageLayer } from 'storage'; module.exports = function initContext( storageLayer: StorageLayer, customAuthStepFn: ?CustomAuthFunction ) { - return function ( + return function initContext( req: express$Request, res: express$Response, next: express$NextFunction ) { const authorizationHeader = req.headers['authorization']; diff --git a/components/middleware/src/loadAccess.js b/components/middleware/src/loadAccess.js index aa453c4fc..62f4a8c4d 100644 --- a/components/middleware/src/loadAccess.js +++ b/components/middleware/src/loadAccess.js @@ -13,7 +13,7 @@ import type { StorageLayer } from 'storage'; // Also, it adds the corresponding access id as a specific response header. // module.exports = function loadAccess(storageLayer: StorageLayer) { - return async function ( + return async function loadAccess( req: express$Request, res: express$Response, next: express$NextFunction ) { diff --git a/components/middleware/src/notFound.js b/components/middleware/src/notFound.js index 64685f0d5..dfd23c8e7 100644 --- a/components/middleware/src/notFound.js +++ b/components/middleware/src/notFound.js @@ -9,6 +9,6 @@ const errors = require('errors').factory; /** * '404' handling to override Express' defaults. Must be set after the routes in the init sequence. */ -module.exports = function (req, res, next) { +module.exports = function notFound(req, res, next) { return next(errors.unknownResource()); }; diff --git a/components/middleware/src/requestTrace.js b/components/middleware/src/requestTrace.js index f33aabd7a..92378f437 100644 --- a/components/middleware/src/requestTrace.js +++ b/components/middleware/src/requestTrace.js @@ -10,7 +10,7 @@ const morgan = require('morgan'); const { getLogger } = require('@pryv/boiler'); -module.exports = function (express: any) { +module.exports = function requestTrace(express: any) { const logger = getLogger('request-trace'); const morganLoggerStreamWrite = (msg: string) => logger.info(msg); diff --git a/components/middleware/src/setMethodId.js b/components/middleware/src/setMethodId.js index 66afc5371..76c232a56 100644 --- a/components/middleware/src/setMethodId.js +++ b/components/middleware/src/setMethodId.js @@ -15,9 +15,9 @@ module.exports = function (methodId: string) { req: express$Request, res: express$Response, next: express$NextFunction ) { if (req.context == null) { - const tracing = initRootSpan('express2'); + const tracing = initRootSpan('expressX'); req.context = { tracing: tracing}; - res.on('finish', () => { tracing.finishSpan('express2', 'e2:' + methodId)} ) + res.on('finish', () => { tracing.finishSpan('expressX', 'e2:' + methodId)} ) } req.context.methodId = methodId; diff --git a/components/middleware/src/subdomainToPath.js b/components/middleware/src/subdomainToPath.js index 8f9ccdc14..1130d12c4 100644 --- a/components/middleware/src/subdomainToPath.js +++ b/components/middleware/src/subdomainToPath.js @@ -21,7 +21,7 @@ const { USERNAME_REGEXP_STR } = require('api-server/src/schema/helpers'); * @return {Function} */ module.exports = function (ignoredPaths: Array) { - return function (req: express$Request, res: express$Response, next: express$NextFunction) { + return function subdomainToPath (req: express$Request, res: express$Response, next: express$NextFunction) { if (isIgnoredPath(req.url)) { return next(); } if (! req.headers.host) { return next(errors.missingHeader('Host')); } diff --git a/components/tracing/src/expressPatch.js b/components/tracing/src/expressPatch.js new file mode 100644 index 000000000..a03087dbd --- /dev/null +++ b/components/tracing/src/expressPatch.js @@ -0,0 +1,66 @@ +/** + * @license + * Copyright (C) 2012-2021 Pryv S.A. https://pryv.com - All Rights Reserved + * Unauthorized copying of this file, via any medium is strictly prohibited + * Proprietary and confidential + */ + + +module.exports = function patchApp(app, expressSpanName) { + if (app.legacy_use != null) throw new Error('Already patched'); + + app.legacy_use = app.use; + app.use = function () { + const newArgs = []; + for (let i = 0; i < arguments.length; i++) { + // !!!! doing nothing for now.. all attemp to patch "use" failed + newArgs.push(patchFunction0(arguments[i])); + } + //console.log(arguments, newArgs); + return app.legacy_use(...newArgs); + } + + patch('get'); + patch('post'); + patch('put'); + patch('delete'); + + function patch(key) { + app['legacy_' + key] = app[key]; + app[key] = function () { + const newArgs = [arguments[0]]; + for (let i = 1; i < arguments.length; i++) { + const fn = arguments[i]; + const spanName = 'e:' + key + ':' + arguments[0] + ':' + (fn.name || ('unamed.' + i)); + newArgs.push(patchFunction(fn, spanName)); + } + return app['legacy_' + key](...newArgs); + } + } + + function patchFunction(fn, spanName) { + return async function (req, res, next) { + req.tracing.startSpan(spanName, {}, expressSpanName); + return await fn(req, res, function nextCloseSpan(err) { + req.tracing.finishSpan(spanName); + next(err); + }); + } + } + + // doing nothing + function patchFunction0(fn) { + console.log('>>>> unpatched: ', fn.name); + return fn; + } + + // kept for reference + function patchFunction2(fn) { + // return fn; + if (fn.constructor.name === 'AsyncFunction') { + return async () => { try { return await fn.apply(null, arguments); } catch (e) { console.log('XXXX', e) } } + } + return () => { try { return fn.apply(null, arguments); } catch (e) { console.log(e) } } + } + +} \ No newline at end of file diff --git a/components/tracing/src/index.js b/components/tracing/src/index.js index b60584904..1f2765ae5 100644 --- a/components/tracing/src/index.js +++ b/components/tracing/src/index.js @@ -8,10 +8,10 @@ -const { Tracing, DummyTracing } = require('./Tracing'); +const { Tracing, DummyTracing } = require('./Tracing'); const { getHookerTracer } = require('./HookedTracer'); - +const expressPatch = require('./expressPatch'); const dataBaseTracer = require('./databaseTracer'); const { getConfigUnsafe } = require('@pryv/boiler'); const isTracingEnabled = getConfigUnsafe(true).get('trace:enable'); @@ -19,8 +19,8 @@ const launchTags = getConfigUnsafe(true).get('trace:tags'); module.exports.DummyTracing = DummyTracing; -module.exports.dataBaseTracer = dataBaseTracer; +module.exports.dataBaseTracer = dataBaseTracer; module.exports.getHookerTracer = getHookerTracer; /** @@ -42,9 +42,9 @@ module.exports.initRootSpan = initRootSpan; /** * Returns an ExpressJS middleware that starts a span and attaches the "tracing" object to the request parameter. */ -module.exports.tracingMiddleware = (name: string = 'express1', tags: ?{}): Function => { +function tracingMiddleware (name: string, tags: ?{}): Function { return function (req: express$Request, res: express$Response, next: express$NextFunction): void { - if (req.tracing != null) { console.log('XXXXX tracing already set', new Error()); return next();} + if (req.tracing != null) { console.log('XXXXX tracing already set', new Error()); return next();} const tracing = initRootSpan (name, tags); res.on('close', () => { const extra = req.context?.methodId || req.url; @@ -54,3 +54,10 @@ module.exports.tracingMiddleware = (name: string = 'express1', tags: ?{}): Funct next(); } } +module.exports.tracingMiddleware = tracingMiddleware; + +module.exports.initExpressTracer = function(app) { + const expressTraceName = 'express2'; + app.use(tracingMiddleware(expressTraceName)); // anyway .. initRootSpan will retrun a dummytracer is not enabled + if (isTracingEnabled) expressPatch(app, expressTraceName); +} \ No newline at end of file