Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 35 additions & 1 deletion docs/advanced-config.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,6 @@ This distribution supports all the configuration options supported by the compon
| `OTEL_SPAN_LINK_COUNT_LIMIT` | `1000`\* | Stable |
| `OTEL_TRACES_EXPORTER`<br>`tracing.spanExporterFactory` | `otlp` | Stable | Chooses the trace exporters. Shortcut for setting `spanExporterFactory`. Comma-delimited list of exporters. Currently supported values: `otlp`, `console`, `none`.
| `OTEL_EXPORTER_OTLP_TRACES_PROTOCOL` | `http/protobuf` | Stable | Metric exporter protocol. Supported values: `http/protobuf`, `grpc`.
| `OTEL_TRACES_SAMPLER` | `parentbased_always_on` | Stable | Sampler to be used for traces. See [Sampling](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#sampling)
| `OTEL_TRACES_SAMPLER_ARG` | | Stable | String value to be used as the sampler argument. Only be used if OTEL_TRACES_SAMPLER is set.
| `SPLUNK_ACCESS_TOKEN`<br>`accessToken` | | Stable | The optional access token for exporting signal data directly to SignalFx API.
| `SPLUNK_REALM`<br>`realm` | | Stable | The name of your organization's realm, for example, ``us0``. When you set the realm, telemetry is sent directly to the ingest endpoint of Splunk Observability Cloud, bypassing the Splunk OpenTelemetry Collector. Overridden by settings that define a complete endpoint URL, like `OTEL_EXPORTER_OTLP_ENDPOINT`.
Expand All @@ -75,6 +74,41 @@ This distribution supports all the configuration options supported by the compon

\*: Overwritten default value

#### Sampling configuration

| Environment variable | Default value | Support | Notes
| ------------------------ | ------------------------ | -------------- | ------- | ----------- |
| `OTEL_TRACES_SAMPLER` | `always_on` | Stable | Sampler to be used for traces. See [Sampling](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/trace/sdk.md#sampling)

Splunk Distribution of OpenTelemetry JS supports all standard samplers as provided by
[OpenTelemetry JS SDK](https://github.com/open-telemetry/opentelemetry-js/tree/main/packages/opentelemetry-sdk-trace-base#built-in-samplers).
In addition, the distribution adds the following samplers:

### `rules`
This sampler allows to ignore individual endpoints and drop all traces that originate from them.
It applies only to spans with `SERVER` kind.

For example, the following configuration results in all requests to `/healthcheck` to be excluded from monitoring:

```shell
export OTEL_TRACES_SAMPLER=rules
export OTEL_TRACES_SAMPLER_ARG=drop=/healthcheck;fallback=parentbased_always_on
```
All requests to downstream services that happen as a consequence of calling an excluded endpoint are also excluded.

The value of `OTEL_TRACES_SAMPLER_ARG` is interpreted as a semicolon-separated list of rules.
The following types of rules are supported:

- `drop=<value>`: The sampler drops a span if its `url.path` (or `http.target` for instrumentations using older semantic conventions) attribute has a substring equal to the provided value.
You can provide as many `drop` rules as you want.
- `fallback=sampler`: Fallback sampler used if no `drop` rule matched a given span.
Supported fallback samplers are `always_on` and `parentbased_always_on`.
Probability samplers such as `traceidratio` are not supported.

> If several `fallback` rules are provided, only the last one will be in effect.

If `OTEL_TRACES_SAMPLER_ARG` is not provided or has en empty value, no `drop` rules are configured and `always_on` sampler is as default.

#### Additional `tracing` config options in `start()`

The following config options can be set by passing them as tracing arguments to `start()`.
Expand Down
103 changes: 103 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@
"@opentelemetry/propagator-b3": "2.5.1",
"@opentelemetry/resource-detector-container": "0.8.3",
"@opentelemetry/resources": "2.5.1",
"@opentelemetry/sampler-composite": "^0.213.0",
"@opentelemetry/sdk-logs": "0.212.0",
"@opentelemetry/sdk-metrics": "2.5.1",
"@opentelemetry/sdk-trace-base": "2.5.1",
Expand Down
114 changes: 114 additions & 0 deletions src/tracing/RuleBasedSampler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
* Copyright Splunk Inc.
*
* 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
*
* http://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 { SpanKind } from '@opentelemetry/api';
import { Sampler } from '@opentelemetry/sdk-trace-base';
import {
createComposableAlwaysOffSampler,
createComposableAlwaysOnSampler,
createComposableParentThresholdSampler,
createComposableRuleBasedSampler,
createCompositeSampler,
type ComposableSampler,
} from '@opentelemetry/sampler-composite';
import { ATTR_URL_PATH } from '@opentelemetry/semantic-conventions';
import { SamplingPredicate } from '@opentelemetry/sampler-composite/build/src/types';

/**
* Deprecated but some instrumentations still default to old semantic conventions
*/
const ATTR_HTTP_TARGET = 'http.target';

function selectComposableSampler(type: string): ComposableSampler {
switch (type) {
case 'always_on':
return createComposableAlwaysOnSampler();
case 'always_off':
return createComposableAlwaysOffSampler();
case 'parentbased_always_on':
return createComposableParentThresholdSampler(
createComposableAlwaysOnSampler()
);
case 'parentbased_always_off':
return createComposableParentThresholdSampler(
createComposableAlwaysOffSampler()
);
default:
throw new Error('Unsupported fallback sampler ' + type);
}
}

/**
* Parses OTEL_TRACES_SAMPLER_ARG for the `rules` sampler and returns a
* composite sampler ready for use.
*
* Format: semicolon-separated list of key=value rules:
* drop=/healthcheck;fallback=parentbased_always_on
*
* Supported rules:
* drop=<value> - drop SERVER spans whose url.path/http.target contains <value>
* fallback=<name> - fallback sampler; supported: always_on, parentbased_always_on
*
* If arg is not set, defaults to no drop rules and parentbased_always_on fallback.
*/
export function createRuleBasedSampler(arg: string | undefined): Sampler {
const dropValues: string[] = [];
let fallback: ComposableSampler | undefined = undefined;

if (arg && arg.trim().length > 0) {
for (const rule of arg.split(';')) {
const eqIndex = rule.indexOf('=');
if (eqIndex === -1) {
continue;
}
const key = rule.substring(0, eqIndex).trim();
const value = rule.substring(eqIndex + 1).trim();

if (key === 'drop' && value.length > 0) {
dropValues.push(value);
} else if (key === 'fallback') {
fallback = selectComposableSampler(value);
}
}
}

const matchesDrops: SamplingPredicate = (
_ctx,
_traceId,
_name,
spanKind,
attributes
) => {
if (spanKind !== SpanKind.SERVER) {
return false;
}

const target =
(attributes[ATTR_URL_PATH] as string | undefined) ??
(attributes[ATTR_HTTP_TARGET] as string | undefined);
if (typeof target !== 'string') {
return false;
}
return dropValues.some((v) => target.includes(v));
};

return createCompositeSampler(
createComposableRuleBasedSampler([
[matchesDrops, createComposableAlwaysOffSampler()],
[() => true, fallback ?? createComposableAlwaysOnSampler()],
])
);
}
9 changes: 8 additions & 1 deletion src/tracing/options.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
SpanExporter,
SpanProcessor,
} from '@opentelemetry/sdk-trace-base';
import { createRuleBasedSampler } from './RuleBasedSampler';
import { B3Propagator, B3InjectEncoding } from '@opentelemetry/propagator-b3';
import { AWSXRayPropagator } from '@opentelemetry/propagator-aws-xray';

Expand Down Expand Up @@ -79,9 +80,15 @@ function createSampler(userConfig: NodeTracerConfig) {
const configSampler = configGetSampler();

if (configSampler === undefined) {
if (getNonEmptyEnvVar('OTEL_TRACES_SAMPLER') === undefined) {
const envSampler = getNonEmptyEnvVar('OTEL_TRACES_SAMPLER');
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should the rule based sampler be configurable via the config file?

  • can it be passed in via programmatic API?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

config file: Probably better to then fully support composite/development sampler

// TODO: Should composite/development be supported?

unclear if in scope for now

programmattic API: User can compose it by using @opentelemetry/sampler-composite package directly

if (envSampler === undefined) {
return new AlwaysOnSampler();
}
if (envSampler === 'rules') {
return createRuleBasedSampler(
getNonEmptyEnvVar('OTEL_TRACES_SAMPLER_ARG')
);
}
}

return configSampler;
Expand Down
1 change: 1 addition & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ export type EnvVarKey =
| 'OTEL_SPAN_LINK_COUNT_LIMIT'
| 'OTEL_TRACES_EXPORTER'
| 'OTEL_TRACES_SAMPLER'
| 'OTEL_TRACES_SAMPLER_ARG'
| 'SPLUNK_ACCESS_TOKEN'
| 'SPLUNK_AUTOINSTRUMENT_PACKAGE_NAMES'
| 'SPLUNK_AUTOMATIC_LOG_COLLECTION'
Expand Down
Loading
Loading