|
|
@@ -0,0 +1,137 @@
|
|
|
+import { Inject } from '@nestjs/common';
|
|
|
+import { ENABLE_INSTRUMENTATION_ENV_VAR, PluginCommonModule, VendurePlugin } from '@vendure/core';
|
|
|
+
|
|
|
+import { defaultMethodHooks } from './config/default-method-hooks';
|
|
|
+import { OtelInstrumentationStrategy } from './config/otel-instrumentation-strategy';
|
|
|
+import { OtelLogger } from './config/otel-logger';
|
|
|
+import { TELEMETRY_PLUGIN_OPTIONS } from './constants';
|
|
|
+import { MethodHooksService } from './service/method-hooks.service';
|
|
|
+import { TelemetryPluginOptions } from './types';
|
|
|
+
|
|
|
+/**
|
|
|
+ * @description
|
|
|
+ * The TelemetryPlugin is used to instrument the Vendure application and collect telemetry data using
|
|
|
+ * [OpenTelemetry](https://opentelemetry.io/).
|
|
|
+ *
|
|
|
+ * ## Installation
|
|
|
+ *
|
|
|
+ * ```
|
|
|
+ * npm install \@vendure/telemetry-plugin
|
|
|
+ * ```
|
|
|
+ *
|
|
|
+ * ## Configuration
|
|
|
+ *
|
|
|
+ * The plugin is configured via the `TelemetryPlugin.init()` method. This method accepts an options object
|
|
|
+ * which defines the OtelLogger options and method hooks.
|
|
|
+ *
|
|
|
+ * @example
|
|
|
+ * ```ts
|
|
|
+ * import { VendureConfig } from '\@vendure/core';
|
|
|
+ * import { TelemetryPlugin, registerMethodHooks } from '\@vendure/telemetry-plugin';
|
|
|
+ *
|
|
|
+ * export const config: VendureConfig = {
|
|
|
+ * // ...
|
|
|
+ * plugins: [
|
|
|
+ * TelemetryPlugin.init({
|
|
|
+ * loggerOptions: {
|
|
|
+ * // Log to the console at the verbose level
|
|
|
+ * console: LogLevel.Verbose,
|
|
|
+ * },
|
|
|
+ * }),
|
|
|
+ * ],
|
|
|
+ * };
|
|
|
+ * ```
|
|
|
+ *
|
|
|
+ * ## Preloading the SDK
|
|
|
+ *
|
|
|
+ * In order to use the OpenTelemetry SDK, you must preload it before the Vendure server is started.
|
|
|
+ * This is done by using the `--require` flag when starting the server with a custom preload script.
|
|
|
+ *
|
|
|
+ * Create a file named `instrumentation.ts` in the root of your project and add the following code:
|
|
|
+ *
|
|
|
+ * ```ts
|
|
|
+ * import { OTLPLogExporter } from '\@opentelemetry/exporter-logs-otlp-proto';
|
|
|
+ * import { OTLPTraceExporter } from '\@opentelemetry/exporter-trace-otlp-http';
|
|
|
+ * import { BatchLogRecordProcessor } from '\@opentelemetry/sdk-logs';
|
|
|
+ * import { NodeSDK } from '\@opentelemetry/sdk-node';
|
|
|
+ * import { BatchSpanProcessor } from '\@opentelemetry/sdk-trace-base';
|
|
|
+ * import { getSdkConfiguration } from '\@vendure/telemetry-plugin/preload';
|
|
|
+ *
|
|
|
+ * // In this example we are using Loki as the OTLP endpoint for logging
|
|
|
+ * process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://localhost:3100/otlp';
|
|
|
+ * process.env.OTEL_LOGS_EXPORTER = 'otlp';
|
|
|
+ * process.env.OTEL_RESOURCE_ATTRIBUTES = 'service.name=vendure-dev-server';
|
|
|
+ *
|
|
|
+ * // We are using Jaeger as the OTLP endpoint for tracing
|
|
|
+ * const traceExporter = new OTLPTraceExporter({
|
|
|
+ * url: 'http://localhost:4318/v1/traces',
|
|
|
+ * });
|
|
|
+ * const logExporter = new OTLPLogExporter();
|
|
|
+ *
|
|
|
+ * // The getSdkConfiguration method returns a configuration object for the OpenTelemetry Node SDK.
|
|
|
+ * // It also performs other configuration tasks such as setting a special environment variable
|
|
|
+ * // to enable instrumentation in the Vendure core code.
|
|
|
+ * const config = getSdkConfiguration({
|
|
|
+ * config: {
|
|
|
+ * // Pass in any custom configuration options for the Node SDK here
|
|
|
+ * spanProcessors: [new BatchSpanProcessor(traceExporter)],
|
|
|
+ * logRecordProcessors: [new BatchLogRecordProcessor(logExporter)],
|
|
|
+ * },
|
|
|
+ * });
|
|
|
+ *
|
|
|
+ * const sdk = new NodeSDK(config);
|
|
|
+ *
|
|
|
+ * sdk.start();
|
|
|
+ * ```
|
|
|
+ *
|
|
|
+ * The server would then be started with the following command:
|
|
|
+ *
|
|
|
+ * ```bash
|
|
|
+ * node --require ./dist/instrumentation.js ./dist/server.js
|
|
|
+ * ```
|
|
|
+ *
|
|
|
+ * or for development with ts-node:
|
|
|
+ *
|
|
|
+ * ```bash
|
|
|
+ * npx ts-node --require ./src/instrumentation.ts ./src/server.ts
|
|
|
+ * ```
|
|
|
+ *
|
|
|
+ * @since 3.3.0
|
|
|
+ * @docsCategory core plugins/TelemetryPlugin
|
|
|
+ */
|
|
|
+@VendurePlugin({
|
|
|
+ imports: [PluginCommonModule],
|
|
|
+ providers: [
|
|
|
+ MethodHooksService,
|
|
|
+ {
|
|
|
+ provide: TELEMETRY_PLUGIN_OPTIONS,
|
|
|
+ useFactory: () => TelemetryPlugin.options,
|
|
|
+ },
|
|
|
+ ],
|
|
|
+ configuration: config => {
|
|
|
+ config.systemOptions.instrumentationStrategy = new OtelInstrumentationStrategy();
|
|
|
+ config.logger = new OtelLogger(TelemetryPlugin.options.loggerOptions ?? {});
|
|
|
+ return config;
|
|
|
+ },
|
|
|
+ // compatibility: '>3.3.0',
|
|
|
+})
|
|
|
+export class TelemetryPlugin {
|
|
|
+ static options: TelemetryPluginOptions = {};
|
|
|
+
|
|
|
+ constructor(
|
|
|
+ methodHooksService: MethodHooksService,
|
|
|
+ @Inject(TELEMETRY_PLUGIN_OPTIONS) options: TelemetryPluginOptions,
|
|
|
+ ) {
|
|
|
+ if (process.env[ENABLE_INSTRUMENTATION_ENV_VAR]) {
|
|
|
+ const allMethodHooks = [...defaultMethodHooks, ...(options.methodHooks ?? [])];
|
|
|
+ for (const methodHook of allMethodHooks) {
|
|
|
+ methodHooksService.registerHooks(methodHook.target, methodHook.hooks);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ static init(options: TelemetryPluginOptions) {
|
|
|
+ TelemetryPlugin.options = options;
|
|
|
+ return TelemetryPlugin;
|
|
|
+ }
|
|
|
+}
|