Plugin development

A plugin patches a Node.js library so calls made by that library create SkyWalking spans. Read an existing plugin close to your target library before adding a new one.

Plugin contract

Each automatic plugin in src/plugins/ implements SwPlugin and exports one default instance:

class ExamplePlugin implements SwPlugin {
  readonly module = 'example-library';
  readonly versions = '^1.0.0';

  install(installer: PluginInstaller): void {
    const target = installer.require?.(this.module) ?? require(this.module);
    // Patch the target API.
  }
}

export default new ExamplePlugin();

Use a file name ending in Plugin.ts. The normal loader scans compiled files in lib/plugins/.

  • module is the npm module that must be installed in the application.
  • versions is a semantic version range checked by the loader.
  • Set isBuiltIn = true only for a Node.js built-in module such as http.
  • Use installer.require when possible so the plugin loads the application’s copy of the module.

Choose a version range from real API support. Do not use * only to avoid checking versions.

Instrumentation rules

Keep library behavior unchanged apart from trace collection:

  1. Save the original function before replacing it.
  2. Preserve this, arguments, return values, thrown errors, callbacks, events, and Promise results.
  3. Create the correct entry, exit, or local span from ContextManager.current.
  4. Set the component, span layer, operation name, peer, and standard tags when the data exists.
  5. Record errors and stop every span on all completion paths.
  6. Use span.async() and span.resync() when work leaves and later returns to the active async path.
  7. Do not record request bodies, database parameters, or other private values by default.

src/core/SwPlugin.ts provides helpers for common callback, Promise, and event-emitter completion forms. Some libraries need a local helper because their API has different completion rules.

Use an existing component in src/trace/Component.ts when it matches. A new component ID must be agreed with the SkyWalking project so it does not conflict with another agent or integration.

Add tests

Create tests/plugins/<module>/ by following a similar plugin suite. A normal suite contains:

  • test.ts to start Docker Compose and send expected data to the mock collector;
  • docker-compose.yml for the collector, application, and target service;
  • client and server files that exercise the instrumented API;
  • expected.data.yaml with the expected SkyWalking segment data.

Add the target package to devDependencies and update package-lock.json when the test needs it. The CI plugin matrix finds each directory under tests/plugins/ except common, so a new directory becomes a CI job without a manual matrix entry.

Run the suite:

npm run test tests/plugins/example-library/

Also run lint and build as described in Build and test.

Webpack support

The normal loader finds a compiled plugin automatically. The Webpack loader does not. If the target package allows its package.json to be imported, add the plugin to PluginInstaller.installBundled() and test a production bundle.

If this is not possible, state the limit in Webpack. Do not claim Webpack support based only on the normal plugin test.

Update documentation

For a new plugin:

  1. Add the library and current CI state to Supported libraries.
  2. Add configuration details if the plugin adds settings.
  3. Add important behavior changes, such as changed batching or payload data.
  4. Update troubleshooting when the plugin has a common setup limit.