@instana/collector
Version:
The Instana Node.js metrics and trace data collector
180 lines (148 loc) • 6.29 kB
JavaScript
/*
* (c) Copyright IBM Corp. 2021
* (c) Copyright Instana Inc. and contributors 2019
*/
;
const { isNodeJsTooOld, minimumNodeJsVersion } = require('@instana/core/src/util/nodeJsVersionCheck');
const { isProcessAvailable } = require('@instana/core/src/util/moduleAvailable');
if (!isProcessAvailable()) {
// eslint-disable-next-line no-console
console.error('The Node.js core module process is not available. This process will not be monitored by Instana.');
module.exports = function noOp() {};
// ESM default exports for TS
module.exports.default = function noOp() {};
// @ts-ignore TS1108 (return can only be used within a function body)
return;
}
if (isNodeJsTooOld()) {
// eslint-disable-next-line no-console
console.error(
`The package @instana/collector requires at least Node.js ${minimumNodeJsVersion} but this process is ` +
`running on Node.js ${process.version}. This process will not be monitored by Instana.`
);
// ESM default exports for TS
module.exports.default = function noOp() {};
// @ts-ignore TS1108 (return can only be used within a function body)
return;
}
let isMainThread = true;
try {
isMainThread = require('worker_threads').isMainThread;
} catch (err) {
// Worker threads are not available, so we know that this is the main thread.
}
const path = require('path');
const instanaNodeJsCore = require('@instana/core');
const instanaSharedMetrics = require('@instana/shared-metrics');
require('./tracing'); // load additional instrumentations
const log = require('./logger');
const normalizeCollectorConfig = require('./util/normalizeConfig');
const experimental = require('./experimental');
// NOTE: Default collector logger && config for cases like `preinit`.
const logger = log.init();
/** @type {import('./types/collector').CollectorConfig} */
let config = instanaNodeJsCore.util.normalizeConfig({}, logger);
/** @type {import('./agentConnection')} */
let agentConnection;
/**
* @param {import('./types/collector').CollectorConfig} [_config]
*/
function init(_config = {}) {
// @ts-ignore: Property '__INSTANA_INITIALIZED' does not exist on type global
if (global.__INSTANA_INITIALIZED) {
// Prevent initializing @instana/collector multiple times for the same process: @instana/collector has already been
// initialized, potentially from a different installation of @instana/collector somewhere else in the file system.
// Find that module in the require cache and return its exports (this is necessary to make sure calls to our API
// work as expected).
let collectorIndexCacheKey = Object.keys(require.cache).find(
cacheKey => cacheKey.indexOf('/@instana/collector/src/index.js') >= 0
);
// Requiring the collector package twice in the test package using a relative path such as `../../..`
if (process.env.NODE_ENV === 'test') {
collectorIndexCacheKey = Object.keys(require.cache).find(
cacheKey => cacheKey.indexOf('collector/src/index.js') >= 0
);
}
if (collectorIndexCacheKey) {
// eslint-disable-next-line no-unused-expressions
process.send && process.send('instana.collector.initialized');
return require.cache[collectorIndexCacheKey].exports;
} else {
// eslint-disable-next-line no-console
console.error(
"Warning: Instana has already been initialized but the module @instana/collector is not present in Node.js' " +
'module cache. The Instana API will not be available.'
);
return init;
}
}
// @ts-ignore: Property '__INSTANA_INITIALIZED' does not exist on type global
global.__INSTANA_INITIALIZED = true;
// CASE: reinit logger if custom logger or log level is provided.
if (_config.logger || _config.level) {
log.init(_config);
}
config = normalizeCollectorConfig(_config);
config = instanaNodeJsCore.util.normalizeConfig(config, logger);
agentConnection = require('./agentConnection');
const agentOpts = require('./agent/opts');
const pidStore = require('./pidStore');
const uncaught = require('./uncaught');
const announceCycle = require('./announceCycle');
const metrics = require('./metrics');
pidStore.init(config);
agentOpts.init(config);
announceCycle.init(config, pidStore);
agentConnection.init(config, pidStore);
instanaNodeJsCore.init(config, agentConnection, pidStore);
if (isMainThread) {
uncaught.init(config, agentConnection, pidStore);
metrics.init(config, pidStore);
}
logger.info(`@instana/collector module version: ${require(path.join(__dirname, '..', 'package.json')).version}`);
announceCycle.start();
return init;
}
init.currentSpan = function getHandleForCurrentSpan() {
return instanaNodeJsCore.tracing.getHandleForCurrentSpan();
};
init.isTracing = function isTracing() {
return instanaNodeJsCore.tracing.getCls() ? instanaNodeJsCore.tracing.getCls().isTracing() : false;
};
init.isConnected = function isConnected() {
return agentConnection && agentConnection.isConnected();
};
/**
* Keep in mind:
* Instana logs might appear in the internal log level for a very short time
* if you set a custom logger with a different log level. That is because "setLogger"
* is called after the initialization of the collector.
*
* const instana = require('@instana/collector')();
* const pino = require('pino')
* instana.setLogger(pinoLogger)
*
* Setting the **same** log level in the custom logger and the collector logger
* will prevent this behavior.
*
* @param {import('@instana/core/src/core').GenericLogger} _logger
*/
init.setLogger = function setLogger(_logger) {
// NOTE: Override our default logger with customer's logger
log.init({ logger: _logger });
};
init.core = instanaNodeJsCore;
init.sharedMetrics = instanaSharedMetrics;
init.experimental = experimental;
init.opentracing = instanaNodeJsCore.tracing.opentracing;
init.sdk = instanaNodeJsCore.tracing.sdk;
if (process.env.INSTANA_IMMEDIATE_INIT != null && process.env.INSTANA_IMMEDIATE_INIT.toLowerCase() === 'true') {
init();
} else if (
process.env.INSTANA_EARLY_INSTRUMENTATION != null &&
process.env.INSTANA_EARLY_INSTRUMENTATION.toLowerCase() === 'true'
) {
instanaNodeJsCore.preInit(config);
}
module.exports = init;
module.exports.default = init;