Latest Threat Research:SANDWORM_MODE: Shai-Hulud-Style npm Worm Hijacks CI Workflows and Poisons AI Toolchains.Details
Socket
Book a DemoInstallSign in
Socket

@instana/serverless

Package Overview
Dependencies
Maintainers
4
Versions
290
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@instana/serverless - npm Package Compare versions

Comparing version
5.0.0
to
5.0.1
+169
src/logger.js
/*
* (c) Copyright IBM Corp. 2021
* (c) Copyright Instana Inc. and contributors 2019
*/
/* eslint-disable no-console */
'use strict';
// 30 = info
let minLevel = 30;
const DEBUG_LEVEL = 20;
const consoleLogger = {
debug: createLogFn(20, console.debug || console.log),
info: createLogFn(30, console.log),
warn: createLogFn(40, console.warn),
error: createLogFn(50, console.error)
};
let instanaServerlessLogger;
function createLogFn(level, fn) {
return function log() {
if (level >= minLevel) {
fn.apply(console, arguments);
}
};
}
class InstanaServerlessLogger {
/**
* @param {import('@instana/core/src/core').GenericLogger} logger
*/
constructor(logger) {
this.logger = logger;
}
/**
* @param {import('@instana/core/src/core').GenericLogger} _logger
*/
setLogger(_logger) {
this.logger = _logger;
}
isInDebugMode() {
if (!this.logger) return false;
if (typeof this.logger.level === 'function') {
return this.logger.level() === DEBUG_LEVEL;
}
if (typeof this.logger.level === 'number') {
return this.logger.level === DEBUG_LEVEL;
}
if (typeof this.logger.getLevel === 'function') {
return this.logger.getLevel() === DEBUG_LEVEL;
}
return minLevel === DEBUG_LEVEL;
}
warn = (...args) => this.logger?.warn?.(...args);
error = (...args) => this.logger?.error?.(...args);
info = (...args) => this.logger?.info?.(...args);
debug = (...args) => this.logger?.debug?.(...args);
trace = (...args) => this.logger?.trace?.(...args);
}
exports.init = function init(userConfig = {}) {
let parentLogger;
// CASE: prevent circular references
if (userConfig.logger && userConfig.logger instanceof InstanaServerlessLogger && userConfig.logger.logger) {
userConfig.logger = userConfig.logger.logger;
}
// CASE: customer overrides logger in serverless land.
if (userConfig.logger && typeof userConfig.logger.child === 'function') {
// A bunyan or pino logger has been provided via config. In either case we create a child logger directly under the
// given logger which serves as the parent for all loggers we create later on.
// BUG: Winston does not support child logger levels! Neither in `.child` nor with `level()`
// Setting INSTANA_DEBUG=true has no affect in the child winston logger.
// It takes the parent logger level.
parentLogger = userConfig.logger.child({
module: 'instana-nodejs-serverless-logger'
});
} else if (userConfig.logger && hasLoggingFunctions(userConfig.logger)) {
// A custom non-bunyan/non-pino logger has been provided via config. We use it as is.
parentLogger = userConfig.logger;
} else {
parentLogger = consoleLogger;
}
// NOTE: We accept for `process.env.INSTANA_DEBUG` any string value - does not have to be "true".
if (process.env.INSTANA_DEBUG || userConfig.level || process.env.INSTANA_LOG_LEVEL) {
setLoggerLevel(process.env.INSTANA_DEBUG ? 'debug' : userConfig.level || process.env.INSTANA_LOG_LEVEL);
}
if (!instanaServerlessLogger) {
instanaServerlessLogger = new InstanaServerlessLogger(parentLogger);
} else {
instanaServerlessLogger.setLogger(parentLogger);
}
return instanaServerlessLogger;
};
exports.getLogger = () => {
return instanaServerlessLogger;
};
// TODO: Legacy. Remove in next major release.
['info', 'warn', 'error', 'debug'].forEach(level => {
exports[level] = function () {
if (!instanaServerlessLogger) {
exports.init();
}
return instanaServerlessLogger[level].apply(instanaServerlessLogger, arguments);
};
});
/**
* @param {import('@instana/core/src/core').GenericLogger | *} _logger
* @returns {boolean}
*/
function hasLoggingFunctions(_logger) {
return (
typeof _logger.debug === 'function' &&
typeof _logger.info === 'function' &&
typeof _logger.warn === 'function' &&
typeof _logger.error === 'function'
);
}
function setLoggerLevel(level) {
// eslint-disable-next-line yoda
if (typeof level === 'number' && 0 < level && level <= 50) {
minLevel = level;
return;
}
if (typeof level === 'string') {
switch (level) {
case 'debug':
minLevel = 20;
break;
case 'info':
minLevel = 30;
break;
case 'warn':
minLevel = 40;
break;
case 'error':
minLevel = 50;
break;
default:
break;
}
}
}
+392
-8

@@ -6,20 +6,404 @@ # Change Log

# [5.0.0](https://github.com/instana/nodejs/compare/v3.21.0...v5.0.0) (2024-10-23)
## [5.0.1](https://github.com/instana/nodejs/compare/v5.0.0...v5.0.1) (2025-12-16)
**Note:** Version bump only for package @instana/serverless
# [5.0.0](https://github.com/instana/nodejs/compare/v4.31.0...v5.0.0) (2025-12-16)
### Bug Fixes
- dropped support for node v14 and v16 ([#1348](https://github.com/instana/nodejs/issues/1348)) ([aaa9ad4](https://github.com/instana/nodejs/commit/aaa9ad41ebf82b11eedcf913afc31d3addd53868))
- removed deprecated INSTANA_URL and INSTANA_KEY environment variables ([#1373](https://github.com/instana/nodejs/issues/1373)) ([955cf67](https://github.com/instana/nodejs/commit/955cf67f4c83757329a8a1ad9b843dc8801b4300))
* enforced Node.js 18.19 as the minimum supported version ([#2151](https://github.com/instana/nodejs/issues/2151)) ([5d688e2](https://github.com/instana/nodejs/commit/5d688e22ff03a6e4721a0363011002801a1ee045))
* removed setLogger fn from serverless logger ([#2188](https://github.com/instana/nodejs/issues/2188)) ([47763f5](https://github.com/instana/nodejs/commit/47763f5d683d191c5512a804359adfb7e7f925de))
### BREAKING CHANGES
- - The INSTANA_URL and INSTANA_KEY environment variables have been removed.
* Dropped support for Node.js versions below 18.19
* Any references to these should be replaced with the environment variables INSTANA_ENDPOINT_URL and INSTANA_AGENT_KEY.
- - Dropped support for Node.js versions 14 and 16.
* Reason: These versions have reached their end of life.
* More info: https://github.com/nodejs/Release?tab=readme-ov-file#end-of-life-releases
# [4.31.0](https://github.com/instana/nodejs/compare/v4.30.1...v4.31.0) (2025-12-08)
### Bug Fixes
* **serverless:** resolved TypeError when agent key is not available ([#2197](https://github.com/instana/nodejs/issues/2197)) ([d24e759](https://github.com/instana/nodejs/commit/d24e759cd220dfb92975d88fd845cd3de5b99ad2))
## [4.30.1](https://github.com/instana/nodejs/compare/v4.30.0...v4.30.1) (2025-11-18)
**Note:** Version bump only for package @instana/serverless
# [4.30.0](https://github.com/instana/nodejs/compare/v4.29.0...v4.30.0) (2025-11-17)
**Note:** Version bump only for package @instana/serverless
# [4.29.0](https://github.com/instana/nodejs/compare/v4.28.0...v4.29.0) (2025-11-07)
**Note:** Version bump only for package @instana/serverless
# [4.28.0](https://github.com/instana/nodejs/compare/v4.27.1...v4.28.0) (2025-11-06)
**Note:** Version bump only for package @instana/serverless
## [4.27.1](https://github.com/instana/nodejs/compare/v4.27.0...v4.27.1) (2025-11-03)
**Note:** Version bump only for package @instana/serverless
# [4.27.0](https://github.com/instana/nodejs/compare/v4.26.4...v4.27.0) (2025-10-23)
**Note:** Version bump only for package @instana/serverless
## [4.26.4](https://github.com/instana/nodejs/compare/v4.26.3...v4.26.4) (2025-10-21)
**Note:** Version bump only for package @instana/serverless
## [4.26.3](https://github.com/instana/nodejs/compare/v4.26.2...v4.26.3) (2025-10-21)
**Note:** Version bump only for package @instana/serverless
## [4.26.2](https://github.com/instana/nodejs/compare/v4.26.1...v4.26.2) (2025-10-15)
**Note:** Version bump only for package @instana/serverless
## [4.26.1](https://github.com/instana/nodejs/compare/v4.26.0...v4.26.1) (2025-10-13)
**Note:** Version bump only for package @instana/serverless
# [4.26.0](https://github.com/instana/nodejs/compare/v4.25.0...v4.26.0) (2025-10-08)
**Note:** Version bump only for package @instana/serverless
# [4.25.0](https://github.com/instana/nodejs/compare/v4.24.1...v4.25.0) (2025-09-23)
**Note:** Version bump only for package @instana/serverless
## [4.24.1](https://github.com/instana/nodejs/compare/v4.24.0...v4.24.1) (2025-09-18)
### Bug Fixes
* forced debug dependency to ^4.4.3 ([#2007](https://github.com/instana/nodejs/issues/2007)) ([3d5caaa](https://github.com/instana/nodejs/commit/3d5caaa70a9b841945a4adfe5978db54734cf9fa))
# [4.24.0](https://github.com/instana/nodejs/compare/v4.23.1...v4.24.0) (2025-09-11)
**Note:** Version bump only for package @instana/serverless
## [4.23.1](https://github.com/instana/nodejs/compare/v4.23.0...v4.23.1) (2025-09-01)
**Note:** Version bump only for package @instana/serverless
# [4.23.0](https://github.com/instana/nodejs/compare/v4.22.0...v4.23.0) (2025-08-25)
**Note:** Version bump only for package @instana/serverless
# [4.22.0](https://github.com/instana/nodejs/compare/v4.21.3...v4.22.0) (2025-08-13)
**Note:** Version bump only for package @instana/serverless
## [4.21.3](https://github.com/instana/nodejs/compare/v4.21.2...v4.21.3) (2025-08-07)
### Bug Fixes
* **aws-lambda:** improved coldstarts ([#1933](https://github.com/instana/nodejs/issues/1933)) ([cfede29](https://github.com/instana/nodejs/commit/cfede297893f4535b6a53602a8737c07cb4c7f5a))
* bumped https-proxy-agent from 7.0.2 to 7.0.6 ([#1929](https://github.com/instana/nodejs/issues/1929)) ([e8f1c4d](https://github.com/instana/nodejs/commit/e8f1c4d356d35880fa38876d7502df5c6e15784f))
## [4.21.2](https://github.com/instana/nodejs/compare/v4.21.1...v4.21.2) (2025-08-05)
### Bug Fixes
* **aws-lambda:** improved heartbeat timeout ([#1912](https://github.com/instana/nodejs/issues/1912)) ([08290bb](https://github.com/instana/nodejs/commit/08290bbe83772c3c49bb4cb4cdab0a745c7b6455))
## [4.21.1](https://github.com/instana/nodejs/compare/v4.21.0...v4.21.1) (2025-08-05)
### Bug Fixes
* **aws-lambda:** increased initial heartbeat timeout ([6b1b1df](https://github.com/instana/nodejs/commit/6b1b1df37cfd04ab051f0472c3ea29ed81d852fa))
* **aws-lambda:** transformed heartbeat warning into debug log ([a6bf4dd](https://github.com/instana/nodejs/commit/a6bf4dd09ab2ea3a79e2d221eac67412968e7f89))
# [4.21.0](https://github.com/instana/nodejs/compare/v4.20.0...v4.21.0) (2025-07-31)
### Features
* **aws-lambda:** improved overhaul performance ([#1315](https://github.com/instana/nodejs/issues/1315)) ([4620113](https://github.com/instana/nodejs/commit/46201132dac6a73e7719d085c4edaa5c5a5ae526))
# [4.20.0](https://github.com/instana/nodejs/compare/v4.19.1...v4.20.0) (2025-07-30)
**Note:** Version bump only for package @instana/serverless
## [4.19.1](https://github.com/instana/nodejs/compare/v4.19.0...v4.19.1) (2025-07-25)
### Bug Fixes
* **serverless:** resolved maximum call stack error ([#1877](https://github.com/instana/nodejs/issues/1877)) ([985b3c1](https://github.com/instana/nodejs/commit/985b3c165b0533a068e9e37c1e0b99a1fbfb4da0))
# [4.19.0](https://github.com/instana/nodejs/compare/v4.18.1...v4.19.0) (2025-07-24)
### Bug Fixes
* **serverless:** removed trailing slashes from instana endpoint url ([#1862](https://github.com/instana/nodejs/issues/1862)) ([305f2b2](https://github.com/instana/nodejs/commit/305f2b249900d6e5e0ffb7b305b486fd1cf2621a))
## [4.18.1](https://github.com/instana/nodejs/compare/v4.18.0...v4.18.1) (2025-07-14)
**Note:** Version bump only for package @instana/serverless
# [4.18.0](https://github.com/instana/nodejs/compare/v4.17.0...v4.18.0) (2025-07-10)
**Note:** Version bump only for package @instana/serverless
# [4.17.0](https://github.com/instana/nodejs/compare/v4.16.0...v4.17.0) (2025-06-30)
**Note:** Version bump only for package @instana/serverless
# [4.16.0](https://github.com/instana/nodejs/compare/v4.15.3...v4.16.0) (2025-06-24)
### Bug Fixes
* **collector:** optimized flushing spans before application dies ([#1765](https://github.com/instana/nodejs/issues/1765)) ([3507599](https://github.com/instana/nodejs/commit/35075996e879734a7bb9437c1ce375b9d979fe3a)), closes [#1315](https://github.com/instana/nodejs/issues/1315)
## [4.15.3](https://github.com/instana/nodejs/compare/v4.15.2...v4.15.3) (2025-06-11)
**Note:** Version bump only for package @instana/serverless
## [4.15.2](https://github.com/instana/nodejs/compare/v4.15.1...v4.15.2) (2025-06-11)
**Note:** Version bump only for package @instana/serverless
## [4.15.1](https://github.com/instana/nodejs/compare/v4.15.0...v4.15.1) (2025-06-09)
**Note:** Version bump only for package @instana/serverless
# [4.15.0](https://github.com/instana/nodejs/compare/v4.14.0...v4.15.0) (2025-05-27)
**Note:** Version bump only for package @instana/serverless
# [4.14.0](https://github.com/instana/nodejs/compare/v4.13.0...v4.14.0) (2025-05-13)
**Note:** Version bump only for package @instana/serverless
# [4.13.0](https://github.com/instana/nodejs/compare/v4.12.0...v4.13.0) (2025-05-08)
**Note:** Version bump only for package @instana/serverless
# [4.12.0](https://github.com/instana/nodejs/compare/v4.11.1...v4.12.0) (2025-05-06)
### Bug Fixes
- **aws-lambda:** resolved TypeError when memory is too low ([#1715](https://github.com/instana/nodejs/issues/1715)) ([03a8eb3](https://github.com/instana/nodejs/commit/03a8eb39f386af44f98c5405d668369b3736bff0))
## [4.11.1](https://github.com/instana/nodejs/compare/v4.11.0...v4.11.1) (2025-04-24)
**Note:** Version bump only for package @instana/serverless
# [4.11.0](https://github.com/instana/nodejs/compare/v4.10.0...v4.11.0) (2025-04-22)
**Note:** Version bump only for package @instana/serverless
# [4.10.0](https://github.com/instana/nodejs/compare/v4.9.0...v4.10.0) (2025-04-01)
### Features
- added support for express v5 ([#1654](https://github.com/instana/nodejs/issues/1654)) ([8576291](https://github.com/instana/nodejs/commit/85762916de721833f6722fbb75f978aceafd83a8))
# [4.9.0](https://github.com/instana/nodejs/compare/v4.8.0...v4.9.0) (2025-03-20)
**Note:** Version bump only for package @instana/serverless
# [4.8.0](https://github.com/instana/nodejs/compare/v4.7.0...v4.8.0) (2025-03-19)
### Features
- **serverless:** added request id to instana debug logs ([#1623](https://github.com/instana/nodejs/issues/1623)) ([c5a1b69](https://github.com/instana/nodejs/commit/c5a1b691ed9f01363bbc76bc262f985a4901744e))
# [4.7.0](https://github.com/instana/nodejs/compare/v4.6.3...v4.7.0) (2025-03-11)
**Note:** Version bump only for package @instana/serverless
## [4.6.3](https://github.com/instana/nodejs/compare/v4.6.2...v4.6.3) (2025-03-05)
**Note:** Version bump only for package @instana/serverless
## [4.6.2](https://github.com/instana/nodejs/compare/v4.6.1...v4.6.2) (2025-02-24)
**Note:** Version bump only for package @instana/serverless
## [4.6.1](https://github.com/instana/nodejs/compare/v4.6.0...v4.6.1) (2025-01-29)
**Note:** Version bump only for package @instana/serverless
# [4.6.0](https://github.com/instana/nodejs/compare/v4.5.3...v4.6.0) (2025-01-18)
**Note:** Version bump only for package @instana/serverless
## [4.5.3](https://github.com/instana/nodejs/compare/v4.5.2...v4.5.3) (2025-01-14)
### Bug Fixes
- resolved more logging objects structure ([#1510](https://github.com/instana/nodejs/issues/1510)) ([bd4c9bb](https://github.com/instana/nodejs/commit/bd4c9bbda2c82aee7f6c59fcca03ac5588566839))
## [4.5.2](https://github.com/instana/nodejs/compare/v4.5.1...v4.5.2) (2025-01-13)
### Bug Fixes
- resolved logging objects being undefined or missing ([#1509](https://github.com/instana/nodejs/issues/1509)) ([7715fed](https://github.com/instana/nodejs/commit/7715fed5843716a6e49d79f221efcec33a9a1c9d))
## [4.5.1](https://github.com/instana/nodejs/compare/v4.5.0...v4.5.1) (2025-01-13)
**Note:** Version bump only for package @instana/serverless
# [4.5.0](https://github.com/instana/nodejs/compare/v4.4.0...v4.5.0) (2024-12-16)
**Note:** Version bump only for package @instana/serverless
# [4.4.0](https://github.com/instana/nodejs/compare/v4.3.0...v4.4.0) (2024-12-12)
**Note:** Version bump only for package @instana/serverless
# [4.3.0](https://github.com/instana/nodejs/compare/v4.2.0...v4.3.0) (2024-12-10)
### Features
- **serverless-collector:** added service name detection ([#1468](https://github.com/instana/nodejs/issues/1468)) ([5491461](https://github.com/instana/nodejs/commit/54914613bf40c6e81c652801899f2d3136308073))
# [4.2.0](https://github.com/instana/nodejs/compare/v4.1.0...v4.2.0) (2024-11-22)
**Note:** Version bump only for package @instana/serverless
# [4.1.0](https://github.com/instana/nodejs/compare/v4.0.1...v4.1.0) (2024-11-19)
**Note:** Version bump only for package @instana/serverless
## [4.0.1](https://github.com/instana/nodejs/compare/v4.0.0...v4.0.1) (2024-10-28)
**Note:** Version bump only for package @instana/serverless
# [4.0.0](https://github.com/instana/nodejs/compare/v3.21.0...v4.0.0) (2024-10-23)

@@ -26,0 +410,0 @@

+15
-10
{
"name": "@instana/serverless",
"version": "5.0.0",
"version": "5.0.1",
"description": "Internal utility package for serverless Node.js tracing and monitoring with Instana",

@@ -15,2 +15,5 @@ "author": {

],
"engines": {
"node": ">=18.19.0"
},
"publishConfig": {

@@ -32,3 +35,3 @@ "access": "public"

"test:ci": "mocha --config=test/.mocharc.js --reporter mocha-multi-reporters --reporter-options configFile=reporter-config.json 'test/**/*test.js'",
"test:debug": "WITH_STDOUT=true npm run test",
"test:debug": "WITH_STDOUT=true npm run test --",
"lint": "eslint src test",

@@ -53,6 +56,2 @@ "verify": "npm run lint && npm test",

{
"name": "Willian Carvalho",
"email": "willian.carvalho@instana.com"
},
{
"name": "Katharina Irrgang",

@@ -64,2 +63,6 @@ "email": "katharina.irrgang@ibm.com"

"email": "arya.mohanan@ibm.com"
},
{
"name": "Abhilash Sivan",
"email": "abhilash.sivan@ibm.com"
}

@@ -72,7 +75,9 @@ ],

"dependencies": {
"@instana/core": "5.0.0",
"agent-base": "^6.0.2",
"https-proxy-agent": "^7.0.2"
"@instana/core": "5.0.1",
"https-proxy-agent": "^7.0.6"
},
"gitHead": "209e1ac4c64c5224c99bffcc82a172c3d22e4438"
"overrides": {
"debug": "^4.4.3"
},
"gitHead": "77fa60d1e6d88a852540f0e88ff3a8b2e36826ac"
}

@@ -11,6 +11,3 @@ /*

const uninstrumented = require('./uninstrumentedHttp');
const constants = require('./constants');
let logger = require('./console_logger');
const layerExtensionHostname = 'localhost';

@@ -20,67 +17,104 @@ const layerExtensionPort = process.env.INSTANA_LAYER_EXTENSION_PORT

: 7365;
let useLambdaExtension = false;
const timeoutEnvVar = 'INSTANA_TIMEOUT';
let defaultTimeout = 500;
const layerExtensionTimeout = process.env.INSTANA_LAMBDA_EXTENSION_TIMEOUT_IN_MS
// NOTE: The heartbeat is usually really, really fast (<30ms).
// But we have seen in some cases that it can take up to 500ms.
const layerExtensionHeartbeatTimeout = 500;
// NOTE: The initial heartbeat can be very slow when the Lambda is in cold start.
const initialLayerExtensionHeartbeatTimeout = 3000;
// NOTE: When lambda is in cold start, the communication between the handler
// and the extension can take a while. We need to have a bigger timeout
// for the initially.
const initialLayerExtensionRequestTimeout = 3000;
const layerExtensionRequestTimeout = process.env.INSTANA_LAMBDA_EXTENSION_TIMEOUT_IN_MS
? Number(process.env.INSTANA_LAMBDA_EXTENSION_TIMEOUT_IN_MS)
: 500;
let backendTimeout = defaultTimeout;
const proxyEnvVar = 'INSTANA_ENDPOINT_PROXY';
const disableCaCheckEnvVar = 'INSTANA_DISABLE_CA_CHECK';
const disableCaCheck = process.env[disableCaCheckEnvVar] === 'true';
let proxyAgent;
let stopSendingOnFailure = true;
let propagateErrorsUpstream = false;
let requestHasFailed = false;
let warningsHaveBeenLogged = false;
let firstRequestToExtension = true;
const disableCaCheckEnvVar = 'INSTANA_DISABLE_CA_CHECK';
const disableCaCheck = process.env[disableCaCheckEnvVar] === 'true';
const defaults = {
config: {},
identityProvider: null,
isLambdaRequest: false,
backendTimeout: 500,
useLambdaExtension: false,
retries: false
};
if (process.env[proxyEnvVar] && !environmentUtil.sendUnencrypted) {
const proxyUrl = process.env[proxyEnvVar];
logger.info(
`The environment variable ${proxyEnvVar} is set. Requests to the Instana back end will be routed via a proxy ` +
`server: ${proxyUrl}.`
);
let logger;
let options;
let hostHeader;
const { HttpsProxyAgent } = require('https-proxy-agent');
proxyAgent = new HttpsProxyAgent(proxyUrl);
} else if (process.env[proxyEnvVar] && environmentUtil.sendUnencrypted) {
logger.warn(
`Both ${proxyEnvVar} and ${environmentUtil.sendUnencryptedEnvVar} are set, but this combination is not supported.` +
' Requests to the Instana back end will not be routed via a proxy server.'
);
}
/**
* Most of the logs are debug logs in the backend connector, because
* on serverless we do not want to log too much.
* If the debug mode is enabled, we want to add a request id to the instana
* logs because the AWS runtime freezes requests and they are waking up
* as soon as the next request is coming in. With the request id we can
* identify the logs of a single request.
*
* Due to performance reasons, we do not want to generate a request id
* in the non debug mode.
*/
const getRequestId = () => {
if (logger && logger.isInDebugMode && logger.isInDebugMode()) {
// Although the usage of "Math.random()"" is not allowed for being FedRamp compliant, but
// this use case is a non secure workflow.
return `instana_${Date.now().toString(36) + Math.random().toString(36).slice(2)}`;
}
let hostHeader;
return 'instana';
};
exports.init = function init(
identityProvider,
_logger,
_stopSendingOnFailure,
_propagateErrorsUpstream,
_defaultTimeout,
_useLambdaExtension
) {
stopSendingOnFailure = _stopSendingOnFailure == null ? true : _stopSendingOnFailure;
propagateErrorsUpstream = _propagateErrorsUpstream == null ? false : _propagateErrorsUpstream;
defaultTimeout = _defaultTimeout == null ? defaultTimeout : _defaultTimeout;
useLambdaExtension = _useLambdaExtension;
backendTimeout = defaultTimeout;
const requests = {};
exports.init = function init(opts) {
options = Object.assign(defaults, opts);
logger = options.config.logger;
// TODO: refactor environment.js into serverless normalize config
// and move the following code into the new unit
if (process.env[proxyEnvVar] && !environmentUtil.sendUnencrypted) {
const proxyUrl = process.env[proxyEnvVar];
logger.info(
`The environment variable ${proxyEnvVar} is set. Requests to the Instana back end will be routed via a proxy ` +
`server: ${proxyUrl}.`
);
const { HttpsProxyAgent } = require('https-proxy-agent');
proxyAgent = new HttpsProxyAgent(proxyUrl);
} else if (process.env[proxyEnvVar] && environmentUtil.sendUnencrypted) {
logger.warn(
`Both ${proxyEnvVar} and ${environmentUtil.sendUnencryptedEnvVar} are set, ` +
'but this combination is not supported.' +
' Requests to the Instana back end will not be routed via a proxy server.'
);
}
// TODO: refactor environment.js into serverless normalize config
// and move the following code into the new unit
if (process.env[timeoutEnvVar]) {
backendTimeout = parseInt(process.env[timeoutEnvVar], 10);
if (isNaN(backendTimeout) || backendTimeout < 0) {
options.backendTimeout = parseInt(process.env[timeoutEnvVar], 10);
if (isNaN(options.backendTimeout) || options.backendTimeout < 0) {
logger.warn(
`The value of ${timeoutEnvVar} (${process.env[timeoutEnvVar]}) cannot be parsed to a valid numerical value. ` +
`Will fall back to the default timeout (${defaultTimeout} ms).`
`Will fall back to the default timeout (${defaults.backendTimeout} ms).`
);
backendTimeout = defaultTimeout;
options.backendTimeout = defaults.backendTimeout;
}
}
if (identityProvider) {
hostHeader = identityProvider.getHostHeader();
if (options.identityProvider) {
hostHeader = options.identityProvider.getHostHeader();
if (hostHeader == null) {

@@ -93,8 +127,2 @@ hostHeader = 'nodejs-serverless';

if (_logger) {
logger = _logger;
}
requestHasFailed = false;
// Heartbeat is only for the AWS Lambda extension

@@ -104,3 +132,3 @@ // IMPORTANT: the @instana/aws-lambda package will not

// SpanBuffer sends data asap and when the handler is finished the rest is sent.
if (useLambdaExtension) {
if (options.useLambdaExtension) {
scheduleLambdaExtensionHeartbeatRequest();

@@ -110,36 +138,40 @@ }

exports.setLogger = function setLogger(_logger) {
logger = _logger;
};
/**
*
* "finalLambdaRequest":
* When using AWS Lambda, we send metrics and spans together
* using the function "sendBundle". The variable was invented to indicate
* that this is the last request to be sent before the AWS Lambda runtime might freeze the process.
* Currently, there is exactly one request to send all the data and
* the variable is always true.
* using the function "sendBundle" at the end of the invocation - before the AWS Lambda
* runtime might freeze the process. The span buffer sends data reguarly using `sendSpans`.
*/
exports.sendBundle = function sendBundle(bundle, finalLambdaRequest, callback) {
logger.debug(`Sending bundle to Instana (no. of spans: ${bundle?.spans?.length ?? 'unknown'})`);
send('/bundle', bundle, finalLambdaRequest, callback);
const requestId = getRequestId();
logger.debug(`[${requestId}] Sending bundle to Instana (no. of spans: ${bundle?.spans?.length ?? 'unknown'})`);
send({ resourcePath: '/bundle', payload: bundle, finalLambdaRequest, callback, requestId });
};
exports.sendMetrics = function sendMetrics(metrics, callback) {
send('/metrics', metrics, false, callback);
const requestId = getRequestId();
logger.debug(`[${requestId}] Sending metrics to Instana (no. of metrics: ${metrics?.plugins?.length})`);
send({ resourcePath: '/metrics', payload: metrics, finalLambdaRequest: false, callback, requestId });
};
exports.sendSpans = function sendSpans(spans, callback) {
logger.debug(`Sending spans to Instana (no. of spans: ${spans.length})`);
send('/traces', spans, false, callback);
const requestId = getRequestId();
logger.debug(`[${requestId}] Sending spans to Instana (no. of spans: ${spans?.length})`);
send({ resourcePath: '/traces', payload: spans, finalLambdaRequest: false, callback, requestId });
};
let heartbeatInterval;
let heartbeatIsActive = false;
function scheduleLambdaExtensionHeartbeatRequest() {
const executeHeartbeat = () => {
logger.debug('Executing Heartbeat request to Lambda extension.');
const executeHeartbeat = (heartbeatOpts = {}) => {
if (heartbeatIsActive) {
return;
}
const startTime = Date.now();
const requestId = getRequestId();
logger.debug(`[${requestId}] Executing Heartbeat request to Lambda extension.`);
heartbeatIsActive = true;
const req = uninstrumented.http.request(

@@ -151,16 +183,17 @@ {

method: 'POST',
Connection: 'close',
// This sets a timeout for establishing the socket connection, see setTimeout below for a timeout for an
// idle connection after the socket has been opened.
timeout: layerExtensionTimeout
headers: {
Connection: 'keep-alive'
}
},
res => {
logger.debug(`[${requestId}] Took ${Date.now() - startTime} ms to send heartbeat to the extension.`);
if (res.statusCode === 200) {
logger.debug('The Instana Lambda extension Heartbeat request has succeeded.');
logger.debug(`[${requestId}] The Instana Lambda extension heartbeat request has succeeded.`);
} else {
handleHeartbeatError(
new Error(
`The Instana Lambda extension Heartbeat request has returned an unexpected status code: ${res.statusCode}`
)
logger.debug(
`[${requestId}] The Instana Lambda extension heartbeat request has failed. Status Code: ${res.statusCode}`
);
handleHeartbeatError();
}

@@ -170,2 +203,3 @@

// we need to register the handlers to avoid running into a timeout
// because the request expects to receive body data
});

@@ -176,3 +210,5 @@

const duration = endTime - startTime;
logger.debug(`Took ${duration}ms to receive response from extension`);
logger.debug(`[${requestId}] Took ${duration}ms to receive response from extension for the heartbeat.`);
heartbeatIsActive = false;
});

@@ -182,21 +218,17 @@ }

req.once('finish', () => {
const endTime = Date.now();
const duration = endTime - startTime;
logger.debug(`Took ${duration}ms to send data to extension`);
req.once('error', e => {
logger.debug(`[${requestId}] The Heartbeat request did not succeed.`, e);
// req.destroyed indicates that we have run into a timeout and have
// already handled the timeout error.
if (req.destroyed) {
return;
}
handleHeartbeatError();
});
function handleHeartbeatError(e) {
// Make sure we do not try to talk to the Lambda extension again.
useLambdaExtension = false;
clearInterval(heartbeatInterval);
req.setTimeout(heartbeatOpts.heartbeatTimeout, () => {
logger.debug(`[${requestId}] Heartbeat request timed out.`);
logger.debug(
'The Instana Lambda extension Heartbeat request did not succeed. Falling back to talking to the Instana back ' +
'end directly.',
e
);
}
req.once('error', e => {
// req.destroyed indicates that we have run into a timeout and have already handled the timeout error.

@@ -207,18 +239,13 @@ if (req.destroyed) {

handleHeartbeatError(e);
handleHeartbeatError();
});
// Handle timeouts that occur after connecting to the socket (no response from the extension).
req.setTimeout(layerExtensionTimeout, () => {
handleHeartbeatError(new Error('The Lambda extension Heartbeat request timed out.'));
function handleHeartbeatError() {
logger.debug(`[${requestId}] Heartbeat request failed. Falling back to the serverless acceptor instead.`);
// Destroy timed out request manually as mandated in https://nodejs.org/api/http.html#event-timeout.
if (req && !req.destroyed) {
try {
destroyRequest(req);
} catch (e) {
// ignore
}
}
});
options.useLambdaExtension = false;
clearInterval(heartbeatInterval);
cleanupRequest(req);
heartbeatIsActive = false;
}

@@ -229,11 +256,15 @@ req.end();

// call immediately
executeHeartbeat();
// timeout is bigger because of possible coldstart
executeHeartbeat({ heartbeatTimeout: initialLayerExtensionHeartbeatTimeout });
// NOTE: it is fine to use interval, because the req timeout is 300ms and the interval is 500
heartbeatInterval = setInterval(executeHeartbeat, 500);
heartbeatInterval = setInterval(() => {
logger.debug('Heartbeat interval is alive.');
executeHeartbeat({ heartbeatTimeout: layerExtensionHeartbeatTimeout });
}, 300);
heartbeatInterval.unref();
}
function getTransport(localUseLambdaExtension) {
if (localUseLambdaExtension) {
function getTransport() {
if (options.useLambdaExtension) {
// The Lambda extension is always HTTP without TLS on localhost.

@@ -246,7 +277,16 @@ return uninstrumented.http;

function getBackendTimeout(localUseLambdaExtension) {
return localUseLambdaExtension ? layerExtensionTimeout : backendTimeout;
function getBackendTimeout() {
if (options.useLambdaExtension) {
if (firstRequestToExtension) {
firstRequestToExtension = false;
return initialLayerExtensionRequestTimeout;
} else {
return layerExtensionRequestTimeout;
}
}
return options.backendTimeout;
}
function send(resourcePath, payload, finalLambdaRequest, callback) {
function send({ resourcePath, payload, finalLambdaRequest, callback, tries, requestId }) {
let callbackWasCalled = false;

@@ -259,14 +299,4 @@ const handleCallback = args => {

// We need a local copy of the global useLambdaExtension variable, otherwise it might be changed concurrently by
// scheduleLambdaExtensionHeartbeatRequest. But we need to remember the value at the time we _started_ the request to
// decide whether to fall back to sending to the back end directly or give up sending data completely.
let localUseLambdaExtension = useLambdaExtension;
if (requestHasFailed && stopSendingOnFailure) {
logger.info(
`Not attempting to send data to ${resourcePath} as a previous request has already timed out or failed.`
);
handleCallback();
return;
if (tries === undefined) {
tries = 0;
}

@@ -277,4 +307,5 @@

if (environmentUtil.sendUnencrypted) {
logger.error(
`${environmentUtil.sendUnencryptedEnvVar} is set, which means that all traffic to Instana is send ` +
logger.warn(
`[${requestId}] ${environmentUtil.sendUnencryptedEnvVar} is set, which means that all traffic ` +
'to Instana is send ' +
'unencrypted via plain HTTP, not via HTTPS. This will effectively make that traffic public. This setting ' +

@@ -284,5 +315,7 @@ 'should never be used in production.'

}
if (disableCaCheck) {
logger.warn(
`${disableCaCheckEnvVar} is set, which means that the server certificate will not be verified against ` +
`[${requestId}] ${disableCaCheckEnvVar} is set, which means that the server certificate will ` +
'not be verified against ' +
'the list of known CAs. This makes your service vulnerable to MITM attacks when connecting to Instana. ' +

@@ -295,16 +328,23 @@ 'This setting should never be used in production, unless you use our on-premises product and are unable to ' +

const instanaAgentKey = environmentUtil.getInstanaAgentKey();
// CASE: aws lambda ssm key cannot be fetched, but either detached requests are sent
// after the handler finished or coldstart delayed the handler execution
if (!instanaAgentKey) {
logger.warn(`[${requestId}] No Instana agent key configured. Cannot send data to Instana.`);
return handleCallback();
}
// prepend backend's path if the configured URL has a path component
const requestPath =
localUseLambdaExtension || environmentUtil.getBackendPath() === '/'
options.useLambdaExtension || environmentUtil.getBackendPath() === '/'
? resourcePath
: environmentUtil.getBackendPath() + resourcePath;
logger.debug(`Sending data to Instana (${requestPath}).`);
// serialize the payload object
const serializedPayload = JSON.stringify(payload);
const options = {
hostname: localUseLambdaExtension ? layerExtensionHostname : environmentUtil.getBackendHost(),
port: localUseLambdaExtension ? layerExtensionPort : environmentUtil.getBackendPort(),
const reqOptions = {
hostname: options.useLambdaExtension ? layerExtensionHostname : environmentUtil.getBackendHost(),
port: options.useLambdaExtension ? layerExtensionPort : environmentUtil.getBackendPort(),
path: requestPath,

@@ -315,4 +355,5 @@ method: 'POST',

'Content-Length': Buffer.byteLength(serializedPayload),
Connection: 'keep-alive',
[constants.xInstanaHost]: hostHeader,
[constants.xInstanaKey]: environmentUtil.getInstanaAgentKey()
[constants.xInstanaKey]: instanaAgentKey
},

@@ -322,11 +363,19 @@ rejectUnauthorized: !disableCaCheck

options.timeout = getBackendTimeout(localUseLambdaExtension);
logger.debug(
`[${requestId}] Sending data to Instana (${reqOptions.hostname}, ${reqOptions.port}, ${reqOptions.path},
${reqOptions.headers?.['Content-Length']}).`
);
if (proxyAgent && !localUseLambdaExtension) {
options.agent = proxyAgent;
// This timeout is for **inactivity** - Backend sends no data at all
// So if the timeout is set to 500ms, it does not mean that the request will be aborted after 500ms
reqOptions.timeout = getBackendTimeout(options.useLambdaExtension);
if (proxyAgent && !options.useLambdaExtension) {
reqOptions.agent = proxyAgent;
}
let req;
const skipWaitingForHttpResponse = !proxyAgent && !localUseLambdaExtension;
const transport = getTransport(localUseLambdaExtension);
const skipWaitingForHttpResponse = !proxyAgent && !options.useLambdaExtension;
const transport = getTransport(options.useLambdaExtension);
const start = Date.now();

@@ -342,3 +391,3 @@ if (skipWaitingForHttpResponse) {

req = transport.request(options);
req = transport.request(reqOptions);
} else {

@@ -353,3 +402,3 @@ // If (a) our Lambda extension is available, or if (b) a user-provided proxy is in use, we do *not* apply the

// to end the processing. Otherwise, the callback is provided here to http.request().
req = transport.request(options, () => {
req = transport.request(reqOptions, () => {
// When the Node.js process is frozen while the request is pending, and then thawed later,

@@ -362,9 +411,3 @@ // this can trigger a stale, bogus timeout event (because from the perspective of the freshly thawed Node.js

if (finalLambdaRequest) {
req.removeAllListeners();
req.on('error', () => {});
// Finally, abort the request because from our end we are no longer interested in the response and we also do
// not want to let pending IO actions linger in the event loop. This will also call request.destoy and
// req.socket.destroy() internally.
destroyRequest(req);
cleanupRequests();
}

@@ -376,2 +419,22 @@

if (options.isLambdaRequest) {
requests[requestId] = req;
}
req.on('response', res => {
const { statusCode } = res;
if (statusCode >= 200 && statusCode < 300) {
logger.debug(`${requestId} Received response from Instana (${requestPath}).`);
} else {
logger.debug(`${requestId} Received response from Instana has been failed (${requestPath}).`);
}
logger.debug(`[${requestId}] Received HTTP status code ${statusCode} from Instana (${requestPath}).`);
logger.debug(`[${requestId}] Sending and receiving data to Instana took: ${Date.now() - start} ms.`);
cleanupRequest(req);
delete requests[requestId];
});
// See above for the difference between the timeout attribute in the request options and handling the 'timeout'

@@ -383,7 +446,19 @@ // event. This only adds a read timeout after the connection has been established and we need the timout attribute

// > socket.setTimeout() will be called.
req.on('timeout', () =>
onTimeout(localUseLambdaExtension, req, resourcePath, payload, finalLambdaRequest, handleCallback)
);
req.on('timeout', () => {
logger.debug(`[${requestId}] Timeout while sending data to Instana (${requestPath}).`);
if (options.isLambdaRequest) {
delete requests[requestId];
}
onTimeout(req, resourcePath, payload, finalLambdaRequest, handleCallback, tries, requestId);
});
req.on('error', e => {
logger.debug(`[${requestId}] Error while sending data to Instana (${requestPath}): ${e?.message} ${e?.stack}`);
if (options.isLambdaRequest) {
delete requests[requestId];
}
// CASE: we manually destroy streams, skip these errors

@@ -399,3 +474,3 @@ // Otherwise we will produce `Error: socket hang up` errors in the logs

if (localUseLambdaExtension) {
if (options.useLambdaExtension) {
// This is a failure from talking to the Lambda extension on localhost. Most probably it is simply not available

@@ -406,39 +481,51 @@ // because @instana/aws-lambda has been installed as a normal npm dependency instead of using Instana's

// target in place.
logger.debug(
'Could not connect to the Instana Lambda extension. Falling back to talking to the Instana back end directly.',
e
);
logger.debug(`[${requestId}] Could not connect to the Instana Lambda extension (tries: ${tries}).`);
// Make sure we do not try to talk to the Lambda extension again.
useLambdaExtension = localUseLambdaExtension = false;
clearInterval(heartbeatInterval);
if (options.retries === false || tries >= 1) {
clearInterval(heartbeatInterval);
// Retry the request immediately, this time sending it to serverless-acceptor directly.
send(resourcePath, payload, finalLambdaRequest, callback);
// Retry the request immediately, this time sending it to serverless-acceptor directly.
logger.warn(
// eslint-disable-next-line max-len
`[${requestId}] Trying to send data to Instana serverless acceptor instead because the Lambda extension cannot be reached in time.`
);
options.useLambdaExtension = false;
return send({ resourcePath, payload, finalLambdaRequest, callback, tries: 0, requestId });
}
logger.debug(`[${requestId}] Retrying...`);
send({ resourcePath, payload, finalLambdaRequest, callback, tries: tries + 1, requestId });
} else {
// We are not using the Lambda extension, because we are either not in an AWS Lambda, or a previous request to the
// extension has already failed. Thus, this is a failure from talking directly to serverless-acceptor
// (or a user-provided proxy).
requestHasFailed = true;
if (proxyAgent) {
logger.warn(
`[${requestId}] Could not send trace data to ${resourcePath}. Could not connect to the configured proxy ` +
`${process.env[proxyEnvVar]}.` +
`${e?.message} ${e?.stack}`
);
} else {
logger.warn(
`[${requestId}] Could not send trace data to ${resourcePath}. ` +
`The Instana back end seems to be unavailable. ${e?.message} , ${e?.stack}`
);
}
if (!propagateErrorsUpstream) {
if (proxyAgent) {
logger.warn(
'Could not send traces and metrics to Instana. Could not connect to the configured proxy ' +
`${process.env[proxyEnvVar]}.`,
e
);
} else {
logger.warn('Could not send traces and metrics to Instana. The Instana back end seems to be unavailable.', e);
}
if (options.retries === false || tries >= 1) {
logger.debug(`[${requestId}] Giving up...`);
return handleCallback(e);
}
handleCallback(propagateErrorsUpstream ? e : undefined);
logger.debug(`[${requestId}] Retrying...`);
send({ resourcePath, payload, finalLambdaRequest, callback, tries: tries + 1, requestId });
}
});
// This only indicates that the request has been successfully send! Independent of the response!
req.on('finish', () => {
logger.debug(`Sent data to Instana (${requestPath}).`);
logger.debug(
// eslint-disable-next-line max-len
`[${requestId}] The data have been successfully sent to Instana.`
);
if (useLambdaExtension && finalLambdaRequest) {
if (options.useLambdaExtension && finalLambdaRequest) {
clearInterval(heartbeatInterval);

@@ -449,20 +536,7 @@ }

if (skipWaitingForHttpResponse) {
// NOTE: When the callback of `.end` is called, the data was successfully send to the server.
// That does not mean the server has responded in any way!
req.end(serializedPayload, () => {
if (finalLambdaRequest) {
// When the Node.js process is frozen while the request is pending, and then thawed later,
// this can trigger a stale, bogus timeout event (because from the perspective of the freshly thawed Node.js
// runtime, the request has been pending and inactive since a long time). To avoid that, we remove all listeners
// (including the timeout listener) on the request. Since the Lambda runtime will be frozen afterwards (or
// reused for a different, unrelated invocation), it is safe to assume that we are no longer interested in any
// events emitted by the request or the underlying socket.
req.removeAllListeners();
// We need to have a listener for errors that ignores everything, otherwise aborting the request/socket will
// produce an "Unhandled 'error' event"
req.on('error', () => {});
// Finally, abort the request because from our end we are no longer interested in the response and we also do
// not want to let pending IO actions linger in the event loop. This will also call request.destoy and
// req.socket.destroy() internally.
destroyRequest(req);
if (options.isLambdaRequest && finalLambdaRequest) {
cleanupRequests();
}

@@ -480,35 +554,32 @@

function onTimeout(localUseLambdaExtension, req, resourcePath, payload, finalLambdaRequest, handleCallback) {
if (localUseLambdaExtension) {
function onTimeout(req, resourcePath, payload, finalLambdaRequest, handleCallback, tries, requestId) {
if (options.useLambdaExtension) {
// This is a timeout from talking to the Lambda extension on localhost. Most probably it is simply not available
// because @instana/aws-lambda has been installed as a normal npm dependency instead of using Instana's
// Lambda layer. We use this failure as a signal to not try to the extension again and instead fall back to
// talking to serverless-acceptor directly. We also immediately retry the current request with that new downstream
// talking to serverless acceptor directly. We also immediately retry the current request with that new downstream
// target in place.
logger.debug(
'Request timed out while trying to talk to Instana Lambda extension. Falling back to talking to the Instana ' +
'back end directly.'
);
logger.debug(`[${requestId}] Request timed out while trying to talk to Instana Lambda extension.`);
// Make sure we do not try to talk to the Lambda extension again.
useLambdaExtension = localUseLambdaExtension = false;
clearInterval(heartbeatInterval);
cleanupRequest(req);
if (req && !req.destroyed) {
try {
destroyRequest(req);
} catch (e) {
// ignore
}
// CASE: It could be that a parallel request or the heartbeat already set useLambdaExtension to false.
if (options.retries === false || tries >= 1) {
clearInterval(heartbeatInterval);
// Retry the request immediately, this time sending it to serverless acceptor directly.
logger.warn(
`[${requestId}] Trying to send data to Instana serverless acceptor instead because the Lambda extension ` +
'cannot be reached in time.'
);
options.useLambdaExtension = false;
return send({ resourcePath, payload, finalLambdaRequest, callback: handleCallback, tries: 0, requestId });
}
// Retry the request immediately, this time sending it to serverless-acceptor directly.
send(resourcePath, payload, finalLambdaRequest, handleCallback);
logger.debug(`[${requestId}] Retrying...`);
send({ resourcePath, payload, finalLambdaRequest, callback: handleCallback, tries: tries + 1, requestId });
} else {
// We are not using the Lambda extension, because we are either not in an AWS Lambda, or a previous request to the
// extension has already failed. Thus, this is a timeout from talking directly to serverless-acceptor
// (or a user-provided proxy).
requestHasFailed = true;
// We need to destroy the request manually, otherwise it keeps the runtime running (and timing out) when
// We need to destroy the request manually, otherwise it keeps the runtime running
// (and timing out) when:
// (a) the wrapped Lambda handler uses the callback API, and

@@ -518,24 +589,60 @@ // (b) context.callbackWaitsForEmptyEventLoop = false is not set.

// https://nodejs.org/api/http.html#http_event_timeout.
if (req && !req.destroyed) {
try {
destroyRequest(req);
} catch (e) {
// ignore
}
}
cleanupRequest(req);
const message =
'Could not send traces and metrics to Instana. The Instana back end did not respond in the configured timeout ' +
`of ${backendTimeout} ms. The timeout can be configured by setting the environment variable ${timeoutEnvVar}.`;
`[${requestId}] Could not send data to ${resourcePath}. The Instana back end did not respond ` +
'in the configured timeout ' +
`of ${options.backendTimeout} ms. The timeout can be configured by ` +
`setting the environment variable ${timeoutEnvVar}.`;
if (!propagateErrorsUpstream) {
logger.warn(message);
logger.warn(`[${requestId}] ${message}`);
if (options.retries === false || tries >= 1) {
logger.debug(`[${requestId}] Giving up...`);
return handleCallback();
}
handleCallback(propagateErrorsUpstream ? new Error(message) : undefined);
logger.debug(`[${requestId}] Retrying...`);
send({ resourcePath, payload, finalLambdaRequest, callback: handleCallback, tries: tries + 1, requestId });
}
}
function cleanupRequests() {
Object.keys(requests).forEach(key => {
const requestToCleanup = requests[key];
cleanupRequest(requestToCleanup);
});
}
function cleanupRequest(req) {
// When the Node.js process is frozen while the request is pending, and then thawed later,
// this can trigger a stale, bogus timeout event (because from the perspective of the freshly thawed Node.js
// runtime, the request has been pending and inactive since a long time).
// To avoid that, we remove all listeners
// (including the timeout listener) on the request. Since the Lambda runtime will be frozen afterwards (or
// reused for a different, unrelated invocation), it is safe to assume that
// we are no longer interested in any
// events emitted by the request or the underlying socket.
req.removeAllListeners();
// We need to have a listener for errors that ignores everything, otherwise aborting the request/socket will
// produce an "Unhandled 'error' event"
req.once('error', () => {});
// Finally, abort the request because from our end we are no longer interested in the response and we also do
// not want to let pending IO actions linger in the event loop. This will also call request.destroy and
// req.socket.destroy() internally.
destroyRequest(req);
}
function destroyRequest(req) {
req.destroy();
// Destroy timed out request manually as mandated in
// https://nodejs.org/api/http.html#event-timeout.
if (req && !req.destroyed) {
try {
req.destroy();
} catch (e) {
// ignore
}
}
}

@@ -8,4 +8,2 @@ /*

const logger = require('./console_logger');
const instanaEndpointUrlEnvVar = 'INSTANA_ENDPOINT_URL';

@@ -15,2 +13,3 @@ const instanaAgentKeyEnvVar = 'INSTANA_AGENT_KEY';

let logger;
let valid = false;

@@ -30,2 +29,6 @@ let backendHost = null;

exports.init = config => {
logger = config.logger;
};
exports.validate = function validate({ validateInstanaAgentKey } = {}) {

@@ -89,3 +92,3 @@ _validate(process.env[instanaEndpointUrlEnvVar], process.env[instanaAgentKeyEnvVar], validateInstanaAgentKey);

backendPath = parsedUrl.pathname;
backendPath = sanitizePath(parsedUrl.pathname);

@@ -184,1 +187,11 @@ instanaAgentKey = _instanaAgentKey;

}
// Removes trailing slashes from the path (except when it's just '/')
// Prevents double slashes when building backend URLs.
// Example: "https://example.instana.io/serverless/" + "/bundle" → "https://example.instana.io/serverless/bundle"
function sanitizePath(pathName) {
if (!pathName || pathName === '/') {
return pathName;
}
return pathName.replace(/\/+$/, '');
}

@@ -9,5 +9,6 @@ /*

exports.backendConnector = require('./backend_connector');
exports.consoleLogger = require('./console_logger');
// TODO: rename in major release to simply "exports.logger"
exports.consoleLogger = require('./logger');
exports.constants = require('./constants');
exports.environment = require('./environment');
exports.headers = require('./headers');
/*
* (c) Copyright IBM Corp. 2021
* (c) Copyright Instana Inc. and contributors 2019
*/
/* eslint-disable no-console */
'use strict';
let minLevel = 30;
module.exports = exports = {
debug: createLogFn(20, console.debug || console.log),
info: createLogFn(30, console.log),
warn: createLogFn(40, console.warn),
error: createLogFn(50, console.error)
};
function createLogFn(level, fn) {
return function log() {
if (level >= minLevel) {
fn.apply(console, arguments);
}
};
}
exports.setLevel = function setLevel(level) {
// eslint-disable-next-line yoda
if (typeof level === 'number' && 0 < level && level <= 50) {
minLevel = level;
return;
}
if (typeof level === 'string') {
switch (level) {
case 'debug':
minLevel = 20;
break;
case 'info':
minLevel = 30;
break;
case 'warn':
minLevel = 40;
break;
case 'error':
minLevel = 50;
break;
default:
exports.warn(`Unknown log level: ${level}`);
}
}
};