@prairielearn/opentelemetry 2.1.16 → 3.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,25 @@
1
1
  # @prairielearn/opentelemetry
2
2
 
3
+ ## 3.0.1
4
+
5
+ ### Patch Changes
6
+
7
+ - b55261c: Upgrade to TypeScript 5.9
8
+
9
+ ## 3.0.0
10
+
11
+ ### Major Changes
12
+
13
+ - a7d1ad9: Always configure `NodeTracerProvider`, even when `openTelemetryEnabled === false`.
14
+
15
+ This change is being made to ensure that Sentry's request isolation works correctly, as it relies on the `NodeTracerProvider` being set up.
16
+
17
+ - 423ab32: Upgrade to v2 of the OpenTelemetry packages
18
+
19
+ ### Patch Changes
20
+
21
+ - 23adb05: Upgrade all JavaScript dependencies
22
+
3
23
  ## 2.1.16
4
24
 
5
25
  ### Patch Changes
package/dist/init.d.ts CHANGED
@@ -22,6 +22,10 @@ export type OpenTelemetryConfig = OpenTelemetryConfigEnabled | OpenTelemetryConf
22
22
  * Should be called once we've loaded our config; this will allow us to set up
23
23
  * the correct metadata for the Honeycomb exporter. We don't actually have that
24
24
  * information available until we've loaded our config.
25
+ *
26
+ * Note that even when `openTelemetryEnabled` is `false`, we'll still configure
27
+ * the `NodeTraceProvider` and instrumentations, as Sentry relies on that for
28
+ * scope isolation. However, we won't actually set up any exporters.
25
29
  */
26
30
  export declare function init(config: OpenTelemetryConfig): Promise<void>;
27
31
  /**
package/dist/init.js CHANGED
@@ -13,11 +13,11 @@ import { IORedisInstrumentation } from '@opentelemetry/instrumentation-ioredis';
13
13
  import { PgInstrumentation } from '@opentelemetry/instrumentation-pg';
14
14
  import { RedisInstrumentation } from '@opentelemetry/instrumentation-redis';
15
15
  import { awsEc2Detector } from '@opentelemetry/resource-detector-aws';
16
- import { Resource, detectResourcesSync, envDetector, processDetector, } from '@opentelemetry/resources';
16
+ import { detectResources, envDetector, processDetector, resourceFromAttributes, } from '@opentelemetry/resources';
17
17
  import { AggregationTemporality, ConsoleMetricExporter, MeterProvider, PeriodicExportingMetricReader, } from '@opentelemetry/sdk-metrics';
18
- import { AlwaysOffSampler, AlwaysOnSampler, BatchSpanProcessor, ConsoleSpanExporter, ParentBasedSampler, SimpleSpanProcessor, TraceIdRatioBasedSampler, } from '@opentelemetry/sdk-trace-base';
18
+ import { AlwaysOffSampler, AlwaysOnSampler, BatchSpanProcessor, ConsoleSpanExporter, NoopSpanProcessor, ParentBasedSampler, SimpleSpanProcessor, TraceIdRatioBasedSampler, } from '@opentelemetry/sdk-trace-base';
19
19
  import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
20
- import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
20
+ import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
21
21
  /**
22
22
  * Extends `BatchSpanProcessor` to give it the ability to filter out spans
23
23
  * before they're queued up to send. This enhances our sampling process so
@@ -110,7 +110,7 @@ function getHoneycombMetadata(config, datasetSuffix = '') {
110
110
  return metadata;
111
111
  }
112
112
  function getTraceExporter(config) {
113
- if (!config.openTelemetryExporter)
113
+ if (!config.openTelemetryEnabled || !config.openTelemetryExporter)
114
114
  return null;
115
115
  if (typeof config.openTelemetryExporter === 'object') {
116
116
  return config.openTelemetryExporter;
@@ -131,7 +131,7 @@ function getTraceExporter(config) {
131
131
  }
132
132
  }
133
133
  function getMetricExporter(config) {
134
- if (!config.openTelemetryMetricExporter)
134
+ if (!config.openTelemetryEnabled || !config.openTelemetryMetricExporter)
135
135
  return null;
136
136
  if (typeof config.openTelemetryMetricExporter === 'object') {
137
137
  return config.openTelemetryMetricExporter;
@@ -156,12 +156,14 @@ function getMetricExporter(config) {
156
156
  }
157
157
  }
158
158
  function getSpanProcessor(config) {
159
+ if (!config.openTelemetryEnabled)
160
+ return new NoopSpanProcessor();
159
161
  if (typeof config.openTelemetrySpanProcessor === 'object') {
160
162
  return config.openTelemetrySpanProcessor;
161
163
  }
162
164
  const traceExporter = getTraceExporter(config);
163
165
  if (!traceExporter)
164
- return null;
166
+ return new NoopSpanProcessor();
165
167
  switch (config.openTelemetrySpanProcessor ?? 'batch') {
166
168
  case 'batch': {
167
169
  return new FilterBatchSpanProcessor(traceExporter, filter);
@@ -174,49 +176,44 @@ function getSpanProcessor(config) {
174
176
  }
175
177
  }
176
178
  }
177
- /**
178
- * Should be called once we've loaded our config; this will allow us to set up
179
- * the correct metadata for the Honeycomb exporter. We don't actually have that
180
- * information available until we've loaded our config.
181
- */
182
- export async function init(config) {
183
- if (!config.openTelemetryEnabled) {
184
- // If not enabled, do nothing. We used to disable the instrumentations, but
185
- // per maintainers, that can actually be problematic. See the comments on
186
- // https://github.com/open-telemetry/opentelemetry-js-contrib/issues/970
187
- // The Express instrumentation also logs a benign error, which can be
188
- // confusing to users. There's a fix in progress if we want to switch back
189
- // to disabling instrumentations in the future:
190
- // https://github.com/open-telemetry/opentelemetry-js-contrib/pull/972
191
- return;
192
- }
193
- const metricExporter = getMetricExporter(config);
194
- const spanProcessor = getSpanProcessor(config);
195
- let sampler;
179
+ function getSampler(config) {
180
+ if (!config.openTelemetryEnabled)
181
+ return new AlwaysOffSampler();
196
182
  switch (config.openTelemetrySamplerType ?? 'always-on') {
197
183
  case 'always-on': {
198
- sampler = new AlwaysOnSampler();
199
- break;
184
+ return new AlwaysOnSampler();
200
185
  }
201
186
  case 'always-off': {
202
- sampler = new AlwaysOffSampler();
203
- break;
187
+ return new AlwaysOffSampler();
204
188
  }
205
189
  case 'trace-id-ratio': {
206
- sampler = new ParentBasedSampler({
190
+ return new ParentBasedSampler({
207
191
  root: new TraceIdRatioBasedSampler(config.openTelemetrySampleRate),
208
192
  });
209
- break;
210
193
  }
211
194
  default:
212
195
  throw new Error(`Unknown OpenTelemetry sampler type: ${config.openTelemetrySamplerType}`);
213
196
  }
197
+ }
198
+ /**
199
+ * Should be called once we've loaded our config; this will allow us to set up
200
+ * the correct metadata for the Honeycomb exporter. We don't actually have that
201
+ * information available until we've loaded our config.
202
+ *
203
+ * Note that even when `openTelemetryEnabled` is `false`, we'll still configure
204
+ * the `NodeTraceProvider` and instrumentations, as Sentry relies on that for
205
+ * scope isolation. However, we won't actually set up any exporters.
206
+ */
207
+ export async function init(config) {
208
+ const metricExporter = getMetricExporter(config);
209
+ const spanProcessor = getSpanProcessor(config);
210
+ const sampler = getSampler(config);
214
211
  // Much of this functionality is copied from `@opentelemetry/sdk-node`, but
215
212
  // we can't use the SDK directly because of the fact that we load our config
216
213
  // asynchronously. We need to initialize our instrumentations first; only
217
214
  // then can we actually start requiring all of our code that loads our config
218
215
  // and ultimately tells us how to configure OpenTelemetry.
219
- let resource = detectResourcesSync({
216
+ let resource = detectResources({
220
217
  // The AWS resource detector always tries to reach out to the EC2 metadata
221
218
  // service endpoint. When running locally, or otherwise in a non-AWS environment,
222
219
  // this will typically fail immediately wih `EHOSTDOWN`, but will sometimes wait
@@ -235,16 +232,14 @@ export async function init(config) {
235
232
  ].filter((d) => !!d),
236
233
  });
237
234
  if (config.serviceName) {
238
- resource = resource.merge(new Resource({ [SemanticResourceAttributes.SERVICE_NAME]: config.serviceName }));
235
+ resource = resource.merge(resourceFromAttributes({ [ATTR_SERVICE_NAME]: config.serviceName }));
239
236
  }
240
237
  // Set up tracing instrumentation.
241
238
  const nodeTracerProvider = new NodeTracerProvider({
242
239
  sampler,
243
240
  resource,
241
+ spanProcessors: [spanProcessor].filter((p) => !!p),
244
242
  });
245
- if (spanProcessor) {
246
- nodeTracerProvider.addSpanProcessor(spanProcessor);
247
- }
248
243
  nodeTracerProvider.register({
249
244
  contextManager: config.contextManager,
250
245
  });
package/dist/init.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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","sourcesContent":["import { Metadata, credentials } from '@grpc/grpc-js';\nimport { type ContextManager, metrics } from '@opentelemetry/api';\nimport { hrTimeToMilliseconds } from '@opentelemetry/core';\nimport { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc';\nimport { OTLPTraceExporter as OTLPTraceExporterHttp } from '@opentelemetry/exporter-trace-otlp-http';\nimport { AwsInstrumentation } from '@opentelemetry/instrumentation-aws-sdk';\nimport { ConnectInstrumentation } from '@opentelemetry/instrumentation-connect';\nimport { DnsInstrumentation } from '@opentelemetry/instrumentation-dns';\nimport { ExpressInstrumentation, ExpressLayerType } from '@opentelemetry/instrumentation-express';\nimport { HttpInstrumentation } from '@opentelemetry/instrumentation-http';\nimport { IORedisInstrumentation } from '@opentelemetry/instrumentation-ioredis';\nimport { PgInstrumentation } from '@opentelemetry/instrumentation-pg';\nimport { RedisInstrumentation } from '@opentelemetry/instrumentation-redis';\nimport { awsEc2Detector } from '@opentelemetry/resource-detector-aws';\nimport {\n Resource,\n detectResourcesSync,\n envDetector,\n processDetector,\n} from '@opentelemetry/resources';\nimport {\n AggregationTemporality,\n ConsoleMetricExporter,\n MeterProvider,\n PeriodicExportingMetricReader,\n type PushMetricExporter,\n} from '@opentelemetry/sdk-metrics';\nimport {\n AlwaysOffSampler,\n AlwaysOnSampler,\n BatchSpanProcessor,\n ConsoleSpanExporter,\n ParentBasedSampler,\n type ReadableSpan,\n type Sampler,\n SimpleSpanProcessor,\n type SpanExporter,\n type SpanProcessor,\n TraceIdRatioBasedSampler,\n} from '@opentelemetry/sdk-trace-base';\nimport { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';\nimport { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';\n\n/**\n * Extends `BatchSpanProcessor` to give it the ability to filter out spans\n * before they're queued up to send. This enhances our sampling process so\n * that we can filter spans _after_ they've been emitted.\n */\nclass FilterBatchSpanProcessor extends BatchSpanProcessor {\n private filter: (span: ReadableSpan) => boolean;\n\n constructor(exporter: SpanExporter, filter: (span: ReadableSpan) => boolean) {\n super(exporter);\n this.filter = filter;\n }\n\n /**\n * This is invoked after a span is \"finalized\". `super.onEnd` will queue up\n * the span to be exported, but if we don't call that, we can just drop the\n * span and the parent will be none the wiser!\n */\n onEnd(span: ReadableSpan) {\n if (!this.filter(span)) return;\n\n super.onEnd(span);\n }\n}\n\n/**\n * This will be used with our {@link FilterBatchSpanProcessor} to filter out\n * events that we're not interested in. This helps reduce our event volume\n * but still gives us fine-grained control over which events we keep.\n */\nfunction filter(span: ReadableSpan) {\n if (span.name === 'pg-pool.connect') {\n // Looking at historical data, this generally happens in under a millisecond,\n // precisely because we maintain a pool of long-lived connections. The only\n // time obtaining a client should take longer than that is if we're\n // establishing a connection for the first time, which should happen only at\n // bootup, or if a connection errors out. Those are the cases we're\n // interested in, so we'll filter accordingly.\n return hrTimeToMilliseconds(span.duration) > 1;\n }\n\n // Always return true so that we default to including a span.\n return true;\n}\n\n// When adding new instrumentation here, add the corresponding packages to\n// `commonjs-preloads.ts` so that we can ensure that they're loaded via CJS\n// before anything tries to load them via CJS. This is necessary because the\n// instrumentations can't hook into the ESM loader.\nconst instrumentations = [\n new AwsInstrumentation(),\n new ConnectInstrumentation(),\n new DnsInstrumentation(),\n new ExpressInstrumentation({\n // We use a lot of middleware; it makes the traces way too noisy. If we\n // want telemetry on a particular middleware, we should instrument it\n // manually.\n ignoreLayersType: [ExpressLayerType.MIDDLEWARE],\n ignoreLayers: [\n // These don't provide useful information to us.\n 'router - /',\n 'request handler - /*',\n ],\n }),\n new HttpInstrumentation({\n ignoreIncomingRequestHook(req) {\n return [\n // socket.io requests are generally just long-polling; they don't add\n // useful information for us.\n /\\/socket.io\\//,\n // We get several of these per second; they just chew through our event quota.\n // They don't really do anything interesting anyways.\n /\\/pl\\/webhooks\\/ping/,\n ].some((re) => re.test(req.url ?? '/'));\n },\n }),\n new IORedisInstrumentation(),\n new PgInstrumentation(),\n new RedisInstrumentation(),\n];\n\n// Enable all instrumentations now, even though we haven't configured our\n// span processors or trace exporters yet. We'll set those up later.\ninstrumentations.forEach((i) => {\n i.enable();\n});\n\nlet tracerProvider: NodeTracerProvider | null;\n\ninterface OpenTelemetryConfigEnabled {\n openTelemetryEnabled: boolean;\n openTelemetryExporter?: 'console' | 'honeycomb' | 'jaeger' | SpanExporter | null;\n openTelemetryMetricExporter?: 'console' | 'honeycomb' | PushMetricExporter | null;\n openTelemetryMetricExportIntervalMillis?: number;\n openTelemetrySamplerType: 'always-on' | 'always-off' | 'trace-id-ratio';\n openTelemetrySampleRate?: number;\n openTelemetrySpanProcessor?: 'batch' | 'simple' | SpanProcessor;\n contextManager?: ContextManager;\n honeycombApiKey?: string | null;\n honeycombDataset?: string | null;\n serviceName?: string;\n}\n\n// When we know for sure that OpenTelemetry is disabled, we won't require\n// any other attributes to be set.\ninterface OpenTelemetryConfigDisabled extends Partial<OpenTelemetryConfigEnabled> {\n openTelemetryEnabled: false;\n}\n\nexport type OpenTelemetryConfig = OpenTelemetryConfigEnabled | OpenTelemetryConfigDisabled;\n\nfunction getHoneycombMetadata(config: OpenTelemetryConfig, datasetSuffix = ''): Metadata {\n if (!config.honeycombApiKey) throw new Error('Missing Honeycomb API key');\n if (!config.honeycombDataset) throw new Error('Missing Honeycomb dataset');\n\n const metadata = new Metadata();\n\n metadata.set('x-honeycomb-team', config.honeycombApiKey);\n metadata.set('x-honeycomb-dataset', config.honeycombDataset + datasetSuffix);\n\n return metadata;\n}\n\nfunction getTraceExporter(config: OpenTelemetryConfig): SpanExporter | null {\n if (!config.openTelemetryExporter) return null;\n\n if (typeof config.openTelemetryExporter === 'object') {\n return config.openTelemetryExporter;\n }\n\n switch (config.openTelemetryExporter) {\n case 'console':\n return new ConsoleSpanExporter();\n case 'honeycomb':\n return new OTLPTraceExporter({\n url: 'grpc://api.honeycomb.io:443/',\n credentials: credentials.createSsl(),\n metadata: getHoneycombMetadata(config),\n });\n case 'jaeger':\n return new OTLPTraceExporterHttp();\n default:\n throw new Error(`Unknown OpenTelemetry exporter: ${config.openTelemetryExporter}`);\n }\n}\n\nfunction getMetricExporter(config: OpenTelemetryConfig): PushMetricExporter | null {\n if (!config.openTelemetryMetricExporter) return null;\n\n if (typeof config.openTelemetryMetricExporter === 'object') {\n return config.openTelemetryMetricExporter;\n }\n\n switch (config.openTelemetryMetricExporter) {\n case 'console':\n return new ConsoleMetricExporter();\n case 'honeycomb':\n return new OTLPMetricExporter({\n url: 'grpc://api.honeycomb.io:443/',\n credentials: credentials.createSsl(),\n // Honeycomb recommends using a separate dataset for metrics, so we'll\n // adopt the convention of appending '-metrics' to the dataset name.\n metadata: getHoneycombMetadata(config, '-metrics'),\n // Delta temporality means that sums, histograms, etc. will reset each\n // time data is collected. This more closely matches how we want to\n // observe our metrics than the default cumulative temporality.\n temporalityPreference: AggregationTemporality.DELTA,\n });\n default:\n throw new Error(\n `Unknown OpenTelemetry metric exporter: ${config.openTelemetryMetricExporter}`,\n );\n }\n}\n\nfunction getSpanProcessor(config: OpenTelemetryConfig): SpanProcessor | null {\n if (typeof config.openTelemetrySpanProcessor === 'object') {\n return config.openTelemetrySpanProcessor;\n }\n\n const traceExporter = getTraceExporter(config);\n if (!traceExporter) return null;\n\n switch (config.openTelemetrySpanProcessor ?? 'batch') {\n case 'batch': {\n return new FilterBatchSpanProcessor(traceExporter, filter);\n }\n case 'simple': {\n return new SimpleSpanProcessor(traceExporter);\n }\n default: {\n throw new Error(`Unknown OpenTelemetry span processor: ${config.openTelemetrySpanProcessor}`);\n }\n }\n}\n\n/**\n * Should be called once we've loaded our config; this will allow us to set up\n * the correct metadata for the Honeycomb exporter. We don't actually have that\n * information available until we've loaded our config.\n */\nexport async function init(config: OpenTelemetryConfig) {\n if (!config.openTelemetryEnabled) {\n // If not enabled, do nothing. We used to disable the instrumentations, but\n // per maintainers, that can actually be problematic. See the comments on\n // https://github.com/open-telemetry/opentelemetry-js-contrib/issues/970\n // The Express instrumentation also logs a benign error, which can be\n // confusing to users. There's a fix in progress if we want to switch back\n // to disabling instrumentations in the future:\n // https://github.com/open-telemetry/opentelemetry-js-contrib/pull/972\n return;\n }\n\n const metricExporter = getMetricExporter(config);\n const spanProcessor = getSpanProcessor(config);\n\n let sampler: Sampler;\n switch (config.openTelemetrySamplerType ?? 'always-on') {\n case 'always-on': {\n sampler = new AlwaysOnSampler();\n break;\n }\n case 'always-off': {\n sampler = new AlwaysOffSampler();\n break;\n }\n case 'trace-id-ratio': {\n sampler = new ParentBasedSampler({\n root: new TraceIdRatioBasedSampler(config.openTelemetrySampleRate),\n });\n break;\n }\n default:\n throw new Error(`Unknown OpenTelemetry sampler type: ${config.openTelemetrySamplerType}`);\n }\n\n // Much of this functionality is copied from `@opentelemetry/sdk-node`, but\n // we can't use the SDK directly because of the fact that we load our config\n // asynchronously. We need to initialize our instrumentations first; only\n // then can we actually start requiring all of our code that loads our config\n // and ultimately tells us how to configure OpenTelemetry.\n\n let resource = detectResourcesSync({\n // The AWS resource detector always tries to reach out to the EC2 metadata\n // service endpoint. When running locally, or otherwise in a non-AWS environment,\n // this will typically fail immediately wih `EHOSTDOWN`, but will sometimes wait\n // 5 seconds before failing with a network timeout error. This causes problems\n // when running tests, as 5 seconds is longer than Mocha lets tests and hooks run\n // for by default. This causes nondeterministic test failures when the EC2 metadata\n // request fails with a network timeout.\n //\n // To work around this, the AWS resource detector is only enabled when running in\n // a production environment. In general this is reasonable, as we only care about\n // AWS resource detection in production-like environments.\n detectors: [\n process.env.NODE_ENV === 'production' ? awsEc2Detector : null,\n processDetector,\n envDetector,\n ].filter((d) => !!d),\n });\n\n if (config.serviceName) {\n resource = resource.merge(\n new Resource({ [SemanticResourceAttributes.SERVICE_NAME]: config.serviceName }),\n );\n }\n\n // Set up tracing instrumentation.\n const nodeTracerProvider = new NodeTracerProvider({\n sampler,\n resource,\n });\n if (spanProcessor) {\n nodeTracerProvider.addSpanProcessor(spanProcessor);\n }\n nodeTracerProvider.register({\n contextManager: config.contextManager,\n });\n instrumentations.forEach((i) => i.setTracerProvider(nodeTracerProvider));\n\n // Save the provider so we can shut it down later.\n tracerProvider = nodeTracerProvider;\n\n // Set up metrics instrumentation if it's enabled.\n if (metricExporter) {\n const meterProvider = new MeterProvider({\n resource,\n readers: [\n new PeriodicExportingMetricReader({\n exporter: metricExporter,\n exportIntervalMillis: config.openTelemetryMetricExportIntervalMillis ?? 30_000,\n }),\n ],\n });\n metrics.setGlobalMeterProvider(meterProvider);\n }\n}\n\n/**\n * Gracefully shuts down the OpenTelemetry instrumentation. Should be called\n * when a `SIGTERM` signal is handled.\n */\nexport async function shutdown(): Promise<void> {\n if (tracerProvider) {\n await tracerProvider.shutdown();\n tracerProvider = null;\n }\n}\n\n/**\n * Disables all OpenTelemetry instrumentations. This is useful for tests that\n * need to access the unwrapped modules.\n */\nexport function disableInstrumentations() {\n instrumentations.forEach((i) => i.disable());\n}\n"]}
1
+ {"version":3,"file":"init.js","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"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","sourcesContent":["import { Metadata, credentials } from '@grpc/grpc-js';\nimport { type ContextManager, metrics } from '@opentelemetry/api';\nimport { hrTimeToMilliseconds } from '@opentelemetry/core';\nimport { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc';\nimport { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc';\nimport { OTLPTraceExporter as OTLPTraceExporterHttp } from '@opentelemetry/exporter-trace-otlp-http';\nimport { AwsInstrumentation } from '@opentelemetry/instrumentation-aws-sdk';\nimport { ConnectInstrumentation } from '@opentelemetry/instrumentation-connect';\nimport { DnsInstrumentation } from '@opentelemetry/instrumentation-dns';\nimport { ExpressInstrumentation, ExpressLayerType } from '@opentelemetry/instrumentation-express';\nimport { HttpInstrumentation } from '@opentelemetry/instrumentation-http';\nimport { IORedisInstrumentation } from '@opentelemetry/instrumentation-ioredis';\nimport { PgInstrumentation } from '@opentelemetry/instrumentation-pg';\nimport { RedisInstrumentation } from '@opentelemetry/instrumentation-redis';\nimport { awsEc2Detector } from '@opentelemetry/resource-detector-aws';\nimport {\n detectResources,\n envDetector,\n processDetector,\n resourceFromAttributes,\n} from '@opentelemetry/resources';\nimport {\n AggregationTemporality,\n ConsoleMetricExporter,\n MeterProvider,\n PeriodicExportingMetricReader,\n type PushMetricExporter,\n} from '@opentelemetry/sdk-metrics';\nimport {\n AlwaysOffSampler,\n AlwaysOnSampler,\n BatchSpanProcessor,\n ConsoleSpanExporter,\n NoopSpanProcessor,\n ParentBasedSampler,\n type ReadableSpan,\n type Sampler,\n SimpleSpanProcessor,\n type SpanExporter,\n type SpanProcessor,\n TraceIdRatioBasedSampler,\n} from '@opentelemetry/sdk-trace-base';\nimport { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';\nimport { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';\n\n/**\n * Extends `BatchSpanProcessor` to give it the ability to filter out spans\n * before they're queued up to send. This enhances our sampling process so\n * that we can filter spans _after_ they've been emitted.\n */\nclass FilterBatchSpanProcessor extends BatchSpanProcessor {\n private filter: (span: ReadableSpan) => boolean;\n\n constructor(exporter: SpanExporter, filter: (span: ReadableSpan) => boolean) {\n super(exporter);\n this.filter = filter;\n }\n\n /**\n * This is invoked after a span is \"finalized\". `super.onEnd` will queue up\n * the span to be exported, but if we don't call that, we can just drop the\n * span and the parent will be none the wiser!\n */\n onEnd(span: ReadableSpan) {\n if (!this.filter(span)) return;\n\n super.onEnd(span);\n }\n}\n\n/**\n * This will be used with our {@link FilterBatchSpanProcessor} to filter out\n * events that we're not interested in. This helps reduce our event volume\n * but still gives us fine-grained control over which events we keep.\n */\nfunction filter(span: ReadableSpan) {\n if (span.name === 'pg-pool.connect') {\n // Looking at historical data, this generally happens in under a millisecond,\n // precisely because we maintain a pool of long-lived connections. The only\n // time obtaining a client should take longer than that is if we're\n // establishing a connection for the first time, which should happen only at\n // bootup, or if a connection errors out. Those are the cases we're\n // interested in, so we'll filter accordingly.\n return hrTimeToMilliseconds(span.duration) > 1;\n }\n\n // Always return true so that we default to including a span.\n return true;\n}\n\n// When adding new instrumentation here, add the corresponding packages to\n// `commonjs-preloads.ts` so that we can ensure that they're loaded via CJS\n// before anything tries to load them via CJS. This is necessary because the\n// instrumentations can't hook into the ESM loader.\nconst instrumentations = [\n new AwsInstrumentation(),\n new ConnectInstrumentation(),\n new DnsInstrumentation(),\n new ExpressInstrumentation({\n // We use a lot of middleware; it makes the traces way too noisy. If we\n // want telemetry on a particular middleware, we should instrument it\n // manually.\n ignoreLayersType: [ExpressLayerType.MIDDLEWARE],\n ignoreLayers: [\n // These don't provide useful information to us.\n 'router - /',\n 'request handler - /*',\n ],\n }),\n new HttpInstrumentation({\n ignoreIncomingRequestHook(req) {\n return [\n // socket.io requests are generally just long-polling; they don't add\n // useful information for us.\n /\\/socket.io\\//,\n // We get several of these per second; they just chew through our event quota.\n // They don't really do anything interesting anyways.\n /\\/pl\\/webhooks\\/ping/,\n ].some((re) => re.test(req.url ?? '/'));\n },\n }),\n new IORedisInstrumentation(),\n new PgInstrumentation(),\n new RedisInstrumentation(),\n];\n\n// Enable all instrumentations now, even though we haven't configured our\n// span processors or trace exporters yet. We'll set those up later.\ninstrumentations.forEach((i) => {\n i.enable();\n});\n\nlet tracerProvider: NodeTracerProvider | null;\n\ninterface OpenTelemetryConfigEnabled {\n openTelemetryEnabled: boolean;\n openTelemetryExporter?: 'console' | 'honeycomb' | 'jaeger' | SpanExporter | null;\n openTelemetryMetricExporter?: 'console' | 'honeycomb' | PushMetricExporter | null;\n openTelemetryMetricExportIntervalMillis?: number;\n openTelemetrySamplerType: 'always-on' | 'always-off' | 'trace-id-ratio';\n openTelemetrySampleRate?: number;\n openTelemetrySpanProcessor?: 'batch' | 'simple' | SpanProcessor;\n contextManager?: ContextManager;\n honeycombApiKey?: string | null;\n honeycombDataset?: string | null;\n serviceName?: string;\n}\n\n// When we know for sure that OpenTelemetry is disabled, we won't require\n// any other attributes to be set.\ninterface OpenTelemetryConfigDisabled extends Partial<OpenTelemetryConfigEnabled> {\n openTelemetryEnabled: false;\n}\n\nexport type OpenTelemetryConfig = OpenTelemetryConfigEnabled | OpenTelemetryConfigDisabled;\n\nfunction getHoneycombMetadata(config: OpenTelemetryConfig, datasetSuffix = ''): Metadata {\n if (!config.honeycombApiKey) throw new Error('Missing Honeycomb API key');\n if (!config.honeycombDataset) throw new Error('Missing Honeycomb dataset');\n\n const metadata = new Metadata();\n\n metadata.set('x-honeycomb-team', config.honeycombApiKey);\n metadata.set('x-honeycomb-dataset', config.honeycombDataset + datasetSuffix);\n\n return metadata;\n}\n\nfunction getTraceExporter(config: OpenTelemetryConfig): SpanExporter | null {\n if (!config.openTelemetryEnabled || !config.openTelemetryExporter) return null;\n\n if (typeof config.openTelemetryExporter === 'object') {\n return config.openTelemetryExporter;\n }\n\n switch (config.openTelemetryExporter) {\n case 'console':\n return new ConsoleSpanExporter();\n case 'honeycomb':\n return new OTLPTraceExporter({\n url: 'grpc://api.honeycomb.io:443/',\n credentials: credentials.createSsl(),\n metadata: getHoneycombMetadata(config),\n });\n case 'jaeger':\n return new OTLPTraceExporterHttp();\n default:\n throw new Error(`Unknown OpenTelemetry exporter: ${config.openTelemetryExporter}`);\n }\n}\n\nfunction getMetricExporter(config: OpenTelemetryConfig): PushMetricExporter | null {\n if (!config.openTelemetryEnabled || !config.openTelemetryMetricExporter) return null;\n\n if (typeof config.openTelemetryMetricExporter === 'object') {\n return config.openTelemetryMetricExporter;\n }\n\n switch (config.openTelemetryMetricExporter) {\n case 'console':\n return new ConsoleMetricExporter();\n case 'honeycomb':\n return new OTLPMetricExporter({\n url: 'grpc://api.honeycomb.io:443/',\n credentials: credentials.createSsl(),\n // Honeycomb recommends using a separate dataset for metrics, so we'll\n // adopt the convention of appending '-metrics' to the dataset name.\n metadata: getHoneycombMetadata(config, '-metrics'),\n // Delta temporality means that sums, histograms, etc. will reset each\n // time data is collected. This more closely matches how we want to\n // observe our metrics than the default cumulative temporality.\n temporalityPreference: AggregationTemporality.DELTA,\n });\n default:\n throw new Error(\n `Unknown OpenTelemetry metric exporter: ${config.openTelemetryMetricExporter}`,\n );\n }\n}\n\nfunction getSpanProcessor(config: OpenTelemetryConfig): SpanProcessor | null {\n if (!config.openTelemetryEnabled) return new NoopSpanProcessor();\n\n if (typeof config.openTelemetrySpanProcessor === 'object') {\n return config.openTelemetrySpanProcessor;\n }\n\n const traceExporter = getTraceExporter(config);\n if (!traceExporter) return new NoopSpanProcessor();\n\n switch (config.openTelemetrySpanProcessor ?? 'batch') {\n case 'batch': {\n return new FilterBatchSpanProcessor(traceExporter, filter);\n }\n case 'simple': {\n return new SimpleSpanProcessor(traceExporter);\n }\n default: {\n throw new Error(`Unknown OpenTelemetry span processor: ${config.openTelemetrySpanProcessor}`);\n }\n }\n}\n\nfunction getSampler(config: OpenTelemetryConfig): Sampler {\n if (!config.openTelemetryEnabled) return new AlwaysOffSampler();\n\n switch (config.openTelemetrySamplerType ?? 'always-on') {\n case 'always-on': {\n return new AlwaysOnSampler();\n }\n case 'always-off': {\n return new AlwaysOffSampler();\n }\n case 'trace-id-ratio': {\n return new ParentBasedSampler({\n root: new TraceIdRatioBasedSampler(config.openTelemetrySampleRate),\n });\n }\n default:\n throw new Error(`Unknown OpenTelemetry sampler type: ${config.openTelemetrySamplerType}`);\n }\n}\n\n/**\n * Should be called once we've loaded our config; this will allow us to set up\n * the correct metadata for the Honeycomb exporter. We don't actually have that\n * information available until we've loaded our config.\n *\n * Note that even when `openTelemetryEnabled` is `false`, we'll still configure\n * the `NodeTraceProvider` and instrumentations, as Sentry relies on that for\n * scope isolation. However, we won't actually set up any exporters.\n */\nexport async function init(config: OpenTelemetryConfig) {\n const metricExporter = getMetricExporter(config);\n const spanProcessor = getSpanProcessor(config);\n const sampler = getSampler(config);\n\n // Much of this functionality is copied from `@opentelemetry/sdk-node`, but\n // we can't use the SDK directly because of the fact that we load our config\n // asynchronously. We need to initialize our instrumentations first; only\n // then can we actually start requiring all of our code that loads our config\n // and ultimately tells us how to configure OpenTelemetry.\n\n let resource = detectResources({\n // The AWS resource detector always tries to reach out to the EC2 metadata\n // service endpoint. When running locally, or otherwise in a non-AWS environment,\n // this will typically fail immediately wih `EHOSTDOWN`, but will sometimes wait\n // 5 seconds before failing with a network timeout error. This causes problems\n // when running tests, as 5 seconds is longer than Mocha lets tests and hooks run\n // for by default. This causes nondeterministic test failures when the EC2 metadata\n // request fails with a network timeout.\n //\n // To work around this, the AWS resource detector is only enabled when running in\n // a production environment. In general this is reasonable, as we only care about\n // AWS resource detection in production-like environments.\n detectors: [\n process.env.NODE_ENV === 'production' ? awsEc2Detector : null,\n processDetector,\n envDetector,\n ].filter((d) => !!d),\n });\n\n if (config.serviceName) {\n resource = resource.merge(resourceFromAttributes({ [ATTR_SERVICE_NAME]: config.serviceName }));\n }\n\n // Set up tracing instrumentation.\n const nodeTracerProvider = new NodeTracerProvider({\n sampler,\n resource,\n spanProcessors: [spanProcessor].filter((p) => !!p),\n });\n nodeTracerProvider.register({\n contextManager: config.contextManager,\n });\n instrumentations.forEach((i) => i.setTracerProvider(nodeTracerProvider));\n\n // Save the provider so we can shut it down later.\n tracerProvider = nodeTracerProvider;\n\n // Set up metrics instrumentation if it's enabled.\n if (metricExporter) {\n const meterProvider = new MeterProvider({\n resource,\n readers: [\n new PeriodicExportingMetricReader({\n exporter: metricExporter,\n exportIntervalMillis: config.openTelemetryMetricExportIntervalMillis ?? 30_000,\n }),\n ],\n });\n metrics.setGlobalMeterProvider(meterProvider);\n }\n}\n\n/**\n * Gracefully shuts down the OpenTelemetry instrumentation. Should be called\n * when a `SIGTERM` signal is handled.\n */\nexport async function shutdown(): Promise<void> {\n if (tracerProvider) {\n await tracerProvider.shutdown();\n tracerProvider = null;\n }\n}\n\n/**\n * Disables all OpenTelemetry instrumentations. This is useful for tests that\n * need to access the unwrapped modules.\n */\nexport function disableInstrumentations() {\n instrumentations.forEach((i) => i.disable());\n}\n"]}
@@ -9,21 +9,22 @@ async function waitForMetricsExport(exporter) {
9
9
  }
10
10
  describe('instrumentedWithMetrics', () => {
11
11
  let exporter;
12
- let metricReader;
12
+ let meterProvider;
13
13
  let meter;
14
14
  beforeEach(async () => {
15
- const meterProvider = new MeterProvider();
16
- meter = meterProvider.getMeter('test');
17
15
  exporter = new InMemoryMetricExporter(AggregationTemporality.DELTA);
18
- metricReader = new PeriodicExportingMetricReader({
19
- exporter,
20
- exportIntervalMillis: 50,
16
+ meterProvider = new MeterProvider({
17
+ readers: [
18
+ new PeriodicExportingMetricReader({
19
+ exporter,
20
+ exportIntervalMillis: 50,
21
+ }),
22
+ ],
21
23
  });
22
- meterProvider.addMetricReader(metricReader);
24
+ meter = meterProvider.getMeter('test');
23
25
  });
24
26
  afterEach(async () => {
25
- await exporter.shutdown();
26
- await metricReader.shutdown();
27
+ await meterProvider.shutdown();
27
28
  });
28
29
  it('records a histogram for the function duration', async () => {
29
30
  await instrumentedWithMetrics(meter, 'test', async () => { });
@@ -1 +1 @@
1
- {"version":3,"file":"metrics.test.js","sourceRoot":"","sources":["../src/metrics.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Meter } from '@opentelemetry/api';\nimport {\n AggregationTemporality,\n type Histogram,\n InMemoryMetricExporter,\n MeterProvider,\n PeriodicExportingMetricReader,\n} from '@opentelemetry/sdk-metrics';\nimport { afterEach, assert, beforeEach, describe, expect, it } from 'vitest';\n\nimport { instrumentedWithMetrics } from './metrics.js';\n\nasync function waitForMetricsExport(exporter: InMemoryMetricExporter) {\n while (exporter.getMetrics().length === 0) {\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n}\n\ndescribe('instrumentedWithMetrics', () => {\n let exporter: InMemoryMetricExporter;\n let metricReader: PeriodicExportingMetricReader;\n let meter: Meter;\n\n beforeEach(async () => {\n const meterProvider = new MeterProvider();\n meter = meterProvider.getMeter('test');\n exporter = new InMemoryMetricExporter(AggregationTemporality.DELTA);\n metricReader = new PeriodicExportingMetricReader({\n exporter,\n exportIntervalMillis: 50,\n });\n meterProvider.addMetricReader(metricReader);\n });\n\n afterEach(async () => {\n await exporter.shutdown();\n await metricReader.shutdown();\n });\n\n it('records a histogram for the function duration', async () => {\n await instrumentedWithMetrics(meter, 'test', async () => {});\n\n await waitForMetricsExport(exporter);\n const exportedMetrics = exporter.getMetrics();\n const { scope, metrics } = exportedMetrics[0].scopeMetrics[0];\n\n // We won't see an exported metric for the error counter because the\n // Metrics SDK no longer exports metrics with no data points.\n // https://github.com/open-telemetry/opentelemetry-js/pull/4135\n assert.lengthOf(metrics, 1);\n const [histogramMetric] = metrics;\n\n assert.equal(scope.name, 'test');\n\n assert.ok(histogramMetric);\n assert.equal(histogramMetric.descriptor.name, 'test.duration');\n assert.equal((histogramMetric.dataPoints[0].value as Histogram).count, 1);\n });\n\n it('records an error count', async () => {\n await expect(\n instrumentedWithMetrics(meter, 'test', async () => {\n throw new Error('error for test');\n }),\n ).rejects.toThrow('error for test');\n\n await waitForMetricsExport(exporter);\n const exportedMetrics = exporter.getMetrics();\n const { metrics, scope } = exportedMetrics[0].scopeMetrics[0];\n\n // An error was reported above, so there will be both the error counter\n // and histogram metrics.\n assert.lengthOf(metrics, 2);\n const [counterMetric, histogramMetric] = metrics;\n\n assert.ok(scope);\n assert.equal(scope.name, 'test');\n\n assert.ok(counterMetric);\n assert.equal(counterMetric.descriptor.name, 'test.error');\n assert.equal(counterMetric.dataPoints[0].value, 1);\n\n assert.ok(histogramMetric);\n assert.equal(histogramMetric.descriptor.name, 'test.duration');\n assert.equal((histogramMetric.dataPoints[0].value as Histogram).count, 1);\n });\n});\n"]}
1
+ {"version":3,"file":"metrics.test.js","sourceRoot":"","sources":["../src/metrics.test.ts"],"names":[],"mappings":"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","sourcesContent":["import { type Meter } from '@opentelemetry/api';\nimport {\n AggregationTemporality,\n type Histogram,\n InMemoryMetricExporter,\n MeterProvider,\n PeriodicExportingMetricReader,\n} from '@opentelemetry/sdk-metrics';\nimport { afterEach, assert, beforeEach, describe, expect, it } from 'vitest';\n\nimport { instrumentedWithMetrics } from './metrics.js';\n\nasync function waitForMetricsExport(exporter: InMemoryMetricExporter) {\n while (exporter.getMetrics().length === 0) {\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n}\n\ndescribe('instrumentedWithMetrics', () => {\n let exporter: InMemoryMetricExporter;\n let meterProvider: MeterProvider;\n let meter: Meter;\n\n beforeEach(async () => {\n exporter = new InMemoryMetricExporter(AggregationTemporality.DELTA);\n meterProvider = new MeterProvider({\n readers: [\n new PeriodicExportingMetricReader({\n exporter,\n exportIntervalMillis: 50,\n }),\n ],\n });\n meter = meterProvider.getMeter('test');\n });\n\n afterEach(async () => {\n await meterProvider.shutdown();\n });\n\n it('records a histogram for the function duration', async () => {\n await instrumentedWithMetrics(meter, 'test', async () => {});\n\n await waitForMetricsExport(exporter);\n const exportedMetrics = exporter.getMetrics();\n const { scope, metrics } = exportedMetrics[0].scopeMetrics[0];\n\n // We won't see an exported metric for the error counter because the\n // Metrics SDK no longer exports metrics with no data points.\n // https://github.com/open-telemetry/opentelemetry-js/pull/4135\n assert.lengthOf(metrics, 1);\n const [histogramMetric] = metrics;\n\n assert.equal(scope.name, 'test');\n\n assert.ok(histogramMetric);\n assert.equal(histogramMetric.descriptor.name, 'test.duration');\n assert.equal((histogramMetric.dataPoints[0].value as Histogram).count, 1);\n });\n\n it('records an error count', async () => {\n await expect(\n instrumentedWithMetrics(meter, 'test', async () => {\n throw new Error('error for test');\n }),\n ).rejects.toThrow('error for test');\n\n await waitForMetricsExport(exporter);\n const exportedMetrics = exporter.getMetrics();\n const { metrics, scope } = exportedMetrics[0].scopeMetrics[0];\n\n // An error was reported above, so there will be both the error counter\n // and histogram metrics.\n assert.lengthOf(metrics, 2);\n const [counterMetric, histogramMetric] = metrics;\n\n assert.ok(scope);\n assert.equal(scope.name, 'test');\n\n assert.ok(counterMetric);\n assert.equal(counterMetric.descriptor.name, 'test.error');\n assert.equal(counterMetric.dataPoints[0].value, 1);\n\n assert.ok(histogramMetric);\n assert.equal(histogramMetric.descriptor.name, 'test.duration');\n assert.equal((histogramMetric.dataPoints[0].value as Histogram).count, 1);\n });\n});\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@prairielearn/opentelemetry",
3
- "version": "2.1.16",
3
+ "version": "3.0.1",
4
4
  "type": "module",
5
5
  "repository": {
6
6
  "type": "git",
@@ -16,34 +16,34 @@
16
16
  "dependencies": {
17
17
  "@grpc/grpc-js": "^1.13.4",
18
18
  "@opentelemetry/api": "^1.9.0",
19
- "@opentelemetry/core": "^1.30.1",
20
- "@opentelemetry/exporter-metrics-otlp-grpc": "^0.57.2",
21
- "@opentelemetry/exporter-trace-otlp-grpc": "^0.57.2",
22
- "@opentelemetry/exporter-trace-otlp-http": "^0.57.2",
23
- "@opentelemetry/instrumentation": "^0.57.2",
24
- "@opentelemetry/instrumentation-aws-sdk": "^0.49.1",
25
- "@opentelemetry/instrumentation-connect": "^0.43.1",
26
- "@opentelemetry/instrumentation-dns": "^0.43.1",
27
- "@opentelemetry/instrumentation-express": "^0.47.1",
28
- "@opentelemetry/instrumentation-http": "^0.57.2",
29
- "@opentelemetry/instrumentation-ioredis": "^0.47.1",
30
- "@opentelemetry/instrumentation-pg": "^0.51.1",
31
- "@opentelemetry/instrumentation-redis": "^0.46.1",
32
- "@opentelemetry/resource-detector-aws": "^1.12.0",
33
- "@opentelemetry/resources": "^1.30.1",
34
- "@opentelemetry/sdk-metrics": "^1.30.1",
35
- "@opentelemetry/sdk-node": "^0.57.2",
36
- "@opentelemetry/sdk-trace-base": "^1.30.1",
37
- "@opentelemetry/sdk-trace-node": "^1.30.1",
38
- "@opentelemetry/semantic-conventions": "^1.30.0"
19
+ "@opentelemetry/core": "^2.0.1",
20
+ "@opentelemetry/exporter-metrics-otlp-grpc": "^0.203.0",
21
+ "@opentelemetry/exporter-trace-otlp-grpc": "^0.203.0",
22
+ "@opentelemetry/exporter-trace-otlp-http": "^0.203.0",
23
+ "@opentelemetry/instrumentation": "^0.203.0",
24
+ "@opentelemetry/instrumentation-aws-sdk": "^0.56.0",
25
+ "@opentelemetry/instrumentation-connect": "^0.47.0",
26
+ "@opentelemetry/instrumentation-dns": "^0.47.0",
27
+ "@opentelemetry/instrumentation-express": "^0.52.0",
28
+ "@opentelemetry/instrumentation-http": "^0.203.0",
29
+ "@opentelemetry/instrumentation-ioredis": "^0.51.0",
30
+ "@opentelemetry/instrumentation-pg": "^0.55.0",
31
+ "@opentelemetry/instrumentation-redis": "^0.51.0",
32
+ "@opentelemetry/resource-detector-aws": "^2.3.0",
33
+ "@opentelemetry/resources": "^2.0.1",
34
+ "@opentelemetry/sdk-metrics": "^2.0.1",
35
+ "@opentelemetry/sdk-node": "^0.203.0",
36
+ "@opentelemetry/sdk-trace-base": "^2.0.1",
37
+ "@opentelemetry/sdk-trace-node": "^2.0.1",
38
+ "@opentelemetry/semantic-conventions": "^1.36.0"
39
39
  },
40
40
  "devDependencies": {
41
- "@opentelemetry/context-async-hooks": "^1.30.1",
41
+ "@opentelemetry/context-async-hooks": "^2.0.1",
42
42
  "@prairielearn/tsconfig": "^0.0.0",
43
- "@types/node": "^22.15.34",
43
+ "@types/node": "^22.17.0",
44
44
  "@vitest/coverage-v8": "^3.2.4",
45
45
  "tsx": "^4.20.3",
46
- "typescript": "^5.8.3",
46
+ "typescript": "^5.9.2",
47
47
  "vitest": "^3.2.4"
48
48
  }
49
49
  }
package/src/init.ts CHANGED
@@ -14,10 +14,10 @@ import { PgInstrumentation } from '@opentelemetry/instrumentation-pg';
14
14
  import { RedisInstrumentation } from '@opentelemetry/instrumentation-redis';
15
15
  import { awsEc2Detector } from '@opentelemetry/resource-detector-aws';
16
16
  import {
17
- Resource,
18
- detectResourcesSync,
17
+ detectResources,
19
18
  envDetector,
20
19
  processDetector,
20
+ resourceFromAttributes,
21
21
  } from '@opentelemetry/resources';
22
22
  import {
23
23
  AggregationTemporality,
@@ -31,6 +31,7 @@ import {
31
31
  AlwaysOnSampler,
32
32
  BatchSpanProcessor,
33
33
  ConsoleSpanExporter,
34
+ NoopSpanProcessor,
34
35
  ParentBasedSampler,
35
36
  type ReadableSpan,
36
37
  type Sampler,
@@ -40,7 +41,7 @@ import {
40
41
  TraceIdRatioBasedSampler,
41
42
  } from '@opentelemetry/sdk-trace-base';
42
43
  import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
43
- import { SemanticResourceAttributes } from '@opentelemetry/semantic-conventions';
44
+ import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
44
45
 
45
46
  /**
46
47
  * Extends `BatchSpanProcessor` to give it the ability to filter out spans
@@ -166,7 +167,7 @@ function getHoneycombMetadata(config: OpenTelemetryConfig, datasetSuffix = ''):
166
167
  }
167
168
 
168
169
  function getTraceExporter(config: OpenTelemetryConfig): SpanExporter | null {
169
- if (!config.openTelemetryExporter) return null;
170
+ if (!config.openTelemetryEnabled || !config.openTelemetryExporter) return null;
170
171
 
171
172
  if (typeof config.openTelemetryExporter === 'object') {
172
173
  return config.openTelemetryExporter;
@@ -189,7 +190,7 @@ function getTraceExporter(config: OpenTelemetryConfig): SpanExporter | null {
189
190
  }
190
191
 
191
192
  function getMetricExporter(config: OpenTelemetryConfig): PushMetricExporter | null {
192
- if (!config.openTelemetryMetricExporter) return null;
193
+ if (!config.openTelemetryEnabled || !config.openTelemetryMetricExporter) return null;
193
194
 
194
195
  if (typeof config.openTelemetryMetricExporter === 'object') {
195
196
  return config.openTelemetryMetricExporter;
@@ -218,12 +219,14 @@ function getMetricExporter(config: OpenTelemetryConfig): PushMetricExporter | nu
218
219
  }
219
220
 
220
221
  function getSpanProcessor(config: OpenTelemetryConfig): SpanProcessor | null {
222
+ if (!config.openTelemetryEnabled) return new NoopSpanProcessor();
223
+
221
224
  if (typeof config.openTelemetrySpanProcessor === 'object') {
222
225
  return config.openTelemetrySpanProcessor;
223
226
  }
224
227
 
225
228
  const traceExporter = getTraceExporter(config);
226
- if (!traceExporter) return null;
229
+ if (!traceExporter) return new NoopSpanProcessor();
227
230
 
228
231
  switch (config.openTelemetrySpanProcessor ?? 'batch') {
229
232
  case 'batch': {
@@ -238,45 +241,39 @@ function getSpanProcessor(config: OpenTelemetryConfig): SpanProcessor | null {
238
241
  }
239
242
  }
240
243
 
241
- /**
242
- * Should be called once we've loaded our config; this will allow us to set up
243
- * the correct metadata for the Honeycomb exporter. We don't actually have that
244
- * information available until we've loaded our config.
245
- */
246
- export async function init(config: OpenTelemetryConfig) {
247
- if (!config.openTelemetryEnabled) {
248
- // If not enabled, do nothing. We used to disable the instrumentations, but
249
- // per maintainers, that can actually be problematic. See the comments on
250
- // https://github.com/open-telemetry/opentelemetry-js-contrib/issues/970
251
- // The Express instrumentation also logs a benign error, which can be
252
- // confusing to users. There's a fix in progress if we want to switch back
253
- // to disabling instrumentations in the future:
254
- // https://github.com/open-telemetry/opentelemetry-js-contrib/pull/972
255
- return;
256
- }
257
-
258
- const metricExporter = getMetricExporter(config);
259
- const spanProcessor = getSpanProcessor(config);
244
+ function getSampler(config: OpenTelemetryConfig): Sampler {
245
+ if (!config.openTelemetryEnabled) return new AlwaysOffSampler();
260
246
 
261
- let sampler: Sampler;
262
247
  switch (config.openTelemetrySamplerType ?? 'always-on') {
263
248
  case 'always-on': {
264
- sampler = new AlwaysOnSampler();
265
- break;
249
+ return new AlwaysOnSampler();
266
250
  }
267
251
  case 'always-off': {
268
- sampler = new AlwaysOffSampler();
269
- break;
252
+ return new AlwaysOffSampler();
270
253
  }
271
254
  case 'trace-id-ratio': {
272
- sampler = new ParentBasedSampler({
255
+ return new ParentBasedSampler({
273
256
  root: new TraceIdRatioBasedSampler(config.openTelemetrySampleRate),
274
257
  });
275
- break;
276
258
  }
277
259
  default:
278
260
  throw new Error(`Unknown OpenTelemetry sampler type: ${config.openTelemetrySamplerType}`);
279
261
  }
262
+ }
263
+
264
+ /**
265
+ * Should be called once we've loaded our config; this will allow us to set up
266
+ * the correct metadata for the Honeycomb exporter. We don't actually have that
267
+ * information available until we've loaded our config.
268
+ *
269
+ * Note that even when `openTelemetryEnabled` is `false`, we'll still configure
270
+ * the `NodeTraceProvider` and instrumentations, as Sentry relies on that for
271
+ * scope isolation. However, we won't actually set up any exporters.
272
+ */
273
+ export async function init(config: OpenTelemetryConfig) {
274
+ const metricExporter = getMetricExporter(config);
275
+ const spanProcessor = getSpanProcessor(config);
276
+ const sampler = getSampler(config);
280
277
 
281
278
  // Much of this functionality is copied from `@opentelemetry/sdk-node`, but
282
279
  // we can't use the SDK directly because of the fact that we load our config
@@ -284,7 +281,7 @@ export async function init(config: OpenTelemetryConfig) {
284
281
  // then can we actually start requiring all of our code that loads our config
285
282
  // and ultimately tells us how to configure OpenTelemetry.
286
283
 
287
- let resource = detectResourcesSync({
284
+ let resource = detectResources({
288
285
  // The AWS resource detector always tries to reach out to the EC2 metadata
289
286
  // service endpoint. When running locally, or otherwise in a non-AWS environment,
290
287
  // this will typically fail immediately wih `EHOSTDOWN`, but will sometimes wait
@@ -304,19 +301,15 @@ export async function init(config: OpenTelemetryConfig) {
304
301
  });
305
302
 
306
303
  if (config.serviceName) {
307
- resource = resource.merge(
308
- new Resource({ [SemanticResourceAttributes.SERVICE_NAME]: config.serviceName }),
309
- );
304
+ resource = resource.merge(resourceFromAttributes({ [ATTR_SERVICE_NAME]: config.serviceName }));
310
305
  }
311
306
 
312
307
  // Set up tracing instrumentation.
313
308
  const nodeTracerProvider = new NodeTracerProvider({
314
309
  sampler,
315
310
  resource,
311
+ spanProcessors: [spanProcessor].filter((p) => !!p),
316
312
  });
317
- if (spanProcessor) {
318
- nodeTracerProvider.addSpanProcessor(spanProcessor);
319
- }
320
313
  nodeTracerProvider.register({
321
314
  contextManager: config.contextManager,
322
315
  });
@@ -18,23 +18,24 @@ async function waitForMetricsExport(exporter: InMemoryMetricExporter) {
18
18
 
19
19
  describe('instrumentedWithMetrics', () => {
20
20
  let exporter: InMemoryMetricExporter;
21
- let metricReader: PeriodicExportingMetricReader;
21
+ let meterProvider: MeterProvider;
22
22
  let meter: Meter;
23
23
 
24
24
  beforeEach(async () => {
25
- const meterProvider = new MeterProvider();
26
- meter = meterProvider.getMeter('test');
27
25
  exporter = new InMemoryMetricExporter(AggregationTemporality.DELTA);
28
- metricReader = new PeriodicExportingMetricReader({
29
- exporter,
30
- exportIntervalMillis: 50,
26
+ meterProvider = new MeterProvider({
27
+ readers: [
28
+ new PeriodicExportingMetricReader({
29
+ exporter,
30
+ exportIntervalMillis: 50,
31
+ }),
32
+ ],
31
33
  });
32
- meterProvider.addMetricReader(metricReader);
34
+ meter = meterProvider.getMeter('test');
33
35
  });
34
36
 
35
37
  afterEach(async () => {
36
- await exporter.shutdown();
37
- await metricReader.shutdown();
38
+ await meterProvider.shutdown();
38
39
  });
39
40
 
40
41
  it('records a histogram for the function duration', async () => {