@instana/shared-metrics
Advanced tools
Comparing version 1.130.1 to 1.131.0
{ | ||
"name": "@instana/shared-metrics", | ||
"version": "1.130.1", | ||
"version": "1.131.0", | ||
"description": "Internal metrics plug-in package for Node.js monitoring with Instana", | ||
@@ -29,3 +29,3 @@ "author": { | ||
"test": "npm run test:mocha", | ||
"test:mocha": "mocha --sort --reporter mocha-multi --reporter-options spec=-,xunit=../../test-results/shared-metris/results.xml $(find test -iname '*test.js')", | ||
"test:mocha": "mocha --sort --reporter mocha-multi --reporter-options spec=-,xunit=../../test-results/shared-metris/results.xml $(find test -iname '*test.js' -not -path '*node_modules*')", | ||
"test:debug": "WITH_STDOUT=true npm run test:mocha", | ||
@@ -63,7 +63,7 @@ "lint": "eslint src test", | ||
"dependencies": { | ||
"@instana/core": "1.130.1", | ||
"@instana/core": "1.131.0", | ||
"detect-libc": "^1.0.3", | ||
"event-loop-lag": "^1.4.0", | ||
"recursive-copy": "^2.0.13", | ||
"tar": "^5.0.7" | ||
"tar": "^5.0.9" | ||
}, | ||
@@ -83,3 +83,3 @@ "devDependencies": { | ||
}, | ||
"gitHead": "6fe6e2f1d58b616bfb5ef408f13af2febe9ad19a" | ||
"gitHead": "c143ddafc50f8305684638f5abff0ee847830408" | ||
} |
@@ -15,2 +15,5 @@ /* | ||
const CountDownLatch = require('./util/CountDownLatch'); | ||
const { DependencyDistanceCalculator, MAX_DEPTH } = require('./util/DependencyDistanceCalculator'); | ||
/** | ||
@@ -23,7 +26,16 @@ * @param {import('@instana/core/src/logger').GenericLogger} _logger | ||
/** @type {number} */ | ||
exports.MAX_DEPENDENCIES = 750; | ||
/** @type {string} */ | ||
exports.payloadPrefix = 'dependencies'; | ||
/** @type {Object.<string, *>} */ | ||
const preliminaryPayload = {}; | ||
/** @type {Object.<string, *>} */ | ||
exports.currentPayload = {}; | ||
const MAX_ATTEMPTS = 20; | ||
exports.MAX_ATTEMPTS = 20; | ||
const DELAY = 1000; | ||
@@ -34,6 +46,8 @@ let attempts = 0; | ||
attempts++; | ||
const started = Date.now(); | ||
applicationUnderMonitoring.getMainPackageJsonPathStartingAtMainModule((err, packageJsonPath) => { | ||
if (err) { | ||
return logger.warn('Failed to determine main package.json. Reason: %s %s ', err.message, err.stack); | ||
} else if (!packageJsonPath && attempts < MAX_ATTEMPTS) { | ||
} else if (!packageJsonPath && attempts < exports.MAX_ATTEMPTS) { | ||
logger.debug('Main package.json could not be found. Will try again later.'); | ||
@@ -54,3 +68,4 @@ setTimeout(exports.activate, DELAY).unref(); | ||
} | ||
addDependenciesFromDir(path.join(nodeModulesFolder)); | ||
addAllDependencies(path.join(nodeModulesFolder), started, null); | ||
}); | ||
@@ -66,3 +81,3 @@ return; | ||
} | ||
addDependenciesFromDir(dependencyDir); | ||
addAllDependencies(dependencyDir, started, packageJsonPath); | ||
}); | ||
@@ -72,35 +87,26 @@ }; | ||
/** | ||
* Finds all installed modules in the given dependencyDir (say, /path/to/app/node_modules) and saves the dependency with | ||
* the associated version into preliminaryPayload. | ||
* | ||
* @param {string} dependencyDir | ||
* @param {number} started | ||
* @param {string} packageJsonPath | ||
*/ | ||
function addDependenciesFromDir(dependencyDir) { | ||
fs.readdir(dependencyDir, (readDirErr, dependencies) => { | ||
if (readDirErr) { | ||
return logger.warn('Cannot analyse dependencies due to %s', readDirErr.message); | ||
function addAllDependencies(dependencyDir, started, packageJsonPath) { | ||
addDependenciesFromDir(dependencyDir, () => { | ||
if (Object.keys(preliminaryPayload).length <= exports.MAX_DEPENDENCIES) { | ||
exports.currentPayload = preliminaryPayload; | ||
logger.debug(`Collection of dependencies took ${Date.now() - started} ms.`); | ||
return; | ||
} | ||
dependencies | ||
.filter( | ||
( | ||
dependency // exclude the .bin directory | ||
) => dependency !== '.bin' | ||
) | ||
.forEach(dependency => { | ||
if (dependency.indexOf('@') === 0) { | ||
addDependenciesFromDir(path.join(dependencyDir, dependency)); | ||
} else { | ||
const fullDirPath = path.join(dependencyDir, dependency); | ||
// Only check directories. For example, yarn adds a .yarn-integrity file to /node_modules/ which we need to | ||
// exclude, otherwise we get a confusing "Failed to identify version of .yarn-integrity dependency due to: | ||
// ENOTDIR: not a directory, open '.../node_modules/.yarn-integrity/package.json'." in the logs. | ||
fs.stat(fullDirPath, (statErr, stats) => { | ||
if (statErr) { | ||
return logger.warn('Cannot analyse dependency %s due to %s', fullDirPath, statErr.message); | ||
} | ||
if (stats.isDirectory()) { | ||
const fullPackageJsonPath = path.join(fullDirPath, 'package.json'); | ||
addDependency(dependency, fullPackageJsonPath); | ||
} | ||
}); | ||
} | ||
if (packageJsonPath) { | ||
new DependencyDistanceCalculator().calculateDistancesFrom(packageJsonPath, distancesFromRoot => { | ||
logger.debug(`Collection of dependencies took ${Date.now() - started} ms.`); | ||
limitAndSet(distancesFromRoot); | ||
}); | ||
} else { | ||
logger.debug(`Collection of dependencies took ${Date.now() - started} ms.`); | ||
limitAndSet(); | ||
} | ||
}); | ||
@@ -110,6 +116,70 @@ } | ||
/** | ||
* @param {*} dependency | ||
* @param {string} packageJsonPath | ||
* Finds all installed modules in dependencyDir (say, /path/to/app/node_modules) and saves the dependency with the | ||
* associated version into preliminaryPayload. | ||
* | ||
* @param {string} dependencyDir | ||
* @param {() => void} callback | ||
*/ | ||
function addDependency(dependency, packageJsonPath) { | ||
function addDependenciesFromDir(dependencyDir, callback) { | ||
fs.readdir(dependencyDir, (readDirErr, dependencies) => { | ||
if (readDirErr || !dependencies) { | ||
logger.warn('Cannot analyse dependencies due to %s', readDirErr.message); | ||
callback(); | ||
return; | ||
} | ||
const filteredDependendencies = dependencies.filter( | ||
( | ||
dependency // exclude the .bin directory | ||
) => dependency !== '.bin' | ||
); | ||
if (filteredDependendencies.length === 0) { | ||
callback(); | ||
return; | ||
} | ||
// This latch fires once all dependencies of the current directory in the node_modules tree have been analysed. | ||
const countDownLatch = new CountDownLatch(filteredDependendencies.length); | ||
countDownLatch.once('done', () => { | ||
callback(); | ||
}); | ||
filteredDependendencies.forEach(dependency => { | ||
if (dependency.indexOf('@') === 0) { | ||
addDependenciesFromDir(path.join(dependencyDir, dependency), () => { | ||
countDownLatch.countDown(); | ||
}); | ||
} else { | ||
const fullDirPath = path.join(dependencyDir, dependency); | ||
// Only check directories. For example, yarn adds a .yarn-integrity file to /node_modules/ which we need to | ||
// exclude, otherwise we get a confusing "Failed to identify version of .yarn-integrity dependency due to: | ||
// ENOTDIR: not a directory, open '.../node_modules/.yarn-integrity/package.json'." in the logs. | ||
fs.stat(fullDirPath, (statErr, stats) => { | ||
if (statErr) { | ||
countDownLatch.countDown(); | ||
logger.warn('Cannot analyse dependency %s due to %s', fullDirPath, statErr.message); | ||
return; | ||
} | ||
if (!stats.isDirectory()) { | ||
countDownLatch.countDown(); | ||
return; | ||
} | ||
addDependency(dependency, fullDirPath, countDownLatch); | ||
}); | ||
} | ||
}); | ||
}); | ||
} | ||
/** | ||
* Parses the package.json file in the given directory and then adds the given dependency (with its version) to | ||
* preliminaryPayload. | ||
* | ||
* @param {string} dependency | ||
* @param {string} dependencyDirPath | ||
* @param {import('./util/CountDownLatch')} countDownLatch | ||
*/ | ||
function addDependency(dependency, dependencyDirPath, countDownLatch) { | ||
const packageJsonPath = path.join(dependencyDirPath, 'package.json'); | ||
fs.readFile(packageJsonPath, { encoding: 'utf8' }, (err, contents) => { | ||
@@ -119,5 +189,8 @@ if (err && err.code === 'ENOENT') { | ||
// We can simply ignore this. | ||
return logger.debug(`No package.json at ${packageJsonPath}, ignoring this directory.`); | ||
countDownLatch.countDown(); | ||
logger.debug(`No package.json at ${packageJsonPath}, ignoring this directory.`); | ||
return; | ||
} else if (err) { | ||
return logger.info( | ||
countDownLatch.countDown(); | ||
logger.info( | ||
'Failed to identify version of %s dependency due to: %s. This means that you will not be ' + | ||
@@ -128,8 +201,11 @@ 'able to see details about this dependency within Instana.', | ||
); | ||
return; | ||
} | ||
try { | ||
const pckg = JSON.parse(contents); | ||
exports.currentPayload[pckg.name] = pckg.version; | ||
} catch (subErr) { | ||
const parsedPackageJson = JSON.parse(contents); | ||
if (!preliminaryPayload[parsedPackageJson.name]) { | ||
preliminaryPayload[parsedPackageJson.name] = parsedPackageJson.version; | ||
} | ||
} catch (parseErr) { | ||
return logger.info( | ||
@@ -139,6 +215,67 @@ 'Failed to identify version of %s dependency due to: %s. This means that you will not be ' + | ||
dependency, | ||
subErr.message | ||
parseErr.message | ||
); | ||
} | ||
const potentialNestedNodeModulesFolder = path.join(dependencyDirPath, 'node_modules'); | ||
fs.stat(potentialNestedNodeModulesFolder, (statErr, stats) => { | ||
if (statErr || !stats.isDirectory()) { | ||
countDownLatch.countDown(); | ||
return; | ||
} | ||
addDependenciesFromDir(potentialNestedNodeModulesFolder, () => { | ||
countDownLatch.countDown(); | ||
}); | ||
}); | ||
}); | ||
} | ||
/** | ||
* Limits the collected dependencies to exports.MAX_DEPENDENCIES entries and commits them to exports.currentPayload. | ||
* | ||
* @param {Object<string, any>} distances | ||
*/ | ||
function limitAndSet(distances = {}) { | ||
const keys = Object.keys(preliminaryPayload); | ||
keys.sort(sortByDistance.bind(null, distances)); | ||
// After sorting, the most distant (and therefore, most uninteresting) packages are a the start of the array. For | ||
// packages with the same distance, we sort in a reverse lexicographic order. That means, that if no distances are | ||
// available at all, packages will be in reverse lexicographical order. | ||
// | ||
// At any rate, we start deleting collected depenencies from the payload at index 0, that is, we either remove the | ||
// most distant ones or the ones that are at the end of the lexicographic order. | ||
for (let i = 0; i < keys.length - exports.MAX_DEPENDENCIES; i++) { | ||
delete preliminaryPayload[keys[i]]; | ||
} | ||
exports.currentPayload = preliminaryPayload; | ||
} | ||
/** | ||
* Compares the given dependencies by their distance. | ||
* | ||
* @param {Object<string, any>} distances | ||
* @param {string} dependency1 | ||
* @param {string} dependency2 | ||
*/ | ||
function sortByDistance(distances, dependency1, dependency2) { | ||
// To make troubleshooting easier, we always want to include the Instana dependencies, therefore they will be sorted | ||
// to the end of the array. | ||
const isInstana1 = dependency1.indexOf('instana') >= 0; | ||
const isInstana2 = dependency2.indexOf('instana') >= 0; | ||
if (isInstana1 && isInstana2) { | ||
return dependency2.localeCompare(dependency1); | ||
} else if (isInstana1) { | ||
return 1; | ||
} else if (isInstana2) { | ||
return -1; | ||
} | ||
const d1 = distances[dependency1] || MAX_DEPTH + 1; | ||
const d2 = distances[dependency2] || MAX_DEPTH + 1; | ||
if (d1 === d2) { | ||
// for the same distance, sort lexicographically | ||
return dependency2.localeCompare(dependency1); | ||
} | ||
return d2 - d1; | ||
} |
@@ -59,2 +59,3 @@ /* | ||
function addDirectDependenciesFromMainPackageJson(packageJsonPath) { | ||
const started = Date.now(); | ||
fs.readFile(packageJsonPath, { encoding: 'utf8' }, (err, contents) => { | ||
@@ -71,3 +72,5 @@ if (err) { | ||
exports.currentPayload[pckg.name] = pckg.version; | ||
logger.debug(`Collection of direct dependencies took ${Date.now() - started} ms.`); | ||
} catch (subErr) { | ||
logger.debug(`Collection of direct dependencies took ${Date.now() - started} ms.`); | ||
return logger.debug('Failed to parse package.json %s dependency due to: %s', packageJsonPath, subErr.message); | ||
@@ -74,0 +77,0 @@ } |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
630820
48
1471
13
+ Added@instana/core@1.131.0(transitive)
- Removed@instana/core@1.130.1(transitive)
Updated@instana/core@1.131.0
Updatedtar@^5.0.9