@prairielearn/opentelemetry 3.0.5 → 3.1.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 +14 -0
- package/dist/init.d.ts +2 -0
- package/dist/init.d.ts.map +1 -1
- package/dist/init.js +34 -5
- package/dist/init.js.map +1 -1
- package/package.json +21 -21
- package/src/init.ts +61 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# @prairielearn/opentelemetry
|
|
2
2
|
|
|
3
|
+
## 3.1.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 70a8029: Upgrade all JavaScript dependencies
|
|
8
|
+
|
|
9
|
+
## 3.1.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- 28a9137: - Add support for an `incomingHttpRequestHook` function that can be used to add specific attributes to the root span of an incoming HTTP request
|
|
14
|
+
- Add `ForceSampleSampler`
|
|
15
|
+
- When using the `id-trace-ratio` sampler, if a span contains a `force_sample` attribute with value `true`, the span will be forcibly sampled
|
|
16
|
+
|
|
3
17
|
## 3.0.5
|
|
4
18
|
|
|
5
19
|
### Patch Changes
|
package/dist/init.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ContextManager } from '@opentelemetry/api';
|
|
2
|
+
import { type StartIncomingSpanCustomAttributeFunction } from '@opentelemetry/instrumentation-http';
|
|
2
3
|
import { type PushMetricExporter } from '@opentelemetry/sdk-metrics';
|
|
3
4
|
import { type SpanExporter, type SpanProcessor } from '@opentelemetry/sdk-trace-base';
|
|
4
5
|
interface OpenTelemetryConfigEnabled {
|
|
@@ -13,6 +14,7 @@ interface OpenTelemetryConfigEnabled {
|
|
|
13
14
|
honeycombApiKey?: string | null;
|
|
14
15
|
honeycombDataset?: string | null;
|
|
15
16
|
serviceName?: string;
|
|
17
|
+
incomingHttpRequestHook?: StartIncomingSpanCustomAttributeFunction;
|
|
16
18
|
}
|
|
17
19
|
interface OpenTelemetryConfigDisabled extends Partial<OpenTelemetryConfigEnabled> {
|
|
18
20
|
openTelemetryEnabled: false;
|
package/dist/init.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AACA,OAAO,
|
|
1
|
+
{"version":3,"file":"init.d.ts","sourceRoot":"","sources":["../src/init.ts"],"names":[],"mappings":"AACA,OAAO,EAGL,KAAK,cAAc,EAIpB,MAAM,oBAAoB,CAAC;AAS5B,OAAO,EAEL,KAAK,wCAAwC,EAC9C,MAAM,qCAAqC,CAAC;AAW7C,OAAO,EAKL,KAAK,kBAAkB,EACxB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAWL,KAAK,YAAY,EACjB,KAAK,aAAa,EAEnB,MAAM,+BAA+B,CAAC;AAqIvC,UAAU,0BAA0B;IAClC,oBAAoB,EAAE,OAAO,CAAC;IAC9B,qBAAqB,CAAC,EAAE,SAAS,GAAG,WAAW,GAAG,QAAQ,GAAG,YAAY,GAAG,IAAI,CAAC;IACjF,2BAA2B,CAAC,EAAE,SAAS,GAAG,WAAW,GAAG,kBAAkB,GAAG,IAAI,CAAC;IAClF,uCAAuC,CAAC,EAAE,MAAM,CAAC;IACjD,wBAAwB,EAAE,WAAW,GAAG,YAAY,GAAG,gBAAgB,CAAC;IACxE,uBAAuB,CAAC,EAAE,MAAM,CAAC;IACjC,0BAA0B,CAAC,EAAE,OAAO,GAAG,QAAQ,GAAG,aAAa,CAAC;IAChE,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,eAAe,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,uBAAuB,CAAC,EAAE,wCAAwC,CAAC;CACpE;AAID,UAAU,2BAA4B,SAAQ,OAAO,CAAC,0BAA0B,CAAC;IAC/E,oBAAoB,EAAE,KAAK,CAAC;CAC7B;AAED,MAAM,MAAM,mBAAmB,GAAG,0BAA0B,GAAG,2BAA2B,CAAC;AA+G3F;;;;;;;;GAQG;AACH,wBAAsB,IAAI,CAAC,MAAM,EAAE,mBAAmB,iBA+DrD;AAED;;;GAGG;AACH,wBAAsB,QAAQ,IAAI,OAAO,CAAC,IAAI,CAAC,CAK9C;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,SAEtC"}
|
package/dist/init.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Metadata, credentials } from '@grpc/grpc-js';
|
|
2
|
-
import { metrics } from '@opentelemetry/api';
|
|
2
|
+
import { metrics, } from '@opentelemetry/api';
|
|
3
3
|
import { hrTimeToMilliseconds } from '@opentelemetry/core';
|
|
4
4
|
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc';
|
|
5
5
|
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc';
|
|
@@ -8,16 +8,40 @@ import { AwsInstrumentation } from '@opentelemetry/instrumentation-aws-sdk';
|
|
|
8
8
|
import { ConnectInstrumentation } from '@opentelemetry/instrumentation-connect';
|
|
9
9
|
import { DnsInstrumentation } from '@opentelemetry/instrumentation-dns';
|
|
10
10
|
import { ExpressInstrumentation, ExpressLayerType } from '@opentelemetry/instrumentation-express';
|
|
11
|
-
import { HttpInstrumentation } from '@opentelemetry/instrumentation-http';
|
|
11
|
+
import { HttpInstrumentation, } from '@opentelemetry/instrumentation-http';
|
|
12
12
|
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
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, NoopSpanProcessor, ParentBasedSampler, SimpleSpanProcessor, TraceIdRatioBasedSampler, } from '@opentelemetry/sdk-trace-base';
|
|
18
|
+
import { AlwaysOffSampler, AlwaysOnSampler, BatchSpanProcessor, ConsoleSpanExporter, NoopSpanProcessor, ParentBasedSampler, SamplingDecision, SimpleSpanProcessor, TraceIdRatioBasedSampler, } from '@opentelemetry/sdk-trace-base';
|
|
19
19
|
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
|
|
20
20
|
import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
|
|
21
|
+
/**
|
|
22
|
+
* Allows applications to force sampling of specific spans/traces by setting
|
|
23
|
+
* a `force_sample` attribute to `true`. When that attribute is set, the span
|
|
24
|
+
* will be sampled regardless of the underlying sampler's decision.
|
|
25
|
+
*
|
|
26
|
+
* This is most useful when that attribute is added to the root span of a trace,
|
|
27
|
+
* and when using a `ParentBasedSampler`, as that will cause all spans in the trace
|
|
28
|
+
* to be sampled.
|
|
29
|
+
*/
|
|
30
|
+
class ForceSampleSampler {
|
|
31
|
+
sampler;
|
|
32
|
+
constructor(sampler) {
|
|
33
|
+
this.sampler = sampler;
|
|
34
|
+
}
|
|
35
|
+
shouldSample(context, traceId, spanName, spanKind, attributes, links) {
|
|
36
|
+
if (attributes['force_sample'] === true) {
|
|
37
|
+
return { decision: SamplingDecision.RECORD_AND_SAMPLED };
|
|
38
|
+
}
|
|
39
|
+
return this.sampler.shouldSample(context, traceId, spanName, spanKind, attributes, links);
|
|
40
|
+
}
|
|
41
|
+
toString() {
|
|
42
|
+
return `ForceSampleSampler{${this.sampler.toString()}}`;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
21
45
|
/**
|
|
22
46
|
* Extends `BatchSpanProcessor` to give it the ability to filter out spans
|
|
23
47
|
* before they're queued up to send. This enhances our sampling process so
|
|
@@ -58,6 +82,7 @@ function filter(span) {
|
|
|
58
82
|
// Always return true so that we default to including a span.
|
|
59
83
|
return true;
|
|
60
84
|
}
|
|
85
|
+
let incomingHttpRequestHook = undefined;
|
|
61
86
|
// When adding new instrumentation here, add the corresponding packages to
|
|
62
87
|
// `commonjs-preloads.ts` so that we can ensure that they're loaded via CJS
|
|
63
88
|
// before anything tries to load them via CJS. This is necessary because the
|
|
@@ -88,6 +113,9 @@ const instrumentations = [
|
|
|
88
113
|
/\/pl\/webhooks\/ping/,
|
|
89
114
|
].some((re) => re.test(req.url ?? '/'));
|
|
90
115
|
},
|
|
116
|
+
startIncomingSpanHook(req) {
|
|
117
|
+
return incomingHttpRequestHook?.(req) ?? {};
|
|
118
|
+
},
|
|
91
119
|
}),
|
|
92
120
|
new IORedisInstrumentation(),
|
|
93
121
|
new PgInstrumentation(),
|
|
@@ -187,9 +215,9 @@ function getSampler(config) {
|
|
|
187
215
|
return new AlwaysOffSampler();
|
|
188
216
|
}
|
|
189
217
|
case 'trace-id-ratio': {
|
|
190
|
-
return new ParentBasedSampler({
|
|
218
|
+
return new ForceSampleSampler(new ParentBasedSampler({
|
|
191
219
|
root: new TraceIdRatioBasedSampler(config.openTelemetrySampleRate),
|
|
192
|
-
});
|
|
220
|
+
}));
|
|
193
221
|
}
|
|
194
222
|
default:
|
|
195
223
|
throw new Error(`Unknown OpenTelemetry sampler type: ${config.openTelemetrySamplerType}`);
|
|
@@ -205,6 +233,7 @@ function getSampler(config) {
|
|
|
205
233
|
* scope isolation. However, we won't actually set up any exporters.
|
|
206
234
|
*/
|
|
207
235
|
export async function init(config) {
|
|
236
|
+
incomingHttpRequestHook = config.incomingHttpRequestHook;
|
|
208
237
|
const metricExporter = getMetricExporter(config);
|
|
209
238
|
const spanProcessor = getSpanProcessor(config);
|
|
210
239
|
const sampler = getSampler(config);
|
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 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"]}
|
|
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 {\n type Attributes,\n type Context,\n type ContextManager,\n type Link,\n type SpanKind,\n metrics,\n} 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 {\n HttpInstrumentation,\n type StartIncomingSpanCustomAttributeFunction,\n} 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 SamplingDecision,\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 * Allows applications to force sampling of specific spans/traces by setting\n * a `force_sample` attribute to `true`. When that attribute is set, the span\n * will be sampled regardless of the underlying sampler's decision.\n *\n * This is most useful when that attribute is added to the root span of a trace,\n * and when using a `ParentBasedSampler`, as that will cause all spans in the trace\n * to be sampled.\n */\nclass ForceSampleSampler implements Sampler {\n private sampler: Sampler;\n constructor(sampler: Sampler) {\n this.sampler = sampler;\n }\n\n shouldSample(\n context: Context,\n traceId: string,\n spanName: string,\n spanKind: SpanKind,\n attributes: Attributes,\n links: Link[],\n ) {\n if (attributes['force_sample'] === true) {\n return { decision: SamplingDecision.RECORD_AND_SAMPLED };\n }\n\n return this.sampler.shouldSample(context, traceId, spanName, spanKind, attributes, links);\n }\n\n toString() {\n return `ForceSampleSampler{${this.sampler.toString()}}`;\n }\n}\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\nlet incomingHttpRequestHook: StartIncomingSpanCustomAttributeFunction | undefined = undefined;\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 startIncomingSpanHook(req) {\n return incomingHttpRequestHook?.(req) ?? {};\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 incomingHttpRequestHook?: StartIncomingSpanCustomAttributeFunction;\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 ForceSampleSampler(\n new ParentBasedSampler({\n root: new TraceIdRatioBasedSampler(config.openTelemetrySampleRate),\n }),\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 incomingHttpRequestHook = config.incomingHttpRequestHook;\n\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"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@prairielearn/opentelemetry",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -14,36 +14,36 @@
|
|
|
14
14
|
"test": "vitest run --coverage"
|
|
15
15
|
},
|
|
16
16
|
"dependencies": {
|
|
17
|
-
"@grpc/grpc-js": "^1.14.
|
|
17
|
+
"@grpc/grpc-js": "^1.14.3",
|
|
18
18
|
"@opentelemetry/api": "^1.9.0",
|
|
19
19
|
"@opentelemetry/core": "^2.2.0",
|
|
20
|
-
"@opentelemetry/exporter-metrics-otlp-grpc": "^0.
|
|
21
|
-
"@opentelemetry/exporter-trace-otlp-grpc": "^0.
|
|
22
|
-
"@opentelemetry/exporter-trace-otlp-http": "^0.
|
|
23
|
-
"@opentelemetry/instrumentation": "^0.
|
|
24
|
-
"@opentelemetry/instrumentation-aws-sdk": "^0.
|
|
25
|
-
"@opentelemetry/instrumentation-connect": "^0.
|
|
26
|
-
"@opentelemetry/instrumentation-dns": "^0.
|
|
27
|
-
"@opentelemetry/instrumentation-express": "^0.
|
|
28
|
-
"@opentelemetry/instrumentation-http": "^0.
|
|
29
|
-
"@opentelemetry/instrumentation-ioredis": "^0.
|
|
30
|
-
"@opentelemetry/instrumentation-pg": "^0.
|
|
31
|
-
"@opentelemetry/instrumentation-redis": "^0.
|
|
32
|
-
"@opentelemetry/resource-detector-aws": "^2.
|
|
20
|
+
"@opentelemetry/exporter-metrics-otlp-grpc": "^0.208.0",
|
|
21
|
+
"@opentelemetry/exporter-trace-otlp-grpc": "^0.208.0",
|
|
22
|
+
"@opentelemetry/exporter-trace-otlp-http": "^0.208.0",
|
|
23
|
+
"@opentelemetry/instrumentation": "^0.208.0",
|
|
24
|
+
"@opentelemetry/instrumentation-aws-sdk": "^0.64.1",
|
|
25
|
+
"@opentelemetry/instrumentation-connect": "^0.52.0",
|
|
26
|
+
"@opentelemetry/instrumentation-dns": "^0.52.0",
|
|
27
|
+
"@opentelemetry/instrumentation-express": "^0.57.1",
|
|
28
|
+
"@opentelemetry/instrumentation-http": "^0.208.0",
|
|
29
|
+
"@opentelemetry/instrumentation-ioredis": "^0.57.0",
|
|
30
|
+
"@opentelemetry/instrumentation-pg": "^0.61.2",
|
|
31
|
+
"@opentelemetry/instrumentation-redis": "^0.57.2",
|
|
32
|
+
"@opentelemetry/resource-detector-aws": "^2.9.0",
|
|
33
33
|
"@opentelemetry/resources": "^2.2.0",
|
|
34
34
|
"@opentelemetry/sdk-metrics": "^2.2.0",
|
|
35
|
-
"@opentelemetry/sdk-node": "^0.
|
|
35
|
+
"@opentelemetry/sdk-node": "^0.208.0",
|
|
36
36
|
"@opentelemetry/sdk-trace-base": "^2.2.0",
|
|
37
37
|
"@opentelemetry/sdk-trace-node": "^2.2.0",
|
|
38
|
-
"@opentelemetry/semantic-conventions": "^1.
|
|
38
|
+
"@opentelemetry/semantic-conventions": "^1.38.0"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
41
|
"@opentelemetry/context-async-hooks": "^2.2.0",
|
|
42
42
|
"@prairielearn/tsconfig": "^0.0.0",
|
|
43
|
-
"@types/node": "^22.
|
|
44
|
-
"@vitest/coverage-v8": "^4.0.
|
|
45
|
-
"tsx": "^4.
|
|
43
|
+
"@types/node": "^22.19.3",
|
|
44
|
+
"@vitest/coverage-v8": "^4.0.16",
|
|
45
|
+
"tsx": "^4.21.0",
|
|
46
46
|
"typescript": "^5.9.3",
|
|
47
|
-
"vitest": "^4.0.
|
|
47
|
+
"vitest": "^4.0.16"
|
|
48
48
|
}
|
|
49
49
|
}
|
package/src/init.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import { Metadata, credentials } from '@grpc/grpc-js';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
type Attributes,
|
|
4
|
+
type Context,
|
|
5
|
+
type ContextManager,
|
|
6
|
+
type Link,
|
|
7
|
+
type SpanKind,
|
|
8
|
+
metrics,
|
|
9
|
+
} from '@opentelemetry/api';
|
|
3
10
|
import { hrTimeToMilliseconds } from '@opentelemetry/core';
|
|
4
11
|
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-grpc';
|
|
5
12
|
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-grpc';
|
|
@@ -8,7 +15,10 @@ import { AwsInstrumentation } from '@opentelemetry/instrumentation-aws-sdk';
|
|
|
8
15
|
import { ConnectInstrumentation } from '@opentelemetry/instrumentation-connect';
|
|
9
16
|
import { DnsInstrumentation } from '@opentelemetry/instrumentation-dns';
|
|
10
17
|
import { ExpressInstrumentation, ExpressLayerType } from '@opentelemetry/instrumentation-express';
|
|
11
|
-
import {
|
|
18
|
+
import {
|
|
19
|
+
HttpInstrumentation,
|
|
20
|
+
type StartIncomingSpanCustomAttributeFunction,
|
|
21
|
+
} from '@opentelemetry/instrumentation-http';
|
|
12
22
|
import { IORedisInstrumentation } from '@opentelemetry/instrumentation-ioredis';
|
|
13
23
|
import { PgInstrumentation } from '@opentelemetry/instrumentation-pg';
|
|
14
24
|
import { RedisInstrumentation } from '@opentelemetry/instrumentation-redis';
|
|
@@ -35,6 +45,7 @@ import {
|
|
|
35
45
|
ParentBasedSampler,
|
|
36
46
|
type ReadableSpan,
|
|
37
47
|
type Sampler,
|
|
48
|
+
SamplingDecision,
|
|
38
49
|
SimpleSpanProcessor,
|
|
39
50
|
type SpanExporter,
|
|
40
51
|
type SpanProcessor,
|
|
@@ -43,6 +54,41 @@ import {
|
|
|
43
54
|
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
|
|
44
55
|
import { ATTR_SERVICE_NAME } from '@opentelemetry/semantic-conventions';
|
|
45
56
|
|
|
57
|
+
/**
|
|
58
|
+
* Allows applications to force sampling of specific spans/traces by setting
|
|
59
|
+
* a `force_sample` attribute to `true`. When that attribute is set, the span
|
|
60
|
+
* will be sampled regardless of the underlying sampler's decision.
|
|
61
|
+
*
|
|
62
|
+
* This is most useful when that attribute is added to the root span of a trace,
|
|
63
|
+
* and when using a `ParentBasedSampler`, as that will cause all spans in the trace
|
|
64
|
+
* to be sampled.
|
|
65
|
+
*/
|
|
66
|
+
class ForceSampleSampler implements Sampler {
|
|
67
|
+
private sampler: Sampler;
|
|
68
|
+
constructor(sampler: Sampler) {
|
|
69
|
+
this.sampler = sampler;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
shouldSample(
|
|
73
|
+
context: Context,
|
|
74
|
+
traceId: string,
|
|
75
|
+
spanName: string,
|
|
76
|
+
spanKind: SpanKind,
|
|
77
|
+
attributes: Attributes,
|
|
78
|
+
links: Link[],
|
|
79
|
+
) {
|
|
80
|
+
if (attributes['force_sample'] === true) {
|
|
81
|
+
return { decision: SamplingDecision.RECORD_AND_SAMPLED };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return this.sampler.shouldSample(context, traceId, spanName, spanKind, attributes, links);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
toString() {
|
|
88
|
+
return `ForceSampleSampler{${this.sampler.toString()}}`;
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
46
92
|
/**
|
|
47
93
|
* Extends `BatchSpanProcessor` to give it the ability to filter out spans
|
|
48
94
|
* before they're queued up to send. This enhances our sampling process so
|
|
@@ -88,6 +134,8 @@ function filter(span: ReadableSpan) {
|
|
|
88
134
|
return true;
|
|
89
135
|
}
|
|
90
136
|
|
|
137
|
+
let incomingHttpRequestHook: StartIncomingSpanCustomAttributeFunction | undefined = undefined;
|
|
138
|
+
|
|
91
139
|
// When adding new instrumentation here, add the corresponding packages to
|
|
92
140
|
// `commonjs-preloads.ts` so that we can ensure that they're loaded via CJS
|
|
93
141
|
// before anything tries to load them via CJS. This is necessary because the
|
|
@@ -118,6 +166,9 @@ const instrumentations = [
|
|
|
118
166
|
/\/pl\/webhooks\/ping/,
|
|
119
167
|
].some((re) => re.test(req.url ?? '/'));
|
|
120
168
|
},
|
|
169
|
+
startIncomingSpanHook(req) {
|
|
170
|
+
return incomingHttpRequestHook?.(req) ?? {};
|
|
171
|
+
},
|
|
121
172
|
}),
|
|
122
173
|
new IORedisInstrumentation(),
|
|
123
174
|
new PgInstrumentation(),
|
|
@@ -144,6 +195,7 @@ interface OpenTelemetryConfigEnabled {
|
|
|
144
195
|
honeycombApiKey?: string | null;
|
|
145
196
|
honeycombDataset?: string | null;
|
|
146
197
|
serviceName?: string;
|
|
198
|
+
incomingHttpRequestHook?: StartIncomingSpanCustomAttributeFunction;
|
|
147
199
|
}
|
|
148
200
|
|
|
149
201
|
// When we know for sure that OpenTelemetry is disabled, we won't require
|
|
@@ -252,9 +304,11 @@ function getSampler(config: OpenTelemetryConfig): Sampler {
|
|
|
252
304
|
return new AlwaysOffSampler();
|
|
253
305
|
}
|
|
254
306
|
case 'trace-id-ratio': {
|
|
255
|
-
return new
|
|
256
|
-
|
|
257
|
-
|
|
307
|
+
return new ForceSampleSampler(
|
|
308
|
+
new ParentBasedSampler({
|
|
309
|
+
root: new TraceIdRatioBasedSampler(config.openTelemetrySampleRate),
|
|
310
|
+
}),
|
|
311
|
+
);
|
|
258
312
|
}
|
|
259
313
|
default:
|
|
260
314
|
throw new Error(`Unknown OpenTelemetry sampler type: ${config.openTelemetrySamplerType}`);
|
|
@@ -271,6 +325,8 @@ function getSampler(config: OpenTelemetryConfig): Sampler {
|
|
|
271
325
|
* scope isolation. However, we won't actually set up any exporters.
|
|
272
326
|
*/
|
|
273
327
|
export async function init(config: OpenTelemetryConfig) {
|
|
328
|
+
incomingHttpRequestHook = config.incomingHttpRequestHook;
|
|
329
|
+
|
|
274
330
|
const metricExporter = getMetricExporter(config);
|
|
275
331
|
const spanProcessor = getSpanProcessor(config);
|
|
276
332
|
const sampler = getSampler(config);
|