From 18f2170a325b333b0d9bc153d2a353ebca4ce263 Mon Sep 17 00:00:00 2001 From: Harel Moshe Date: Sun, 22 Sep 2024 16:42:52 +0300 Subject: [PATCH] feat(tracing): allow opting out from tracing (#503) * feat(tracing): allow opting out from tracing (disable exporters + block instrumentations) * fix(tests): fix flaky tests --- README.md | 1 + src/bootstrap.ts | 46 ++++++++++------ src/constants.ts | 10 ++++ .../@grpc/grpc-js/GrpcInstrumentation.ts | 4 +- .../@nestjs/core/NestInstrumentation.ts | 4 +- .../amqplib/AmqplibInstrumentation.ts | 4 +- .../aws-sdk/LumigoAwsSdkLibInstrumentation.ts | 4 +- .../bunyan/BunyanInstrumentation.ts | 8 +-- .../express/ExpressInstrumentation.ts | 4 +- .../fastify/FastifyInstrumentation.ts | 4 +- .../https/HttpInstrumentation.ts | 4 +- src/instrumentations/instrumentor.ts | 15 +++++- .../ioredis/IORedisInstrumentation.ts | 4 +- .../kafkajs/KafkaJsInstrumentation.ts | 4 +- .../mongodb/MongoDBInstrumentation.ts | 4 +- src/instrumentations/pg/PgInstrumentation.ts | 4 +- .../prisma/PrismaInstrumentation.ts | 4 +- .../redis/RedisInstrumentation.ts | 4 +- .../winston/WinstonInstrumentation.ts | 8 +-- test/instrumentations/bunyan/bunyan.test.ts | 29 +---------- test/instrumentations/features/app/.gitignore | 1 + test/instrumentations/features/app/app.js | 31 +++++++++++ .../features/app/package.json | 15 ++++++ .../features/features.test.ts | 45 ++++++++++++++++ test/instrumentations/winston/winston.test.ts | 33 ++---------- test/utils/fake-edge.ts | 52 ++++++++++++------- test/utils/versions.ts | 1 - 27 files changed, 215 insertions(+), 132 deletions(-) create mode 100644 test/instrumentations/features/app/.gitignore create mode 100644 test/instrumentations/features/app/app.js create mode 100644 test/instrumentations/features/app/package.json create mode 100644 test/instrumentations/features/features.test.ts diff --git a/README.md b/README.md index 6548d496..805529a8 100644 --- a/README.md +++ b/README.md @@ -100,6 +100,7 @@ Specifically supported are: `@lumigo/opentelemetry` additionally supports the following configuration options as environment variables: +* `LUMIGO_ENABLE_TRACES` - Default: `true`. When set to `false`, turns off all of the *tracing* instrumentations. Note that this does not turn off the *logging* instrumentations, which are controlled by the `LUMIGO_ENABLE_LOGS` environment variable. * `LUMIGO_TRACER_TOKEN=`: Configure the Lumigo token to enable to upload of telemetry to Lumigo; without this environment variable, your Node.js process will not send telemetry to Lumigo. * `LUMIGO_DEBUG=TRUE`: Enables debug logging * `LUMIGO_DEBUG_SPANDUMP=`: Log all spans collected to the `` file or, the value is `console:log` or `console:error`, to `console.log` or `console.error`; this is an option intended only for debugging purposes and should *not* be used in production. diff --git a/src/bootstrap.ts b/src/bootstrap.ts index 0bd50e80..3fc4b9c9 100644 --- a/src/bootstrap.ts +++ b/src/bootstrap.ts @@ -17,6 +17,8 @@ import { DEFAULT_DEPENDENCIES_ENDPOINT, DEFAULT_LUMIGO_TRACES_ENDPOINT, DEFAULT_LUMIGO_LOGS_ENDPOINT, + TRACING_ENABLED, + LOGGING_ENABLED, } from './constants'; import { report } from './dependencies'; import { FileLogExporter, FileSpanExporter } from './exporters'; @@ -241,14 +243,20 @@ export const init = async (): Promise => { }, }); - tracerProvider.addSpanProcessor( - new LumigoSpanProcessor(otlpTraceExporter, { - // The maximum queue size. After the size is reached spans are dropped. - maxQueueSize: 1000, - // The maximum batch size of every export. It must be smaller or equal to maxQueueSize. - maxExportBatchSize: 100, - }) - ); + if (TRACING_ENABLED) { + tracerProvider.addSpanProcessor( + new LumigoSpanProcessor(otlpTraceExporter, { + // The maximum queue size. After the size is reached spans are dropped. + maxQueueSize: 1000, + // The maximum batch size of every export. It must be smaller or equal to maxQueueSize. + maxExportBatchSize: 100, + }) + ); + } else { + logger.info( + 'Tracing is disabled (the "LUMIGO_ENABLE_TRACES" environment variable is not set to "true"): no traces will be sent to Lumigo.' + ); + } const otlpLogExporter = new OTLPLogExporter({ url: lumigoLogEndpoint, @@ -257,14 +265,20 @@ export const init = async (): Promise => { }, }); - loggerProvider.addLogRecordProcessor( - new BatchLogRecordProcessor(otlpLogExporter, { - // The maximum queue size. After the size is reached logs are dropped. - maxQueueSize: 1000, - // The maximum batch size of every export. It must be smaller or equal to maxQueueSize. - maxExportBatchSize: 100, - }) - ); + if (LOGGING_ENABLED) { + loggerProvider.addLogRecordProcessor( + new BatchLogRecordProcessor(otlpLogExporter, { + // The maximum queue size. After the size is reached logs are dropped. + maxQueueSize: 1000, + // The maximum batch size of every export. It must be smaller or equal to maxQueueSize. + maxExportBatchSize: 100, + }) + ); + } else { + logger.info( + 'Logging is disabled (the "LUMIGO_ENABLE_LOGS" environment variable is not set to "true"): no logs will be sent to Lumigo.' + ); + } /* * We do not wait for this promise, we do not want to delay the application. diff --git a/src/constants.ts b/src/constants.ts index 8a6a2edd..1806720b 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,7 +1,17 @@ export const LUMIGO_LOGGING_NAMESPACE = '@lumigo/opentelemetry'; + export const DEFAULT_LUMIGO_TRACES_ENDPOINT = 'https://ga-otlp.lumigo-tracer-edge.golumigo.com/v1/traces'; + export const DEFAULT_LUMIGO_LOGS_ENDPOINT = 'https://ga-otlp.lumigo-tracer-edge.golumigo.com/v1/logs'; + export const DEFAULT_DEPENDENCIES_ENDPOINT = 'https://ga-otlp.lumigo-tracer-edge.golumigo.com/v1/dependencies'; + +// Since tracing is on by default, we allow omitting it and consider it enabled +export const TRACING_ENABLED = + process.env.LUMIGO_ENABLE_TRACES === undefined || + process.env.LUMIGO_ENABLE_TRACES?.toLowerCase() === 'true'; + +export const LOGGING_ENABLED = process.env.LUMIGO_ENABLE_LOGS?.toLowerCase() === 'true'; diff --git a/src/instrumentations/@grpc/grpc-js/GrpcInstrumentation.ts b/src/instrumentations/@grpc/grpc-js/GrpcInstrumentation.ts index ba76ddd9..c3cfc926 100644 --- a/src/instrumentations/@grpc/grpc-js/GrpcInstrumentation.ts +++ b/src/instrumentations/@grpc/grpc-js/GrpcInstrumentation.ts @@ -1,9 +1,9 @@ import { GrpcInstrumentation } from '@opentelemetry/instrumentation-grpc'; -import { Instrumentor } from '../../instrumentor'; +import { TracingInstrumentor } from '../../instrumentor'; import { wrapServer } from './wrapGrpcServer'; import { wrapClient } from './wrapGrpcClient'; -export default class LumigoGrpcInstrumentation extends Instrumentor { +export default class LumigoGrpcInstrumentation extends TracingInstrumentor { getInstrumentedModule(): string { return '@grpc/grpc-js'; } diff --git a/src/instrumentations/@nestjs/core/NestInstrumentation.ts b/src/instrumentations/@nestjs/core/NestInstrumentation.ts index d33b0f54..10738289 100644 --- a/src/instrumentations/@nestjs/core/NestInstrumentation.ts +++ b/src/instrumentations/@nestjs/core/NestInstrumentation.ts @@ -1,7 +1,7 @@ import { NestInstrumentation } from '@opentelemetry/instrumentation-nestjs-core'; -import { Instrumentor } from '../../instrumentor'; +import { TracingInstrumentor } from '../../instrumentor'; -export default class LumigoNestInstrumentation extends Instrumentor { +export default class LumigoNestInstrumentation extends TracingInstrumentor { override isApplicable(): boolean { return ( super.isApplicable() && diff --git a/src/instrumentations/amqplib/AmqplibInstrumentation.ts b/src/instrumentations/amqplib/AmqplibInstrumentation.ts index 5906c536..b736b699 100644 --- a/src/instrumentations/amqplib/AmqplibInstrumentation.ts +++ b/src/instrumentations/amqplib/AmqplibInstrumentation.ts @@ -6,9 +6,9 @@ import { PublishInfo, } from '@opentelemetry/instrumentation-amqplib'; import { getSpanAttributeMaxLength } from '../../utils'; -import { Instrumentor } from '../instrumentor'; +import { TracingInstrumentor } from '../instrumentor'; -export default class LumigoAmqplibInstrumentation extends Instrumentor { +export default class LumigoAmqplibInstrumentation extends TracingInstrumentor { getInstrumentedModule(): string { return 'amqplib'; } diff --git a/src/instrumentations/aws-sdk/LumigoAwsSdkLibInstrumentation.ts b/src/instrumentations/aws-sdk/LumigoAwsSdkLibInstrumentation.ts index 4d1207dc..972fb4ce 100644 --- a/src/instrumentations/aws-sdk/LumigoAwsSdkLibInstrumentation.ts +++ b/src/instrumentations/aws-sdk/LumigoAwsSdkLibInstrumentation.ts @@ -1,8 +1,8 @@ import { AwsInstrumentation } from '@opentelemetry/instrumentation-aws-sdk'; -import { Instrumentor } from '../instrumentor'; +import { TracingInstrumentor } from '../instrumentor'; import { preRequestHook, responseHook, sqsProcessHook } from './hooks'; -export abstract class LumigoAwsSdkLibInstrumentation extends Instrumentor { +export abstract class LumigoAwsSdkLibInstrumentation extends TracingInstrumentor { override isApplicable(): boolean { return ( super.isApplicable() && diff --git a/src/instrumentations/bunyan/BunyanInstrumentation.ts b/src/instrumentations/bunyan/BunyanInstrumentation.ts index feb0e579..308352bc 100644 --- a/src/instrumentations/bunyan/BunyanInstrumentation.ts +++ b/src/instrumentations/bunyan/BunyanInstrumentation.ts @@ -1,7 +1,7 @@ import { BunyanInstrumentation } from '@opentelemetry/instrumentation-bunyan'; -import { Instrumentor } from '../instrumentor'; +import { LoggingInstrumentor } from '../instrumentor'; -export default class LumigoBunyanInstrumentation extends Instrumentor { +export default class LumigoBunyanInstrumentation extends LoggingInstrumentor { getInstrumentedModule(): string { return 'bunyan'; } @@ -9,8 +9,4 @@ export default class LumigoBunyanInstrumentation extends Instrumentor { +export default class LumigoExpressInstrumentation extends TracingInstrumentor { getInstrumentedModule(): string { return 'express'; } diff --git a/src/instrumentations/fastify/FastifyInstrumentation.ts b/src/instrumentations/fastify/FastifyInstrumentation.ts index fad4fc49..575be2e3 100644 --- a/src/instrumentations/fastify/FastifyInstrumentation.ts +++ b/src/instrumentations/fastify/FastifyInstrumentation.ts @@ -3,9 +3,9 @@ import type { Span } from '@opentelemetry/api'; import { FastifyInstrumentation, FastifyRequestInfo } from '@opentelemetry/instrumentation-fastify'; import { contentType, scrubHttpPayload } from '../../tools/payloads'; import { getSpanAttributeMaxLength } from '../../utils'; -import { Instrumentor } from '../instrumentor'; +import { TracingInstrumentor } from '../instrumentor'; -export default class LumigoFastifyInstrumentation extends Instrumentor { +export default class LumigoFastifyInstrumentation extends TracingInstrumentor { getInstrumentedModule(): string { return 'fastify'; } diff --git a/src/instrumentations/https/HttpInstrumentation.ts b/src/instrumentations/https/HttpInstrumentation.ts index a3871f77..17cdf004 100644 --- a/src/instrumentations/https/HttpInstrumentation.ts +++ b/src/instrumentations/https/HttpInstrumentation.ts @@ -2,9 +2,9 @@ import type { RequestOptions } from 'https'; import { HttpInstrumentation } from '@opentelemetry/instrumentation-http'; import { HttpHooks } from './http'; -import { Instrumentor } from '../instrumentor'; +import { TracingInstrumentor } from '../instrumentor'; -export default class LumigoHttpInstrumentation extends Instrumentor { +export default class LumigoHttpInstrumentation extends TracingInstrumentor { private readonly ignoredHostnames: string[]; constructor(...ignoredHostnames: string[]) { diff --git a/src/instrumentations/instrumentor.ts b/src/instrumentations/instrumentor.ts index e39499ba..67462479 100644 --- a/src/instrumentations/instrumentor.ts +++ b/src/instrumentations/instrumentor.ts @@ -1,7 +1,8 @@ import type { Instrumentation } from '@opentelemetry/instrumentation'; import { canRequireModule } from '../utils'; +import { LOGGING_ENABLED, TRACING_ENABLED } from '../constants'; -export abstract class Instrumentor { +abstract class Instrumentor { abstract getInstrumentedModule(): string; abstract getInstrumentation(options?): T; @@ -10,3 +11,15 @@ export abstract class Instrumentor { return canRequireModule(this.getInstrumentedModule()); } } + +export abstract class LoggingInstrumentor extends Instrumentor { + override isApplicable() { + return LOGGING_ENABLED && super.isApplicable(); + } +} + +export abstract class TracingInstrumentor extends Instrumentor { + override isApplicable() { + return TRACING_ENABLED && super.isApplicable(); + } +} diff --git a/src/instrumentations/ioredis/IORedisInstrumentation.ts b/src/instrumentations/ioredis/IORedisInstrumentation.ts index 6464dffe..c72cc39a 100644 --- a/src/instrumentations/ioredis/IORedisInstrumentation.ts +++ b/src/instrumentations/ioredis/IORedisInstrumentation.ts @@ -2,9 +2,9 @@ import { CommonUtils, ScrubContext } from '@lumigo/node-core'; import type { Span } from '@opentelemetry/api'; import { IORedisInstrumentation } from '@opentelemetry/instrumentation-ioredis'; import { getSpanAttributeMaxLength } from '../../utils'; -import { Instrumentor } from '../instrumentor'; +import { TracingInstrumentor } from '../instrumentor'; -export default class LumigoIORedisInstrumentation extends Instrumentor { +export default class LumigoIORedisInstrumentation extends TracingInstrumentor { getInstrumentedModule(): string { return 'ioredis'; } diff --git a/src/instrumentations/kafkajs/KafkaJsInstrumentation.ts b/src/instrumentations/kafkajs/KafkaJsInstrumentation.ts index 5c140c4b..1103b7be 100644 --- a/src/instrumentations/kafkajs/KafkaJsInstrumentation.ts +++ b/src/instrumentations/kafkajs/KafkaJsInstrumentation.ts @@ -3,9 +3,9 @@ import type { Span } from '@opentelemetry/api'; import type { Message } from 'kafkajs'; import { KafkaJsInstrumentation } from 'opentelemetry-instrumentation-kafkajs'; import { getSpanAttributeMaxLength } from '../../utils'; -import { Instrumentor } from '../instrumentor'; +import { TracingInstrumentor } from '../instrumentor'; -export default class LumigoKafkaJsInstrumentation extends Instrumentor { +export default class LumigoKafkaJsInstrumentation extends TracingInstrumentor { getInstrumentedModule(): string { return 'kafkajs'; } diff --git a/src/instrumentations/mongodb/MongoDBInstrumentation.ts b/src/instrumentations/mongodb/MongoDBInstrumentation.ts index b52e17df..d8ce6370 100644 --- a/src/instrumentations/mongodb/MongoDBInstrumentation.ts +++ b/src/instrumentations/mongodb/MongoDBInstrumentation.ts @@ -1,7 +1,7 @@ import { MongoDBInstrumentation } from '@opentelemetry/instrumentation-mongodb'; -import { Instrumentor } from '../instrumentor'; +import { TracingInstrumentor } from '../instrumentor'; -export default class LumigoMongoDBInstrumentation extends Instrumentor { +export default class LumigoMongoDBInstrumentation extends TracingInstrumentor { getInstrumentedModule(): string { return 'mongodb'; } diff --git a/src/instrumentations/pg/PgInstrumentation.ts b/src/instrumentations/pg/PgInstrumentation.ts index fe2de7a6..19c5f8f6 100644 --- a/src/instrumentations/pg/PgInstrumentation.ts +++ b/src/instrumentations/pg/PgInstrumentation.ts @@ -1,7 +1,7 @@ import { PgInstrumentation } from '@opentelemetry/instrumentation-pg'; -import { Instrumentor } from '../instrumentor'; +import { TracingInstrumentor } from '../instrumentor'; -export default class LumigoPgInstrumentation extends Instrumentor { +export default class LumigoPgInstrumentation extends TracingInstrumentor { override isApplicable(): boolean { return ( super.isApplicable() && diff --git a/src/instrumentations/prisma/PrismaInstrumentation.ts b/src/instrumentations/prisma/PrismaInstrumentation.ts index bf649df6..84f32d46 100644 --- a/src/instrumentations/prisma/PrismaInstrumentation.ts +++ b/src/instrumentations/prisma/PrismaInstrumentation.ts @@ -1,7 +1,7 @@ import { PrismaInstrumentation } from '@prisma/instrumentation'; -import { Instrumentor } from '../instrumentor'; +import { TracingInstrumentor } from '../instrumentor'; -export default class LumigoPrismaInstrumentation extends Instrumentor { +export default class LumigoPrismaInstrumentation extends TracingInstrumentor { getInstrumentedModule(): string { return 'prisma'; } diff --git a/src/instrumentations/redis/RedisInstrumentation.ts b/src/instrumentations/redis/RedisInstrumentation.ts index c62dd435..40306764 100644 --- a/src/instrumentations/redis/RedisInstrumentation.ts +++ b/src/instrumentations/redis/RedisInstrumentation.ts @@ -2,9 +2,9 @@ import { CommonUtils, ScrubContext } from '@lumigo/node-core'; import type { Span } from '@opentelemetry/api'; import { RedisInstrumentation } from '@opentelemetry/instrumentation-redis-4'; import { getSpanAttributeMaxLength } from '../../utils'; -import { Instrumentor } from '../instrumentor'; +import { TracingInstrumentor } from '../instrumentor'; -export default class LumigoRedisInstrumentation extends Instrumentor { +export default class LumigoRedisInstrumentation extends TracingInstrumentor { getInstrumentedModule(): string { return 'redis'; } diff --git a/src/instrumentations/winston/WinstonInstrumentation.ts b/src/instrumentations/winston/WinstonInstrumentation.ts index d8d46f1a..606269ce 100644 --- a/src/instrumentations/winston/WinstonInstrumentation.ts +++ b/src/instrumentations/winston/WinstonInstrumentation.ts @@ -1,7 +1,7 @@ import { WinstonInstrumentation } from '@opentelemetry/instrumentation-winston'; -import { Instrumentor } from '../instrumentor'; +import { LoggingInstrumentor } from '../instrumentor'; -export default class LumigoWinstonInstrumentation extends Instrumentor { +export default class LumigoWinstonInstrumentation extends LoggingInstrumentor { getInstrumentedModule(): string { return 'winston'; } @@ -9,8 +9,4 @@ export default class LumigoWinstonInstrumentation extends Instrumentor fakeEdge.resources.length == 1, - 'waiting for resources to be processed' - ); - await fakeEdge.waitFor(() => fakeEdge.logs.length == 2, 'waiting for logs to be processed'); + await fakeEdge.waitFor(({ resources }) => resources.length > 1, 'waiting for resources to be processed'); + await fakeEdge.waitFor(({ logs } ) => logs.length == 2, 'waiting for logs to be processed'); expect(fakeEdge.resources[0].attributes).toIncludeAllMembers([ { @@ -103,28 +100,6 @@ describe.each(versionsToTest(INSTRUMENTATION_NAME, INSTRUMENTATION_NAME))( } ); - itTest( - { - testName: `${INSTRUMENTATION_NAME} logger: ${versionToTest} - logging off`, - packageName: INSTRUMENTATION_NAME, - version: versionToTest, - timeout: 20_000, - }, - async function () { - testApp = new TestApp(TEST_APP_DIR, INSTRUMENTATION_NAME, { - logDumpPath: `${LOGS_DIR}/${INSTRUMENTATION_NAME}.${INSTRUMENTATION_NAME}-logs@${versionToTest}.json`, - env: { - LUMIGO_ENABLE_LOGS: 'false', - }, - }); - - await writeLogLine('Hello Bunyan!'); - - // We expect no logs to be sent, therefore waiting for 1 log should fail - await expect(testApp.getFinalLogs(1)).rejects.toThrow(); - } - ); - const writeLogLine = async (logLine: any) => testApp.invokeGetPath( `/write-log-line?logLine=${encodeURIComponent(JSON.stringify(logLine))}` diff --git a/test/instrumentations/features/app/.gitignore b/test/instrumentations/features/app/.gitignore new file mode 100644 index 00000000..483a9c42 --- /dev/null +++ b/test/instrumentations/features/app/.gitignore @@ -0,0 +1 @@ +package-lock.json \ No newline at end of file diff --git a/test/instrumentations/features/app/app.js b/test/instrumentations/features/app/app.js new file mode 100644 index 00000000..8dee329e --- /dev/null +++ b/test/instrumentations/features/app/app.js @@ -0,0 +1,31 @@ +const lumigo = require('@lumigo/opentelemetry'); +const fastify = require('fastify')({ logger: true }); +const bunyan = require('bunyan'); +const bunyanLogger = bunyan.createLogger({ name: __filename }) + +let tracerProvider; +let loggerProvider; + +fastify.get('/', async (request, reply) => reply.send('server is ready')); + +fastify.get('/quit', async (request, reply) => { + bunyanLogger.info('this should not be exported to Lumigo'); + + console.log('Received quit command, flushing and exiting'); + await tracerProvider.forceFlush(); + await loggerProvider.forceFlush(); + + // we could have used fastify.close(), but it just takes too long + reply.send({}).then(() => process.exit(0)) +}); + +fastify.listen({ port: 0 }, async (err, address) => { + if (err) { + throw err; + } + const lumigoSdk = await lumigo.init + tracerProvider = lumigoSdk.tracerProvider; + loggerProvider = lumigoSdk.loggerProvider; + const port = fastify.server.address().port; + console.error(`HTTP server listening on port ${port}`); +}); diff --git a/test/instrumentations/features/app/package.json b/test/instrumentations/features/app/package.json new file mode 100644 index 00000000..fc499f57 --- /dev/null +++ b/test/instrumentations/features/app/package.json @@ -0,0 +1,15 @@ +{ + "name": "lumigo-features-test", + "version": "1.0.0", + "description": "", + "scripts": { + "start": "node -r @lumigo/opentelemetry app.js" + }, + "author": "", + "license": "ISC", + "dependencies": { + "@lumigo/opentelemetry": "file:../../../../distro.tgz", + "fastify": "4.0.1", + "bunyan": "1.8.15" + } +} diff --git a/test/instrumentations/features/features.test.ts b/test/instrumentations/features/features.test.ts new file mode 100644 index 00000000..ef3a401d --- /dev/null +++ b/test/instrumentations/features/features.test.ts @@ -0,0 +1,45 @@ +import path from "path"; +import { TestApp } from "../../utils/test-apps"; +import { reinstallPackages } from "../../utils/test-setup"; +import { FakeEdge } from "../../utils/fake-edge"; + +const APP_DIR = path.join(__dirname, 'app'); +const SETUP_TIMEOUT = 2 * 60 * 1000; + +describe("disabled traces and logs (LUMIGO_ENABLE_TRACES and LUMIGO_ENABLE_LOGS set to 'false')", () => { + let testApp: TestApp; + const fakeEdge = new FakeEdge(); + + beforeAll(async () => { + await fakeEdge.start(); + reinstallPackages({ appDir: APP_DIR }); + }, SETUP_TIMEOUT); + + 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); + + afterEach(() => testApp && testApp.invokeShutdown().catch((err) => console.warn('Failed to kill test app', err))); + + afterAll(() => fakeEdge.stop()); + + test('should not send traces and logs', async () => { + await testApp.invokeGetPath('/'); + + 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); +}) \ No newline at end of file diff --git a/test/instrumentations/winston/winston.test.ts b/test/instrumentations/winston/winston.test.ts index 16d727e2..eb8af0c8 100644 --- a/test/instrumentations/winston/winston.test.ts +++ b/test/instrumentations/winston/winston.test.ts @@ -51,7 +51,7 @@ describe.each(versionsToTest(INSTRUMENTATION_NAME, INSTRUMENTATION_NAME))( testName: `${INSTRUMENTATION_NAME} logger: ${versionToTest}`, packageName: INSTRUMENTATION_NAME, version: versionToTest, - timeout: 20_000, + timeout: 30_000, }, async function () { testApp = new TestApp(TEST_APP_DIR, INSTRUMENTATION_NAME, { @@ -68,11 +68,8 @@ describe.each(versionsToTest(INSTRUMENTATION_NAME, INSTRUMENTATION_NAME))( await writeLogLine('Hello Winston!'); await writeLogLine({ a: 1, sekret: 'this is secret!' }); - await fakeEdge.waitFor( - () => fakeEdge.resources.length == 1, - 'waiting for resources to be processed' - ); - await fakeEdge.waitFor(() => fakeEdge.logs.length == 2, 'waiting for logs to be processed'); + await expect(fakeEdge.waitFor(({ logs }) => logs.length === 2, 'waiting for logs to be processed')).toBeTruthy(); + await expect(fakeEdge.waitFor(({ resources }) => resources.length >= 1, 'waiting for resources to be processed')).resolves.toBeTruthy(); expect(fakeEdge.resources[0].attributes).toIncludeAllMembers([ { @@ -81,7 +78,7 @@ describe.each(versionsToTest(INSTRUMENTATION_NAME, INSTRUMENTATION_NAME))( stringValue: 'winston', }, }, - ]); + ]) expect(fakeEdge.logs[0].body).toEqual({ stringValue: 'Hello Winston!' }); // Span context is available since the test app is an instrumented HTTP server @@ -104,28 +101,6 @@ describe.each(versionsToTest(INSTRUMENTATION_NAME, INSTRUMENTATION_NAME))( } ); - itTest( - { - testName: `${INSTRUMENTATION_NAME} logger: ${versionToTest} - logging off`, - packageName: INSTRUMENTATION_NAME, - version: versionToTest, - timeout: 20_000, - }, - async function () { - testApp = new TestApp(TEST_APP_DIR, INSTRUMENTATION_NAME, { - logDumpPath: `${LOGS_DIR}/${INSTRUMENTATION_NAME}.${INSTRUMENTATION_NAME}-logs-off@${versionToTest}.json`, - env: { - LUMIGO_ENABLE_LOGS: 'false', - }, - }); - - await writeLogLine('Hello Winston!'); - - // We expect no logs to be sent, therefore waiting for 1 log should fail - await expect(testApp.getFinalLogs(1)).rejects.toThrow(); - } - ); - const writeLogLine = async (logLine: any) => testApp.invokeGetPath( `/write-log-line?logLine=${encodeURIComponent(JSON.stringify(logLine))}` diff --git a/test/utils/fake-edge.ts b/test/utils/fake-edge.ts index d79450b8..6cdc00ed 100644 --- a/test/utils/fake-edge.ts +++ b/test/utils/fake-edge.ts @@ -1,14 +1,22 @@ import {IResource} from "@opentelemetry/resources"; import { LogRecord } from "@opentelemetry/sdk-logs"; +import type { Span } from '@opentelemetry/api'; import express, { Express, Request, Response } from 'express'; import { Server } from "http"; import { AddressInfo } from "net"; -import pRetry, { FailedAttemptError } from 'p-retry'; +import pRetry from 'p-retry'; + +type FakeEdgeState = { + logs: LogRecord[]; + spans: Span[]; + resources: IResource[]; +} export class FakeEdge { private app: Express; - private _logs: LogRecord[] = []; - private _resources: IResource[] = []; + public logs: LogRecord[] = []; + public spans: Span[] = []; + public resources: IResource[] = []; private server: Server; private baseUrl: string; @@ -16,18 +24,29 @@ export class FakeEdge { this.app = express(); this.app.use(express.json()); this.app.use('/v1/traces', (req: Request, res: Response) => { - // Currently not used in any tests, just respond with 200 so spans posted during logging tests will not produce errors + try { + const scopeSpans = req.body.resourceSpans.flatMap(rl => rl.scopeSpans.flatMap(sl => sl.scopeSpans)) + console.log(`Received ${scopeSpans.length} logs in edge`); + this.spans.push(...scopeSpans) + + const resources = req.body.resourceSpans.map(rl => rl.resource) + console.log(`Received ${resources.length} resources in edge`); + this.resources.push(...resources) + } catch (e) { + console.error('Error parsing spans in edge: ', e); + return res.sendStatus(500); + } res.sendStatus(200); }); this.app.use('/v1/logs', (req: Request, res: Response) => { try { const logRecords = req.body.resourceLogs.flatMap(rl => rl.scopeLogs.flatMap(sl => sl.logRecords)) console.log(`Received ${logRecords.length} logs in edge`); - this._logs.push(...logRecords) + this.logs.push(...logRecords) const resources = req.body.resourceLogs.map(rl => rl.resource) console.log(`Received ${resources.length} resources in edge`); - this._resources.push(...resources) + this.resources.push(...resources) } catch (e) { console.error('Error parsing logs in edge: ', e); @@ -59,10 +78,10 @@ export class FakeEdge { return `http://${this.baseUrl}/v1/traces`; } - async waitFor(condition: () => boolean, message: string, timeout = 5000) { - await pRetry(() => { - if (condition()) { - return Promise.resolve(); + async waitFor(condition: (edgeFakeState: FakeEdgeState) => boolean, message: string, timeout = 5000) { + return pRetry(() => { + if (condition({ logs: this.logs, spans: this.spans, resources: this.resources })) { + return Promise.resolve(true); } else { return Promise.reject(new Error('Condition not met')); } @@ -81,16 +100,9 @@ export class FakeEdge { }); } - get logs() { - return this._logs; - } - - get resources() { - return this._resources; - } - reset() { - this._logs = []; - this._resources = []; + this.logs = []; + this.spans = []; + this.resources = []; } } \ No newline at end of file diff --git a/test/utils/versions.ts b/test/utils/versions.ts index 06c2afd4..f0177b14 100644 --- a/test/utils/versions.ts +++ b/test/utils/versions.ts @@ -1,7 +1,6 @@ import { readFileSync } from 'fs'; import { dirname } from 'path'; import { instrumentationsVersionManager } from '../helpers/InstrumentationsVersionManager'; -import {runOneTimeWrapper} from "@lumigo/node-core/lib/common"; const VERSION_UNDER_TEST = process.env.INSTRUMENTATION_UNDER_TEST &&