Skip to content

Commit c4abc75

Browse files
abhilash-sivankirrg001aryamohanan
authored
fix: resolved bunyan npm installation warning (#1447)
refs https://jsw.ibm.com/browse/INSTA-12533 Co-authored-by: kirrg001 <[email protected]> Co-authored-by: Arya <[email protected]>
1 parent e9af945 commit c4abc75

22 files changed

+477
-213
lines changed

package-lock.json

+6-28
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

packages/collector/package.json

+5-2
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,10 @@
7373
{
7474
"name": "Arya Mohanan",
7575
"email": "[email protected]"
76+
},
77+
{
78+
"name": "Abhilash Sivan",
79+
"email": "[email protected]"
7680
}
7781
],
7882
"bugs": {
@@ -81,15 +85,14 @@
8185
"dependencies": {
8286
"@instana/core": "4.5.0",
8387
"@instana/shared-metrics": "4.5.0",
84-
"bunyan": "^1.8.15",
88+
"pino": "^9.6.0",
8589
"semver": "^7.5.4",
8690
"serialize-error": "^8.1.0"
8791
},
8892
"optionalDependencies": {
8993
"@instana/autoprofile": "4.5.0"
9094
},
9195
"devDependencies": {
92-
"@types/bunyan": "^1.8.8",
9396
"opentracing": "^0.14.5",
9497
"typeorm": "^0.3.20"
9598
}

packages/collector/src/agentConnection.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -267,9 +267,11 @@ exports.sendSpans = function sendSpans(spans, cb) {
267267
if (err && !maxContentErrorHasBeenLogged && err instanceof PayloadTooLargeError) {
268268
logLargeSpans(spans);
269269
} else if (err) {
270-
logger.debug('Failed to send: %s', getSpanLengthInfo(spans));
270+
const spanInfo = getSpanLengthInfo(spans);
271+
logger.debug(`Failed to send: ${JSON.stringify(spanInfo)}`);
271272
} else {
272-
logger.debug('Successfully sent: %s', getSpanLengthInfo(spans));
273+
const spanInfo = getSpanLengthInfo(spans);
274+
logger.debug(`Successfully sent:${JSON.stringify(spanInfo)}`);
273275
}
274276
cb(err);
275277
});

packages/collector/src/logger.js

+80-33
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,12 @@ try {
1616
// thread (0).
1717
}
1818

19-
const bunyan = require('bunyan');
20-
const { logger } = require('@instana/core');
19+
const uninstrumentedLogger = require('./uninstrumentedLogger');
2120

22-
const bunyanToAgentStream = require('./agent/bunyanToAgentStream');
21+
const { logger } = require('@instana/core');
22+
const loggerToAgentStream = require('./agent/loggerToAgentStream');
2323

24-
/** @type {bunyan | import('@instana/core/src/core').GenericLogger} */
24+
/** @type {import('@instana/core/src/core').GenericLogger} */
2525
let parentLogger = null;
2626
/** @type {Object.<string, (logger: import('@instana/core/src/core').GenericLogger) => *>} */
2727
const registry = {};
@@ -35,37 +35,63 @@ exports.init = function init(config, isReInit) {
3535
// A bunyan or pino logger has been provided via config. In either case we create a child logger directly under the
3636
// given logger which serves as the parent for all loggers we create later on.
3737
parentLogger = config.logger.child({
38-
module: 'instana-nodejs-logger-parent',
39-
__in: 1
38+
module: 'instana-nodejs-logger-parent'
4039
});
4140
} else if (config.logger && hasLoggingFunctions(config.logger)) {
42-
// A custom non-bunyan logger has been provided via config. We use it as is.
41+
// A custom non-bunyan/non-pino logger has been provided via config. We use it as is.
4342
parentLogger = config.logger;
4443
} else {
45-
// No custom logger has been provided via config, we create a new bunyan logger as the parent logger for all loggers
44+
// No custom logger has been provided via config, we create a new pino logger as the parent logger for all loggers
4645
// we create later on.
47-
parentLogger = bunyan.createLogger({
46+
parentLogger = uninstrumentedLogger({
4847
name: '@instana/collector',
49-
thread: threadId,
50-
__in: 1
48+
level: 'info'
5149
});
5250
}
53-
if (isBunyan(parentLogger)) {
51+
52+
// Passing the log stream to agentStream for Debugging purposes
53+
// TODO: consider adding winston and other major loggers also for this
54+
if (isPinoLogger(parentLogger)) {
55+
// This consoleStream creates a destination stream for the logger that writes log data to the standard output.
56+
// Since we are using multistream here, this needs to be specified explicitly
57+
58+
const consoleStream = uninstrumentedLogger.destination(parentLogger.destination);
59+
60+
const multiStream = {
61+
/**
62+
* Custom write method to send logs to multiple destinations
63+
* @param {string} chunk
64+
*/
65+
write(chunk) {
66+
consoleStream.write(chunk);
67+
68+
loggerToAgentStream.write(chunk);
69+
}
70+
};
71+
72+
parentLogger = uninstrumentedLogger(
73+
{
74+
...parentLogger.levels,
75+
level: parentLogger.level || 'info',
76+
base: parentLogger.bindings()
77+
},
78+
multiStream
79+
);
80+
} else if (parentLogger && parentLogger.addStream) {
5481
// in case we are using a bunyan logger we also forward logs to the agent
55-
/** @type {bunyan} */ (parentLogger).addStream({
82+
parentLogger.addStream({
5683
type: 'raw',
57-
stream: bunyanToAgentStream,
84+
stream: loggerToAgentStream,
5885
level: 'info'
5986
});
60-
if (process.env['INSTANA_DEBUG']) {
61-
/** @type {bunyan} */ (parentLogger).level('debug');
62-
} else if (config.level) {
63-
/** @type {bunyan} */ (parentLogger).level(/** @type {import('bunyan').LogLevel} */ (config.level));
64-
} else if (process.env['INSTANA_LOG_LEVEL']) {
65-
/** @type {bunyan} */ (parentLogger).level(
66-
/** @type {import('bunyan').LogLevel} */ (process.env['INSTANA_LOG_LEVEL'].toLowerCase())
67-
);
68-
}
87+
}
88+
89+
if (process.env['INSTANA_DEBUG']) {
90+
setLoggerLevel(parentLogger, 'debug');
91+
} else if (config.level) {
92+
setLoggerLevel(parentLogger, config.level);
93+
} else if (process.env['INSTANA_LOG_LEVEL']) {
94+
setLoggerLevel(parentLogger, process.env['INSTANA_LOG_LEVEL'].toLowerCase());
6995
}
7096

7197
if (isReInit) {
@@ -81,18 +107,21 @@ exports.init = function init(config, isReInit) {
81107
/**
82108
* @param {string} loggerName
83109
* @param {(logger: import('@instana/core/src/core').GenericLogger) => *} [reInitFn]
110+
* @param {string|null} [level] - Optional log level
84111
* @returns {import('@instana/core/src/core').GenericLogger}
85112
*/
86-
exports.getLogger = function getLogger(loggerName, reInitFn) {
113+
exports.getLogger = function getLogger(loggerName, reInitFn, level) {
87114
if (!parentLogger) {
88115
exports.init({});
89116
}
117+
90118
let _logger;
91119

92120
if (typeof parentLogger.child === 'function') {
93121
// Either bunyan or pino, both support parent-child relationships between loggers.
94122
_logger = parentLogger.child({
95-
module: loggerName
123+
module: loggerName,
124+
threadId
96125
});
97126
} else {
98127
// Unknown logger type (neither bunyan nor pino), we simply return the user provided custom logger as-is.
@@ -106,17 +135,13 @@ exports.getLogger = function getLogger(loggerName, reInitFn) {
106135
registry[loggerName] = reInitFn;
107136
}
108137

138+
if (level) {
139+
setLoggerLevel(_logger, level);
140+
}
141+
109142
return /** @type {import('@instana/core/src/core').GenericLogger} */ (_logger);
110143
};
111144

112-
/**
113-
* @param {import('bunyan') | *} _logger
114-
* @returns {boolean}
115-
*/
116-
function isBunyan(_logger) {
117-
return _logger instanceof bunyan;
118-
}
119-
120145
/**
121146
* @param {import('@instana/core/src/core').GenericLogger | *} _logger
122147
* @returns {boolean}
@@ -129,3 +154,25 @@ function hasLoggingFunctions(_logger) {
129154
typeof _logger.error === 'function'
130155
);
131156
}
157+
158+
/**
159+
* @param {import("@instana/core/src/core").GenericLogger} _logger
160+
* @param {string|number} level
161+
*/
162+
function setLoggerLevel(_logger, level) {
163+
if (typeof _logger.setLevel === 'function') {
164+
_logger.setLevel(level);
165+
} else {
166+
_logger.level = level;
167+
}
168+
}
169+
170+
/**
171+
* @param {*} _logger
172+
* @returns {boolean}
173+
*/
174+
function isPinoLogger(_logger) {
175+
return (
176+
_logger && typeof _logger === 'object' && typeof _logger.child === 'function' && typeof _logger.level === 'string'
177+
);
178+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
/*
2+
* (c) Copyright IBM Corp. 2024
3+
*/
4+
5+
'use strict';
6+
7+
const pinoWasRequiredBeforeUs = Object.keys(require.cache).some(key => key.includes('node_modules/pino'));
8+
9+
// eslint-disable-next-line import/no-extraneous-dependencies, instana/no-unsafe-require
10+
11+
const pino = require('pino').default;
12+
13+
// TODO: Fix the issue with Pino instrumentation. If Pino is required multiple times,
14+
// only the first require is instrumented. `onFileLoad` causes the behavior for that.
15+
// See https://jsw.ibm.com/browse/INSTA-23066
16+
// NOTE: We need the removal of the cache here anyway, because we do not want to trigger the pino instrumentation.
17+
// This is an uninstrumented pino logger.
18+
// If pino was required before us, we leave the cache as it is.
19+
// NOTE: Clearing the require cache is only needed because of onFileLoad usage in the pino instrumentation.
20+
// As soon as we can migrate to use onModuleLoad in the instrumentation,
21+
// we can remove this and ensure that the internal logger is called before the instr initialization.
22+
if (!pinoWasRequiredBeforeUs) {
23+
Object.keys(require.cache).forEach(key => {
24+
if (key.includes('node_modules/pino')) {
25+
delete require.cache[key];
26+
}
27+
});
28+
}
29+
30+
module.exports = pino;

0 commit comments

Comments
 (0)