diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index b2aa177d..b8d18463 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -13,7 +13,7 @@ jobs: matrix: meteorRelease: # Meteor 3 Alpha - - '--release 3.0-alpha.16' + - '--release 3.0-alpha.17' steps: - name: Checkout code uses: actions/checkout@v2 @@ -30,7 +30,4 @@ jobs: - name: Run Tests run: | - # Fix using old versions of Meteor - export NODE_TLS_REJECT_UNAUTHORIZED=0 - mtest --package ./ --once ${{ matrix.meteorRelease }} diff --git a/experimental.js b/experimental.js deleted file mode 100644 index e3345d5e..00000000 --- a/experimental.js +++ /dev/null @@ -1,80 +0,0 @@ -const asyncHooks = require('async_hooks'); - -const ignoredStackFrames = [/internal\/async_hooks/, /^\s+at AsyncHook\.init/]; - -function stackTrace () { - const restoreLimit = Error.stackTraceLimit; - Error.stackTraceLimit = 10; - - const obj = {}; - Error.captureStackTrace(obj, stackTrace); - const structuredStackTrace = obj.stack; - - Error.stackTraceLimit = restoreLimit; - - return deleteLinesWhichMatchAnyOf(structuredStackTrace, ignoredStackFrames); -} - -function deleteLinesWhichMatchAnyOf (string, regexes) { - return string.split('\n').filter(line => !regexes.some(regex => regex.test(line))).join('\n'); -} - -const resources = new Map(); - -const hook = asyncHooks.createHook({ - init: (asyncId, type, triggerAsyncId) => { - if (type !== 'PROMISE') return; - - if (triggerAsyncId) { - const parent = resources.get(triggerAsyncId); - if (parent) { - parent.children = parent.children || []; - parent.children.push(asyncId); - } - } - - resources.set(asyncId, { - asyncId, - triggerAsyncId, - type, - trace: stackTrace(), - }); - }, - before: (asyncId) => { - if (!resources.has(asyncId)) return; - resources.get(asyncId).before = Date.now(); - }, - after: (asyncId) => { - if (!resources.has(asyncId)) return; - resources.get(asyncId).after = Date.now(); - }, - promiseResolve: (asyncId) => { - if (!resources.has(asyncId)) return; - resources.get(asyncId).promiseResolve = Date.now(); - }, - destroy: (asyncId) => { - if (!resources.has(asyncId)) return; - resources.get(asyncId).destroy = Date.now(); - } -}); - -hook.enable(); - -process.once('beforeExit', function () { - hook.disable(); -}); - -async function foo () { - await 0; - return true; -} - -async function start () { - await new Promise((resolve) => setTimeout(resolve, 1000)).then(() => true); - - await foo(); -} - -start().catch(console.error).finally(() => { - console.log(resources); -}); diff --git a/lib/auto_connect.js b/lib/auto_connect.js index 0abbad56..2690c2bd 100644 --- a/lib/auto_connect.js +++ b/lib/auto_connect.js @@ -44,12 +44,9 @@ Kadira._connectWithSettings = function () { * Previously there was two log messages (one for instrumentation, * and another for connection), this way we merged both of them. */ - -console.log('Monti APM: Instrumentation started...'); - Kadira._startInstrumenting(function () { Kadira._connectWithEnv(); Kadira._connectWithSettings(); console.log('Monti APM: Instrumentation completed'); -}).catch(console.error); +}); diff --git a/lib/common/unify.js b/lib/common/unify.js index 51934e32..9cb28bfc 100644 --- a/lib/common/unify.js +++ b/lib/common/unify.js @@ -8,7 +8,7 @@ Monti = Kadira; Kadira.wrapAsync = Meteor.wrapAsync; if (Meteor.isServer) { - const EventEmitter = require('events').EventEmitter; + const EventEmitter = Npm.require('events').EventEmitter; const eventBus = new EventEmitter(); eventBus.setMaxListeners(0); diff --git a/lib/constants.js b/lib/constants.js index 9596828f..1187ea36 100644 --- a/lib/constants.js +++ b/lib/constants.js @@ -11,4 +11,5 @@ export const EventType = { HTTP: 'http', Start: 'start', Wait: 'wait', + Email: 'email', }; diff --git a/lib/hijack/db/get-cursor-data.js b/lib/hijack/db/get-cursor-data.js index dee34b3c..19a8e1a5 100644 --- a/lib/hijack/db/get-cursor-data.js +++ b/lib/hijack/db/get-cursor-data.js @@ -1,5 +1,3 @@ -import { pick } from '../../utils'; - export function getCursorData ({ type, cursor }) { const cursorDescription = cursor._cursorDescription; @@ -11,12 +9,15 @@ export function getCursorData ({ type, cursor }) { }); if (cursorDescription.options) { - let cursorOptions = pick(cursorDescription.options, ['fields', 'projection', 'sort', 'limit']); - for (let field in cursorOptions) { - let value = cursorOptions[field]; + const fields = ['fields', 'projection', 'sort', 'limit']; + + for (let field of fields) { + let value = cursorDescription.options[field]; + if (typeof value === 'object') { value = JSON.stringify(value); } + payload[field] = value; } } diff --git a/lib/hijack/db/hijack-async-methods.js b/lib/hijack/db/hijack-async-methods.js index 78e079be..0d67f539 100644 --- a/lib/hijack/db/hijack-async-methods.js +++ b/lib/hijack/db/hijack-async-methods.js @@ -42,9 +42,6 @@ export function hijackAsyncMethods () { } return Kadira.tracer.asyncEvent(EventType.DB, payload, null, async (event) => { - // this cause V8 to avoid any performance optimizations, but this is must use - // otherwise, if the error adds try catch block our logs get messy and didn't work - // see: issue #6 const result = await originalFunc.apply(this, arguments); // handling functions which can be triggered with an asyncCallback let endData = {}; diff --git a/lib/hijack/email.js b/lib/hijack/email.js index aded9f7b..208706fc 100644 --- a/lib/hijack/email.js +++ b/lib/hijack/email.js @@ -1,11 +1,12 @@ import { pick } from '../utils'; +import { EventType } from '../constants'; const CAPTURED_OPTIONS = ['from', 'to', 'cc', 'bcc', 'replyTo', 'messageId']; -const getWrapper = (originalSend, eventName = 'email') => function wrapper (options) { - const data = pick(options, CAPTURED_OPTIONS); +const getWrapper = (originalSend, func) => function wrapper (options) { + const data = { ...pick(options, CAPTURED_OPTIONS), func }; - return Kadira.tracer.asyncEvent(eventName, data, null, () => originalSend.call(this, options)); + return Kadira.tracer.asyncEvent(EventType.Email, data, null, () => originalSend.call(this, options)); }; if (Package['email']) { diff --git a/lib/hijack/fs.js b/lib/hijack/fs.js index d83f3a84..0e852df4 100644 --- a/lib/hijack/fs.js +++ b/lib/hijack/fs.js @@ -27,15 +27,9 @@ export function handleErrorEvent (eventEmitter, trace, event) { } export function wrapFs () { - // Some npm packages will do fs calls in the - // callback of another fs call. - // This variable is set with the kadiraInfo while - // a callback is run so we can track other fs calls - let fsKadiraInfo = null; - let originalStat = fs.stat; fs.stat = function () { - const kadiraInfo = Kadira._getInfo() || fsKadiraInfo; + const kadiraInfo = Kadira._getInfo(); if (kadiraInfo) { let event = Kadira.tracer.event(kadiraInfo.trace, EventType.FS, { @@ -47,11 +41,7 @@ export function wrapFs () { wrapCallback(arguments, (cb) => function () { Kadira.tracer.eventEnd(kadiraInfo.trace, event); - try { - cb(...arguments); - } finally { - fsKadiraInfo = null; - } + cb(...arguments); }); } @@ -60,7 +50,7 @@ export function wrapFs () { let originalCreateReadStream = fs.createReadStream; fs.createReadStream = function () { - const kadiraInfo = Kadira._getInfo() || fsKadiraInfo; + const kadiraInfo = Kadira._getInfo(); let stream = originalCreateReadStream.apply(this, arguments); if (kadiraInfo) { diff --git a/package.js b/package.js index 88eed798..24d0afde 100644 --- a/package.js +++ b/package.js @@ -87,9 +87,7 @@ Package.onTest(function (api) { 'tests/event_loop_monitor.js', ], 'server'); - if (canRunTestsWithFetch()) { - api.addFiles(['tests/hijack/http_fetch.js'], 'server'); - } + api.addFiles(['tests/hijack/http_fetch.js'], 'server'); // common client api.addFiles([ @@ -110,21 +108,6 @@ Package.onTest(function (api) { ], ['client', 'server']); }); -// use meteor/fetch in tests only for NodeJS 8.11+ (Meteor 1.7+) -function canRunTestsWithFetch () { - const nums = process.versions.node.split('.').map(Number); - - const major = nums[0]; - const minor = nums[1]; - - if (major < 8) return false; - - if (major > 8) return true; - - // major === 8 and ... - return minor >= 11; -} - function configurePackage (api, isTesting) { api.versionsFrom('METEOR@2.0'); api.use('montiapm:meteorx@2.3.1', ['server']); @@ -143,7 +126,7 @@ function configurePackage (api, isTesting) { api.use(['http', 'email'], 'server', { weak: !isTesting }); api.use('fetch', 'server', { - weak: !(isTesting && canRunTestsWithFetch()), + weak: !isTesting, }); api.use(['random', 'ecmascript', 'tracker'], ['client']); diff --git a/tests/hijack/base.js b/tests/hijack/base.js index 8202a392..94732122 100644 --- a/tests/hijack/base.js +++ b/tests/hijack/base.js @@ -13,7 +13,12 @@ addAsyncTest( let events = getLastMethodEvents([0, 2, 3]); - let expected = [['start',{userId: null,params: '[10,"abc"]'}],['wait',{waitOn: []},{at: 1,endAt: 1}],['db',{coll: 'tinytest-data',func: 'insertAsync'},{at: 1,endAt: 1}],['complete']]; + let expected = [ + ['start',{userId: null,params: '[10,"abc"]'}], + ['wait',{waitOn: []},{at: 1,endAt: 1}], + ['db',{coll: 'tinytest-data',func: 'insertAsync'},{at: 1,endAt: 1}], + ['complete'] + ]; test.stableEqual(events, expected); } diff --git a/tests/hijack/email.js b/tests/hijack/email.js index 386963fc..08a5e09d 100644 --- a/tests/hijack/email.js +++ b/tests/hijack/email.js @@ -20,7 +20,7 @@ addAsyncTest( const expected = [ ['start'], ['wait'], - ['emailAsync'], + ['email'], ['complete'] ]; diff --git a/tests/hijack/user.js b/tests/hijack/user.js index 22b43ab5..cd789f93 100644 --- a/tests/hijack/user.js +++ b/tests/hijack/user.js @@ -14,7 +14,12 @@ addAsyncTest( let events = getLastMethodEvents([0, 2, 3]); - let expected = [['start',{userId: null,params: '[]'}],['wait',{waitOn: []},{at: 1,endAt: 1}],['db',{coll: 'tinytest-data',func: 'insertAsync'},{at: 1,endAt: 1}],['complete']]; + let expected = [ + ['start',{userId: null,params: '[]'}], + ['wait',{waitOn: []},{at: 1,endAt: 1}], + ['db',{coll: 'tinytest-data',func: 'insertAsync'},{at: 1,endAt: 1}], + ['complete'] + ]; test.stableEqual(events, expected); } diff --git a/tests/hijack/webapp.js b/tests/hijack/webapp.js index e44021c6..54bc3297 100644 --- a/tests/hijack/webapp.js +++ b/tests/hijack/webapp.js @@ -1,57 +1,47 @@ import { WebApp } from 'meteor/webapp'; -import { wrapWebApp } from '../../lib/hijack/wrap_webapp'; -import { addAsyncTest, releaseParts } from '../_helpers/helpers'; +import { addAsyncTest } from '../_helpers/helpers'; -// Check if Meteor 1.7 or newer, which are the -// versions that wrap connect handlers in a fiber and are easy -// to wrap the static middleware -const httpMonitoringEnabled = releaseParts[0] > 1 || - (releaseParts[0] > 0 && releaseParts[1] > 6); -if (httpMonitoringEnabled) { - wrapWebApp(); - - addAsyncTest( - 'Webapp - return connect app from .use', - async function (test) { - const result = WebApp.expressHandlers.use((req, res, next) => { - next(); - }); - - test.equal(result, WebApp.expressHandlers); - } - ); - - addAsyncTest( - 'Webapp - filter headers', - async function (test) { - Kadira.tracer.redactField('x--test--authorization'); - - let req = { - url: '/test', - method: 'GET', - headers: { - 'content-type': 'application/json', - 'content-length': '1000', - 'x--test--authorization': 'secret' - } - }; - - const firstMiddleware = WebApp.rawExpressHandlers.parent._router.stack[0].handle; +addAsyncTest( + 'Webapp - return connect app from .use', + async function (test) { + const result = WebApp.expressHandlers.use((req, res, next) => { + next(); + }); - await new Promise((resolve) => { - firstMiddleware( - req, - { on () {} }, - function () { - const expected = JSON.stringify({ - 'content-type': 'application/json', - 'content-length': '1000', - 'x--test--authorization': 'Monti: redacted' - }); - test.equal(req.__kadiraInfo.trace.events[0].data.headers, expected); - resolve(); + test.equal(result, WebApp.expressHandlers); + } +); + +addAsyncTest( + 'Webapp - filter headers', + async function (test) { + Kadira.tracer.redactField('x--test--authorization'); + + let req = { + url: '/test', + method: 'GET', + headers: { + 'content-type': 'application/json', + 'content-length': '1000', + 'x--test--authorization': 'secret' + } + }; + + const firstMiddleware = WebApp.rawExpressHandlers.parent._router.stack[0].handle; + + await new Promise((resolve) => { + firstMiddleware( + req, + { on () {} }, + function () { + const expected = JSON.stringify({ + 'content-type': 'application/json', + 'content-length': '1000', + 'x--test--authorization': 'Monti: redacted' }); - }); + test.equal(req.__kadiraInfo.trace.events[0].data.headers, expected); + resolve(); + }); }); -} + }); diff --git a/tests/tracer/tracer.js b/tests/tracer/tracer.js index f4bba114..27925ac5 100644 --- a/tests/tracer/tracer.js +++ b/tests/tracer/tracer.js @@ -566,7 +566,26 @@ addAsyncTest('Tracer - Build Trace - custom with nested parallel events', async const events = getLastMethodEvents([0, 2, 3]); - const expected = [['start',{userId: null,params: '[]'}],['wait',{waitOn: []},{at: 1,endAt: 1}],['custom',null,{name: 'test',at: 1,endAt: 1,nested: [['db',{coll: 'tinytest-data',func: 'insertAsync'},{at: 1,endAt: 1}],['db',{coll: 'tinytest-data',func: 'insertAsync'},{at: 1,endAt: 1}],['db',{coll: 'tinytest-data',func: 'insertAsync'},{at: 1,endAt: 1}],['emailAsync',{from: 'arunoda@meteorhacks.com',to: 'hello@meteor.com'},{at: 1,endAt: 1}],['db',{coll: 'tinytest-data',selector: '{"_id":"a"}',func: 'fetch',cursor: true,limit: 1,docsFetched: 1,docSize: 1},{at: 1,endAt: 1}],['db',{coll: 'tinytest-data',selector: '{"_id":"b"}',func: 'fetch',cursor: true,limit: 1,docsFetched: 1,docSize: 1},{at: 1,endAt: 1}],['db',{coll: 'tinytest-data',selector: '{"_id":"c"}',func: 'fetch',cursor: true,limit: 1,docsFetched: 1,docSize: 1},{at: 1,endAt: 1}],['db',{coll: 'tinytest-data',selector: '{"_id":"a1"}',func: 'fetch',cursor: true,limit: 1,docsFetched: 0,docSize: 0},{at: 1,endAt: 1}],['db',{coll: 'tinytest-data',selector: '{"_id":"a2"}',func: 'fetch',cursor: true,limit: 1,docsFetched: 0,docSize: 0},{at: 1,endAt: 1}]]}],['complete']]; + const expected = [ + ['start',{userId: null,params: '[]'}], + ['wait',{waitOn: []},{at: 1,endAt: 1}], + ['custom',null,{name: 'test', + at: 1, + endAt: 1, + nested: [ + ['db',{coll: 'tinytest-data',func: 'insertAsync'},{at: 1,endAt: 1}], + ['db',{coll: 'tinytest-data',func: 'insertAsync'},{at: 1,endAt: 1}], + ['db',{coll: 'tinytest-data',func: 'insertAsync'},{at: 1,endAt: 1}], + ['email',{from: 'arunoda@meteorhacks.com',to: 'hello@meteor.com', func: 'emailAsync'},{at: 1,endAt: 1}], + ['db',{coll: 'tinytest-data',selector: '{"_id":"a"}',func: 'fetch',cursor: true,limit: 1,docsFetched: 1,docSize: 1},{at: 1,endAt: 1}], + ['db',{coll: 'tinytest-data',selector: '{"_id":"b"}',func: 'fetch',cursor: true,limit: 1,docsFetched: 1,docSize: 1},{at: 1,endAt: 1}], + ['db',{coll: 'tinytest-data',selector: '{"_id":"c"}',func: 'fetch',cursor: true,limit: 1,docsFetched: 1,docSize: 1},{at: 1,endAt: 1}], + ['db',{coll: 'tinytest-data',selector: '{"_id":"a1"}',func: 'fetch',cursor: true,limit: 1,docsFetched: 0,docSize: 0},{at: 1,endAt: 1}], + ['db',{coll: 'tinytest-data',selector: '{"_id":"a2"}',func: 'fetch',cursor: true,limit: 1,docsFetched: 0,docSize: 0},{at: 1,endAt: 1}] + ]} + ], + ['complete'] + ]; test.stableEqual(events, expected); });