diff --git a/CHANGELOG.md b/CHANGELOG.md
index fff7a1c22e..0c18cad260 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,8 @@ All notable changes to this project will be documented in this file.
### :rocket: (Enhancement)
+feat(sdk-trace-base): move Sampler declaration into sdk-trace-base [#3088](https://github.com/open-telemetry/opentelemetry-js/pull/3088) @legendecas
+
### :bug: (Bug Fix)
### :books: (Refine Doc)
diff --git a/experimental/packages/opentelemetry-sdk-node/src/types.ts b/experimental/packages/opentelemetry-sdk-node/src/types.ts
index 90c1e08401..705481174c 100644
--- a/experimental/packages/opentelemetry-sdk-node/src/types.ts
+++ b/experimental/packages/opentelemetry-sdk-node/src/types.ts
@@ -15,14 +15,15 @@
*/
import type { ContextManager, SpanAttributes } from '@opentelemetry/api';
-import { Sampler, TextMapPropagator } from '@opentelemetry/api';
+import { TextMapPropagator } from '@opentelemetry/api';
import { InstrumentationOption } from '@opentelemetry/instrumentation';
import { Resource } from '@opentelemetry/resources';
import { MetricReader } from '@opentelemetry/sdk-metrics-base';
import {
+ Sampler,
SpanExporter,
SpanLimits,
- SpanProcessor
+ SpanProcessor,
} from '@opentelemetry/sdk-trace-base';
export interface NodeSDKConfiguration {
diff --git a/packages/opentelemetry-core/README.md b/packages/opentelemetry-core/README.md
index 573c3bc08b..1d543dca22 100644
--- a/packages/opentelemetry-core/README.md
+++ b/packages/opentelemetry-core/README.md
@@ -13,11 +13,6 @@ This package provides default implementations of the OpenTelemetry API for trace
- [W3CTraceContextPropagator Propagator](#w3ctracecontextpropagator-propagator)
- [Composite Propagator](#composite-propagator)
- [Baggage Propagator](#baggage-propagator)
- - [Built-in Sampler](#built-in-sampler)
- - [AlwaysOn Sampler](#alwayson-sampler)
- - [AlwaysOff Sampler](#alwaysoff-sampler)
- - [TraceIdRatioBased Sampler](#traceidratiobased-sampler)
- - [ParentBased Sampler](#parentbased-sampler)
- [Useful links](#useful-links)
- [License](#license)
@@ -61,107 +56,6 @@ const { W3CBaggagePropagator } = require("@opentelemetry/core");
api.propagation.setGlobalPropagator(new W3CBaggagePropagator());
```
-### Built-in Sampler
-
-Sampler is used to make decisions on `Span` sampling.
-
-#### AlwaysOn Sampler
-
-Samples every trace regardless of upstream sampling decisions.
-
-> This is used as a default Sampler
-
-```js
-const { NodeTracerProvider } = require("@opentelemetry/sdk-trace-node");
-const { AlwaysOnSampler } = require("@opentelemetry/core");
-
-const tracerProvider = new NodeTracerProvider({
- sampler: new AlwaysOnSampler()
-});
-```
-
-#### AlwaysOff Sampler
-
-Doesn't sample any trace, regardless of upstream sampling decisions.
-
-```js
-const { NodeTracerProvider } = require("@opentelemetry/sdk-trace-node");
-const { AlwaysOffSampler } = require("@opentelemetry/core");
-
-const tracerProvider = new NodeTracerProvider({
- sampler: new AlwaysOffSampler()
-});
-```
-
-#### TraceIdRatioBased Sampler
-
-Samples some percentage of traces, calculated deterministically using the trace ID.
-Any trace that would be sampled at a given percentage will also be sampled at any higher percentage.
-
-The `TraceIDRatioSampler` may be used with the `ParentBasedSampler` to respect the sampled flag of an incoming trace.
-
-```js
-const { NodeTracerProvider } = require("@opentelemetry/sdk-trace-node");
-const { TraceIdRatioBasedSampler } = require("@opentelemetry/core");
-
-const tracerProvider = new NodeTracerProvider({
- // See details of ParentBasedSampler below
- sampler: new ParentBasedSampler({
- // Trace ID Ratio Sampler accepts a positional argument
- // which represents the percentage of traces which should
- // be sampled.
- root: new TraceIdRatioBasedSampler(0.5)
- });
-});
-```
-
-#### ParentBased Sampler
-
-- This is a composite sampler. `ParentBased` helps distinguished between the
-following cases:
- - No parent (root span).
- - Remote parent with `sampled` flag `true`
- - Remote parent with `sampled` flag `false`
- - Local parent with `sampled` flag `true`
- - Local parent with `sampled` flag `false`
-
-Required parameters:
-
-- `root(Sampler)` - Sampler called for spans with no parent (root spans)
-
-Optional parameters:
-
-- `remoteParentSampled(Sampler)` (default: `AlwaysOn`)
-- `remoteParentNotSampled(Sampler)` (default: `AlwaysOff`)
-- `localParentSampled(Sampler)` (default: `AlwaysOn`)
-- `localParentNotSampled(Sampler)` (default: `AlwaysOff`)
-
-|Parent| parent.isRemote() | parent.isSampled()| Invoke sampler|
-|--|--|--|--|
-|absent| n/a | n/a |`root()`|
-|present|true|true|`remoteParentSampled()`|
-|present|true|false|`remoteParentNotSampled()`|
-|present|false|true|`localParentSampled()`|
-|present|false|false|`localParentNotSampled()`|
-
-```js
-const { NodeTracerProvider } = require("@opentelemetry/sdk-trace-node");
-const { ParentBasedSampler, AlwaysOffSampler, TraceIdRatioBasedSampler } = require("@opentelemetry/core");
-
-const tracerProvider = new NodeTracerProvider({
- sampler: new ParentBasedSampler({
- // By default, the ParentBasedSampler will respect the parent span's sampling
- // decision. This is configurable by providing a different sampler to use
- // based on the situation. See configuration details above.
- //
- // This will delegate the sampling decision of all root traces (no parent)
- // to the TraceIdRatioBasedSampler.
- // See details of TraceIdRatioBasedSampler above.
- root: new TraceIdRatioBasedSampler(0.5)
- })
-});
-```
-
## Useful links
- For more information on OpenTelemetry, visit:
diff --git a/packages/opentelemetry-core/src/platform/browser/RandomIdGenerator.ts b/packages/opentelemetry-core/src/platform/browser/RandomIdGenerator.ts
index 1094fe4e83..634ba0a32f 100644
--- a/packages/opentelemetry-core/src/platform/browser/RandomIdGenerator.ts
+++ b/packages/opentelemetry-core/src/platform/browser/RandomIdGenerator.ts
@@ -18,6 +18,9 @@ import { IdGenerator } from '../../trace/IdGenerator';
const SPAN_ID_BYTES = 8;
const TRACE_ID_BYTES = 16;
+/**
+ * @deprecated Use the one defined in @opentelemetry/sdk-trace-base instead.
+ */
export class RandomIdGenerator implements IdGenerator {
/**
* Returns a random 16-byte trace ID formatted/encoded as a 32 lowercase hex
diff --git a/packages/opentelemetry-core/src/platform/node/RandomIdGenerator.ts b/packages/opentelemetry-core/src/platform/node/RandomIdGenerator.ts
index 219bcd0464..34d4971e55 100644
--- a/packages/opentelemetry-core/src/platform/node/RandomIdGenerator.ts
+++ b/packages/opentelemetry-core/src/platform/node/RandomIdGenerator.ts
@@ -18,6 +18,9 @@ import { IdGenerator } from '../../trace/IdGenerator';
const SPAN_ID_BYTES = 8;
const TRACE_ID_BYTES = 16;
+/**
+ * @deprecated Use the one defined in @opentelemetry/sdk-trace-base instead.
+ */
export class RandomIdGenerator implements IdGenerator {
/**
* Returns a random 16-byte trace ID formatted/encoded as a 32 lowercase hex
diff --git a/packages/opentelemetry-core/src/trace/IdGenerator.ts b/packages/opentelemetry-core/src/trace/IdGenerator.ts
index 65f63f9c0c..bf027f905a 100644
--- a/packages/opentelemetry-core/src/trace/IdGenerator.ts
+++ b/packages/opentelemetry-core/src/trace/IdGenerator.ts
@@ -14,7 +14,10 @@
* limitations under the License.
*/
-/** IdGenerator provides an interface for generating Trace Id and Span Id */
+/**
+ * @deprecated Use the one defined in @opentelemetry/sdk-trace-base instead.
+ * IdGenerator provides an interface for generating Trace Id and Span Id.
+ */
export interface IdGenerator {
/** Returns a trace ID composed of 32 lowercase hex characters. */
generateTraceId(): string;
diff --git a/packages/opentelemetry-core/src/trace/sampler/AlwaysOffSampler.ts b/packages/opentelemetry-core/src/trace/sampler/AlwaysOffSampler.ts
index 67c169efe9..ebe824d90b 100644
--- a/packages/opentelemetry-core/src/trace/sampler/AlwaysOffSampler.ts
+++ b/packages/opentelemetry-core/src/trace/sampler/AlwaysOffSampler.ts
@@ -16,7 +16,10 @@
import { Sampler, SamplingDecision, SamplingResult } from '@opentelemetry/api';
-/** Sampler that samples no traces. */
+/**
+ * @deprecated Use the one defined in @opentelemetry/sdk-trace-base instead.
+ * Sampler that samples no traces.
+ */
export class AlwaysOffSampler implements Sampler {
shouldSample(): SamplingResult {
return {
diff --git a/packages/opentelemetry-core/src/trace/sampler/AlwaysOnSampler.ts b/packages/opentelemetry-core/src/trace/sampler/AlwaysOnSampler.ts
index 5d15b88526..8967d49645 100644
--- a/packages/opentelemetry-core/src/trace/sampler/AlwaysOnSampler.ts
+++ b/packages/opentelemetry-core/src/trace/sampler/AlwaysOnSampler.ts
@@ -16,7 +16,10 @@
import { Sampler, SamplingDecision, SamplingResult } from '@opentelemetry/api';
-/** Sampler that samples all traces. */
+/**
+ * @deprecated Use the one defined in @opentelemetry/sdk-trace-base instead.
+ * Sampler that samples all traces.
+ */
export class AlwaysOnSampler implements Sampler {
shouldSample(): SamplingResult {
return {
diff --git a/packages/opentelemetry-core/src/trace/sampler/ParentBasedSampler.ts b/packages/opentelemetry-core/src/trace/sampler/ParentBasedSampler.ts
index 1097ac1476..4b2b6cec57 100644
--- a/packages/opentelemetry-core/src/trace/sampler/ParentBasedSampler.ts
+++ b/packages/opentelemetry-core/src/trace/sampler/ParentBasedSampler.ts
@@ -29,6 +29,7 @@ import { AlwaysOffSampler } from './AlwaysOffSampler';
import { AlwaysOnSampler } from './AlwaysOnSampler';
/**
+ * @deprecated Use the one defined in @opentelemetry/sdk-trace-base instead.
* A composite sampler that either respects the parent span's sampling decision
* or delegates to `delegateSampler` for root spans.
*/
diff --git a/packages/opentelemetry-core/src/trace/sampler/TraceIdRatioBasedSampler.ts b/packages/opentelemetry-core/src/trace/sampler/TraceIdRatioBasedSampler.ts
index 5e8a66dbac..c4928d1cf0 100644
--- a/packages/opentelemetry-core/src/trace/sampler/TraceIdRatioBasedSampler.ts
+++ b/packages/opentelemetry-core/src/trace/sampler/TraceIdRatioBasedSampler.ts
@@ -21,7 +21,10 @@ import {
isValidTraceId,
} from '@opentelemetry/api';
-/** Sampler that samples a given fraction of traces based of trace id deterministically. */
+/**
+ * @deprecated Use the one defined in @opentelemetry/sdk-trace-base instead.
+ * Sampler that samples a given fraction of traces based of trace id deterministically.
+ */
export class TraceIdRatioBasedSampler implements Sampler {
private _upperBound: number;
diff --git a/packages/opentelemetry-sdk-trace-base/README.md b/packages/opentelemetry-sdk-trace-base/README.md
index f9d2871874..c4a40cc6f5 100644
--- a/packages/opentelemetry-sdk-trace-base/README.md
+++ b/packages/opentelemetry-sdk-trace-base/README.md
@@ -46,6 +46,107 @@ Tracing configuration is a merge of user supplied configuration with both the de
configuration as specified in [config.ts](./src/config.ts) and an
environmentally configurable sampling (via `OTEL_TRACES_SAMPLER` and `OTEL_TRACES_SAMPLER_ARG`).
+## Built-in Samplers
+
+Sampler is used to make decisions on `Span` sampling.
+
+### AlwaysOn Sampler
+
+Samples every trace regardless of upstream sampling decisions.
+
+> This is used as a default Sampler
+
+```js
+const { NodeTracerProvider } = require("@opentelemetry/sdk-trace-node");
+const { AlwaysOnSampler } = require("@opentelemetry/core");
+
+const tracerProvider = new NodeTracerProvider({
+ sampler: new AlwaysOnSampler()
+});
+```
+
+### AlwaysOff Sampler
+
+Doesn't sample any trace, regardless of upstream sampling decisions.
+
+```js
+const { NodeTracerProvider } = require("@opentelemetry/sdk-trace-node");
+const { AlwaysOffSampler } = require("@opentelemetry/core");
+
+const tracerProvider = new NodeTracerProvider({
+ sampler: new AlwaysOffSampler()
+});
+```
+
+### TraceIdRatioBased Sampler
+
+Samples some percentage of traces, calculated deterministically using the trace ID.
+Any trace that would be sampled at a given percentage will also be sampled at any higher percentage.
+
+The `TraceIDRatioSampler` may be used with the `ParentBasedSampler` to respect the sampled flag of an incoming trace.
+
+```js
+const { NodeTracerProvider } = require("@opentelemetry/sdk-trace-node");
+const { TraceIdRatioBasedSampler } = require("@opentelemetry/core");
+
+const tracerProvider = new NodeTracerProvider({
+ // See details of ParentBasedSampler below
+ sampler: new ParentBasedSampler({
+ // Trace ID Ratio Sampler accepts a positional argument
+ // which represents the percentage of traces which should
+ // be sampled.
+ root: new TraceIdRatioBasedSampler(0.5)
+ });
+});
+```
+
+### ParentBased Sampler
+
+- This is a composite sampler. `ParentBased` helps distinguished between the
+following cases:
+ - No parent (root span).
+ - Remote parent with `sampled` flag `true`
+ - Remote parent with `sampled` flag `false`
+ - Local parent with `sampled` flag `true`
+ - Local parent with `sampled` flag `false`
+
+Required parameters:
+
+- `root(Sampler)` - Sampler called for spans with no parent (root spans)
+
+Optional parameters:
+
+- `remoteParentSampled(Sampler)` (default: `AlwaysOn`)
+- `remoteParentNotSampled(Sampler)` (default: `AlwaysOff`)
+- `localParentSampled(Sampler)` (default: `AlwaysOn`)
+- `localParentNotSampled(Sampler)` (default: `AlwaysOff`)
+
+|Parent| parent.isRemote() | parent.isSampled()| Invoke sampler|
+|--|--|--|--|
+|absent| n/a | n/a |`root()`|
+|present|true|true|`remoteParentSampled()`|
+|present|true|false|`remoteParentNotSampled()`|
+|present|false|true|`localParentSampled()`|
+|present|false|false|`localParentNotSampled()`|
+
+```js
+const { NodeTracerProvider } = require("@opentelemetry/sdk-trace-node");
+const { ParentBasedSampler, AlwaysOffSampler, TraceIdRatioBasedSampler } = require("@opentelemetry/core");
+
+const tracerProvider = new NodeTracerProvider({
+ sampler: new ParentBasedSampler({
+ // By default, the ParentBasedSampler will respect the parent span's sampling
+ // decision. This is configurable by providing a different sampler to use
+ // based on the situation. See configuration details above.
+ //
+ // This will delegate the sampling decision of all root traces (no parent)
+ // to the TraceIdRatioBasedSampler.
+ // See details of TraceIdRatioBasedSampler above.
+ root: new TraceIdRatioBasedSampler(0.5)
+ })
+});
+```
+
## Example
See [examples/basic-tracer-node](https://github.com/open-telemetry/opentelemetry-js/tree/main/examples/basic-tracer-node) for an end-to-end example, including exporting created spans.
diff --git a/packages/opentelemetry-sdk-trace-base/src/IdGenerator.ts b/packages/opentelemetry-sdk-trace-base/src/IdGenerator.ts
new file mode 100644
index 0000000000..65f63f9c0c
--- /dev/null
+++ b/packages/opentelemetry-sdk-trace-base/src/IdGenerator.ts
@@ -0,0 +1,23 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/** IdGenerator provides an interface for generating Trace Id and Span Id */
+export interface IdGenerator {
+ /** Returns a trace ID composed of 32 lowercase hex characters. */
+ generateTraceId(): string;
+ /** Returns a span ID composed of 16 lowercase hex characters. */
+ generateSpanId(): string;
+}
diff --git a/packages/opentelemetry-sdk-trace-base/src/Sampler.ts b/packages/opentelemetry-sdk-trace-base/src/Sampler.ts
new file mode 100644
index 0000000000..8c610180a4
--- /dev/null
+++ b/packages/opentelemetry-sdk-trace-base/src/Sampler.ts
@@ -0,0 +1,89 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Context, Link, SpanAttributes, SpanKind } from '@opentelemetry/api';
+
+/**
+ * A sampling decision that determines how a {@link Span} will be recorded
+ * and collected.
+ */
+export enum SamplingDecision {
+ /**
+ * `Span.isRecording() === false`, span will not be recorded and all events
+ * and attributes will be dropped.
+ */
+ NOT_RECORD,
+ /**
+ * `Span.isRecording() === true`, but `Sampled` flag in {@link TraceFlags}
+ * MUST NOT be set.
+ */
+ RECORD,
+ /**
+ * `Span.isRecording() === true` AND `Sampled` flag in {@link TraceFlags}
+ * MUST be set.
+ */
+ RECORD_AND_SAMPLED,
+}
+
+/**
+ * A sampling result contains a decision for a {@link Span} and additional
+ * attributes the sampler would like to added to the Span.
+ */
+export interface SamplingResult {
+ /**
+ * A sampling decision, refer to {@link SamplingDecision} for details.
+ */
+ decision: SamplingDecision;
+ /**
+ * The list of attributes returned by SamplingResult MUST be immutable.
+ * Caller may call {@link Sampler}.shouldSample any number of times and
+ * can safely cache the returned value.
+ */
+ attributes?: Readonly;
+}
+
+/**
+ * This interface represent a sampler. Sampling is a mechanism to control the
+ * noise and overhead introduced by OpenTelemetry by reducing the number of
+ * samples of traces collected and sent to the backend.
+ */
+export interface Sampler {
+ /**
+ * Checks whether span needs to be created and tracked.
+ *
+ * @param context Parent Context which may contain a span.
+ * @param traceId of the span to be created. It can be different from the
+ * traceId in the {@link SpanContext}. Typically in situations when the
+ * span to be created starts a new trace.
+ * @param spanName of the span to be created.
+ * @param spanKind of the span to be created.
+ * @param attributes Initial set of SpanAttributes for the Span being constructed.
+ * @param links Collection of links that will be associated with the Span to
+ * be created. Typically useful for batch operations.
+ * @returns a {@link SamplingResult}.
+ */
+ shouldSample(
+ context: Context,
+ traceId: string,
+ spanName: string,
+ spanKind: SpanKind,
+ attributes: SpanAttributes,
+ links: Link[]
+ ): SamplingResult;
+
+ /** Returns the sampler name or short description with the configuration. */
+ toString(): string;
+}
diff --git a/packages/opentelemetry-sdk-trace-base/src/Tracer.ts b/packages/opentelemetry-sdk-trace-base/src/Tracer.ts
index aa4321d5e5..b77b5346b5 100644
--- a/packages/opentelemetry-sdk-trace-base/src/Tracer.ts
+++ b/packages/opentelemetry-sdk-trace-base/src/Tracer.ts
@@ -16,9 +16,7 @@
import * as api from '@opentelemetry/api';
import {
- IdGenerator,
InstrumentationLibrary,
- RandomIdGenerator,
sanitizeAttributes,
isTracingSuppressed,
} from '@opentelemetry/core';
@@ -28,12 +26,15 @@ import { Span } from './Span';
import { GeneralLimits, SpanLimits, TracerConfig } from './types';
import { mergeConfig } from './utility';
import { SpanProcessor } from './SpanProcessor';
+import { Sampler } from './Sampler';
+import { IdGenerator } from './IdGenerator';
+import { RandomIdGenerator } from './platform';
/**
* This class represents a basic tracer.
*/
export class Tracer implements api.Tracer {
- private readonly _sampler: api.Sampler;
+ private readonly _sampler: Sampler;
private readonly _generalLimits: GeneralLimits;
private readonly _spanLimits: SpanLimits;
private readonly _idGenerator: IdGenerator;
diff --git a/packages/opentelemetry-sdk-trace-base/src/config.ts b/packages/opentelemetry-sdk-trace-base/src/config.ts
index 7895514597..47e32f5b35 100644
--- a/packages/opentelemetry-sdk-trace-base/src/config.ts
+++ b/packages/opentelemetry-sdk-trace-base/src/config.ts
@@ -14,16 +14,17 @@
* limitations under the License.
*/
-import { diag, Sampler } from '@opentelemetry/api';
+import { diag } from '@opentelemetry/api';
import {
- AlwaysOffSampler,
- AlwaysOnSampler,
getEnv,
TracesSamplerValues,
- ParentBasedSampler,
ENVIRONMENT,
- TraceIdRatioBasedSampler,
} from '@opentelemetry/core';
+import { Sampler } from './Sampler';
+import { AlwaysOffSampler } from './sampler/AlwaysOffSampler';
+import { AlwaysOnSampler } from './sampler/AlwaysOnSampler';
+import { ParentBasedSampler } from './sampler/ParentBasedSampler';
+import { TraceIdRatioBasedSampler } from './sampler/TraceIdRatioBasedSampler';
const env = getEnv();
const FALLBACK_OTEL_TRACES_SAMPLER = TracesSamplerValues.AlwaysOn;
diff --git a/packages/opentelemetry-sdk-trace-base/src/index.ts b/packages/opentelemetry-sdk-trace-base/src/index.ts
index 3fb4bc2248..38a787950e 100644
--- a/packages/opentelemetry-sdk-trace-base/src/index.ts
+++ b/packages/opentelemetry-sdk-trace-base/src/index.ts
@@ -23,7 +23,13 @@ export * from './export/ReadableSpan';
export * from './export/SimpleSpanProcessor';
export * from './export/SpanExporter';
export * from './export/NoopSpanProcessor';
+export * from './sampler/AlwaysOffSampler';
+export * from './sampler/AlwaysOnSampler';
+export * from './sampler/ParentBasedSampler';
+export * from './sampler/TraceIdRatioBasedSampler';
+export * from './Sampler';
export * from './Span';
export * from './SpanProcessor';
export * from './TimedEvent';
export * from './types';
+export * from './IdGenerator';
diff --git a/packages/opentelemetry-sdk-trace-base/src/platform/browser/RandomIdGenerator.ts b/packages/opentelemetry-sdk-trace-base/src/platform/browser/RandomIdGenerator.ts
new file mode 100644
index 0000000000..5f7e7ee98f
--- /dev/null
+++ b/packages/opentelemetry-sdk-trace-base/src/platform/browser/RandomIdGenerator.ts
@@ -0,0 +1,51 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { IdGenerator } from '../../IdGenerator';
+
+const SPAN_ID_BYTES = 8;
+const TRACE_ID_BYTES = 16;
+
+export class RandomIdGenerator implements IdGenerator {
+ /**
+ * Returns a random 16-byte trace ID formatted/encoded as a 32 lowercase hex
+ * characters corresponding to 128 bits.
+ */
+ generateTraceId = getIdGenerator(TRACE_ID_BYTES);
+
+ /**
+ * Returns a random 8-byte span ID formatted/encoded as a 16 lowercase hex
+ * characters corresponding to 64 bits.
+ */
+ generateSpanId = getIdGenerator(SPAN_ID_BYTES);
+}
+
+const SHARED_CHAR_CODES_ARRAY = Array(32);
+function getIdGenerator(bytes: number): () => string {
+ return function generateId() {
+ for (let i = 0; i < bytes * 2; i++) {
+ SHARED_CHAR_CODES_ARRAY[i] = Math.floor(Math.random() * 16) + 48;
+ // valid hex characters in the range 48-57 and 97-102
+ if (SHARED_CHAR_CODES_ARRAY[i] >= 58) {
+ SHARED_CHAR_CODES_ARRAY[i] += 39;
+ }
+ }
+ return String.fromCharCode.apply(
+ null,
+ SHARED_CHAR_CODES_ARRAY.slice(0, bytes * 2)
+ );
+ };
+}
diff --git a/packages/opentelemetry-sdk-trace-base/src/platform/browser/index.ts b/packages/opentelemetry-sdk-trace-base/src/platform/browser/index.ts
index 7992546b9d..652d686e04 100644
--- a/packages/opentelemetry-sdk-trace-base/src/platform/browser/index.ts
+++ b/packages/opentelemetry-sdk-trace-base/src/platform/browser/index.ts
@@ -15,3 +15,4 @@
*/
export * from './export/BatchSpanProcessor';
+export * from './RandomIdGenerator';
diff --git a/packages/opentelemetry-sdk-trace-base/src/platform/node/RandomIdGenerator.ts b/packages/opentelemetry-sdk-trace-base/src/platform/node/RandomIdGenerator.ts
new file mode 100644
index 0000000000..ee47d993a4
--- /dev/null
+++ b/packages/opentelemetry-sdk-trace-base/src/platform/node/RandomIdGenerator.ts
@@ -0,0 +1,56 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { IdGenerator } from '../../IdGenerator';
+
+const SPAN_ID_BYTES = 8;
+const TRACE_ID_BYTES = 16;
+
+export class RandomIdGenerator implements IdGenerator {
+ /**
+ * Returns a random 16-byte trace ID formatted/encoded as a 32 lowercase hex
+ * characters corresponding to 128 bits.
+ */
+ generateTraceId = getIdGenerator(TRACE_ID_BYTES);
+
+ /**
+ * Returns a random 8-byte span ID formatted/encoded as a 16 lowercase hex
+ * characters corresponding to 64 bits.
+ */
+ generateSpanId = getIdGenerator(SPAN_ID_BYTES);
+}
+
+const SHARED_BUFFER = Buffer.allocUnsafe(TRACE_ID_BYTES);
+function getIdGenerator(bytes: number): () => string {
+ return function generateId() {
+ for (let i = 0; i < bytes / 4; i++) {
+ // unsigned right shift drops decimal part of the number
+ // it is required because if a number between 2**32 and 2**32 - 1 is generated, an out of range error is thrown by writeUInt32BE
+ SHARED_BUFFER.writeUInt32BE((Math.random() * 2 ** 32) >>> 0, i * 4);
+ }
+
+ // If buffer is all 0, set the last byte to 1 to guarantee a valid w3c id is generated
+ for (let i = 0; i < bytes; i++) {
+ if (SHARED_BUFFER[i] > 0) {
+ break;
+ } else if (i === bytes - 1) {
+ SHARED_BUFFER[bytes - 1] = 1;
+ }
+ }
+
+ return SHARED_BUFFER.toString('hex', 0, bytes);
+ };
+}
diff --git a/packages/opentelemetry-sdk-trace-base/src/platform/node/index.ts b/packages/opentelemetry-sdk-trace-base/src/platform/node/index.ts
index 7992546b9d..652d686e04 100644
--- a/packages/opentelemetry-sdk-trace-base/src/platform/node/index.ts
+++ b/packages/opentelemetry-sdk-trace-base/src/platform/node/index.ts
@@ -15,3 +15,4 @@
*/
export * from './export/BatchSpanProcessor';
+export * from './RandomIdGenerator';
diff --git a/packages/opentelemetry-sdk-trace-base/src/sampler/AlwaysOffSampler.ts b/packages/opentelemetry-sdk-trace-base/src/sampler/AlwaysOffSampler.ts
new file mode 100644
index 0000000000..1ea87b5bdc
--- /dev/null
+++ b/packages/opentelemetry-sdk-trace-base/src/sampler/AlwaysOffSampler.ts
@@ -0,0 +1,30 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Sampler, SamplingDecision, SamplingResult } from '../Sampler';
+
+/** Sampler that samples no traces. */
+export class AlwaysOffSampler implements Sampler {
+ shouldSample(): SamplingResult {
+ return {
+ decision: SamplingDecision.NOT_RECORD,
+ };
+ }
+
+ toString(): string {
+ return 'AlwaysOffSampler';
+ }
+}
diff --git a/packages/opentelemetry-sdk-trace-base/src/sampler/AlwaysOnSampler.ts b/packages/opentelemetry-sdk-trace-base/src/sampler/AlwaysOnSampler.ts
new file mode 100644
index 0000000000..9f5d6da15d
--- /dev/null
+++ b/packages/opentelemetry-sdk-trace-base/src/sampler/AlwaysOnSampler.ts
@@ -0,0 +1,30 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { Sampler, SamplingDecision, SamplingResult } from '../Sampler';
+
+/** Sampler that samples all traces. */
+export class AlwaysOnSampler implements Sampler {
+ shouldSample(): SamplingResult {
+ return {
+ decision: SamplingDecision.RECORD_AND_SAMPLED,
+ };
+ }
+
+ toString(): string {
+ return 'AlwaysOnSampler';
+ }
+}
diff --git a/packages/opentelemetry-sdk-trace-base/src/sampler/ParentBasedSampler.ts b/packages/opentelemetry-sdk-trace-base/src/sampler/ParentBasedSampler.ts
new file mode 100644
index 0000000000..0b411024cd
--- /dev/null
+++ b/packages/opentelemetry-sdk-trace-base/src/sampler/ParentBasedSampler.ts
@@ -0,0 +1,140 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import {
+ Context,
+ isSpanContextValid,
+ Link,
+ SpanAttributes,
+ SpanKind,
+ TraceFlags, trace,
+} from '@opentelemetry/api';
+import { globalErrorHandler } from '@opentelemetry/core';
+import { AlwaysOffSampler } from './AlwaysOffSampler';
+import { AlwaysOnSampler } from './AlwaysOnSampler';
+import { Sampler, SamplingResult } from '../Sampler';
+
+/**
+ * A composite sampler that either respects the parent span's sampling decision
+ * or delegates to `delegateSampler` for root spans.
+ */
+export class ParentBasedSampler implements Sampler {
+ private _root: Sampler;
+ private _remoteParentSampled: Sampler;
+ private _remoteParentNotSampled: Sampler;
+ private _localParentSampled: Sampler;
+ private _localParentNotSampled: Sampler;
+
+ constructor(config: ParentBasedSamplerConfig) {
+ this._root = config.root;
+
+ if (!this._root) {
+ globalErrorHandler(
+ new Error('ParentBasedSampler must have a root sampler configured')
+ );
+ this._root = new AlwaysOnSampler();
+ }
+
+ this._remoteParentSampled =
+ config.remoteParentSampled ?? new AlwaysOnSampler();
+ this._remoteParentNotSampled =
+ config.remoteParentNotSampled ?? new AlwaysOffSampler();
+ this._localParentSampled =
+ config.localParentSampled ?? new AlwaysOnSampler();
+ this._localParentNotSampled =
+ config.localParentNotSampled ?? new AlwaysOffSampler();
+ }
+
+ shouldSample(
+ context: Context,
+ traceId: string,
+ spanName: string,
+ spanKind: SpanKind,
+ attributes: SpanAttributes,
+ links: Link[]
+ ): SamplingResult {
+ const parentContext = trace.getSpanContext(context);
+
+ if (!parentContext || !isSpanContextValid(parentContext)) {
+ return this._root.shouldSample(
+ context,
+ traceId,
+ spanName,
+ spanKind,
+ attributes,
+ links
+ );
+ }
+
+ if (parentContext.isRemote) {
+ if (parentContext.traceFlags & TraceFlags.SAMPLED) {
+ return this._remoteParentSampled.shouldSample(
+ context,
+ traceId,
+ spanName,
+ spanKind,
+ attributes,
+ links
+ );
+ }
+ return this._remoteParentNotSampled.shouldSample(
+ context,
+ traceId,
+ spanName,
+ spanKind,
+ attributes,
+ links
+ );
+ }
+
+ if (parentContext.traceFlags & TraceFlags.SAMPLED) {
+ return this._localParentSampled.shouldSample(
+ context,
+ traceId,
+ spanName,
+ spanKind,
+ attributes,
+ links
+ );
+ }
+
+ return this._localParentNotSampled.shouldSample(
+ context,
+ traceId,
+ spanName,
+ spanKind,
+ attributes,
+ links
+ );
+ }
+
+ toString(): string {
+ return `ParentBased{root=${this._root.toString()}, remoteParentSampled=${this._remoteParentSampled.toString()}, remoteParentNotSampled=${this._remoteParentNotSampled.toString()}, localParentSampled=${this._localParentSampled.toString()}, localParentNotSampled=${this._localParentNotSampled.toString()}}`;
+ }
+}
+
+interface ParentBasedSamplerConfig {
+ /** Sampler called for spans with no parent */
+ root: Sampler;
+ /** Sampler called for spans with a remote parent which was sampled. Default AlwaysOn */
+ remoteParentSampled?: Sampler;
+ /** Sampler called for spans with a remote parent which was not sampled. Default AlwaysOff */
+ remoteParentNotSampled?: Sampler;
+ /** Sampler called for spans with a local parent which was sampled. Default AlwaysOn */
+ localParentSampled?: Sampler;
+ /** Sampler called for spans with a local parent which was not sampled. Default AlwaysOff */
+ localParentNotSampled?: Sampler;
+}
diff --git a/packages/opentelemetry-sdk-trace-base/src/sampler/TraceIdRatioBasedSampler.ts b/packages/opentelemetry-sdk-trace-base/src/sampler/TraceIdRatioBasedSampler.ts
new file mode 100644
index 0000000000..33344a90ae
--- /dev/null
+++ b/packages/opentelemetry-sdk-trace-base/src/sampler/TraceIdRatioBasedSampler.ts
@@ -0,0 +1,57 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { isValidTraceId } from '@opentelemetry/api';
+import { Sampler, SamplingDecision, SamplingResult } from '../Sampler';
+
+
+/** Sampler that samples a given fraction of traces based of trace id deterministically. */
+export class TraceIdRatioBasedSampler implements Sampler {
+ private _upperBound: number;
+
+ constructor(private readonly _ratio: number = 0) {
+ this._ratio = this._normalize(_ratio);
+ this._upperBound = Math.floor(this._ratio * 0xffffffff);
+ }
+
+ shouldSample(context: unknown, traceId: string): SamplingResult {
+ return {
+ decision:
+ isValidTraceId(traceId) && this._accumulate(traceId) < this._upperBound
+ ? SamplingDecision.RECORD_AND_SAMPLED
+ : SamplingDecision.NOT_RECORD,
+ };
+ }
+
+ toString(): string {
+ return `TraceIdRatioBased{${this._ratio}}`;
+ }
+
+ private _normalize(ratio: number): number {
+ if (typeof ratio !== 'number' || isNaN(ratio)) return 0;
+ return ratio >= 1 ? 1 : ratio <= 0 ? 0 : ratio;
+ }
+
+ private _accumulate(traceId: string): number {
+ let accumulation = 0;
+ for (let i = 0; i < traceId.length / 8; i++) {
+ const pos = i * 8;
+ const part = parseInt(traceId.slice(pos, pos + 8), 16);
+ accumulation = (accumulation ^ part) >>> 0;
+ }
+ return accumulation;
+ }
+}
diff --git a/packages/opentelemetry-sdk-trace-base/src/types.ts b/packages/opentelemetry-sdk-trace-base/src/types.ts
index d60df053ac..54eaaf97d9 100644
--- a/packages/opentelemetry-sdk-trace-base/src/types.ts
+++ b/packages/opentelemetry-sdk-trace-base/src/types.ts
@@ -14,11 +14,10 @@
* limitations under the License.
*/
-import { TextMapPropagator, Sampler } from '@opentelemetry/api';
-import { IdGenerator } from '@opentelemetry/core';
-
-import { ContextManager } from '@opentelemetry/api';
+import { ContextManager, TextMapPropagator } from '@opentelemetry/api';
import { Resource } from '@opentelemetry/resources';
+import { IdGenerator } from './IdGenerator';
+import { Sampler } from './Sampler';
/**
* TracerConfig provides an interface for configuring a Basic Tracer.
diff --git a/packages/opentelemetry-sdk-trace-base/src/utility.ts b/packages/opentelemetry-sdk-trace-base/src/utility.ts
index 684f68a210..819d725d49 100644
--- a/packages/opentelemetry-sdk-trace-base/src/utility.ts
+++ b/packages/opentelemetry-sdk-trace-base/src/utility.ts
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-import { Sampler } from '@opentelemetry/api';
import { buildSamplerFromEnv, loadDefaultConfig } from './config';
+import { Sampler } from './Sampler';
import { SpanLimits, TracerConfig, GeneralLimits } from './types';
import {
DEFAULT_ATTRIBUTE_COUNT_LIMIT,
diff --git a/packages/opentelemetry-sdk-trace-base/test/common/BasicTracerProvider.test.ts b/packages/opentelemetry-sdk-trace-base/test/common/BasicTracerProvider.test.ts
index 88ec6fa16a..05ec1152de 100644
--- a/packages/opentelemetry-sdk-trace-base/test/common/BasicTracerProvider.test.ts
+++ b/packages/opentelemetry-sdk-trace-base/test/common/BasicTracerProvider.test.ts
@@ -29,8 +29,6 @@ import {
} from '@opentelemetry/api';
import { CompositePropagator } from '@opentelemetry/core';
import {
- AlwaysOffSampler,
- AlwaysOnSampler,
TraceState,
W3CTraceContextPropagator,
} from '@opentelemetry/core';
@@ -44,6 +42,8 @@ import {
InMemorySpanExporter,
SpanExporter,
BatchSpanProcessor,
+ AlwaysOnSampler,
+ AlwaysOffSampler,
} from '../../src';
class DummyPropagator implements TextMapPropagator {
diff --git a/packages/opentelemetry-sdk-trace-base/test/common/Sampler.test.ts b/packages/opentelemetry-sdk-trace-base/test/common/Sampler.test.ts
new file mode 100644
index 0000000000..1e4682e356
--- /dev/null
+++ b/packages/opentelemetry-sdk-trace-base/test/common/Sampler.test.ts
@@ -0,0 +1,54 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { context, SpanKind } from '@opentelemetry/api';
+import {
+ AlwaysOffSampler,
+ AlwaysOnSampler,
+ ParentBasedSampler,
+ Sampler,
+ SamplingDecision,
+ SamplingResult,
+ TraceIdRatioBasedSampler,
+} from '../../src';
+import { assertAssignable } from './util';
+
+describe('Sampler', () => {
+ const samplers = [
+ new AlwaysOffSampler(),
+ new AlwaysOnSampler(),
+ new ParentBasedSampler({ root: new AlwaysOffSampler() }),
+ new TraceIdRatioBasedSampler(),
+ ] as const;
+
+ it('Samplers defined in @opentelemetry/core should fit the interface', () => {
+ for (const sampler of samplers) {
+ assertAssignable(sampler);
+ }
+ });
+
+ it('Sampler return values should fit SamplerResult', () => {
+ function assertResult(sampler: T) {
+ const result = sampler.shouldSample(context.active(), 'trace-id', 'span-name', SpanKind.INTERNAL, {}, []);
+ assertAssignable(result);
+ assertAssignable(result.decision);
+ }
+
+ for (const sampler of samplers) {
+ assertResult(sampler);
+ }
+ });
+});
diff --git a/packages/opentelemetry-sdk-trace-base/test/common/Tracer.test.ts b/packages/opentelemetry-sdk-trace-base/test/common/Tracer.test.ts
index 31652f8b6e..d55374514c 100644
--- a/packages/opentelemetry-sdk-trace-base/test/common/Tracer.test.ts
+++ b/packages/opentelemetry-sdk-trace-base/test/common/Tracer.test.ts
@@ -21,8 +21,6 @@ import {
INVALID_TRACEID,
Link,
ROOT_CONTEXT,
- Sampler,
- SamplingDecision,
SpanContext,
SpanKind,
trace,
@@ -30,14 +28,21 @@ import {
} from '@opentelemetry/api';
import { getSpan } from '@opentelemetry/api/build/src/trace/context-utils';
import {
- AlwaysOffSampler,
- AlwaysOnSampler,
InstrumentationLibrary,
sanitizeAttributes,
suppressTracing
} from '@opentelemetry/core';
import * as assert from 'assert';
-import { BasicTracerProvider, Span, SpanProcessor, Tracer } from '../../src';
+import {
+ AlwaysOffSampler,
+ AlwaysOnSampler,
+ BasicTracerProvider,
+ Sampler,
+ SamplingDecision,
+ Span,
+ SpanProcessor,
+ Tracer,
+} from '../../src';
import { TestStackContextManager } from './export/TestStackContextManager';
import * as sinon from 'sinon';
import { invalidAttributes, validAttributes } from './util';
diff --git a/packages/opentelemetry-sdk-trace-base/test/common/config.test.ts b/packages/opentelemetry-sdk-trace-base/test/common/config.test.ts
index f627cc18a7..c3c929fad2 100644
--- a/packages/opentelemetry-sdk-trace-base/test/common/config.test.ts
+++ b/packages/opentelemetry-sdk-trace-base/test/common/config.test.ts
@@ -13,13 +13,14 @@
* See the License for the specific language governing permissions and
* limitations under the License.
*/
+
+import * as assert from 'assert';
import {
AlwaysOffSampler,
AlwaysOnSampler,
ParentBasedSampler,
TraceIdRatioBasedSampler,
-} from '@opentelemetry/core';
-import * as assert from 'assert';
+} from '../../src';
import { buildSamplerFromEnv } from '../../src/config';
describe('config', () => {
diff --git a/packages/opentelemetry-sdk-trace-base/test/common/export/BatchSpanProcessorBase.test.ts b/packages/opentelemetry-sdk-trace-base/test/common/export/BatchSpanProcessorBase.test.ts
index 52cd832a3e..8d8de40d32 100644
--- a/packages/opentelemetry-sdk-trace-base/test/common/export/BatchSpanProcessorBase.test.ts
+++ b/packages/opentelemetry-sdk-trace-base/test/common/export/BatchSpanProcessorBase.test.ts
@@ -16,14 +16,13 @@
import { diag, ROOT_CONTEXT } from '@opentelemetry/api';
import {
- AlwaysOnSampler,
ExportResultCode,
loggingErrorHandler,
setGlobalErrorHandler,
} from '@opentelemetry/core';
import * as assert from 'assert';
import * as sinon from 'sinon';
-import { BasicTracerProvider, BufferConfig, InMemorySpanExporter, Span } from '../../../src';
+import { AlwaysOnSampler, BasicTracerProvider, BufferConfig, InMemorySpanExporter, Span } from '../../../src';
import { context } from '@opentelemetry/api';
import { TestRecordOnlySampler } from './TestRecordOnlySampler';
import { TestTracingSpanExporter } from './TestTracingSpanExporter';
diff --git a/packages/opentelemetry-sdk-trace-base/test/common/export/ConsoleSpanExporter.test.ts b/packages/opentelemetry-sdk-trace-base/test/common/export/ConsoleSpanExporter.test.ts
index 0a66b13fc2..e6712adb95 100644
--- a/packages/opentelemetry-sdk-trace-base/test/common/export/ConsoleSpanExporter.test.ts
+++ b/packages/opentelemetry-sdk-trace-base/test/common/export/ConsoleSpanExporter.test.ts
@@ -18,10 +18,10 @@ import {
SpanContext,
TraceFlags,
} from '@opentelemetry/api';
-import { AlwaysOnSampler } from '@opentelemetry/core';
import * as assert from 'assert';
import * as sinon from 'sinon';
import {
+ AlwaysOnSampler,
BasicTracerProvider,
ConsoleSpanExporter,
SimpleSpanProcessor,
diff --git a/packages/opentelemetry-sdk-trace-base/test/common/export/TestRecordOnlySampler.ts b/packages/opentelemetry-sdk-trace-base/test/common/export/TestRecordOnlySampler.ts
index c456780e86..02d0626dc7 100644
--- a/packages/opentelemetry-sdk-trace-base/test/common/export/TestRecordOnlySampler.ts
+++ b/packages/opentelemetry-sdk-trace-base/test/common/export/TestRecordOnlySampler.ts
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-import { Sampler, SamplingDecision, SamplingResult } from '@opentelemetry/api';
+import { Sampler, SamplingDecision, SamplingResult } from '../../../src';
/** Sampler that always records but doesn't sample spans. */
export class TestRecordOnlySampler implements Sampler {
diff --git a/packages/opentelemetry-sdk-trace-base/test/common/export/TestTracingSpanExporter.ts b/packages/opentelemetry-sdk-trace-base/test/common/export/TestTracingSpanExporter.ts
index 9415c67fe7..89eae8d8b5 100644
--- a/packages/opentelemetry-sdk-trace-base/test/common/export/TestTracingSpanExporter.ts
+++ b/packages/opentelemetry-sdk-trace-base/test/common/export/TestTracingSpanExporter.ts
@@ -14,14 +14,15 @@
* limitations under the License.
*/
+import { ExportResult } from '@opentelemetry/core';
import {
BasicTracerProvider,
InMemorySpanExporter,
ReadableSpan,
Tracer,
SpanProcessor,
+ AlwaysOnSampler,
} from '../../../src';
-import { ExportResult, AlwaysOnSampler } from '@opentelemetry/core';
/**
* A test-only span exporter that naively simulates triggering instrumentation
diff --git a/packages/opentelemetry-sdk-trace-base/test/common/platform/RandomIdGenerator.test.ts b/packages/opentelemetry-sdk-trace-base/test/common/platform/RandomIdGenerator.test.ts
new file mode 100644
index 0000000000..ad1ba5028a
--- /dev/null
+++ b/packages/opentelemetry-sdk-trace-base/test/common/platform/RandomIdGenerator.test.ts
@@ -0,0 +1,53 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import * as assert from 'assert';
+import { RandomIdGenerator } from '../../../src/platform';
+
+const idGenerator = new RandomIdGenerator();
+
+describe('randomTraceId', () => {
+ let traceId1: string, traceId2: string;
+ beforeEach(() => {
+ traceId1 = idGenerator.generateTraceId();
+ traceId2 = idGenerator.generateTraceId();
+ });
+
+ it('returns 32 character hex strings', () => {
+ assert.ok(traceId1.match(/[a-f0-9]{32}/));
+ assert.ok(!traceId1.match(/^0+$/));
+ });
+
+ it('returns different ids on each call', () => {
+ assert.notDeepStrictEqual(traceId1, traceId2);
+ });
+});
+
+describe('randomSpanId', () => {
+ let spanId1: string, spanId2: string;
+ beforeEach(() => {
+ spanId1 = idGenerator.generateSpanId();
+ spanId2 = idGenerator.generateSpanId();
+ });
+
+ it('returns 16 character hex strings', () => {
+ assert.ok(spanId1.match(/[a-f0-9]{16}/));
+ assert.ok(!spanId1.match(/^0+$/));
+ });
+
+ it('returns different ids on each call', () => {
+ assert.notDeepStrictEqual(spanId1, spanId2);
+ });
+});
diff --git a/packages/opentelemetry-sdk-trace-base/test/common/sampler/AlwaysOffSampler.test.ts b/packages/opentelemetry-sdk-trace-base/test/common/sampler/AlwaysOffSampler.test.ts
new file mode 100644
index 0000000000..d591dc53e7
--- /dev/null
+++ b/packages/opentelemetry-sdk-trace-base/test/common/sampler/AlwaysOffSampler.test.ts
@@ -0,0 +1,32 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import * as assert from 'assert';
+import * as api from '@opentelemetry/api';
+import { AlwaysOffSampler } from '../../../src/sampler/AlwaysOffSampler';
+
+describe('AlwaysOffSampler', () => {
+ it('should reflect sampler name', () => {
+ const sampler = new AlwaysOffSampler();
+ assert.strictEqual(sampler.toString(), 'AlwaysOffSampler');
+ });
+
+ it('should return decision: api.SamplingDecision.NOT_RECORD for AlwaysOffSampler', () => {
+ const sampler = new AlwaysOffSampler();
+ assert.deepStrictEqual(sampler.shouldSample(), {
+ decision: api.SamplingDecision.NOT_RECORD,
+ });
+ });
+});
diff --git a/packages/opentelemetry-sdk-trace-base/test/common/sampler/AlwaysOnSampler.test.ts b/packages/opentelemetry-sdk-trace-base/test/common/sampler/AlwaysOnSampler.test.ts
new file mode 100644
index 0000000000..95825dc798
--- /dev/null
+++ b/packages/opentelemetry-sdk-trace-base/test/common/sampler/AlwaysOnSampler.test.ts
@@ -0,0 +1,32 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import * as assert from 'assert';
+import * as api from '@opentelemetry/api';
+import { AlwaysOnSampler } from '../../../src/sampler/AlwaysOnSampler';
+
+describe('AlwaysOnSampler', () => {
+ it('should reflect sampler name', () => {
+ const sampler = new AlwaysOnSampler();
+ assert.strictEqual(sampler.toString(), 'AlwaysOnSampler');
+ });
+
+ it('should return api.SamplingDecision.RECORD_AND_SAMPLED for AlwaysOnSampler', () => {
+ const sampler = new AlwaysOnSampler();
+ assert.deepStrictEqual(sampler.shouldSample(), {
+ decision: api.SamplingDecision.RECORD_AND_SAMPLED,
+ });
+ });
+});
diff --git a/packages/opentelemetry-sdk-trace-base/test/common/sampler/ParentBasedSampler.test.ts b/packages/opentelemetry-sdk-trace-base/test/common/sampler/ParentBasedSampler.test.ts
new file mode 100644
index 0000000000..22ebfb8092
--- /dev/null
+++ b/packages/opentelemetry-sdk-trace-base/test/common/sampler/ParentBasedSampler.test.ts
@@ -0,0 +1,170 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+import * as assert from 'assert';
+import * as api from '@opentelemetry/api';
+import { TraceFlags, SpanKind, trace } from '@opentelemetry/api';
+import {
+ AlwaysOnSampler,
+ ParentBasedSampler,
+ AlwaysOffSampler,
+ TraceIdRatioBasedSampler,
+} from '../../../src';
+
+const traceId = 'd4cda95b652f4a1592b449d5929fda1b';
+const spanId = '6e0c63257de34c92';
+const spanName = 'foobar';
+
+describe('ParentBasedSampler', () => {
+ it('should reflect sampler name with delegate sampler', () => {
+ let sampler = new ParentBasedSampler({ root: new AlwaysOnSampler() });
+ assert.strictEqual(
+ sampler.toString(),
+ 'ParentBased{root=AlwaysOnSampler, remoteParentSampled=AlwaysOnSampler, remoteParentNotSampled=AlwaysOffSampler, localParentSampled=AlwaysOnSampler, localParentNotSampled=AlwaysOffSampler}'
+ );
+
+ sampler = new ParentBasedSampler({ root: new AlwaysOffSampler() });
+ assert.strictEqual(
+ sampler.toString(),
+ 'ParentBased{root=AlwaysOffSampler, remoteParentSampled=AlwaysOnSampler, remoteParentNotSampled=AlwaysOffSampler, localParentSampled=AlwaysOnSampler, localParentNotSampled=AlwaysOffSampler}'
+ );
+
+ sampler = new ParentBasedSampler({
+ root: new TraceIdRatioBasedSampler(0.5),
+ });
+ assert.strictEqual(
+ sampler.toString(),
+ 'ParentBased{root=TraceIdRatioBased{0.5}, remoteParentSampled=AlwaysOnSampler, remoteParentNotSampled=AlwaysOffSampler, localParentSampled=AlwaysOnSampler, localParentNotSampled=AlwaysOffSampler}'
+ );
+ });
+
+ it('should return api.SamplingDecision.NOT_RECORD for not sampled parent while composited with AlwaysOnSampler', () => {
+ const sampler = new ParentBasedSampler({ root: new AlwaysOnSampler() });
+
+ const spanContext = {
+ traceId,
+ spanId,
+ traceFlags: TraceFlags.NONE,
+ };
+ assert.deepStrictEqual(
+ sampler.shouldSample(
+ trace.setSpanContext(api.ROOT_CONTEXT, spanContext),
+ traceId,
+ spanName,
+ SpanKind.CLIENT,
+ {},
+ []
+ ),
+ {
+ decision: api.SamplingDecision.NOT_RECORD,
+ }
+ );
+ });
+
+ it('should return api.SamplingDecision.RECORD_AND_SAMPLED for invalid parent spanContext while composited with AlwaysOnSampler', () => {
+ const sampler = new ParentBasedSampler({ root: new AlwaysOnSampler() });
+
+ assert.deepStrictEqual(
+ sampler.shouldSample(
+ trace.setSpanContext(api.ROOT_CONTEXT, api.INVALID_SPAN_CONTEXT),
+ traceId,
+ spanName,
+ SpanKind.CLIENT,
+ {},
+ []
+ ),
+ {
+ decision: api.SamplingDecision.RECORD_AND_SAMPLED,
+ }
+ );
+ });
+
+ it('should return api.SamplingDecision.RECORD_AND_SAMPLED while composited with AlwaysOnSampler', () => {
+ const sampler = new ParentBasedSampler({ root: new AlwaysOnSampler() });
+
+ assert.deepStrictEqual(
+ sampler.shouldSample(
+ api.ROOT_CONTEXT,
+ traceId,
+ spanName,
+ SpanKind.CLIENT,
+ {},
+ []
+ ),
+ {
+ decision: api.SamplingDecision.RECORD_AND_SAMPLED,
+ }
+ );
+ });
+
+ it('should return api.SamplingDecision.RECORD_AND_SAMPLED for sampled parent while composited with AlwaysOffSampler', () => {
+ const sampler = new ParentBasedSampler({ root: new AlwaysOffSampler() });
+
+ const spanContext = {
+ traceId,
+ spanId,
+ traceFlags: TraceFlags.SAMPLED,
+ };
+ assert.deepStrictEqual(
+ sampler.shouldSample(
+ trace.setSpanContext(api.ROOT_CONTEXT, spanContext),
+ traceId,
+ spanName,
+ SpanKind.CLIENT,
+ {},
+ []
+ ),
+ {
+ decision: api.SamplingDecision.RECORD_AND_SAMPLED,
+ }
+ );
+ });
+
+ it('should return api.SamplingDecision.NOT_RECORD for invalid parent spanContext while composited with AlwaysOffSampler', () => {
+ const sampler = new ParentBasedSampler({ root: new AlwaysOffSampler() });
+
+ assert.deepStrictEqual(
+ sampler.shouldSample(
+ trace.setSpanContext(api.ROOT_CONTEXT, api.INVALID_SPAN_CONTEXT),
+ traceId,
+ spanName,
+ SpanKind.CLIENT,
+ {},
+ []
+ ),
+ {
+ decision: api.SamplingDecision.NOT_RECORD,
+ }
+ );
+ });
+
+ it('should return api.SamplingDecision.RECORD_AND_SAMPLED while composited with AlwaysOffSampler', () => {
+ const sampler = new ParentBasedSampler({ root: new AlwaysOffSampler() });
+
+ assert.deepStrictEqual(
+ sampler.shouldSample(
+ api.ROOT_CONTEXT,
+ traceId,
+ spanName,
+ SpanKind.CLIENT,
+ {},
+ []
+ ),
+ {
+ decision: api.SamplingDecision.NOT_RECORD,
+ }
+ );
+ });
+});
diff --git a/packages/opentelemetry-sdk-trace-base/test/common/sampler/TraceIdRatioBasedSampler.test.ts b/packages/opentelemetry-sdk-trace-base/test/common/sampler/TraceIdRatioBasedSampler.test.ts
new file mode 100644
index 0000000000..d40219f956
--- /dev/null
+++ b/packages/opentelemetry-sdk-trace-base/test/common/sampler/TraceIdRatioBasedSampler.test.ts
@@ -0,0 +1,197 @@
+/*
+ * Copyright The OpenTelemetry Authors
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * https://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import * as assert from 'assert';
+import * as api from '@opentelemetry/api';
+import { TraceIdRatioBasedSampler } from '../../../src/sampler/TraceIdRatioBasedSampler';
+
+const spanContext = (traceId = '1') => ({
+ traceId,
+ spanId: '1.1',
+ traceFlags: api.TraceFlags.NONE,
+});
+
+const traceId = (part: string) => ('0'.repeat(32) + part).slice(-32);
+
+describe('TraceIdRatioBasedSampler', () => {
+ it('should reflect sampler name with ratio', () => {
+ let sampler = new TraceIdRatioBasedSampler(1.0);
+ assert.strictEqual(sampler.toString(), 'TraceIdRatioBased{1}');
+
+ sampler = new TraceIdRatioBasedSampler(0.5);
+ assert.strictEqual(sampler.toString(), 'TraceIdRatioBased{0.5}');
+
+ sampler = new TraceIdRatioBasedSampler(0);
+ assert.strictEqual(sampler.toString(), 'TraceIdRatioBased{0}');
+
+ sampler = new TraceIdRatioBasedSampler(-0);
+ assert.strictEqual(sampler.toString(), 'TraceIdRatioBased{0}');
+
+ sampler = new TraceIdRatioBasedSampler(undefined);
+ assert.strictEqual(sampler.toString(), 'TraceIdRatioBased{0}');
+
+ sampler = new TraceIdRatioBasedSampler(NaN);
+ assert.strictEqual(sampler.toString(), 'TraceIdRatioBased{0}');
+
+ sampler = new TraceIdRatioBasedSampler(+Infinity);
+ assert.strictEqual(sampler.toString(), 'TraceIdRatioBased{1}');
+
+ sampler = new TraceIdRatioBasedSampler(-Infinity);
+ assert.strictEqual(sampler.toString(), 'TraceIdRatioBased{0}');
+ });
+
+ it('should return a always sampler for 1', () => {
+ const sampler = new TraceIdRatioBasedSampler(1);
+ assert.deepStrictEqual(
+ sampler.shouldSample(spanContext(traceId('1')), traceId('1')),
+ {
+ decision: api.SamplingDecision.RECORD_AND_SAMPLED,
+ }
+ );
+ });
+
+ it('should return a always sampler for >1', () => {
+ const sampler = new TraceIdRatioBasedSampler(100);
+ assert.deepStrictEqual(
+ sampler.shouldSample(spanContext(traceId('1')), traceId('1')),
+ {
+ decision: api.SamplingDecision.RECORD_AND_SAMPLED,
+ }
+ );
+ });
+
+ it('should return a never sampler for 0', () => {
+ const sampler = new TraceIdRatioBasedSampler(0);
+ assert.deepStrictEqual(
+ sampler.shouldSample(spanContext(traceId('1')), traceId('1')),
+ {
+ decision: api.SamplingDecision.NOT_RECORD,
+ }
+ );
+ });
+
+ it('should return a never sampler for <0', () => {
+ const sampler = new TraceIdRatioBasedSampler(-1);
+ assert.deepStrictEqual(
+ sampler.shouldSample(spanContext(traceId('1')), traceId('1')),
+ {
+ decision: api.SamplingDecision.NOT_RECORD,
+ }
+ );
+ });
+
+ it('should handle NaN', () => {
+ const sampler = new TraceIdRatioBasedSampler(NaN);
+ assert.strictEqual(sampler.toString(), 'TraceIdRatioBased{0}');
+ assert.deepStrictEqual(
+ sampler.shouldSample(spanContext(traceId('1')), traceId('1')),
+ {
+ decision: api.SamplingDecision.NOT_RECORD,
+ }
+ );
+ });
+
+ it('should handle -NaN', () => {
+ const sampler = new TraceIdRatioBasedSampler(-NaN);
+ assert.strictEqual(sampler.toString(), 'TraceIdRatioBased{0}');
+ assert.deepStrictEqual(
+ sampler.shouldSample(spanContext(traceId('1')), traceId('1')),
+ {
+ decision: api.SamplingDecision.NOT_RECORD,
+ }
+ );
+ });
+
+ it('should handle undefined', () => {
+ const sampler = new TraceIdRatioBasedSampler(undefined);
+ assert.strictEqual(sampler.toString(), 'TraceIdRatioBased{0}');
+ assert.deepStrictEqual(
+ sampler.shouldSample(spanContext(traceId('1')), traceId('1')),
+ {
+ decision: api.SamplingDecision.NOT_RECORD,
+ }
+ );
+ });
+
+ it('should sample based on trace id', () => {
+ const sampler = new TraceIdRatioBasedSampler(0.2);
+ assert.deepStrictEqual(
+ sampler.shouldSample(spanContext(traceId('1')), traceId('1')),
+ {
+ decision: api.SamplingDecision.RECORD_AND_SAMPLED,
+ }
+ );
+
+ assert.deepStrictEqual(
+ sampler.shouldSample(
+ spanContext(traceId('33333333')),
+ traceId('33333333')
+ ),
+ {
+ decision: api.SamplingDecision.NOT_RECORD,
+ }
+ );
+ });
+
+ it('should not sample with a invalid trace id', () => {
+ const sampler = new TraceIdRatioBasedSampler(1);
+ assert.deepStrictEqual(sampler.shouldSample(spanContext(''), ''), {
+ decision: api.SamplingDecision.NOT_RECORD,
+ });
+
+ assert.deepStrictEqual(
+ sampler.shouldSample(spanContext(traceId('g')), traceId('g')),
+ {
+ decision: api.SamplingDecision.NOT_RECORD,
+ }
+ );
+ });
+
+ it('should sample traces that a lower sampling ratio would sample', () => {
+ const sampler10 = new TraceIdRatioBasedSampler(0.1);
+ const sampler20 = new TraceIdRatioBasedSampler(0.2);
+
+ const id1 = traceId((Math.floor(0xffffffff * 0.1) - 1).toString(16));
+ assert.deepStrictEqual(sampler10.shouldSample(spanContext(id1), id1), {
+ decision: api.SamplingDecision.RECORD_AND_SAMPLED,
+ });
+ assert.deepStrictEqual(sampler20.shouldSample(spanContext(id1), id1), {
+ decision: api.SamplingDecision.RECORD_AND_SAMPLED,
+ });
+
+ const id2 = traceId((Math.floor(0xffffffff * 0.2) - 1).toString(16));
+ assert.deepStrictEqual(sampler10.shouldSample(spanContext(id2), id2), {
+ decision: api.SamplingDecision.NOT_RECORD,
+ });
+ assert.deepStrictEqual(sampler20.shouldSample(spanContext(id2), id2), {
+ decision: api.SamplingDecision.RECORD_AND_SAMPLED,
+ });
+
+ const id2delta = traceId(Math.floor(0xffffffff * 0.2).toString(16));
+ assert.deepStrictEqual(
+ sampler10.shouldSample(spanContext(id2delta), id2delta),
+ {
+ decision: api.SamplingDecision.NOT_RECORD,
+ }
+ );
+ assert.deepStrictEqual(
+ sampler20.shouldSample(spanContext(id2delta), id2delta),
+ {
+ decision: api.SamplingDecision.NOT_RECORD,
+ }
+ );
+ });
+});
diff --git a/packages/opentelemetry-sdk-trace-base/test/common/util.ts b/packages/opentelemetry-sdk-trace-base/test/common/util.ts
index 6b27471c28..ab34f2ab1b 100644
--- a/packages/opentelemetry-sdk-trace-base/test/common/util.ts
+++ b/packages/opentelemetry-sdk-trace-base/test/common/util.ts
@@ -31,3 +31,5 @@ export const invalidAttributes = {
// This empty length attribute should not be set
'': 'empty-key',
};
+
+export function assertAssignable(val: T): asserts val is T {}
diff --git a/packages/opentelemetry-sdk-trace-node/test/NodeTracerProvider.test.ts b/packages/opentelemetry-sdk-trace-node/test/NodeTracerProvider.test.ts
index 24ba20f4b0..3ae6011759 100644
--- a/packages/opentelemetry-sdk-trace-node/test/NodeTracerProvider.test.ts
+++ b/packages/opentelemetry-sdk-trace-node/test/NodeTracerProvider.test.ts
@@ -29,9 +29,10 @@ import {
trace,
TraceFlags,
} from '@opentelemetry/api';
-import { AlwaysOnSampler, AlwaysOffSampler } from '@opentelemetry/core';
import { AsyncHooksContextManager } from '@opentelemetry/context-async-hooks';
import {
+ AlwaysOffSampler,
+ AlwaysOnSampler,
BatchSpanProcessor,
InMemorySpanExporter,
Span,