@instana/core
Version:
Core library for Instana's Node.js packages
113 lines (94 loc) • 3.95 kB
JavaScript
/*
* (c) Copyright IBM Corp. 2021
* (c) Copyright Instana Inc. and contributors 2021
*/
/* eslint-disable max-len */
;
const shimmer = require('../../shimmer');
const tracingUtil = require('../../tracingUtil');
const constants = require('../../constants');
const cls = require('../../cls');
let isActive = false;
exports.init = function init() {
shimmer.wrap(console, 'warn', shimLog({ markAsError: false, level: 'warn' }));
shimmer.wrap(console, 'error', shimLog({ markAsError: true, level: 'error' }));
};
// ATTENTION: Do not use console.warn or console.error in this file, otherwise it could run into endless loop
function shimLog(options) {
return originalLog =>
function () {
// The __instana attribute identifies the Instana logger, so prevent these logs from being traced.
if (this?.__instana) {
return originalLog.apply(this, arguments);
}
if (arguments.length === 0) {
// * arguments.length === 0 -> This is a console.warn() type of call (without arguments), this will not log
// anything but simply return whether the log level in question is enabled for this logger.
return originalLog.apply(this, arguments);
}
if (cls.skipExitTracing({ isActive, skipAllowRootExitSpanPresence: true })) {
return originalLog.apply(this, arguments);
}
const originalArgs = new Array(arguments.length);
for (let i = 0; i < arguments.length; i++) {
originalArgs[i] = arguments[i];
}
instrumentedLog(this, originalLog, originalArgs, options);
};
}
function instrumentedLog(ctx, originalLog, originalArgs, options) {
return cls.ns.runAndReturn(() => {
const span = cls.startSpan({
spanName: 'log.console',
kind: constants.EXIT
});
span.stack = tracingUtil.getStackTrace(instrumentedLog);
const firstArg = originalArgs[0];
let secondArg = originalArgs[1];
if (typeof firstArg === 'string') {
secondArg = firstArg;
} else if (firstArg instanceof Error && typeof secondArg === 'string') {
// CASE: e.g. console.error(new Error('err msg'), 'Another message')
const errorMessage = tracingUtil.extractErrorMessage(firstArg);
secondArg = `${errorMessage} -- ${secondArg}`;
} else if (firstArg instanceof Error) {
// CASE: e.g. console.error(new Error('err msg'))
secondArg = tracingUtil.extractErrorMessage(firstArg);
} else if (firstArg?.err instanceof Error && typeof secondArg === 'string') {
// CASE: e.g. console.error({err: new Error(..)}, 'Another message')
const errorMessage = tracingUtil.extractErrorMessage(firstArg.err);
secondArg = `${errorMessage} -- ${secondArg}`;
} else if (firstArg?.err instanceof Error) {
// CASE: e.g. console.error({err: new Error(..)})
secondArg = tracingUtil.extractErrorMessage(firstArg.err);
} else if (firstArg?.err && typeof firstArg.err.message === 'string' && typeof secondArg === 'string') {
// CASE: e.g. console.error({err: {message: 'err msg'}}, 'Another message')
secondArg = `${firstArg.err.message} -- ${secondArg}`;
} else if (firstArg?.err && typeof firstArg.err.message === 'string') {
// CASE: e.g. console.error({err: {message: 'err msg'}})
secondArg = firstArg.err.message;
} else if (typeof secondArg !== 'string') {
// CASE: e.g. console.error({x:y})
secondArg = "Log call without message. Call won't be serialized by Instana for performance reasons.";
}
span.data.log = {
message: secondArg,
level: options.level
};
if (options.markAsError) {
span.ec = 1;
}
try {
return originalLog.apply(ctx, originalArgs);
} finally {
span.d = Date.now() - span.ts;
span.transmit();
}
});
}
exports.activate = function activate() {
isActive = true;
};
exports.deactivate = function deactivate() {
isActive = false;
};