diff --git a/package-lock.json b/package-lock.json index db3da71029..8508f235b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -37387,11 +37387,15 @@ "version": "0.27.0", "license": "Apache-2.0", "dependencies": { + "@opentelemetry/api-logs": "^0.214.0", + "@opentelemetry/core": "^2.0.0", "@opentelemetry/instrumentation": "^0.214.0" }, "devDependencies": { "@opentelemetry/api": "^1.3.0", - "@opentelemetry/sdk-metrics": "^2.0.0" + "@opentelemetry/sdk-logs": "^0.214.0", + "@opentelemetry/sdk-metrics": "^2.0.0", + "@opentelemetry/semantic-conventions": "^1.29.0" }, "engines": { "node": "^18.19.0 || >=20.6.0" @@ -37400,6 +37404,21 @@ "@opentelemetry/api": "^1.3.0" } }, + "packages/instrumentation-runtime-node/node_modules/@opentelemetry/core": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.5.0.tgz", + "integrity": "sha512-ka4H8OM6+DlUhSAZpONu0cPBtPPTQKxbxVzC4CzVx5+K4JnroJVBtDzLAMx4/3CDTJXRvVFhpFjtl4SaiTNoyQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, "packages/instrumentation-sequelize": { "name": "@opentelemetry/instrumentation-sequelize", "version": "0.7.0", diff --git a/packages/instrumentation-runtime-node/README.md b/packages/instrumentation-runtime-node/README.md index 890269f574..6cc4841537 100644 --- a/packages/instrumentation-runtime-node/README.md +++ b/packages/instrumentation-runtime-node/README.md @@ -4,7 +4,11 @@ [![Apache License][license-image]][license-image] This module provides automatic metric instrumentation that exposes measurements from the [Performance measurement APIs](https://nodejs.org/api/perf_hooks.html) (i.e. `perf_hooks`). -While currently it is limited to metrics, it may be modified to produce other signals in the future. +It can also emit OpenTelemetry logs for uncaught exceptions. +When a configured logger provider exposes `forceFlush()` (for example, the SDK +`LoggerProvider`), this instrumentation calls it immediately after emitting the +uncaught-exception log record as a best-effort attempt to reduce log loss on +process termination. ## Supported Versions @@ -63,6 +67,8 @@ nodejs_performance_event_loop_utilization 0.010140079547955264 | name | type | unit | default | description | |---------------------------------------------|-------|-------------|---------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | [`monitoringPrecision`](./src/types.ts#L25) | `int` | millisecond | `10` | The approximate number of milliseconds for which to calculate event loop utilization averages. A larger value will result in more accurate averages at the expense of less granular data. Should be set to below the scrape interval of your metrics collector to avoid duplicated data points. | +| [`captureUncaughtException`](./src/types.ts#L31) | `bool` | - | `false` | Whether to emit a `LogRecord` for uncaught exceptions (severity `FATAL`). Uses the `uncaughtExceptionMonitor` process event. | +| [`applyCustomAttributes`](./src/types.ts#L43) | `function` | - | `undefined` | Optional callback to attach custom attributes to emitted exception log records. | ## Useful links diff --git a/packages/instrumentation-runtime-node/package.json b/packages/instrumentation-runtime-node/package.json index a89b8b7689..0bdb252efe 100644 --- a/packages/instrumentation-runtime-node/package.json +++ b/packages/instrumentation-runtime-node/package.json @@ -40,11 +40,15 @@ "access": "public" }, "dependencies": { + "@opentelemetry/api-logs": "^0.214.0", + "@opentelemetry/core": "^2.0.0", "@opentelemetry/instrumentation": "^0.214.0" }, "devDependencies": { "@opentelemetry/api": "^1.3.0", - "@opentelemetry/sdk-metrics": "^2.0.0" + "@opentelemetry/sdk-logs": "^0.214.0", + "@opentelemetry/sdk-metrics": "^2.0.0", + "@opentelemetry/semantic-conventions": "^1.29.0" }, "peerDependencies": { "@opentelemetry/api": "^1.3.0" diff --git a/packages/instrumentation-runtime-node/src/instrumentation.ts b/packages/instrumentation-runtime-node/src/instrumentation.ts index 9d0c0b441a..c23b92168e 100644 --- a/packages/instrumentation-runtime-node/src/instrumentation.ts +++ b/packages/instrumentation-runtime-node/src/instrumentation.ts @@ -14,6 +14,13 @@ * limitations under the License. */ import { InstrumentationBase } from '@opentelemetry/instrumentation'; +import type { + AnyValueMap, + LogRecord, + LoggerProvider, +} from '@opentelemetry/api-logs'; +import { SeverityNumber } from '@opentelemetry/api-logs'; +import { hrTime } from '@opentelemetry/core'; import { RuntimeNodeInstrumentationConfig } from './types'; import { MetricCollector } from './types/metricCollector'; @@ -27,10 +34,16 @@ import { PACKAGE_VERSION, PACKAGE_NAME } from './version'; const DEFAULT_CONFIG: RuntimeNodeInstrumentationConfig = { monitoringPrecision: 10, + captureUncaughtException: false, }; export class RuntimeNodeInstrumentation extends InstrumentationBase { private readonly _collectors: MetricCollector[] = []; + private _loggerProvider?: LoggerProvider; + private _onUncaughtExceptionHandler?: ( + error: Error, + origin: NodeJS.UncaughtExceptionOrigin + ) => void; constructor(config: RuntimeNodeInstrumentationConfig = {}) { super( @@ -49,6 +62,7 @@ export class RuntimeNodeInstrumentation extends InstrumentationBase Promise }) + | undefined; + if (typeof loggerProvider?.forceFlush !== 'function') { + return; + } + + try { + void loggerProvider.forceFlush().catch(err => { + this._diag.error( + 'loggerProvider.forceFlush failed while handling an exception', + err + ); + }); + } catch (err) { + this._diag.error( + 'loggerProvider.forceFlush threw while handling an exception', + err + ); + } } } diff --git a/packages/instrumentation-runtime-node/src/types.ts b/packages/instrumentation-runtime-node/src/types.ts index 68227c98f2..c88ec7ed6f 100644 --- a/packages/instrumentation-runtime-node/src/types.ts +++ b/packages/instrumentation-runtime-node/src/types.ts @@ -13,9 +13,24 @@ * See the License for the specific language governing permissions and * limitations under the License. */ +import type { Attributes } from '@opentelemetry/api'; import type { InstrumentationConfig } from '@opentelemetry/instrumentation'; export interface RuntimeNodeInstrumentationConfig extends InstrumentationConfig { monitoringPrecision?: number; + /** + * Capture uncaught exceptions via process 'uncaughtExceptionMonitor' event. + * Disabled by default. + * @experimental + */ + captureUncaughtException?: boolean; + /** + * Add custom attributes to the emitted exception log records. + * @experimental + */ + applyCustomAttributes?: ( + error: unknown, + eventType: 'uncaughtException' + ) => Attributes; } diff --git a/packages/instrumentation-runtime-node/test/event_loop_delay.test.ts b/packages/instrumentation-runtime-node/test/event_loop_delay.test.ts index 31b3a71e55..1a5d3dcc30 100644 --- a/packages/instrumentation-runtime-node/test/event_loop_delay.test.ts +++ b/packages/instrumentation-runtime-node/test/event_loop_delay.test.ts @@ -16,7 +16,7 @@ import * as assert from 'assert'; import { MeterProvider, DataPointType } from '@opentelemetry/sdk-metrics'; -import { RuntimeNodeInstrumentation } from '../src'; +import { RuntimeNodeInstrumentation } from '../src/index'; import { TestMetricReader } from './testMetricsReader'; import * as semconv from '../src/semconv'; @@ -39,6 +39,7 @@ describe('nodejs.eventloop.delay.*', function () { // arrange const instrumentation = new RuntimeNodeInstrumentation({ monitoringPrecision: 10, + captureUncaughtException: false, }); instrumentation.setMeterProvider(meterProvider); diff --git a/packages/instrumentation-runtime-node/test/event_loop_time.test.ts b/packages/instrumentation-runtime-node/test/event_loop_time.test.ts index 57544d16bf..b8960c3210 100644 --- a/packages/instrumentation-runtime-node/test/event_loop_time.test.ts +++ b/packages/instrumentation-runtime-node/test/event_loop_time.test.ts @@ -16,7 +16,7 @@ import * as assert from 'assert'; import { MeterProvider } from '@opentelemetry/sdk-metrics'; -import { RuntimeNodeInstrumentation } from '../src'; +import { RuntimeNodeInstrumentation } from '../src/index'; import { TestMetricReader } from './testMetricsReader'; import { METRIC_NODEJS_EVENTLOOP_TIME } from '../src/semconv'; @@ -38,6 +38,7 @@ describe('nodejs.eventloop.time', function () { const instrumentation = new RuntimeNodeInstrumentation({ monitoringPrecision: MEASUREMENT_INTERVAL, enabled: false, + captureUncaughtException: false, }); instrumentation.setMeterProvider(meterProvider); @@ -55,6 +56,7 @@ describe('nodejs.eventloop.time', function () { // arrange const instrumentation = new RuntimeNodeInstrumentation({ monitoringPrecision: MEASUREMENT_INTERVAL, + captureUncaughtException: false, }); instrumentation.setMeterProvider(meterProvider); diff --git a/packages/instrumentation-runtime-node/test/event_loop_utilization.test.ts b/packages/instrumentation-runtime-node/test/event_loop_utilization.test.ts index 191c98951b..529d17adcf 100644 --- a/packages/instrumentation-runtime-node/test/event_loop_utilization.test.ts +++ b/packages/instrumentation-runtime-node/test/event_loop_utilization.test.ts @@ -16,7 +16,7 @@ import * as assert from 'assert'; import { MeterProvider } from '@opentelemetry/sdk-metrics'; -import { RuntimeNodeInstrumentation } from '../src'; +import { RuntimeNodeInstrumentation } from '../src/index'; import { TestMetricReader } from './testMetricsReader'; import { METRIC_NODEJS_EVENTLOOP_UTILIZATION } from '../src/semconv'; @@ -38,6 +38,7 @@ describe('nodejs.eventloop.utilization', function () { const instrumentation = new RuntimeNodeInstrumentation({ monitoringPrecision: MEASUREMENT_INTERVAL, enabled: false, + captureUncaughtException: false, }); instrumentation.setMeterProvider(meterProvider); @@ -55,6 +56,7 @@ describe('nodejs.eventloop.utilization', function () { // arrange const instrumentation = new RuntimeNodeInstrumentation({ monitoringPrecision: MEASUREMENT_INTERVAL, + captureUncaughtException: false, }); instrumentation.setMeterProvider(meterProvider); @@ -173,9 +175,17 @@ describe('nodejs.eventloop.utilization', function () { 'Expected utilization in fourth measurement to be 1' ); - // Fifth measurement: Do some NON-blocking work (sanity check, should be low) - await new Promise(resolve => setTimeout(resolve, 50)); - const fifthUtilization = await collectUtilization(); + // Fifth measurement: Do some non-blocking work and retry a few times to + // avoid a timing flake where one collection window can still include the + // previous busy period. + let fifthUtilization = 1; + for (let i = 0; i < 5; i++) { + await new Promise(resolve => setTimeout(resolve, 50)); + fifthUtilization = await collectUtilization(); + if (fifthUtilization < 1) { + break; + } + } assert.ok( fifthUtilization < 1, `Expected utilization in fifth measurement to be less than 1, but got ${fifthUtilization}` diff --git a/packages/instrumentation-runtime-node/test/exception-logging.test.ts b/packages/instrumentation-runtime-node/test/exception-logging.test.ts new file mode 100644 index 0000000000..0e9c3fce71 --- /dev/null +++ b/packages/instrumentation-runtime-node/test/exception-logging.test.ts @@ -0,0 +1,375 @@ +/* + * Copyright The OpenTelemetry Authors + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import * as assert from 'assert'; +import { + InMemoryLogRecordExporter, + LoggerProvider, + SimpleLogRecordProcessor, +} from '@opentelemetry/sdk-logs'; +import { SeverityNumber } from '@opentelemetry/api-logs'; +import { + ATTR_EXCEPTION_MESSAGE, + ATTR_EXCEPTION_STACKTRACE, + ATTR_EXCEPTION_TYPE, +} from '@opentelemetry/semantic-conventions'; + +import { RuntimeNodeInstrumentation } from '../src/index'; +import type { RuntimeNodeInstrumentationConfig } from '../src/types'; + +describe('runtime exception logging', () => { + let exporter: InMemoryLogRecordExporter; + let instrumentation: RuntimeNodeInstrumentation; + + beforeEach(() => { + exporter = new InMemoryLogRecordExporter(); + const loggerProvider = new LoggerProvider({ + processors: [new SimpleLogRecordProcessor(exporter)], + }); + instrumentation = new RuntimeNodeInstrumentation({ + captureUncaughtException: true, + }); + instrumentation.setLoggerProvider(loggerProvider); + instrumentation.enable(); + }); + + afterEach(() => { + instrumentation.disable(); + }); + + it('emits a FATAL log for uncaught exceptions', () => { + const error = new Error('Something went wrong'); + error.name = 'ValidationError'; + error.stack = 'Error: Something went wrong\n at foo (file.js:1:1)'; + + (process.emit as unknown as (event: string, ...args: unknown[]) => boolean)( + 'uncaughtExceptionMonitor', + error, + 'uncaughtException' + ); + + const records = exporter.getFinishedLogRecords(); + assert.ok(records.length >= 1); + const record = records[records.length - 1]; + assert.strictEqual(record.body, 'exception'); + assert.strictEqual(record.severityNumber, SeverityNumber.FATAL); + assert.strictEqual( + record.attributes[ATTR_EXCEPTION_MESSAGE], + 'Something went wrong' + ); + assert.strictEqual( + record.attributes[ATTR_EXCEPTION_TYPE], + 'ValidationError' + ); + assert.strictEqual( + record.attributes[ATTR_EXCEPTION_STACKTRACE], + error.stack + ); + }); + + it('captures attributes from non-Error objects', () => { + const rejection = { + name: 'WeirdError', + message: 'bad', + stack: 'stack here', + }; + const asAny = instrumentation as unknown as { + _emitExceptionLog: ( + error: unknown, + severityNumber: SeverityNumber, + eventType: 'uncaughtException' + ) => void; + }; + asAny._emitExceptionLog( + rejection, + SeverityNumber.FATAL, + 'uncaughtException' + ); + + const records = exporter.getFinishedLogRecords(); + assert.ok(records.length >= 1); + const record = records[records.length - 1]; + assert.strictEqual(record.attributes[ATTR_EXCEPTION_TYPE], 'WeirdError'); + assert.strictEqual(record.attributes[ATTR_EXCEPTION_MESSAGE], 'bad'); + assert.strictEqual( + record.attributes[ATTR_EXCEPTION_STACKTRACE], + 'stack here' + ); + }); + + it('does not set exception message for unknown object shapes', () => { + const rejection = { foo: 'bar' }; + const asAny = instrumentation as unknown as { + _emitExceptionLog: ( + error: unknown, + severityNumber: SeverityNumber, + eventType: 'uncaughtException' + ) => void; + }; + asAny._emitExceptionLog( + rejection, + SeverityNumber.FATAL, + 'uncaughtException' + ); + + const records = exporter.getFinishedLogRecords(); + assert.ok(records.length >= 1); + const record = records[records.length - 1]; + assert.strictEqual(record.attributes[ATTR_EXCEPTION_MESSAGE], undefined); + }); + + it('stringifies non-object values', () => { + const asAny = instrumentation as unknown as { + _emitExceptionLog: ( + error: unknown, + severityNumber: SeverityNumber, + eventType: 'uncaughtException' + ) => void; + }; + asAny._emitExceptionLog(42, SeverityNumber.FATAL, 'uncaughtException'); + + const records = exporter.getFinishedLogRecords(); + assert.ok(records.length >= 1); + const record = records[records.length - 1]; + assert.strictEqual(record.attributes[ATTR_EXCEPTION_MESSAGE], '42'); + }); + + it('applies custom attributes and passes event type', () => { + instrumentation.disable(); + instrumentation = new RuntimeNodeInstrumentation({ + captureUncaughtException: true, + applyCustomAttributes: (_error, eventType) => ({ + 'app.event.type': eventType, + }), + }); + const loggerProvider = new LoggerProvider({ + processors: [new SimpleLogRecordProcessor(exporter)], + }); + instrumentation.setLoggerProvider(loggerProvider); + instrumentation.enable(); + + (process.emit as unknown as (event: string, ...args: unknown[]) => boolean)( + 'uncaughtExceptionMonitor', + new Error('boom'), + 'uncaughtException' + ); + + const records = exporter.getFinishedLogRecords(); + assert.ok(records.length >= 1); + const record = records[records.length - 1]; + assert.strictEqual( + record.attributes['app.event.type'], + 'uncaughtException' + ); + }); + + it('emits via internal log helper', () => { + const asAny = instrumentation as unknown as { + _emitExceptionLog: ( + error: unknown, + severityNumber: SeverityNumber, + eventType: 'uncaughtException' + ) => void; + _handleUncaughtException: ( + error: Error, + origin: NodeJS.UncaughtExceptionOrigin + ) => void; + }; + + asAny._emitExceptionLog('boom', SeverityNumber.ERROR, 'uncaughtException'); + asAny._handleUncaughtException(new Error('oops'), 'uncaughtException'); + + const records = exporter.getFinishedLogRecords(); + assert.ok(records.length >= 1); + const severities = records.map(record => record.severityNumber); + assert.ok(severities.includes(SeverityNumber.ERROR)); + assert.ok(severities.includes(SeverityNumber.FATAL)); + }); + + it('keeps emitting when applyCustomAttributes throws', () => { + instrumentation.disable(); + instrumentation = new RuntimeNodeInstrumentation({ + captureUncaughtException: true, + applyCustomAttributes: () => { + throw new Error('boom'); + }, + }); + const loggerProvider = new LoggerProvider({ + processors: [new SimpleLogRecordProcessor(exporter)], + }); + instrumentation.setLoggerProvider(loggerProvider); + instrumentation.enable(); + + (process.emit as unknown as (event: string, ...args: unknown[]) => boolean)( + 'uncaughtExceptionMonitor', + new Error('nope'), + 'uncaughtException' + ); + + const records = exporter.getFinishedLogRecords(); + assert.ok(records.length >= 1); + const record = records[records.length - 1]; + assert.strictEqual(record.attributes[ATTR_EXCEPTION_MESSAGE], 'nope'); + }); + + it('handles applyCustomAttributes returning undefined', () => { + instrumentation.disable(); + const config = { + captureUncaughtException: true, + applyCustomAttributes: () => undefined, + } as unknown as RuntimeNodeInstrumentationConfig; + instrumentation = new RuntimeNodeInstrumentation(config); + const loggerProvider = new LoggerProvider({ + processors: [new SimpleLogRecordProcessor(exporter)], + }); + instrumentation.setLoggerProvider(loggerProvider); + instrumentation.enable(); + + (process.emit as unknown as (event: string, ...args: unknown[]) => boolean)( + 'uncaughtExceptionMonitor', + new Error('nope'), + 'uncaughtException' + ); + + const records = exporter.getFinishedLogRecords(); + assert.ok(records.length >= 1); + const record = records[records.length - 1]; + assert.strictEqual(record.attributes[ATTR_EXCEPTION_MESSAGE], 'nope'); + }); + + it('does not emit logs when disabled', () => { + instrumentation.disable(); + + (process.emit as unknown as (event: string, ...args: unknown[]) => boolean)( + 'uncaughtExceptionMonitor', + new Error('boom'), + 'uncaughtException' + ); + const records = exporter.getFinishedLogRecords(); + assert.strictEqual(records.length, 0); + }); + + it('skips logging when disabled but handlers are invoked', () => { + const handler = + ( + instrumentation as unknown as { + _onUncaughtExceptionHandler?: ( + error: Error, + origin: NodeJS.UncaughtExceptionOrigin + ) => void; + } + )._onUncaughtExceptionHandler ?? undefined; + + assert.ok(handler, 'expected uncaughtException handler to be registered'); + + instrumentation.disable(); + handler(new Error('nope'), 'uncaughtException'); + + const records = exporter.getFinishedLogRecords(); + assert.strictEqual(records.length, 0); + }); + + it('skips logging when emit helper is called while disabled', () => { + const asAny = instrumentation as unknown as { + _emitExceptionLog: ( + error: unknown, + severityNumber: SeverityNumber, + eventType: 'uncaughtException' + ) => void; + }; + + instrumentation.disable(); + asAny._emitExceptionLog('boom', SeverityNumber.ERROR, 'uncaughtException'); + + const records = exporter.getFinishedLogRecords(); + assert.strictEqual(records.length, 0); + }); + + it('does not register handlers when capture is disabled', () => { + instrumentation.disable(); + instrumentation = new RuntimeNodeInstrumentation({ + captureUncaughtException: false, + }); + const loggerProvider = new LoggerProvider({ + processors: [new SimpleLogRecordProcessor(exporter)], + }); + instrumentation.setLoggerProvider(loggerProvider); + instrumentation.enable(); + + (process.emit as unknown as (event: string, ...args: unknown[]) => boolean)( + 'uncaughtExceptionMonitor', + new Error('boom'), + 'uncaughtException' + ); + const records = exporter.getFinishedLogRecords(); + assert.strictEqual(records.length, 0); + }); + + it('calls loggerProvider.forceFlush after emitting uncaught exceptions', () => { + instrumentation.disable(); + exporter = new InMemoryLogRecordExporter(); + const loggerProvider = new LoggerProvider({ + processors: [new SimpleLogRecordProcessor(exporter)], + }); + let forceFlushCalls = 0; + const originalForceFlush = loggerProvider.forceFlush.bind(loggerProvider); + loggerProvider.forceFlush = () => { + forceFlushCalls++; + return originalForceFlush(); + }; + + instrumentation = new RuntimeNodeInstrumentation({ + captureUncaughtException: true, + }); + instrumentation.setLoggerProvider(loggerProvider); + instrumentation.enable(); + + (process.emit as unknown as (event: string, ...args: unknown[]) => boolean)( + 'uncaughtExceptionMonitor', + new Error('boom'), + 'uncaughtException' + ); + + const records = exporter.getFinishedLogRecords(); + assert.ok(records.length >= 1); + assert.strictEqual(forceFlushCalls, 1); + }); + + it('swallows loggerProvider.forceFlush failures', async () => { + instrumentation.disable(); + exporter = new InMemoryLogRecordExporter(); + const loggerProvider = new LoggerProvider({ + processors: [new SimpleLogRecordProcessor(exporter)], + }); + loggerProvider.forceFlush = () => Promise.reject(new Error('flush failed')); + + instrumentation = new RuntimeNodeInstrumentation({ + captureUncaughtException: true, + }); + instrumentation.setLoggerProvider(loggerProvider); + instrumentation.enable(); + + (process.emit as unknown as (event: string, ...args: unknown[]) => boolean)( + 'uncaughtExceptionMonitor', + new Error('boom'), + 'uncaughtException' + ); + await Promise.resolve(); + + const records = exporter.getFinishedLogRecords(); + assert.ok(records.length >= 1); + }); +}); diff --git a/packages/instrumentation-runtime-node/test/heap_space_and_used.test.ts b/packages/instrumentation-runtime-node/test/heap_space_and_used.test.ts index b7ad9113cb..3a414485c9 100644 --- a/packages/instrumentation-runtime-node/test/heap_space_and_used.test.ts +++ b/packages/instrumentation-runtime-node/test/heap_space_and_used.test.ts @@ -17,7 +17,7 @@ import * as assert from 'assert'; import { DataPointType, MeterProvider } from '@opentelemetry/sdk-metrics'; import { GaugeMetricData } from '@opentelemetry/sdk-metrics/build/src/export/MetricData'; -import { RuntimeNodeInstrumentation } from '../src'; +import { RuntimeNodeInstrumentation } from '../src/index'; import { TestMetricReader } from './testMetricsReader'; import { ATTR_V8JS_HEAP_SPACE_NAME, @@ -51,6 +51,7 @@ describe('v8js.memory.heap.*', function () { // arrange const instrumentation = new RuntimeNodeInstrumentation({ monitoringPrecision: MEASUREMENT_INTERVAL, + captureUncaughtException: false, }); instrumentation.setMeterProvider(meterProvider); @@ -98,6 +99,7 @@ describe('v8js.memory.heap.*', function () { // arrange const instrumentation = new RuntimeNodeInstrumentation({ monitoringPrecision: MEASUREMENT_INTERVAL, + captureUncaughtException: false, }); instrumentation.setMeterProvider(meterProvider); const map = [...Array(10).keys()].map(x => x + 10); diff --git a/packages/instrumentation-runtime-node/test/instrumentation.test.ts b/packages/instrumentation-runtime-node/test/instrumentation.test.ts index 4f6e58cafb..cd8960905a 100644 --- a/packages/instrumentation-runtime-node/test/instrumentation.test.ts +++ b/packages/instrumentation-runtime-node/test/instrumentation.test.ts @@ -15,7 +15,7 @@ */ import { MeterProvider } from '@opentelemetry/sdk-metrics'; -import { RuntimeNodeInstrumentation } from '../src'; +import { RuntimeNodeInstrumentation } from '../src/index'; import * as assert from 'assert'; import { TestMetricReader } from './testMetricsReader'; @@ -37,6 +37,7 @@ describe('instrumentation', function () { const instrumentation = new RuntimeNodeInstrumentation({ monitoringPrecision: MEASUREMENT_INTERVAL, enabled: false, + captureUncaughtException: false, }); instrumentation.setMeterProvider(meterProvider); @@ -55,6 +56,7 @@ describe('instrumentation', function () { const instrumentation = new RuntimeNodeInstrumentation({ monitoringPrecision: MEASUREMENT_INTERVAL, enabled: false, + captureUncaughtException: false, }); instrumentation.setMeterProvider(meterProvider);