diff --git a/packages/collector/src/index.js b/packages/collector/src/index.js index 3b5c251630..7b40eaa7ae 100644 --- a/packages/collector/src/index.js +++ b/packages/collector/src/index.js @@ -41,6 +41,25 @@ try { // Worker threads are not available, so we know that this is the main thread. } +// Check if worker threads should be disabled via environment variable. +// Disabling worker threads may be necessary in environments where +// multi-threading causes issues or monitoring of worker threads is not required. +const disableWorkerThreads = process.env.INSTANA_DISABLE_WORKER_THREADS?.toLowerCase() === 'true'; + +if (disableWorkerThreads && !isMainThread) { + // eslint-disable-next-line no-console + console.warn( + 'Worker threads are disabled via INSTANA_DISABLE_WORKER_THREADS. ' + + 'This worker thread will not be monitored by Instana.' + ); + + module.exports = function noOp() {}; + module.exports.default = function noOp() {}; + + // @ts-ignore + return; +} + const path = require('path'); const instanaNodeJsCore = require('@instana/core'); const instanaSharedMetrics = require('@instana/shared-metrics'); diff --git a/packages/collector/test/tracing/logging/pino/app.js b/packages/collector/test/tracing/logging/pino/app.js index d22c6227a4..f5646f182f 100644 --- a/packages/collector/test/tracing/logging/pino/app.js +++ b/packages/collector/test/tracing/logging/pino/app.js @@ -14,7 +14,10 @@ process.on('SIGTERM', () => { }); require('./mockVersion'); -require('../../../..')(); + +if (!process.env.NODE_OPTIONS || !process.env.NODE_OPTIONS.includes('src/immediate')) { + require('../../../..')(); +} const fetch = require('node-fetch-v2'); const bodyParser = require('body-parser'); @@ -212,6 +215,22 @@ app.get('/express-pino-error-random-object-and-string', (req, res) => { finish(res); }); +app.get('/thread-stream-test', (req, res) => { + try { + const mode = process.env.PINO_WORKER_MODE || 'transport'; + + const logger = + mode === 'transport' + ? pino({ transport: { target: 'pino-pretty', options: { destination: 1 } } }) + : pino({ destination: 1 }); + logger.error('Pino worker test error log'); + + res.sendStatus(200); + } catch (e) { + res.status(500).send(`Failed: ${e.message}`); + } +}); + function finish(res) { fetch(`http://127.0.0.1:${agentPort}`).then(() => { res.sendStatus(200); diff --git a/packages/collector/test/tracing/logging/pino/test.js b/packages/collector/test/tracing/logging/pino/test.js index a78b9e3c9a..db6ddf4a5f 100644 --- a/packages/collector/test/tracing/logging/pino/test.js +++ b/packages/collector/test/tracing/logging/pino/test.js @@ -102,6 +102,52 @@ describe('tracing/logging/pino', function () { }); }); }); + describe('pino thread-stream worker', function () { + let controls; + + before(async () => { + controls = new ProcessControls({ + dirname: __dirname, + useGlobalAgent: true, + env: { + PINO_WORKER_MODE: 'transport', + PINO_EXPRESS: 'false', + NODE_OPTIONS: `--require ${path.join(__dirname, '../../../..', 'src', 'immediate.js')}`, + INSTANA_DISABLE_WORKER_THREADS: 'true' + } + }); + + await controls.startAndWaitForAgentConnection(); + }); + + beforeEach(async () => { + await agentControls.clearReceivedTraceData(); + }); + + after(async () => { + await controls.stop(); + }); + + it('must trace without errors', async () => { + await controls.sendRequest({ + method: 'GET', + path: '/thread-stream-test' + }); + await testUtils.delay(1000); + const spans = await agentControls.getSpans(); + + const logSpan = spans.find(s => s.n === 'log.pino'); + expect(logSpan).to.exist; + expect(logSpan.data.log.message).to.equal('Pino worker test error log'); + + const httpSpan = spans.find(s => s.n === 'node.http.server'); + expect(httpSpan).to.exist; + expect(httpSpan.data.http.path_tpl).to.equal('/thread-stream-test'); + expect(httpSpan.data.http.status).to.equal(200); + + expect(spans).to.have.lengthOf(2); + }); + }); function runTests(pinoVersion, useExpressPino) { const suffix = useExpressPino ? ' (express-pino)' : '';