applicationinsights
Version:
Microsoft Application Insights module for Node.js
131 lines • 6.06 kB
JavaScript
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.parseStack = exports._StackFrame = exports.AutoCollectExceptions = void 0;
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for details.
const api_logs_1 = require("@opentelemetry/api-logs");
const util_1 = require("../shared/util");
const UNCAUGHT_EXCEPTION_HANDLER_NAME = "uncaughtException";
const UNHANDLED_REJECTION_HANDLER_NAME = "unhandledRejection";
const FALLBACK_ERROR_MESSAGE = "A promise was rejected without providing an error. Application Insights generated this error stack for you.";
class AutoCollectExceptions {
constructor(client) {
this._client = client;
this._exceptionListenerHandle = this._handleException.bind(this, true, UNCAUGHT_EXCEPTION_HANDLER_NAME);
this._rejectionListenerHandle = this._handleException.bind(this, false, UNHANDLED_REJECTION_HANDLER_NAME); // never rethrows
process.on(UNCAUGHT_EXCEPTION_HANDLER_NAME, this._exceptionListenerHandle);
process.on(UNHANDLED_REJECTION_HANDLER_NAME, this._rejectionListenerHandle);
}
shutdown() {
if (this._exceptionListenerHandle) {
if (this._exceptionListenerHandle) {
process.removeListener(UNCAUGHT_EXCEPTION_HANDLER_NAME, this._exceptionListenerHandle);
}
if (this._rejectionListenerHandle) {
process.removeListener(UNHANDLED_REJECTION_HANDLER_NAME, this._rejectionListenerHandle);
}
}
this._exceptionListenerHandle = undefined;
this._rejectionListenerHandle = undefined;
delete this._exceptionListenerHandle;
delete this._rejectionListenerHandle;
}
_handleException(reThrow, name, error = new Error(FALLBACK_ERROR_MESSAGE)) {
if (this._client) {
this._client.trackException({ exception: error });
try {
api_logs_1.logs.getLoggerProvider().forceFlush().then(() => {
// only rethrow when we are the only listener
if (reThrow && name && process.listeners(name).length === 1) {
// eslint-disable-next-line no-console
console.error(error);
// eslint-disable-next-line no-process-exit
process.exit(1);
}
});
}
catch (error) {
console.error(`Could not get the loggerProvider upon handling a tracked exception: ${error}`);
}
}
else {
// eslint-disable-next-line no-console
console.error(error);
process.exit(1);
}
}
}
exports.AutoCollectExceptions = AutoCollectExceptions;
// regex to match stack frames from ie/chrome/ff
// methodName=$2, fileName=$4, lineNo=$5, column=$6
const stackFramesRegex = /^(\s+at)?(.*?)(\@|\s\(|\s)([^\(\n]+):(\d+):(\d+)(\)?)$/;
class _StackFrame {
constructor(frame, level) {
this.sizeInBytes = 0;
this._baseSize = 58; //'{"method":"","level":,"assembly":"","fileName":"","line":}'.length
this.level = level;
this.method = "<no_method>";
this.assembly = util_1.Util.getInstance().trim(frame);
const matches = frame.match(stackFramesRegex);
if (matches && matches.length >= 5) {
this.method = util_1.Util.getInstance().trim(matches[2]) || this.method;
this.fileName = util_1.Util.getInstance().trim(matches[4]) || "<no_filename>";
this.line = parseInt(matches[5]) || 0;
}
this.sizeInBytes += this.method.length;
this.sizeInBytes += this.fileName.length;
this.sizeInBytes += this.assembly.length;
// todo: these might need to be removed depending on how the back-end settles on their size calculation
this.sizeInBytes += this._baseSize;
this.sizeInBytes += this.level.toString().length;
this.sizeInBytes += this.line.toString().length;
}
}
exports._StackFrame = _StackFrame;
function parseStack(stack) {
let parsedStack = undefined;
if (typeof stack === "string") {
const frames = stack.split("\n");
parsedStack = [];
let level = 0;
let totalSizeInBytes = 0;
for (let i = 0; i <= frames.length; i++) {
const frame = frames[i];
if (stackFramesRegex.test(frame)) {
const parsedFrame = new _StackFrame(frames[i], level++);
totalSizeInBytes += parsedFrame.sizeInBytes;
parsedStack.push(parsedFrame);
}
}
// DP Constraint - exception parsed stack must be < 32KB
// remove frames from the middle to meet the threshold
const exceptionParsedStackThreshold = 32 * 1024;
if (totalSizeInBytes > exceptionParsedStackThreshold) {
let left = 0;
let right = parsedStack.length - 1;
let size = 0;
let acceptedLeft = left;
let acceptedRight = right;
while (left < right) {
// check size
const lSize = parsedStack[left].sizeInBytes;
const rSize = parsedStack[right].sizeInBytes;
size += lSize + rSize;
if (size > exceptionParsedStackThreshold) {
// remove extra frames from the middle
const howMany = acceptedRight - acceptedLeft + 1;
parsedStack.splice(acceptedLeft, howMany);
break;
}
// update pointers
acceptedLeft = left;
acceptedRight = right;
left++;
right--;
}
}
}
return parsedStack;
}
exports.parseStack = parseStack;
//# sourceMappingURL=exceptions.js.map