telemetry.plugin.ts 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142
  1. import { Inject } from '@nestjs/common';
  2. import { ENABLE_INSTRUMENTATION_ENV_VAR, PluginCommonModule, VendurePlugin } from '@vendure/core';
  3. import { defaultMethodHooks } from './config/default-method-hooks';
  4. import { OtelInstrumentationStrategy } from './config/otel-instrumentation-strategy';
  5. import { OtelLogger } from './config/otel-logger';
  6. import { TELEMETRY_PLUGIN_OPTIONS } from './constants';
  7. import { MethodHooksService } from './service/method-hooks.service';
  8. import { TelemetryPluginOptions } from './types';
  9. /**
  10. * @description
  11. * The TelemetryPlugin is used to instrument the Vendure application and collect telemetry data using
  12. * [OpenTelemetry](https://opentelemetry.io/).
  13. *
  14. * ## Installation
  15. *
  16. * ```
  17. * npm install \@vendure/telemetry-plugin
  18. * ```
  19. *
  20. * :::info
  21. * For a complete guide to setting up and working with Open Telemetry, see
  22. * the [Implementing Open Telemetry guide](/guides/how-to/telemetry/).
  23. * :::
  24. *
  25. * ## Configuration
  26. *
  27. * The plugin is configured via the `TelemetryPlugin.init()` method. This method accepts an options object
  28. * which defines the OtelLogger options and method hooks.
  29. *
  30. * @example
  31. * ```ts
  32. * import { VendureConfig } from '\@vendure/core';
  33. * import { TelemetryPlugin, registerMethodHooks } from '\@vendure/telemetry-plugin';
  34. *
  35. * export const config: VendureConfig = {
  36. * // ...
  37. * plugins: [
  38. * TelemetryPlugin.init({
  39. * loggerOptions: {
  40. * // Log to the console at the verbose level
  41. * console: LogLevel.Verbose,
  42. * },
  43. * }),
  44. * ],
  45. * };
  46. * ```
  47. *
  48. * ## Preloading the SDK
  49. *
  50. * In order to use the OpenTelemetry SDK, you must preload it before the Vendure server is started.
  51. * This is done by using the `--require` flag when starting the server with a custom preload script.
  52. *
  53. * Create a file named `instrumentation.ts` in the root of your project and add the following code:
  54. *
  55. * ```ts
  56. * import { OTLPLogExporter } from '\@opentelemetry/exporter-logs-otlp-proto';
  57. * import { OTLPTraceExporter } from '\@opentelemetry/exporter-trace-otlp-http';
  58. * import { BatchLogRecordProcessor } from '\@opentelemetry/sdk-logs';
  59. * import { NodeSDK } from '\@opentelemetry/sdk-node';
  60. * import { BatchSpanProcessor } from '\@opentelemetry/sdk-trace-base';
  61. * import { getSdkConfiguration } from '\@vendure/telemetry-plugin/preload';
  62. *
  63. * // In this example we are using Loki as the OTLP endpoint for logging
  64. * process.env.OTEL_EXPORTER_OTLP_ENDPOINT = 'http://localhost:3100/otlp';
  65. * process.env.OTEL_LOGS_EXPORTER = 'otlp';
  66. * process.env.OTEL_RESOURCE_ATTRIBUTES = 'service.name=vendure-dev-server';
  67. *
  68. * // We are using Jaeger as the OTLP endpoint for tracing
  69. * const traceExporter = new OTLPTraceExporter({
  70. * url: 'http://localhost:4318/v1/traces',
  71. * });
  72. * const logExporter = new OTLPLogExporter();
  73. *
  74. * // The getSdkConfiguration method returns a configuration object for the OpenTelemetry Node SDK.
  75. * // It also performs other configuration tasks such as setting a special environment variable
  76. * // to enable instrumentation in the Vendure core code.
  77. * const config = getSdkConfiguration({
  78. * config: {
  79. * // Pass in any custom configuration options for the Node SDK here
  80. * spanProcessors: [new BatchSpanProcessor(traceExporter)],
  81. * logRecordProcessors: [new BatchLogRecordProcessor(logExporter)],
  82. * },
  83. * });
  84. *
  85. * const sdk = new NodeSDK(config);
  86. *
  87. * sdk.start();
  88. * ```
  89. *
  90. * The server would then be started with the following command:
  91. *
  92. * ```bash
  93. * node --require ./dist/instrumentation.js ./dist/server.js
  94. * ```
  95. *
  96. * or for development with ts-node:
  97. *
  98. * ```bash
  99. * npx ts-node --require ./src/instrumentation.ts ./src/server.ts
  100. * ```
  101. *
  102. * @since 3.3.0
  103. * @docsCategory core plugins/TelemetryPlugin
  104. */
  105. @VendurePlugin({
  106. imports: [PluginCommonModule],
  107. providers: [
  108. MethodHooksService,
  109. {
  110. provide: TELEMETRY_PLUGIN_OPTIONS,
  111. useFactory: () => TelemetryPlugin.options,
  112. },
  113. ],
  114. configuration: config => {
  115. config.systemOptions.instrumentationStrategy = new OtelInstrumentationStrategy();
  116. config.logger = new OtelLogger(TelemetryPlugin.options.loggerOptions ?? {});
  117. return config;
  118. },
  119. compatibility: '>=3.3.0',
  120. })
  121. export class TelemetryPlugin {
  122. static options: TelemetryPluginOptions = {};
  123. constructor(
  124. methodHooksService: MethodHooksService,
  125. @Inject(TELEMETRY_PLUGIN_OPTIONS) options: TelemetryPluginOptions,
  126. ) {
  127. if (process.env[ENABLE_INSTRUMENTATION_ENV_VAR]) {
  128. const allMethodHooks = [...defaultMethodHooks, ...(options.methodHooks ?? [])];
  129. for (const methodHook of allMethodHooks) {
  130. methodHooksService.registerHooks(methodHook.target, methodHook.hooks);
  131. }
  132. }
  133. }
  134. static init(options: TelemetryPluginOptions) {
  135. TelemetryPlugin.options = options;
  136. return TelemetryPlugin;
  137. }
  138. }