Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: support 'otel:bundle:load' diagnostics_channel message for instrumentation in bundles #4818

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@

import * as types from '../../types';
import * as path from 'path';
import * as diagch from 'diagnostics_channel'; // XXX added v14.17. Do we need to conditionally import?
import { types as utilTypes } from 'util';
import { satisfies } from 'semver';
import { wrap, unwrap, massWrap, massUnwrap } from 'shimmer';
Expand All @@ -30,6 +31,7 @@ import {
InstrumentationConfig,
InstrumentationModuleDefinition,
} from '../../types';
import { DiagChSubscribe, OTelBundleLoadMessage } from '../../types_internal';
import { diag } from '@opentelemetry/api';
import type { OnRequireFn } from 'require-in-the-middle';
import { Hook as HookRequire } from 'require-in-the-middle';
Expand Down Expand Up @@ -186,9 +188,10 @@ export abstract class InstrumentationBase<
module: InstrumentationModuleDefinition,
exports: T,
name: string,
baseDir?: string | void
baseDir?: string | void,
version?: string
): T {
if (!baseDir) {
if (!version && !baseDir) {
if (typeof module.patch === 'function') {
module.moduleExports = exports;
if (this._enabled) {
Expand All @@ -204,7 +207,9 @@ export abstract class InstrumentationBase<
return exports;
}

const version = this._extractPackageVersion(baseDir);
if (!version && baseDir) {
version = this._extractPackageVersion(baseDir);
}
module.moduleVersion = version;
if (module.name === name) {
// main module
Expand Down Expand Up @@ -293,6 +298,9 @@ export abstract class InstrumentationBase<
}

this._warnOnPreloadedModules();

const imdFromHookPath: Map<string, InstrumentationModuleDefinition> =

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const imdFromHookPath: Map<string, InstrumentationModuleDefinition> =
const imdsFromHookPath: Map<string, InstrumentationModuleDefinition[]> =

new Map();
for (const module of this._modules) {
const hookFn: HookFn = (exports, name, baseDir) => {
return this._onRequire<typeof exports>(module, exports, name, baseDir);
Expand All @@ -315,6 +323,45 @@ export abstract class InstrumentationBase<
<HookFn>hookFn
);
this._hooks.push(esmHook);

imdFromHookPath.set(module.name, module);
for (const file of module.files) {
imdFromHookPath.set(file.name, module);
}
Comment on lines +327 to +330

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
imdFromHookPath.set(module.name, module);
for (const file of module.files) {
imdFromHookPath.set(file.name, module);
}
const imdsByModuleName = imdsFromHookPath.get(module.name) ?? [];
imdsFromHookPath.set(module.name, imdsByModuleName);
imdsByModuleName.push(module);
for (const file of module.files) {
const imdsByFileName = imdsFromHookPath.get(file.name) ?? [];
imdsFromHookPath.set(file.name, imdsByFileName);
imdsByFileName.push(module);
}

}

// `diagch.subscribe` was added in Node.js v18.7.0, v16.17.0.
const subscribe: DiagChSubscribe = (diagch as any).subscribe;
if (typeof subscribe === 'function') {
// A bundler plugin, e.g. `@opentelemetry/esbuild-plugin`, can pass
// a loaded module to this instrumentation via the well-known
// `otel:bundle:load` diagnostics channel message. The message includes
// the module exports, that can be patched in-place.
subscribe('otel:bundle:load', message_ => {
const message = message_ as OTelBundleLoadMessage; // XXX TS advice
if (
typeof message.name !== 'string' ||
typeof message.version !== 'string'
) {
this._diag.debug(
'skipping invalid "otel:bundle:load" diagch message'
);
return;
}
const imd = imdFromHookPath.get(message.name);
if (!imd) {
Comment on lines +351 to +352
Copy link

@rossipedia rossipedia Sep 7, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const imd = imdFromHookPath.get(message.name);
if (!imd) {
const imds = imdsFromHookPath.get(message.name);
if (!imds) {

// This loaded module is not relevant for this instrumentation.
return;
}
const patchedExports = this._onRequire<typeof exports>(
imd,
message.exports,
message.name,
undefined,
message.version // Package version was determined at bundle-time.
);
message.exports = patchedExports;
Comment on lines +356 to +363

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
const patchedExports = this._onRequire<typeof exports>(
imd,
message.exports,
message.name,
undefined,
message.version // Package version was determined at bundle-time.
);
message.exports = patchedExports;
for (const imd of imds) {
const patchedExports = this._onRequire<typeof exports>(
imd,
message.exports,
message.name,
undefined,
message.version // Package version was determined at bundle-time.
);
message.exports = patchedExports;
}

});
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,3 +28,24 @@ export interface AutoLoaderOptions {
meterProvider?: MeterProvider;
loggerProvider?: LoggerProvider;
}

/**
* A subset of types for Node.js `diagnostics_channel`.
* `diagnostics_channel.subscribe` was added in Node.js v18.7.0, v16.17.0.
* The current `@types/node` dependency is for an earlier version (v14) of
* Node.js
*/
type DiagChChannelListener = (message: unknown, name: string | symbol) => void;
export type DiagChSubscribe = (
name: string | symbol,
onMessage: DiagChChannelListener
) => void;

/**
* The shape of a `otel:bundle:load` diagnostics_channel message.
*/
export type OTelBundleLoadMessage = {
name: string;
version: string;
exports: any;
};
Loading