This is a fork of
DataDog/orchestrion-js
.
This is a library to aid in instrumenting Node.js libraries at build or load time.
It uses SWC's Rust AST walker to inject code that calls Node.js
TracingChannel
.
You likely don't want to use this library directly; instead, consider using:
@apm-js-collab/tracing-hooks/
- ESM and
require
hooks to instrument modules as they are loaded.
- ESM and
apm-js-collab/code-transformer-bundler-plugins
- Bundler plugins for webpack, Vite, Rollup and esbuild to instrument modules at build time.
@apm-js-collab/code-transformer
exposes the Rust library as a WebAssembly
module.
To build the JavaScript module:
- Ensure you have Rust installed
- Install the wasm toolchain
rustup target add wasm32-unknown-unknown --toolchain stable
- Install dependencies and build the module
npm install && npm run build
import * as codeTransformer from "@apm-js-collab/code-transformer";
// The full instrumentation config
const instrumentation = {
// The name of the diagnostics channel
channelName: "my-channel",
// Define the module you'd like to inject tracing channels into
module: {
name: "my-module",
versionRange: ">=1.0.0",
filePath: "./dist/index.js",
},
// Define the function you'd like to instrument
// (e.g., match a method named 'foo' that returns a Promise)
functionQuery: {
methodName: "fetch",
kind: "Async",
},
};
// Create an InstrumentationMatcher with an array of instrumentation configs
const matcher = codeTransformer.create([instrumentation]);
// Get a transformer for a specific module
const transformer = matcher.getTransformer(
"my-module",
"1.2.3",
"./dist/index.js",
);
if (transformer === undefined) {
throw new Error("No transformer found for module");
}
// Transform code
const inputCode = "async function fetch() { return 42; }";
const result = transformer.transform(inputCode, "unknown");
console.log(result.code);
// Both the matcher and transformer should be freed after use!
matcher.free();
transformer.free();
type ModuleType = "esm" | "cjs" | "unknown";
type FunctionKind = "Sync" | "Async";
type FunctionQuery =
| // Match class constructor
{ className: string; index?: number }
| // Match class method
{
className: string;
methodName: string;
kind: FunctionKind;
index?: number;
}
| // Match method on objects
{ methodName: string; kind: FunctionKind; index?: number }
| // Match standalone function
{ functionName: string; kind: FunctionKind; index?: number }
| // Match arrow function or function expression
{ expressionName: string; kind: FunctionKind; index?: number };
type ModuleMatcher = {
name: string; // Module name
versionRange: string; // Matching semver range
filePath: string; // Path to the file from the module root
};
type InstrumentationConfig = {
channelName: string; // Name of the diagnostics channel
module: ModuleMatcher;
functionQuery: FunctionQuery;
};
create(configs: InstrumentationConfig[], dc_module?: string | null): InstrumentationMatcher;
Create a matcher for one or more instrumentation configurations.
configs
- Array of instrumentation configurations.dc_module
- Optional module to importdiagnostics_channel
API from.
getTransformer(module_name: string, version: string, file_path: string): Transformer | undefined;
Gets a transformer for a specific module and file.
Returns a Transformer
for the given module, or undefined
if there were no
matching instrumentation configurations.
module_name
- Name of the module.version
- Version of the module.file_path
- Path to the file from the module root.
free(): void;
Free the matcher memory when it's no longer needed.
transform(code: string, module_type: ModuleType, sourcemap?: string | undefined): TransformOutput;
Transforms the code, injecting tracing as configured.
Returns { code, map }
. map
will be undefined if no sourcemap was supplied.
code
- The JavaScript/TypeScript code to transform.module_type
- The type of module being transformed.sourcemap
- Optional existing source map for the code.
free(): void;
Free the transformer memory when it's no longer needed.
See LICENSE