-
Notifications
You must be signed in to change notification settings - Fork 39
fix: optimized how we receive the package.json information #2176
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
1be8bed
a272b31
682e6af
544c2d8
2c6db63
4c9f247
4f3714a
bc8d385
48a0261
e65511d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| #!/usr/bin/env bash | ||
|
|
||
| ####################################### | ||
| # (c) Copyright IBM Corp. 2025 | ||
| ####################################### | ||
|
|
||
| cd "$(dirname "$0")/../../../collector" | ||
| echo "Running npm pack in $(pwd)" | ||
| npm pack | ||
|
|
||
| version=$(node -p "require('./package.json').version") | ||
| tarball="instana-collector-${version}.tgz" | ||
| cp "./${tarball}" "./test/nativeModuleRetry/collector.tgz" | ||
|
|
||
| cd "$(dirname "$0")/../core" | ||
| echo "Running npm pack in $(pwd)" | ||
| npm pack | ||
|
|
||
| version=$(node -p "require('./package.json').version") | ||
| tarball="instana-core-${version}.tgz" | ||
| cp "./${tarball}" "../collector/test/nativeModuleRetry/core.tgz" | ||
|
|
||
| cd "$(dirname "$0")/../shared-metrics" | ||
| echo "Running npm pack in $(pwd)" | ||
| npm pack | ||
|
|
||
| version=$(node -p "require('./package.json').version") | ||
| tarball="instana-shared-metrics-${version}.tgz" | ||
| cp "./${tarball}" "../collector/test/nativeModuleRetry/shared-metrics.tgz" |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,42 @@ | ||
| /* | ||
| * (c) Copyright IBM Corp. 2025 | ||
| */ | ||
|
|
||
| 'use strict'; | ||
|
|
||
| const path = require('path'); | ||
| const childProcess = require('child_process'); | ||
| const expect = require('chai').expect; | ||
| const supportedVersion = require('@instana/core').tracing.supportedVersion; | ||
| const config = require('../../../../../core/test/config'); | ||
| const mochaSuiteFn = supportedVersion(process.versions.node) ? describe : describe.skip; | ||
|
|
||
| mochaSuiteFn('tracing/invalidApp', function () { | ||
| this.timeout(config.getTestTimeout() * 3); | ||
|
|
||
| it('when the collector is required in interactive shell', cb => { | ||
| const child = childProcess.spawn( | ||
| process.execPath, | ||
| ['--require', path.join(__dirname, '..', '..', '..', '..', 'src', 'immediate.js')], | ||
| { | ||
| stdio: 'inherit', | ||
| cwd: process.cwd(), | ||
| env: process.env | ||
| } | ||
| ); | ||
|
|
||
| child.on('error', err => { | ||
| cb(err); | ||
| }); | ||
|
|
||
| child.on('exit', (code, signal) => { | ||
| expect(signal).to.equal('SIGTERM'); | ||
| expect(code).to.not.exist; | ||
| cb(); | ||
| }); | ||
|
|
||
| setTimeout(() => { | ||
| child.kill('SIGTERM'); | ||
| }, 3 * 1000); | ||
| }); | ||
| }); |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -53,11 +53,17 @@ mochaSuiteFn('opentelemetry tests', function () { | |
| if (process.env.INSTANA_TEST_SKIP_INSTALLING_DEPS === 'true') { | ||
| return; | ||
| } | ||
|
|
||
| execSync('npm install --no-save --no-package-lock --prefix ./ ./core.tgz', { | ||
| cwd: __dirname, | ||
| stdio: 'inherit' | ||
| }); | ||
|
|
||
| execSync('npm install --no-save --no-package-lock --prefix ./ ./shared-metrics.tgz', { | ||
| cwd: __dirname, | ||
| stdio: 'inherit' | ||
| }); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will outsource to main. Test was failing because shared-metrics was not installed from local folder - it took the version from NPM - this does not work if you do refactorings (such as removing fns) |
||
|
|
||
| execSync('npm install --no-save --no-package-lock --prefix ./ ./collector.tgz', { | ||
| cwd: __dirname, | ||
| stdio: 'inherit' | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -5,19 +5,24 @@ | |
|
|
||
| 'use strict'; | ||
|
|
||
| const fs = require('../uninstrumentedFs'); | ||
| const path = require('path'); | ||
| const fs = require('../uninstrumentedFs'); | ||
| const util = require('../util'); | ||
|
|
||
| /** @typedef {import('../config').InstanaConfig} InstanaConfig */ | ||
|
|
||
| /** @type {InstanaConfig} */ | ||
| let config; | ||
|
|
||
| /** @type {import('../core').GenericLogger} */ | ||
| let logger; | ||
|
|
||
| /** | ||
| * @param {InstanaConfig} _config | ||
| */ | ||
| exports.init = _config => { | ||
| config = _config; | ||
| logger = config.logger; | ||
| }; | ||
|
|
||
| /** | ||
|
|
@@ -65,10 +70,20 @@ exports.registerAdditionalMetrics = function registerAdditionalMetrics(additiona | |
| }; | ||
|
|
||
| exports.activate = () => { | ||
| metricsModules.forEach(metricsModule => { | ||
| if (metricsModule.activate) { | ||
| metricsModule.activate(config); | ||
| util.applicationUnderMonitoring.getMainPackageJsonStartingAtMainModule((err, packageJsonObj) => { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There was a bigger problem in the codebase: Any outer pkg calls core.metrics.activate, which activates all registered metrics. |
||
| if (err) { | ||
| logger.warn( | ||
| `Failed to determine main package.json. Some metrics might not work. Reason: ${err?.message} ${err?.stack}` | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Log once. And also mention that only some metrics might not work. |
||
| ); | ||
| return; | ||
| } | ||
|
|
||
| metricsModules.forEach(metricsModule => { | ||
| if (metricsModule.activate) { | ||
| // @ts-ignore | ||
| metricsModule.activate(config, packageJsonObj); | ||
| } | ||
| }); | ||
| }); | ||
| }; | ||
|
|
||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -38,6 +38,10 @@ function isAppInstalledIntoNodeModules() { | |
| return appInstalledIntoNodeModules; | ||
| } | ||
|
|
||
| const MAX_ATTEMPTS = 5; | ||
| const DELAY = 1500; | ||
| let attempts = 0; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Centralized retries (see name.js and version.js etc.). |
||
|
|
||
| /** | ||
| * Looks for the app's main package.json file, parses it and returns the parsed content. The search is started at | ||
| * path.dirname(require.main.filename). | ||
|
|
@@ -50,15 +54,31 @@ function isAppInstalledIntoNodeModules() { | |
| function getMainPackageJsonStartingAtMainModule(cb) { | ||
| // NOTE: we already cached the package.json | ||
| if (parsedMainPackageJson !== undefined) { | ||
| return cb(null, parsedMainPackageJson); | ||
| return cb(null, { file: parsedMainPackageJson, path: mainPackageJsonPath }); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Always return both file + path. Some metrics need the file, some the path. |
||
| } | ||
|
|
||
| // CASE: customer provided custom package.json path, let's try loading it | ||
| if (packageJsonPath) { | ||
| return readFile(packageJsonPath, cb); | ||
| } | ||
|
|
||
| return getMainPackageJsonStartingAtDirectory(null, cb); | ||
| return getMainPackageJsonStartingAtDirectory(null, (err, pkg) => { | ||
| // CASE: using --require or --import can result in an empty require.main initially | ||
| if (err && attempts < MAX_ATTEMPTS) { | ||
| attempts++; | ||
|
|
||
| logger.warn( | ||
| `Could not determine main package.json yet. Retrying ${attempts}/${MAX_ATTEMPTS} after ${DELAY}ms...` | ||
| ); | ||
|
|
||
| return setTimeout(() => { | ||
| getMainPackageJsonStartingAtMainModule(cb); | ||
| }, DELAY).unref(); | ||
| } | ||
|
|
||
| attempts = 0; | ||
| return cb(err, pkg); | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -110,22 +130,10 @@ function readFile(filePath, cb) { | |
| return cb(e, null); | ||
| } | ||
|
|
||
| return cb(null, parsedMainPackageJson); | ||
| return cb(null, { file: parsedMainPackageJson, path: filePath }); | ||
| }); | ||
| } | ||
|
|
||
| /** | ||
| * Looks for path of the app's main package.json file, starting the search at path.dirname(require.main.filename). | ||
| * | ||
| * In case the search is successful, the result will be cached for consecutive invocations. | ||
| * | ||
| * @param {(err: Error, packageJsonPath: string) => void} cb - the callback will be called with an error or the path to | ||
| * the package.json file | ||
| */ | ||
| function getMainPackageJsonPathStartingAtMainModule(cb) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not needed anymore, because we now request pkg json + pkg json path centralized in core metrics index.js. We always provide the found path and the file to the metric. |
||
| return getMainPackageJsonPathStartingAtDirectory(null, cb); | ||
| } | ||
|
|
||
| /** | ||
| * Looks for path of the app's main package.json file, starting the search at the given directory. If the given | ||
| * directory is null or undefined, the search will start at path.dirname(require.main.filename). | ||
|
|
@@ -187,6 +195,12 @@ function getMainPackageJsonPathStartingAtDirectory(startDirectory, cb) { | |
| } | ||
| } | ||
|
|
||
| // CASE: node --require .../src/immediate.js | ||
| if (!mainModule?.filename) { | ||
| const err = new Error('Application entrypoint could not be identified.'); | ||
| return process.nextTick(cb, err, null); | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is the actual fix. @aryamohanan started with it already. |
||
| } | ||
|
|
||
| startDirectory = path.dirname(mainModule.filename); | ||
| } | ||
|
|
||
|
|
@@ -332,14 +346,14 @@ function searchInParentDir(dir, onParentDir, cb) { | |
| const reset = () => { | ||
| parsedMainPackageJson = undefined; | ||
| mainPackageJsonPath = undefined; | ||
| attempts = 0; | ||
| }; | ||
|
|
||
| module.exports = { | ||
| init, | ||
| isAppInstalledIntoNodeModules, | ||
| getMainPackageJsonStartingAtMainModule, | ||
| getMainPackageJsonStartingAtDirectory, | ||
| getMainPackageJsonPathStartingAtMainModule, | ||
| getMainPackageJsonPathStartingAtDirectory, | ||
| findNodeModulesFolder, | ||
| reset | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Will outsource to main. Test was failing because shared-metrics was not installed from local folder - it took the version from NPM - this does not work if you do refactorings (such as removing fns)