diff --git a/package-lock.json b/package-lock.json index 285f732c..608bc942 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2592,14 +2592,6 @@ "shimmer": "^1.2.1" } }, - "@types/ioredis4": { - "version": "npm:@types/ioredis@4.28.10", - "resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-4.28.10.tgz", - "integrity": "sha512-69LyhUgrXdgcNDv7ogs1qXZomnfOEnSmrmMFqKgt1XMJxmoOSG/u3wYy13yACIfKuMJ8IhKgHafDO3sx19zVQQ==", - "requires": { - "@types/node": "*" - } - }, "import-in-the-middle": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/import-in-the-middle/-/import-in-the-middle-1.4.2.tgz", @@ -3111,25 +3103,6 @@ "@opentelemetry/core": "^1.1.0" } }, - "@opentelemetry/winston-transport": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/winston-transport/-/winston-transport-0.2.0.tgz", - "integrity": "sha512-6+2a9xV/mQkYyRIHpn4e5sf/1v58Wd765ke0gp6n+N9YfCZ8rE8xqqvccIDE1AbSR2uUhXxZI6HsTTxqtMaXgw==", - "requires": { - "@opentelemetry/api-logs": "^0.50.0", - "winston-transport": "4.*" - }, - "dependencies": { - "@opentelemetry/api-logs": { - "version": "0.50.0", - "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.50.0.tgz", - "integrity": "sha512-JdZuKrhOYggqOpUljAq4WWNi5nB10PmgoF0y2CvedLGXd0kSawb/UBnWT8gg1ND3bHCNHStAIVT0ELlxJJRqrA==", - "requires": { - "@opentelemetry/api": "^1.0.0" - } - } - } - }, "@pnpm/config.env-replace": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@pnpm/config.env-replace/-/config.env-replace-1.1.0.tgz", @@ -5400,6 +5373,14 @@ "integrity": "sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==", "dev": true }, + "@types/ioredis4": { + "version": "npm:@types/ioredis@4.28.10", + "resolved": "https://registry.npmjs.org/@types/ioredis/-/ioredis-4.28.10.tgz", + "integrity": "sha512-69LyhUgrXdgcNDv7ogs1qXZomnfOEnSmrmMFqKgt1XMJxmoOSG/u3wYy13yACIfKuMJ8IhKgHafDO3sx19zVQQ==", + "requires": { + "@types/node": "*" + } + }, "@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", @@ -5605,11 +5586,6 @@ "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true }, - "@types/triple-beam": { - "version": "1.3.5", - "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", - "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" - }, "@types/yargs": { "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", @@ -5919,14 +5895,6 @@ "through": ">=2.2.7 <3" } }, - "abort-controller": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", - "integrity": "sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg==", - "requires": { - "event-target-shim": "^5.0.0" - } - }, "accepts": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", @@ -6488,7 +6456,8 @@ "base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", - "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==" + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true }, "bcrypt-pbkdf": { "version": "1.0.2", @@ -8223,11 +8192,6 @@ "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", "dev": true }, - "event-target-shim": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/event-target-shim/-/event-target-shim-5.0.1.tgz", - "integrity": "sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ==" - }, "events": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/events/-/events-1.1.1.tgz", @@ -8387,11 +8351,6 @@ "bser": "2.1.1" } }, - "fecha": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/fecha/-/fecha-4.2.3.tgz", - "integrity": "sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==" - }, "figures": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/figures/-/figures-3.2.0.tgz", @@ -10974,26 +10933,6 @@ "log-prefix": "0.1.1" } }, - "logform": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.6.1.tgz", - "integrity": "sha512-CdaO738xRapbKIMVn2m4F6KTj4j7ooJ8POVnebSgKo3KBz5axNXRAL7ZdRjIV6NOr2Uf4vjtRkxrFETOioCqSA==", - "requires": { - "@colors/colors": "1.6.0", - "@types/triple-beam": "^1.3.2", - "fecha": "^4.2.0", - "ms": "^2.1.1", - "safe-stable-stringify": "^2.3.1", - "triple-beam": "^1.3.0" - }, - "dependencies": { - "@colors/colors": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", - "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==" - } - } - }, "long": { "version": "5.2.3", "resolved": "https://registry.npmjs.org/long/-/long-5.2.3.tgz", @@ -15705,11 +15644,6 @@ } } }, - "process": { - "version": "0.11.10", - "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", - "integrity": "sha512-cdGef/drWFoydD1JsMzuFf8100nZl+GT+yacc2bEced5f9Rjk4z+WtFUTBu9PhOi9j/jfmBPu0mMEY4wIdAF8A==" - }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -16280,7 +16214,8 @@ "safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true }, "safe-regex-test": { "version": "1.0.3", @@ -16293,11 +16228,6 @@ "is-regex": "^1.1.4" } }, - "safe-stable-stringify": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/safe-stable-stringify/-/safe-stable-stringify-2.5.0.tgz", - "integrity": "sha512-b3rppTKm9T+PsVCBEOUR46GWI7fdOs00VKZ1+9c1EWDaDMvjQc6tUwuFyIprgGgTcWoVHSKrU8H31ZHA2e0RHA==" - }, "safer-buffer": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", @@ -17188,11 +17118,6 @@ "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==", "dev": true }, - "triple-beam": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", - "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==" - }, "ts-node": { "version": "9.1.1", "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-9.1.1.tgz", @@ -17751,57 +17676,6 @@ "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", "dev": true }, - "winston-transport": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.8.0.tgz", - "integrity": "sha512-qxSTKswC6llEMZKgCQdaWgDuMJQnhuvF5f2Nk3SNXc4byfQ+voo2mX1Px9dkNOuR8p0KAjfPG29PuYUSIb+vSA==", - "requires": { - "logform": "^2.6.1", - "readable-stream": "^4.5.2", - "triple-beam": "^1.3.0" - }, - "dependencies": { - "buffer": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", - "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", - "requires": { - "base64-js": "^1.3.1", - "ieee754": "^1.2.1" - } - }, - "events": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", - "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==" - }, - "ieee754": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", - "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==" - }, - "readable-stream": { - "version": "4.5.2", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-4.5.2.tgz", - "integrity": "sha512-yjavECdqeZ3GLXNgRXgeQEdz9fvDDkNKyHnbHRFtOr7/LcfgBcmct7t/ET+HaCTqfh06OzoAxrkN/IfjJBVe+g==", - "requires": { - "abort-controller": "^3.0.0", - "buffer": "^6.0.3", - "events": "^3.3.0", - "process": "^0.11.10", - "string_decoder": "^1.3.0" - } - }, - "string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "requires": { - "safe-buffer": "~5.2.0" - } - } - } - }, "word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", diff --git a/package.json b/package.json index 9e1affd9..79609481 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,6 @@ "@opentelemetry/sdk-trace-base": "1.9.1", "@opentelemetry/sdk-trace-node": "1.9.1", "@opentelemetry/semantic-conventions": "1.17.1", - "@opentelemetry/winston-transport": "0.2.0", "@prisma/instrumentation": "^4.14.0", "deasync": "^0.1.30", "opentelemetry-instrumentation-express": "0.39.1", @@ -118,7 +117,7 @@ "semantic-release": "^19.0.2", "semver": "^7.3.7", "testcontainers": "^8.16.0", - "tmp": "^0.2.1", + "tmp": "^0.2.3", "ts-node": "^9.1.1", "typescript": "^4.3.4", "utf-8-validate": "^5.0.2", diff --git a/src/bootstrap.ts b/src/bootstrap.ts index cc7f10e8..9ab18c0e 100644 --- a/src/bootstrap.ts +++ b/src/bootstrap.ts @@ -50,7 +50,8 @@ import { LumigoKubernetesDetector, LumigoTagDetector, } from './resources/detectors'; -import { getLogAttributeMaxLength, getSpanAttributeMaxLength, safeRequire } from './utils'; +import { getLogAttributeMaxLength, getSpanAttributeMaxLength } from './utils'; +import { safeRequire } from './requireUtils'; declare global { // eslint-disable-next-line @typescript-eslint/no-namespace diff --git a/src/instrumentations/instrumentor.ts b/src/instrumentations/instrumentor.ts index 67462479..f1a625f8 100644 --- a/src/instrumentations/instrumentor.ts +++ b/src/instrumentations/instrumentor.ts @@ -1,5 +1,5 @@ import type { Instrumentation } from '@opentelemetry/instrumentation'; -import { canRequireModule } from '../utils'; +import { canRequireModule } from '../requireUtils'; import { LOGGING_ENABLED, TRACING_ENABLED } from '../constants'; abstract class Instrumentor { diff --git a/src/requireUtils.test.ts b/src/requireUtils.test.ts new file mode 100644 index 00000000..7ef9bc5f --- /dev/null +++ b/src/requireUtils.test.ts @@ -0,0 +1,33 @@ +import path from 'path'; +import { safeRequire } from './requireUtils'; +import { version } from '../package.json'; + +describe('safeRequire', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + test('requires an existing module with the given path', () => { + const packageJsonPath = path.join(__dirname, '..', 'package.json'); + + const result = safeRequire(packageJsonPath); + + expect(result.version).toEqual(version); + }); + + test('does not fail but returns undefined for a non-existing module', () => { + const result = safeRequire('BlaBlaBlaBla'); + + expect(result).toBeUndefined(); + }); + + test('does not fail but returns undefined when an errors occurs when loading the module', () => { + jest.doMock('fs', () => { + throw Error('RandomError'); + }); + + const result = safeRequire('fs'); + + expect(result).toBeUndefined(); + }); +}); diff --git a/src/requireUtils.ts b/src/requireUtils.ts new file mode 100644 index 00000000..e8a10d19 --- /dev/null +++ b/src/requireUtils.ts @@ -0,0 +1,67 @@ +import path from 'path'; +import { logger } from './logging'; + +const getRequireFunction = (): NodeRequire => + // @ts-ignore __non_webpack_require__ not available at compile time + typeof __non_webpack_require__ !== 'undefined' ? __non_webpack_require__ : require; + +export const safeRequire = (moduleSpecifier) => { + try { + const customRequire = getRequireFunction(); + const resolvedPath = safeResolvePath(moduleSpecifier); + return resolvedPath ? customRequire(resolvedPath) : undefined; + } catch (e) { + logger.warn('Unable to load module', { + error: e, + libId: moduleSpecifier, + }); + + return undefined; + } +}; + +const tryResolveFromPathGroup = ( + customRequire: NodeRequire, + moduleSpecifier: string, + paths: string[] +): string => { + try { + const resolvedPath = customRequire.resolve(moduleSpecifier, { paths }); + if (resolvedPath) { + logger.debug( + `${moduleSpecifier} successfully loaded from ${resolvedPath}. Paths searched: `, + paths + ); + } else { + logger.debug( + `${moduleSpecifier} could not be loaded from any of the following paths: `, + paths + ); + } + return resolvedPath; + } catch (error) { + if (error.code !== 'MODULE_NOT_FOUND') { + logger.warn('Unable to resolve module', { error, moduleSpecifier }); + return undefined; + } + } +}; + +const safeResolvePath = (moduleSpecifier: string): string | undefined => { + const customReq = getRequireFunction(); + + const pathGroups = [ + // default paths - same as not specifying paths to require.resolve() + customReq.resolve?.paths?.(moduleSpecifier) || [], + // paths specified in NODE_PATH, in case the user has set it for some reason + (process.env.NODE_PATH || '').split(':'), + // process CWD - i.e. the node_nodules folder of the process require()-ed the distro + [path.resolve(process.cwd(), 'node_modules')], + ]; + + return pathGroups + .map((pathGroup) => tryResolveFromPathGroup(customReq, moduleSpecifier, pathGroup)) + .find(Boolean); +}; + +export const canRequireModule = (moduleSpecifier) => !!safeResolvePath(moduleSpecifier); diff --git a/src/utils.test.ts b/src/utils.test.ts index 6603539b..46e125d2 100644 --- a/src/utils.test.ts +++ b/src/utils.test.ts @@ -1,6 +1,4 @@ -import path from 'path'; -import { getSpanAttributeMaxLength, safeRequire } from './utils'; -import { version } from '../package.json'; +import { getSpanAttributeMaxLength } from './utils'; describe('getSpanAttributeMaxLength', () => { describe('value according to env. vars', () => { @@ -41,33 +39,3 @@ describe('getSpanAttributeMaxLength', () => { }); }); }); - -describe('safeRequire', () => { - afterEach(() => { - jest.clearAllMocks(); - }); - - test('requires an existing module with the given path', () => { - const packageJsonPath = path.join(__dirname, '..', 'package.json'); - - const result = safeRequire(packageJsonPath); - - expect(result.version).toEqual(version); - }); - - test('does not fail but returns undefined for a non-existing module', () => { - const result = safeRequire('BlaBlaBlaBla'); - - expect(result).toBeUndefined(); - }); - - test('does not fail but returns undefined when an errors occurs when loading the module', () => { - jest.doMock('fs', () => { - throw Error('RandomError'); - }); - - const result = safeRequire('fs'); - - expect(result).toBeUndefined(); - }); -}); diff --git a/src/utils.ts b/src/utils.ts index 1141fb92..7adf7a0c 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -4,6 +4,7 @@ import * as https from 'https'; import { logger } from './logging'; import { sortify } from './tools/jsonSortify'; +import path from 'path'; export const DEFAULT_ATTRIBUTE_VALUE_LENGTH_LIMIT = 2048; export const DEFAULT_CONNECTION_TIMEOUT = 5000; @@ -165,49 +166,6 @@ export const md5Hash = (item: {}): string | undefined => { // @ts-ignore export const removeDuplicates = (arr) => Array.from(new Set(arr)); -const getRequireFunction = () => - // @ts-ignore __non_webpack_require__ not available at compile time - typeof __non_webpack_require__ !== 'undefined' ? __non_webpack_require__ : require; - -export const safeRequire = (moduleNameOrPath) => { - const customReq = getRequireFunction(); - - try { - return customReq(moduleNameOrPath); - } catch (e) { - if (e.code !== 'MODULE_NOT_FOUND') { - logger.warn('Unable to load module', { - error: e, - libId: moduleNameOrPath, - }); - } - - return undefined; - } -}; - -export const canRequireModule = (libId) => { - const customReq = getRequireFunction(); - - try { - return !!customReq.resolve(libId); - } catch (e) { - try { - return !!customReq.resolve(libId, { - paths: (process.env.NODE_PATH || '').split(':'), - }); - } catch (e) { - if (e.code !== 'MODULE_NOT_FOUND') { - logger.warn('Unable to resolve module', { - error: e, - libId: libId, - }); - } - } - } - return false; -}; - export const getSpanAttributeMaxLength = () => { return ( parseInt(process.env.OTEL_SPAN_ATTRIBUTE_VALUE_LENGTH_LIMIT) || diff --git a/test/fixtures/dummy-module/index.js b/test/fixtures/dummy-module/index.js new file mode 100644 index 00000000..b298ea84 --- /dev/null +++ b/test/fixtures/dummy-module/index.js @@ -0,0 +1 @@ +module.exports = "dummy!" \ No newline at end of file diff --git a/test/instrumentations/features/features.test.ts b/test/instrumentations/features/features.test.ts index e6991d5e..afa175f7 100644 --- a/test/instrumentations/features/features.test.ts +++ b/test/instrumentations/features/features.test.ts @@ -2,9 +2,12 @@ import path from "path"; import { TestApp } from "../../utils/test-apps"; import { reinstallPackages } from "../../utils/test-setup"; import { FakeEdge } from "../../utils/fake-edge"; +import tmp from "tmp" +import fs from "fs-extra" const APP_DIR = path.join(__dirname, 'app'); -const SETUP_TIMEOUT = 2 * 60 * 1000; +const MINUTE = 60 * 1000; +const SETUP_TIMEOUT = 2 * MINUTE; describe("global distro features", () => { let testApp: TestApp; @@ -12,7 +15,6 @@ describe("global distro features", () => { beforeAll(async () => { await fakeEdge.start(); - reinstallPackages({ appDir: APP_DIR }); }, SETUP_TIMEOUT); afterEach(async () => { @@ -29,79 +31,144 @@ describe("global distro features", () => { afterAll(() => fakeEdge.stop()); - describe("disabled traces and logs (LUMIGO_ENABLE_TRACES and LUMIGO_ENABLE_LOGS set to 'false')", () => { - beforeEach(async () => { - testApp = new TestApp( - APP_DIR, - "test-disabled-tracing-and-logging", - { - env: { - LUMIGO_TRACER_TOKEN: 't_123456789', - LUMIGO_LOGS_ENDPOINT: fakeEdge.logsUrl, - LUMIGO_ENDPOINT: fakeEdge.tracesUrl, - LUMIGO_ENABLE_TRACES: 'false', - LUMIGO_ENABLE_LOGS: 'false' - } - } - ); - await testApp.waitUntilReady() - }, SETUP_TIMEOUT); + describe("with distro loaded from same folder as the app", () => { + beforeAll(() => { + reinstallPackages({ appDir: APP_DIR }); + }) - test('should not send traces and logs', async () => { - await testApp.invokeGetPath('/'); + describe("disabled traces and logs (LUMIGO_ENABLE_TRACES and LUMIGO_ENABLE_LOGS set to 'false')", () => { + beforeEach(async () => { + testApp = new TestApp( + APP_DIR, + "test-disabled-tracing-and-logging", + { + env: { + LUMIGO_TRACER_TOKEN: 't_123456789', + LUMIGO_LOGS_ENDPOINT: fakeEdge.logsUrl, + LUMIGO_ENDPOINT: fakeEdge.tracesUrl, + LUMIGO_ENABLE_TRACES: 'false', + LUMIGO_ENABLE_LOGS: 'false' + } + } + ); + await testApp.waitUntilReady() + }, SETUP_TIMEOUT); - await expect(fakeEdge.waitFor(({ logs }) => logs.length > 0, 'waiting for logs')).rejects.toThrow();; - await expect(fakeEdge.waitFor(({ spans }) => spans.length > 0, 'waiting for traces')).rejects.toThrow(); - }, 2 * 60 * 1000); - }) + test('should not send traces and logs', async () => { + await testApp.invokeGetPath('/'); - describe("synchronous initialization - Javascript", () => { - beforeEach(async () => { - testApp = new TestApp( - APP_DIR, - "test-sync-init", - { - env: { - LUMIGO_TRACER_TOKEN: 't_123456789', - LUMIGO_LOGS_ENDPOINT: fakeEdge.logsUrl, - LUMIGO_ENDPOINT: fakeEdge.tracesUrl, - LUMIGO_ENABLE_LOGS: 'true' - }, - startupScript: 'start-sync' - } - ); - await testApp.waitUntilReady() - }, SETUP_TIMEOUT); + await expect(fakeEdge.waitFor(({ logs }) => logs.length > 0, 'waiting for logs')).rejects.toThrow();; + await expect(fakeEdge.waitFor(({ spans }) => spans.length > 0, 'waiting for traces')).rejects.toThrow(); + }, 2 * MINUTE); + }) + + describe("synchronous initialization - Javascript", () => { + beforeEach(async () => { + testApp = new TestApp( + APP_DIR, + "test-sync-init", + { + env: { + LUMIGO_TRACER_TOKEN: 't_123456789', + LUMIGO_LOGS_ENDPOINT: fakeEdge.logsUrl, + LUMIGO_ENDPOINT: fakeEdge.tracesUrl, + LUMIGO_ENABLE_LOGS: 'true' + }, + startupScript: 'start-sync' + } + ); + await testApp.waitUntilReady() + }, SETUP_TIMEOUT); + + test('allows logging without await-ing on the init promise', async () => { + await testApp.invokeGetPath('/sync-init'); + + await expect(fakeEdge.waitFor(({ logs }) => logs.some(log => log.body["stringValue"] === "this log should be exported to Lumigo without init"), 'waiting for logs')).resolves.toBeTruthy(); + }, 2 * MINUTE); + }) + + describe("synchronous initialization - Typescript", () => { + beforeEach(async () => { + testApp = new TestApp( + APP_DIR, + "test-sync-init-ts", + { + env: { + LUMIGO_TRACER_TOKEN: 't_123456789', + LUMIGO_LOGS_ENDPOINT: fakeEdge.logsUrl, + LUMIGO_ENDPOINT: fakeEdge.tracesUrl, + LUMIGO_ENABLE_LOGS: 'true' + }, + startupScript: 'start-sync-ts' + } + ); + await testApp.waitUntilReady() + }, SETUP_TIMEOUT); - test('allows logging without await-ing on the init promise', async () => { - await testApp.invokeGetPath('/sync-init'); + test('allows logging without await-ing on the init promise in a Typescript app', async () => { + await testApp.invokeGetPath('/sync-init'); - await expect(fakeEdge.waitFor(({ logs }) => logs.some(log => log.body["stringValue"] === "this log should be exported to Lumigo without init"), 'waiting for logs')).resolves.toBeTruthy(); - }, 2 * 60 * 1000); + await expect(fakeEdge.waitFor(({ logs }) => logs.some(log => log.body["stringValue"] === "this log should be exported to Lumigo without init"), 'waiting for logs')).resolves.toBeTruthy(); + }, 2 * MINUTE); + }) }) - describe("synchronous initialization - Typescript", () => { - beforeEach(async () => { + describe("resolving instrumented packages", () => { + let targetFolder + + beforeAll(async () => { + targetFolder = tmp.dirSync({ keep: process.env.KEEP_TEMP_TEST_FOLDERS == "true" }).name; + + const sourceFolder = path.join(__dirname, "require-precedence"); + + // copy the entire test project-root to a temp folder, to isolate the tests from any + // node_modules folders used in this repo + await fs.copy(sourceFolder, targetFolder), + + // copy the distro tar.gz file to the tests projects using it as a dependency + await Promise.all([ + fs.copy("distro.tgz", path.join(targetFolder, "app-with-distro-dep", "distro.tgz")), + fs.copy("distro.tgz", path.join(targetFolder, "app-with-logger-and-distro-deps", "distro.tgz")), + fs.copy("distro.tgz", path.join(targetFolder, "distro-only", "distro.tgz")), + ]); + + // run `npm install` in test projects with package.json + await Promise.all([ + reinstallPackages({ appDir: path.join(targetFolder, "app-with-distro-dep") }), + reinstallPackages({ appDir: path.join(targetFolder, "app-with-logger-and-distro-deps") }), + reinstallPackages({ appDir: path.join(targetFolder, "app-with-logger-dep") }), + reinstallPackages({ appDir: path.join(targetFolder, "distro-only") }), + reinstallPackages({ appDir: path.join(targetFolder, "logger-only") }) + ]); + }); + + describe.each` + testFolder | NODE_PATH | description + ${"app-with-logger-and-distro-deps"} | ${undefined} | ${"both distro and instrumented module are direct deps of the app"} + ${"app-with-logger-dep"} | ${"../distro-only/node_modules"} | ${"instrumented package is a direct dep of the app, distro is loaded via NODE_PATH"} + ${"app-with-distro-dep"} | ${"../logger-only/node_modules"} | ${"distro is a direct dep of the app, instrumented package is loaded via NODE_PATH"} + `("$description", ({ testFolder, NODE_PATH, description }) => { + beforeEach(async () => { testApp = new TestApp( - APP_DIR, - "test-sync-init-ts", + path.join(targetFolder, testFolder), + description.replace(/ /g, "-"), { env: { LUMIGO_TRACER_TOKEN: 't_123456789', LUMIGO_LOGS_ENDPOINT: fakeEdge.logsUrl, LUMIGO_ENDPOINT: fakeEdge.tracesUrl, - LUMIGO_ENABLE_LOGS: 'true' - }, - startupScript: 'start-sync-ts' - } + LUMIGO_ENABLE_LOGS: 'true', + NODE_PATH + } + }, ); await testApp.waitUntilReady() - }, SETUP_TIMEOUT); - - test('allows logging without await-ing on the init promise in a Typescript app', async () => { - await testApp.invokeGetPath('/sync-init'); + }, SETUP_TIMEOUT); - await expect(fakeEdge.waitFor(({ logs }) => logs.some(log => log.body["stringValue"] === "this log should be exported to Lumigo without init"), 'waiting for logs')).resolves.toBeTruthy(); - }, 2 * 60 * 1000); + test('properly resolves modules to instrument relative to the app folder', async () => { + await testApp.invokeGetPath(`/write-log`); + await expect(fakeEdge.waitFor(({ logs }) => logs.some(log => log.body["stringValue"] === "sure thing it works!"), 'waiting for logs', MINUTE)).resolves.toBeTruthy(); + }, 2 * MINUTE); + }); }) }) \ No newline at end of file diff --git a/test/instrumentations/features/require-precedence/app-with-distro-dep/app.js b/test/instrumentations/features/require-precedence/app-with-distro-dep/app.js new file mode 100644 index 00000000..da605115 --- /dev/null +++ b/test/instrumentations/features/require-precedence/app-with-distro-dep/app.js @@ -0,0 +1,25 @@ +const bunyan = require('bunyan'); +const http = require('http'); + +const bunyanLogger = bunyan.createLogger({ name: __filename }) + +const server = http.createServer(async (req, res) => { + switch (req.url) { + case '/write-log': + bunyanLogger.info('sure thing it works!'); + res.writeHead(200); + res.end(); + break; + case '/quit': + res.writeHead(200); + res.end('server is quitting'); + server.close(); + break; + default: + res.writeHead(404); + res.end(`route handler for ${req.url} not found`); + break; + } +}); + +server.listen(0, "localhost", () => console.error(`HTTP server listening on port ${server.address().port}`)); diff --git a/test/instrumentations/features/require-precedence/app-with-distro-dep/package.json b/test/instrumentations/features/require-precedence/app-with-distro-dep/package.json new file mode 100644 index 00000000..2b51d49b --- /dev/null +++ b/test/instrumentations/features/require-precedence/app-with-distro-dep/package.json @@ -0,0 +1,13 @@ +{ + "name": "lumigo-features-test", + "version": "1.0.0", + "description": "", + "scripts": { + "start": "node -r @lumigo/opentelemetry/sync app.js" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@lumigo/opentelemetry": "file:./distro.tgz" + } +} diff --git a/test/instrumentations/features/require-precedence/app-with-logger-and-distro-deps/app.js b/test/instrumentations/features/require-precedence/app-with-logger-and-distro-deps/app.js new file mode 100644 index 00000000..da605115 --- /dev/null +++ b/test/instrumentations/features/require-precedence/app-with-logger-and-distro-deps/app.js @@ -0,0 +1,25 @@ +const bunyan = require('bunyan'); +const http = require('http'); + +const bunyanLogger = bunyan.createLogger({ name: __filename }) + +const server = http.createServer(async (req, res) => { + switch (req.url) { + case '/write-log': + bunyanLogger.info('sure thing it works!'); + res.writeHead(200); + res.end(); + break; + case '/quit': + res.writeHead(200); + res.end('server is quitting'); + server.close(); + break; + default: + res.writeHead(404); + res.end(`route handler for ${req.url} not found`); + break; + } +}); + +server.listen(0, "localhost", () => console.error(`HTTP server listening on port ${server.address().port}`)); diff --git a/test/instrumentations/features/require-precedence/app-with-logger-and-distro-deps/package.json b/test/instrumentations/features/require-precedence/app-with-logger-and-distro-deps/package.json new file mode 100644 index 00000000..27cd5438 --- /dev/null +++ b/test/instrumentations/features/require-precedence/app-with-logger-and-distro-deps/package.json @@ -0,0 +1,14 @@ +{ + "name": "lumigo-features-test", + "version": "1.0.0", + "description": "", + "scripts": { + "start": "node -r @lumigo/opentelemetry/sync app.js" + }, + "author": "", + "license": "ISC", + "dependencies": { + "bunyan": "1.8.15", + "@lumigo/opentelemetry": "file:./distro.tgz" + } +} diff --git a/test/instrumentations/features/require-precedence/app-with-logger-dep/app.js b/test/instrumentations/features/require-precedence/app-with-logger-dep/app.js new file mode 100644 index 00000000..da605115 --- /dev/null +++ b/test/instrumentations/features/require-precedence/app-with-logger-dep/app.js @@ -0,0 +1,25 @@ +const bunyan = require('bunyan'); +const http = require('http'); + +const bunyanLogger = bunyan.createLogger({ name: __filename }) + +const server = http.createServer(async (req, res) => { + switch (req.url) { + case '/write-log': + bunyanLogger.info('sure thing it works!'); + res.writeHead(200); + res.end(); + break; + case '/quit': + res.writeHead(200); + res.end('server is quitting'); + server.close(); + break; + default: + res.writeHead(404); + res.end(`route handler for ${req.url} not found`); + break; + } +}); + +server.listen(0, "localhost", () => console.error(`HTTP server listening on port ${server.address().port}`)); diff --git a/test/instrumentations/features/require-precedence/app-with-logger-dep/package.json b/test/instrumentations/features/require-precedence/app-with-logger-dep/package.json new file mode 100644 index 00000000..024f9052 --- /dev/null +++ b/test/instrumentations/features/require-precedence/app-with-logger-dep/package.json @@ -0,0 +1,13 @@ +{ + "name": "lumigo-features-test", + "version": "1.0.0", + "description": "", + "scripts": { + "start": "node -r @lumigo/opentelemetry/sync app.js" + }, + "author": "", + "license": "ISC", + "dependencies": { + "bunyan": "1.8.15" + } +} diff --git a/test/instrumentations/features/require-precedence/distro-only/package.json b/test/instrumentations/features/require-precedence/distro-only/package.json new file mode 100644 index 00000000..1f17e1fa --- /dev/null +++ b/test/instrumentations/features/require-precedence/distro-only/package.json @@ -0,0 +1,7 @@ +{ + + "description": "This is a test package.json file for the case where the distro is loaded via NODE_PATH from another path", + "dependencies": { + "@lumigo/opentelemetry": "file:./distro.tgz" + } +} \ No newline at end of file diff --git a/test/instrumentations/features/require-precedence/logger-only/package.json b/test/instrumentations/features/require-precedence/logger-only/package.json new file mode 100644 index 00000000..3f462572 --- /dev/null +++ b/test/instrumentations/features/require-precedence/logger-only/package.json @@ -0,0 +1,5 @@ +{ + "dependencies": { + "bunyan": "1.8.15" + } +} \ No newline at end of file