diff --git a/.github/workflows/test-redis-oplog.yml b/.github/workflows/test-redis-oplog.yml index 2b7dddac..e31ef1a2 100644 --- a/.github/workflows/test-redis-oplog.yml +++ b/.github/workflows/test-redis-oplog.yml @@ -13,18 +13,7 @@ jobs: matrix: meteorRelease: - '--release 1.11' - - '--release 1.12.1' - - '--release 2.1.1' - - '--release 2.2' - - '--release 2.3.2' - - '--release 2.4.1' - - '--release 2.5.6' - - '--release 2.6.1' - - '--release 2.7.3' - - '--release 2.8.2' - - '--release 2.9.0' - # Left empty to use latest version - - + - '--release 2.16' env: REDIS_OPLOG_SETTINGS: '{"debug":true}' steps: diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 2ee0baa5..0d1492ae 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -31,8 +31,13 @@ jobs: - '--release 2.7.3' - '--release 2.8.2' - '--release 2.9.0' - # Left empty to use latest version - - + - '--release 2.10.0' + - '--release 2.11.0' + - '--release 2.12' + - '--release 2.13.3' + - '--release 2.14' + - '--release 2.15' + - '--release 2.16' steps: - name: Checkout code uses: actions/checkout@v2 @@ -40,7 +45,7 @@ jobs: - name: Install Node.js uses: actions/setup-node@v1 with: - node-version: '12.x' + node-version: '22.x' - name: Install Dependencies run: | diff --git a/.gitignore b/.gitignore index e9313b84..a3a16e79 100644 --- a/.gitignore +++ b/.gitignore @@ -1,9 +1,9 @@ .build* node_modules/ -.npm +.npm/.package-garbage-* smart.lock versions.json .idea .eslintcache .envrc -/packages/* \ No newline at end of file +/packages/* diff --git a/.npm/package/.gitignore b/.npm/package/.gitignore new file mode 100644 index 00000000..3c3629e6 --- /dev/null +++ b/.npm/package/.gitignore @@ -0,0 +1 @@ +node_modules diff --git a/.npm/package/README b/.npm/package/README new file mode 100644 index 00000000..3d492553 --- /dev/null +++ b/.npm/package/README @@ -0,0 +1,7 @@ +This directory and the files immediately inside it are automatically generated +when you change this package's NPM dependencies. Commit the files in this +directory (npm-shrinkwrap.json, .gitignore, and this README) to source control +so that others run the same versions of sub-dependencies. + +You should NOT check in the node_modules directory that Meteor automatically +creates; if you are using git, the .gitignore file tells git to ignore it. diff --git a/.npm/package/npm-shrinkwrap.json b/.npm/package/npm-shrinkwrap.json new file mode 100644 index 00000000..f8a412f4 --- /dev/null +++ b/.npm/package/npm-shrinkwrap.json @@ -0,0 +1,198 @@ +{ + "lockfileVersion": 1, + "dependencies": { + "@sinonjs/commons": { + "version": "1.8.6", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.6.tgz", + "integrity": "sha512-Ky+XkAkqPZSm3NLBeUng77EBQl3cmeJhITaGHdYH8kjVB+aun3S4XBRti2zt17mtt0mIUDiNxYeoJm6drVvBJQ==", + "dependencies": { + "type-detect": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", + "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==" + } + } + }, + "@sinonjs/formatio": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/@sinonjs/formatio/-/formatio-3.2.2.tgz", + "integrity": "sha512-B8SEsgd8gArBLMD6zpRw3juQ2FVSsmdd7qlevyDqzS9WTCtvF55/gAL+h6gue8ZvPYcdiPdvueM/qm//9XzyTQ==", + "dependencies": { + "@sinonjs/samsam": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-3.3.3.tgz", + "integrity": "sha512-bKCMKZvWIjYD0BLGnNrxVuw4dkWCYsLqFOUWw8VgKF/+5Y+mE7LfHWPIYoDXowH+3a9LsWDMo0uAP8YDosPvHQ==" + } + } + }, + "@sinonjs/samsam": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-2.1.3.tgz", + "integrity": "sha512-8zNeBkSKhU9a5cRNbpCKau2WWPfan+Q2zDlcXvXyhn9EsMqgYs4qzo0XHNVlXC6ABQL8fT6nV+zzo5RTHJzyXw==" + }, + "@sinonjs/text-encoding": { + "version": "0.7.3", + "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.3.tgz", + "integrity": "sha512-DE427ROAphMQzU4ENbliGYrBSYPXF+TtLg9S8vzeA+OF4ZKzoDdzfL8sxuMUGS/lgRhM6j1URSk9ghf7Xo1tyA==" + }, + "array-from": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/array-from/-/array-from-2.1.1.tgz", + "integrity": "sha512-GQTc6Uupx1FCavi5mPzBvVT7nEOeWMmUA9P95wpfpW1XwMSKs+KaymD5C2Up7KAUKg/mYwbsUYzdZWcoajlNZg==" + }, + "axios": { + "version": "0.21.4", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.21.4.tgz", + "integrity": "sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==" + }, + "debug": { + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-0.8.1.tgz", + "integrity": "sha512-HlXEJm99YsRjLJ8xmuz0Lq8YUwrv7hAJkTEr6/Em3sUlSUNl0UdFA+1SrY4fnykeq1FVkUEUtwRGHs9VvlYbGA==" + }, + "diff": { + "version": "3.5.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-3.5.0.tgz", + "integrity": "sha512-A46qtFgd+g7pDZinpnwiRJtxbC1hpgf0uzP3iG89scHk0AUC7A1TGxf5OiiOUv/JMZR8GOt8hL900hV0bOy5xA==" + }, + "eventemitter2": { + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/eventemitter2/-/eventemitter2-6.4.9.tgz", + "integrity": "sha512-JEPTiaOt9f04oa6NOkc4aH+nVp5I3wEjpHbIPqfgCdD5v5bUzy7xQqwcVO2aDQgOWhI28da57HksMrzK9HlRxg==" + }, + "faye-websocket": { + "version": "0.11.4", + "resolved": "https://registry.npmjs.org/faye-websocket/-/faye-websocket-0.11.4.tgz", + "integrity": "sha512-CzbClwlXAuiRQAlUyfqPgvPoNKTckTPGfwZV4ZdAhVcP2lh9KUxJg2b5GkE7XbjKQ3YJnQ9z6D9ntLAlB+tP8g==" + }, + "follow-redirects": { + "version": "1.15.9", + "resolved": "https://registry.npmjs.org/follow-redirects/-/follow-redirects-1.15.9.tgz", + "integrity": "sha512-gew4GsXizNgdoRyqmyfMHyAmXsZDk6mHkSxZFCzW9gwlbtOW44CDtYavM+y+72qD/Vq2l550kMF52DT8fOLJqQ==" + }, + "has-flag": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", + "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==" + }, + "http-parser-js": { + "version": "0.5.8", + "resolved": "https://registry.npmjs.org/http-parser-js/-/http-parser-js-0.5.8.tgz", + "integrity": "sha512-SGeBX54F94Wgu5RH3X5jsDtf4eHyRogWX1XGT3b4HuW3tQPM4AaBzoUji/4AAJNXCEOWZ5O0DgZmJw1947gD5Q==" + }, + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==" + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha512-ZClg6AaYvamvYEE82d3Iyd3vSSIjQ+odgjaTzRuO3s7toCdFKczob2i0zCh7JE8kWn17yvAWhUVxvqGwUalsRA==" + }, + "just-extend": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", + "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==" + }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, + "lodash.get": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", + "integrity": "sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==" + }, + "lolex": { + "version": "2.7.5", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-2.7.5.tgz", + "integrity": "sha512-l9x0+1offnKKIzYVjyXU2SiwhXDLekRzKyhnbyldPHvC7BvLPVpdNUNR2KeMAiCN2D/kLNttZgQD5WjSxuBx3Q==" + }, + "lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==" + }, + "monti-apm-core": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/monti-apm-core/-/monti-apm-core-1.8.0.tgz", + "integrity": "sha512-s0A6ZUXRKY7EBwi9Nxqe+HfT6SqH4EuIv1SF3WBs0FRvKN8rBE62frTvE/WtLg8a27lVJS8D7j5lgTbF/DHN9Q==", + "dependencies": { + "debug": { + "version": "3.2.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-3.2.7.tgz", + "integrity": "sha512-CFjzYYAi4ThfiQvizrFQevTTXHtnCqWfe7x1AhgEscTz6ZbLbfoLRLPugTQyBth6f8ZERVUSyWHFD/7Wu4t1XQ==" + } + } + }, + "monti-apm-sketches-js": { + "version": "0.0.3", + "resolved": "https://registry.npmjs.org/monti-apm-sketches-js/-/monti-apm-sketches-js-0.0.3.tgz", + "integrity": "sha512-YhRbOtVoXyeWgoBIGfSy70pfxDhGj3zuiA66jH7UEAmFqgbq4u/Xfb3Zh2J87xgMJUp7JrrWkVSXgk8gg9ktuQ==" + }, + "ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==" + }, + "nise": { + "version": "1.5.3", + "resolved": "https://registry.npmjs.org/nise/-/nise-1.5.3.tgz", + "integrity": "sha512-Ymbac/94xeIrMf59REBPOv0thr+CJVFMhrlAkW/gjCIE58BGQdCj0x7KRCb3yz+Ga2Rz3E9XXSvUyyxqqhjQAQ==", + "dependencies": { + "lolex": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/lolex/-/lolex-5.1.2.tgz", + "integrity": "sha512-h4hmjAvHTmd+25JSwrtTIuwbKdwg5NzZVRMLn9saij4SZaepCrTCxPr35H/3bjwfMJtN+t3CX8672UIkglz28A==" + } + } + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-to-regexp": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.9.0.tgz", + "integrity": "sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g==" + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "sinon": { + "version": "6.3.5", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-6.3.5.tgz", + "integrity": "sha512-xgoZ2gKjyVRcF08RrIQc+srnSyY1JDJtxu3Nsz07j1ffjgXoY6uPLf/qja6nDBZgzYYEovVkFryw2+KiZz11xQ==" + }, + "supports-color": { + "version": "5.5.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", + "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==" + }, + "type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==" + }, + "websocket-driver": { + "version": "0.7.4", + "resolved": "https://registry.npmjs.org/websocket-driver/-/websocket-driver-0.7.4.tgz", + "integrity": "sha512-b17KeDIQVjvb0ssuSDF2cYXSg2iztliJ4B9WdsuB6J952qCPKmnVq4DyW5motImXHDC1cBT/1UezrJVsKw5zjg==" + }, + "websocket-extensions": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/websocket-extensions/-/websocket-extensions-0.1.4.tgz", + "integrity": "sha512-OqedPIGOfsDlo31UNwYbCFMSaO9m9G/0faIHj5/dZFDMFqPTcx6UwqyOy3COEaEOg/9VsGIpdqn62W5KhoKSpg==" + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + } + } +} diff --git a/README.md b/README.md index b9eed5b7..c4361644 100644 --- a/README.md +++ b/README.md @@ -103,8 +103,8 @@ You should use the same method that you used to give the agent the app id and se | eventStackTrace | EVENT_STACK_TRACE | false | If true, records a stack trace when an event starts. Slightly decreases server performance. | | disableNtp | OPTIONS_DISABLE_NTP | false | Disable NTP time synchronization used to get the accurate time in case the server or client's clock is wrong | | stalledTimeout | STALLED_TIMEOUT | 1800000 (30m) | Timeout used to detect when methods and subscriptions might be stalled (have been running for a long time and might never return). The value is in milliseconds, and can be disabled by setting it to 0 | -| proxy | MONTI_OPTIONS_PROXY | none | Allows you to connect to Monti APM using a proxy | -| disableInstrumentation | MONTI_DISABLE_INSTRUMENTATION | false | Disables instrumentation. Useful for disabling without uninstalling the package | +| proxy | OPTIONS_PROXY | none | Allows you to connect to Monti APM using a proxy | +| disableInstrumentation | DISABLE_INSTRUMENTATION | false | Disables recording most metrics and traces. Can be configured using Meteor.settings, or by env variable | ### Traces diff --git a/lib/auto_connect.js b/lib/auto_connect.js index d180f069..5c94d130 100644 --- a/lib/auto_connect.js +++ b/lib/auto_connect.js @@ -1,37 +1,37 @@ import { Meteor } from 'meteor/meteor'; -Kadira._connectWithEnv = function () { - const options = Kadira._parseEnv(process.env); - if (options.appId && options.appSecret) { +const envOptions = Kadira._parseEnv(process.env); +const montiSettings = Meteor.settings.monti || Meteor.settings.kadira; + +Kadira._connectWithEnv = function (env) { + if (env.appId && env.appSecret) { Kadira.connect( - options.appId, - options.appSecret, - options + env.appId, + env.appSecret, + env ); Kadira.connect = function () { - throw new Error('Kadira has been already connected using credentials from Environment Variables'); + throw new Error('Monti APM: already connected using credentials from Environment Variables'); }; } }; -Kadira._connectWithSettings = function () { - const montiSettings = Meteor.settings.monti || Meteor.settings.kadira; - +Kadira._connectWithSettings = function (settings) { if ( - montiSettings && - montiSettings.appId && - montiSettings.appSecret + settings && + settings.appId && + settings.appSecret ) { Kadira.connect( - montiSettings.appId, - montiSettings.appSecret, - montiSettings.options || {} + settings.appId, + settings.appSecret, + settings.options || {} ); Kadira.connect = function () { - throw new Error('Kadira has been already connected using credentials from Meteor.settings'); + throw new Error('Monti APM: already connected using credentials from Meteor.settings'); }; } }; @@ -40,11 +40,16 @@ Kadira._connectWithSettings = function () { * We need to instrument this right away, and it's okay * One reason for this is to call `setLabels()` function * Otherwise, CPU profile can't see all our custom labeling - * - * Previously there was two log messages (one for instrumentation, - * and another for connection), this way we merged both of them. */ -Kadira._startInstrumenting(function () { - Kadira._connectWithEnv(); - Kadira._connectWithSettings(); -}); +let settingsOptions = montiSettings && montiSettings.options || {}; +if ( + envOptions.disableInstrumentation || settingsOptions.disableInstrumentation +) { + // eslint-disable-next-line no-console + console.log('Monti APM: Instrumentation is disabled. Metrics and traces will not be recorded.'); +} else { + Kadira._startInstrumenting(); +} + +Kadira._connectWithEnv(envOptions); +Kadira._connectWithSettings(settingsOptions); diff --git a/lib/constants.js b/lib/constants.js new file mode 100644 index 00000000..b45db6fa --- /dev/null +++ b/lib/constants.js @@ -0,0 +1,5 @@ +export const JobType = { + CPU_PROFILE: 'cpuProfile', + HEAP_SNAPSHOT: 'heapSnapshot', + ALLOCATION_PROFILE: 'allocationProfile' +}; diff --git a/lib/environment_variables.js b/lib/environment_variables.js index d418e529..020a0eb1 100644 --- a/lib/environment_variables.js +++ b/lib/environment_variables.js @@ -134,4 +134,8 @@ Kadira._parseEnv._options = { name: 'disableNtp', parser: Kadira._parseEnv.parseBool, }, + MONTI_DISABLE_INSTRUMENTATION: { + name: 'disableInstrumentation', + parser: Kadira._parseEnv.parseBool + } }; diff --git a/lib/hijack/instrument.js b/lib/hijack/instrument.js index b6d764a6..e1a546ef 100644 --- a/lib/hijack/instrument.js +++ b/lib/hijack/instrument.js @@ -19,11 +19,7 @@ import { hijackDBOps } from './db'; import { wrapRedisOplogObserveDriver } from './redis_oplog'; let instrumented = false; -Kadira._startInstrumenting = function (callback) { - if (Meteor.settings?.monti?.disableInstrumentation || process.env.MONTI_DISABLE_INSTRUMENTATION) { - console.log('Monti APM: Instrumentation is disabled.'); - return; - } +Kadira._startInstrumenting = function (callback = () => {}) { if (instrumented) { callback(); return; diff --git a/lib/jobs.js b/lib/jobs.js index a7e93606..9396799d 100644 --- a/lib/jobs.js +++ b/lib/jobs.js @@ -1,3 +1,5 @@ +import { JobType } from './constants'; + let Jobs = Kadira.Jobs = {}; Jobs.getAsync = function (id, callback) { @@ -21,5 +23,79 @@ Jobs.setAsync = function (id, changes, callback) { }); }; -Jobs.set = Kadira._wrapAsync(Jobs.setAsync); -Jobs.get = Kadira._wrapAsync(Jobs.getAsync); +Jobs.set = Meteor.wrapAsync(Jobs.setAsync); +Jobs.get = Meteor.wrapAsync(Jobs.getAsync); + + +let queuePromise = Promise.resolve(); + +export function queueJob (job) { + queuePromise = queuePromise.finally(() => runJob(job)); + + return queuePromise; +} + +async function runJob (job) { + if (!job || !job._id) { + // eslint-disable-next-line no-console + console.log(`Monti APM: Invalid job: ${JSON.stringify(job)}`); + return; + } + + // eslint-disable-next-line no-console + console.log('Monti APM: Starting job', job.type, job.id); + + try { + if (!job.type) { + throw new Error('Invalid job: missing type'); + } + + let runner = JobRunners[job.type]; + + if (!runner) { + throw new Error(`Unrecognized job type: ${job.type}. You might need to update montiapm:agent`); + } + + await runner(job); + + // eslint-disable-next-line no-console + console.log('Monti APM: Finished job', job.type, job._id); + } catch (error) { + // eslint-disable-next-line no-console + console.log(`Monti APM: Error while running job: ${error}`); + Jobs.set(job._id, { state: 'errored', data: { errorMessage: error.message || 'Unknown error' } }); + } +} + +const JobRunners = { + [JobType.CPU_PROFILE] (job) { + const ProfilerPackage = Package['montiapm:profiler']; + + if (!ProfilerPackage) { + throw new Error('Please install the montiapm:profiler package'); + } + + const MontiProfiler = ProfilerPackage.MontiProfiler; + + if (!MontiProfiler || !MontiProfiler._remoteCpuProfile) { + throw new Error('Please update the montiapm:profiler package'); + } + + const {_id, data} = job; + + return MontiProfiler._remoteCpuProfile(data.duration, _id); + }, + async [JobType.HEAP_SNAPSHOT] (job) { + const ProfilerPackage = Package['montiapm:profiler']; + + if (!ProfilerPackage) { + throw new Error('Please install the montiapm:profiler package'); + } + + if (!ProfilerPackage.MontiProfiler._remoteHeapSnapshot) { + throw new Error('Please update montiapm:profiler package'); + } + + await ProfilerPackage.MontiProfiler._remoteHeapSnapshot(job._id); + } +}; diff --git a/lib/kadira.js b/lib/kadira.js index 492c4d70..8dc93830 100644 --- a/lib/kadira.js +++ b/lib/kadira.js @@ -17,14 +17,18 @@ import { Ntp } from './ntp'; import { getClientVersions } from './utils'; import { handleApiResponse } from './sourcemaps'; import { TrackMeteorDebug, TrackUncaughtExceptions, TrackUnhandledRejections } from './hijack/error'; +import { queueJob } from './jobs'; const hostname = Npm.require('os').hostname(); const logger = Npm.require('debug')('kadira:apm'); const Fibers = Npm.require('fibers'); -const KadiraCore = Npm.require('monti-apm-core').Kadira; +const { CoreEvent, Kadira: KadiraCore, Feature } = Npm.require('monti-apm-core'); const DEBUG_PAYLOAD_SIZE = process.env.MONTI_DEBUG_PAYLOAD_SIZE === 'true'; +// The Meteor versions with Node 4 did not support promise.finally +const WEBSOCKETS_SUPPORTED = parseInt(process.versions.node.split('.')[0], 10) > 4; + Kadira.models = {}; Kadira.options = {}; Kadira.env = { @@ -116,6 +120,20 @@ Kadira.connect = function (appId, appSecret, options) { console.log('Monti APM: Connected'); Kadira._sendAppStats(); Kadira._schedulePayloadSend(); + + if ( + WEBSOCKETS_SUPPORTED && + Kadira.coreApi.featureSupported(Feature.WEBSOCKETS) + ) { + logger('websockets supported'); + Kadira.coreApi._initWebSocket(); + + Kadira.coreApi.on(CoreEvent.JOB_CREATED, Meteor.bindEnvironment(job => { + queueJob(job); + })); + } else { + logger('websockets not supported'); + } }) .catch(function (err) { if (err.message === 'Unauthorized') { diff --git a/package.js b/package.js index e38e73f0..af0a37a6 100644 --- a/package.js +++ b/package.js @@ -2,14 +2,14 @@ Package.describe({ summary: 'Performance Monitoring for Meteor', - version: '2.49.4', + version: '2.50.0-beta.2', git: 'https://github.com/monti-apm/monti-apm-agent.git', name: 'montiapm:agent' }); let npmModules = { debug: '0.8.1', - 'monti-apm-core': '1.7.5', + 'monti-apm-core': '1.8.0', 'lru-cache': '5.1.1', 'json-stringify-safe': '5.0.1', 'monti-apm-sketches-js': '0.0.3', @@ -137,7 +137,7 @@ function configurePackage (api, isTesting) { api.use('meteorhacks:zones@1.2.1', { weak: true }); api.use('simple:json-routes@2.1.0', { weak: true }); api.use('zodern:meteor-package-versions@0.2.0'); - api.use('zodern:types@1.0.11'); + api.use('zodern:types@1.0.13'); api.use([ 'minimongo', 'mongo', 'ddp', 'ejson', 'ddp-common', diff --git a/tests/environment_variables.js b/tests/environment_variables.js index 3ff3a32b..f96959ac 100644 --- a/tests/environment_variables.js +++ b/tests/environment_variables.js @@ -23,7 +23,7 @@ Tinytest.add( connectArgs = Array.prototype.slice.call(arguments); }; - Kadira._connectWithEnv(); + Kadira._connectWithEnv(Kadira._parseEnv(process.env)); test.equal(connectArgs[0], 'rcZSEaSgMaxH4c2df'); test.equal(connectArgs[1], '9af3daf3-64f3-4448-8b1e-4286fdf5f499');