Skip to content

Commit 3b264f5

Browse files
authored
Add BatchUnsampledSpanProcessor for exporting unsampled spans (#42)
*Description of changes:* 1. Implemented a new `AwsBatchUnsampledSpanProcessor` to only export unsampled spans(`TraceFlags.SAMPLED !==1`) 2. Registered it as an additional SpanProcessor for `TraceProvider` 3. Add different Prefixes for sampled/unsampled OTel span when sending to FluxPump * Sampled Spans Prefix - `T1S` * Unsampled Spans Prefix - `T1U` *Test* X-Ray data example ``` { "Id": "1-66e11092-767bc32c5717c925604e37e3", "Duration": 0.158, "LimitExceeded": false, "Segments": [ { "Id": "5a5e9957e3a068ef", "Document": { "id": "5a5e9957e3a068ef", "name": "aws-opentelemetry-distro-nodejs", "start_time": 1726025874.375, "trace_id": "1-66e11092-767bc32c5717c925604e37e3", "end_time": 1726025874.527, "parent_id": "5ff5cfd8f1c6ed59", "http": { "response": { "status": 200 } }, "aws": { "request_id": "e4ac3af9-7538-4b8c-84c8-36d154378813" }, "origin": "AWS::Lambda", "resource_arn": "arn:aws:lambda:us-west-2:252610625673:function:aws-opentelemetry-distro-nodejs" } }, { "Id": "30e074d0013a9b6f", "Document": { "id": "30e074d0013a9b6f", "name": "aws-opentelemetry-distro-nodejs/default", "start_time": 1726025874.369, "trace_id": "1-66e11092-767bc32c5717c925604e37e3", "end_time": 1726025874.527, "http": { "request": { "url": "https://aixsrfz8n8.execute-api.us-west-2.amazonaws.com/default", "method": "GET", "user_agent": "curl/8.7.1", "client_ip": "205.251.233.233", "x_forwarded_for": true }, "response": { "status": 200, "content_length": 0 } }, "aws": { "xray": { "sampling_rule_name": "Default" }, "api_gateway": { "account_id": "252610625673", "rest_api_id": "aixsrfz8n8", "stage": "default", "request_id": "41da0ef6-b16c-45e9-9334-a11347f5dec7" } }, "annotations": { "aws:api_id": "aixsrfz8n8", "aws:api_stage": "default" }, "metadata": { "default": { "extended_request_id": "d6-G6FsovHcEMoQ=", "request_id": "41da0ef6-b16c-45e9-9334-a11347f5dec7" } }, "origin": "AWS::ApiGateway::Stage", "resource_arn": "arn:aws:apigateway:us-west-2::/restapis/aixsrfz8n8/stages/default", "subsegments": [ { "id": "5ff5cfd8f1c6ed59", "name": "Lambda", "start_time": 1726025874.371, "end_time": 1726025874.527, "http": { "request": { "url": "https://lambda.us-west-2.amazonaws.com/2015-03-31/functions/arn:aws:lambda:us-west-2:252610625673:function:aws-opentelemetry-distro-nodejs/invocations", "method": "ANY" }, "response": { "status": 200, "content_length": 120 } }, "aws": { "function_name": "aws-opentelemetry-distro-nodejs", "region": "us-west-2", "operation": "Invoke", "resource_names": [ "aws-opentelemetry-distro-nodejs" ] }, "namespace": "aws" } ] } }, { "Id": "7197ce026d312a49", "Document": { "id": "7197ce026d312a49", "name": "aws-opentelemetry-distro-nodejs", "start_time": 1726025874.3797677, "trace_id": "1-66e11092-767bc32c5717c925604e37e3", "end_time": 1726025874.5265381, "parent_id": "5a5e9957e3a068ef", "aws": { "account_id": "252610625673", "function_arn": "arn:aws:lambda:us-west-2:252610625673:function:aws-opentelemetry-distro-nodejs", "cloudwatch_logs": [ { "log_group": "/aws/lambda/aws-opentelemetry-distro-nodejs" } ], "resource_names": [ "aws-opentelemetry-distro-nodejs" ] }, "origin": "AWS::Lambda::Function", "subsegments": [ { "id": "ca73bcb9d61476de", "name": "Invocation", "start_time": 1726025874.3798523, "end_time": 1726025874.5261395, "aws": { "function_arn": "arn:aws:lambda:us-west-2:252610625673:function:aws-opentelemetry-distro-nodejs" } }, { "id": "4153b4f45ff3b1e8", "name": "Overhead", "start_time": 1726025874.5261652, "end_time": 1726025874.5264673, "aws": { "function_arn": "arn:aws:lambda:us-west-2:252610625673:function:aws-opentelemetry-distro-nodejs" } } ] } }, { "Id": "ae60d7baedc1cfb5", "Document": { "id": "ae60d7baedc1cfb5", "name": "aws-opentelemetry-distro-nodejs", "start_time": 1726025874.38, "trace_id": "1-66e11092-767bc32c5717c925604e37e3", "end_time": 1726025874.5246706, "parent_id": "ca73bcb9d61476de", "aws": { "trace.flag.unsampled": true, "is.local.root": true }, "annotations": { "span.kind": "SERVER" }, "metadata": { "faas.id": "arn:aws:lambda:us-west-2:252610625673:function:aws-opentelemetry-distro-nodejs", "cloud.account.id": "252610625673", "faas.execution": "e4ac3af9-7538-4b8c-84c8-36d154378813" }, "subsegments": [ { "id": "328592d80ebf7e72", "name": "GET", "start_time": 1726025874.483, "end_time": 1726025874.5237389, "aws": { "trace.flag.unsampled": true, "local.operation": "aws-opentelemetry-distro-nodejs", "is.local.root": false }, "annotations": { "span.kind": "CLIENT" }, "metadata": { "net.transport": "ip_tcp", "net.peer.name": "jsonplaceholder.typicode.com", "http.status_code": 200, "net.peer.port": 443, "http.target": "/todos/1", "http.flavor": "1.1", "http.url": "https://jsonplaceholder.typicode.com/todos/1", "http.status_text": "OK", "net.peer.ip": "104.21.59.19", "http.method": "GET", "http.host": "jsonplaceholder.typicode.com:443" }, "namespace": "remote" }, { "id": "6d998c5cf661c0ec", "name": "S3.ListBuckets", "start_time": 1726025874.381, "end_time": 1726025874.4829986, "aws": { "signature.version": "s3", "service.name": "Amazon S3", "trace.flag.unsampled": true, "local.operation": "aws-opentelemetry-distro-nodejs", "service.api": "S3", "region": "us-west-2", "is.local.root": false, "service.identifier": "s3", "operation": "listBuckets", "request.id": "XWEGH4NV5PMQZEHV" }, "annotations": { "span.kind": "CLIENT" }, "metadata": { "http.status_code": 200, "rpc.service": "S3", "rpc.method": "ListBuckets", "rpc.system": "aws-api" }, "namespace": "aws", "subsegments": [ { "id": "5b8e13800550b551", "name": "GET", "start_time": 1726025874.381, "end_time": 1726025874.4812143, "aws": { "trace.flag.unsampled": true, "local.operation": "aws-opentelemetry-distro-nodejs", "is.local.root": false, "sdk.descendant": "true" }, "annotations": { "span.kind": "CLIENT" }, "metadata": { "net.transport": "ip_tcp", "net.peer.name": "s3.us-west-2.amazonaws.com", "http.status_code": 200, "net.peer.port": 443, "http.target": "/", "http.flavor": "1.1", "http.url": "https://s3.us-west-2.amazonaws.com/", "http.status_text": "OK", "net.peer.ip": "52.218.178.96", "http.method": "GET", "http.host": "s3.us-west-2.amazonaws.com:443" }, "namespace": "remote" } ] } ] } }, { "Id": "1c146cef33e14019", "Document": { "id": "1c146cef33e14019", "name": "GET", "start_time": 1726025874.381, "trace_id": "1-66e11092-767bc32c5717c925604e37e3", "end_time": 1726025874.4812143, "parent_id": "5b8e13800550b551", "inferred": true } }, { "Id": "2bccb63c011d05ec", "Document": { "id": "2bccb63c011d05ec", "name": "GET", "start_time": 1726025874.483, "trace_id": "1-66e11092-767bc32c5717c925604e37e3", "end_time": 1726025874.5237389, "parent_id": "328592d80ebf7e72", "inferred": true } } ] } ``` By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.
1 parent d8be15b commit 3b264f5

File tree

6 files changed

+761
-5
lines changed

6 files changed

+761
-5
lines changed

aws-distro-opentelemetry-node-autoinstrumentation/src/aws-attribute-keys.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,9 @@ export const AWS_ATTRIBUTE_KEYS: { [key: string]: string } = {
2121
// Used for JavaScript workaround - attribute for pre-calculated value of isLocalRoot
2222
AWS_IS_LOCAL_ROOT: 'aws.is.local.root',
2323

24+
// Trace Span Unsampled flag
25+
AWS_TRACE_FLAG_UNSAMPLED: 'aws.trace.flag.unsampled',
26+
2427
// AWS_#_NAME attributes are not supported in JavaScript as they are not part of the Semantic Conventions.
2528
// TODO:Move to Semantic Conventions when these attributes are added.
2629
AWS_S3_BUCKET: 'aws.s3.bucket',
Lines changed: 252 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,252 @@
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2+
// SPDX-License-Identifier: Apache-2.0
3+
// Modifications Copyright The OpenTelemetry Authors. Licensed under the Apache License 2.0 License.
4+
5+
import { context, Context, diag, TraceFlags } from '@opentelemetry/api';
6+
import {
7+
BindOnceFuture,
8+
ExportResultCode,
9+
getEnv,
10+
globalErrorHandler,
11+
suppressTracing,
12+
unrefTimer,
13+
} from '@opentelemetry/core';
14+
import { ReadableSpan, BufferConfig, Span, SpanProcessor, SpanExporter } from '@opentelemetry/sdk-trace-base';
15+
import { AWS_ATTRIBUTE_KEYS } from './aws-attribute-keys';
16+
17+
/**
18+
* This class is a customized version of the `BatchSpanProcessorBase` from the
19+
* OpenTelemetry SDK (`@opentelemetry/sdk-trace-base/build/src/export/BatchSpanProcessorBase`).
20+
* It inherits much of the behavior of the `BatchSpanProcessorBase` while adding
21+
* specific logic to handle unsampled spans.
22+
*
23+
* It can't directly be inherited `BatchSpanProcessorBase` as child class because
24+
* a few stateful fields are private in `BatchSpanProcessorBase` which need to be accessed
25+
* in `AwsBatchUnsampledSpanProcessor` and we don't plan to update upstream code for it.
26+
*
27+
* In particular, the following methods are modified:
28+
*
29+
* 1. `onStart`: This method is modified to detect unsampled spans and add an
30+
* AWS-specific attribute (`AWS_TRACE_FLAG_UNSAMPLED`) to denote that the span
31+
* is unsampled. This is done by checking the `traceFlags` of the span.
32+
*
33+
* 2. `onEnd`: The logic here is changed to handle unsampled spans. While the
34+
* default behavior of `BatchSpanProcessorBase` is to ignore unsampled spans,
35+
* this version adds them to the buffer for export. The unsampled spans are
36+
* queued and processed similarly to sampled spans.
37+
*
38+
* This processor ensures that even unsampled spans are exported, which is a
39+
* deviation from the typical span processing behavior in OpenTelemetry.
40+
*
41+
* The rest of the behavior—batch processing, queuing, and exporting spans in
42+
* batches—is inherited from the base class and remains largely the same.
43+
*/
44+
export class AwsBatchUnsampledSpanProcessor implements SpanProcessor {
45+
private readonly _maxExportBatchSize: number;
46+
private readonly _maxQueueSize: number;
47+
private readonly _scheduledDelayMillis: number;
48+
private readonly _exportTimeoutMillis: number;
49+
50+
private _isExporting = false;
51+
private _finishedSpans: ReadableSpan[] = [];
52+
private _timer: NodeJS.Timeout | undefined;
53+
private _shutdownOnce: BindOnceFuture<void>;
54+
private _droppedSpansCount: number = 0;
55+
56+
constructor(private readonly _exporter: SpanExporter, config?: BufferConfig) {
57+
const env = getEnv();
58+
this._maxExportBatchSize =
59+
typeof config?.maxExportBatchSize === 'number' ? config.maxExportBatchSize : env.OTEL_BSP_MAX_EXPORT_BATCH_SIZE;
60+
this._maxQueueSize = typeof config?.maxQueueSize === 'number' ? config.maxQueueSize : env.OTEL_BSP_MAX_QUEUE_SIZE;
61+
this._scheduledDelayMillis =
62+
typeof config?.scheduledDelayMillis === 'number' ? config.scheduledDelayMillis : env.OTEL_BSP_SCHEDULE_DELAY;
63+
this._exportTimeoutMillis =
64+
typeof config?.exportTimeoutMillis === 'number' ? config.exportTimeoutMillis : env.OTEL_BSP_EXPORT_TIMEOUT;
65+
66+
this._shutdownOnce = new BindOnceFuture(this._shutdown, this);
67+
68+
if (this._maxExportBatchSize > this._maxQueueSize) {
69+
diag.warn(
70+
'BatchSpanProcessor: maxExportBatchSize must be smaller or equal to maxQueueSize, setting maxExportBatchSize to match maxQueueSize'
71+
);
72+
this._maxExportBatchSize = this._maxQueueSize;
73+
}
74+
}
75+
76+
forceFlush(): Promise<void> {
77+
if (this._shutdownOnce.isCalled) {
78+
return this._shutdownOnce.promise;
79+
}
80+
return this._flushAll();
81+
}
82+
83+
onStart(span: Span, _parentContext: Context): void {
84+
if ((span.spanContext().traceFlags & TraceFlags.SAMPLED) === 0) {
85+
span.setAttribute(AWS_ATTRIBUTE_KEYS.AWS_TRACE_FLAG_UNSAMPLED, true);
86+
return;
87+
}
88+
}
89+
90+
onEnd(span: ReadableSpan): void {
91+
if (this._shutdownOnce.isCalled) {
92+
return;
93+
}
94+
95+
if ((span.spanContext().traceFlags & TraceFlags.SAMPLED) === 1) {
96+
return;
97+
}
98+
99+
this._addToBuffer(span);
100+
}
101+
102+
shutdown(): Promise<void> {
103+
return this._shutdownOnce.call();
104+
}
105+
106+
private _shutdown() {
107+
return Promise.resolve()
108+
.then(() => {
109+
return this.onShutdown();
110+
})
111+
.then(() => {
112+
return this._flushAll();
113+
})
114+
.then(() => {
115+
return this._exporter.shutdown();
116+
});
117+
}
118+
119+
/** Add a span in the buffer. */
120+
private _addToBuffer(span: ReadableSpan) {
121+
if (this._finishedSpans.length >= this._maxQueueSize) {
122+
// limit reached, drop span
123+
124+
if (this._droppedSpansCount === 0) {
125+
diag.debug('maxQueueSize reached, dropping spans');
126+
}
127+
this._droppedSpansCount++;
128+
129+
return;
130+
}
131+
132+
if (this._droppedSpansCount > 0) {
133+
// some spans were dropped, log once with count of spans dropped
134+
diag.warn(`Dropped ${this._droppedSpansCount} spans because maxQueueSize reached`);
135+
this._droppedSpansCount = 0;
136+
}
137+
138+
this._finishedSpans.push(span);
139+
this._maybeStartTimer();
140+
}
141+
142+
/**
143+
* Send all spans to the exporter respecting the batch size limit
144+
* This function is used only on forceFlush or shutdown,
145+
* for all other cases _flush should be used
146+
* */
147+
private _flushAll(): Promise<void> {
148+
return new Promise((resolve, reject) => {
149+
const promises = [];
150+
// calculate number of batches
151+
const count = Math.ceil(this._finishedSpans.length / this._maxExportBatchSize);
152+
for (let i = 0, j = count; i < j; i++) {
153+
promises.push(this._flushOneBatch());
154+
}
155+
Promise.all(promises)
156+
.then(() => {
157+
resolve();
158+
})
159+
.catch(reject);
160+
});
161+
}
162+
163+
private _flushOneBatch(): Promise<void> {
164+
this._clearTimer();
165+
if (this._finishedSpans.length === 0) {
166+
return Promise.resolve();
167+
}
168+
return new Promise((resolve, reject) => {
169+
const timer = setTimeout(() => {
170+
// don't wait anymore for export, this way the next batch can start
171+
reject(new Error('Timeout'));
172+
}, this._exportTimeoutMillis);
173+
// prevent downstream exporter calls from generating spans
174+
context.with(suppressTracing(context.active()), () => {
175+
// Reset the finished spans buffer here because the next invocations of the _flush method
176+
// could pass the same finished spans to the exporter if the buffer is cleared
177+
// outside the execution of this callback.
178+
let spans: ReadableSpan[];
179+
if (this._finishedSpans.length <= this._maxExportBatchSize) {
180+
spans = this._finishedSpans;
181+
this._finishedSpans = [];
182+
} else {
183+
spans = this._finishedSpans.splice(0, this._maxExportBatchSize);
184+
}
185+
186+
const doExport = () =>
187+
this._exporter.export(spans, result => {
188+
clearTimeout(timer);
189+
if (result.code === ExportResultCode.SUCCESS) {
190+
resolve();
191+
} else {
192+
reject(result.error ?? new Error('BatchSpanProcessor: span export failed'));
193+
}
194+
});
195+
196+
let pendingResources: Array<Promise<void>> | null = null;
197+
for (let i = 0, len = spans.length; i < len; i++) {
198+
const span = spans[i];
199+
if (span.resource.asyncAttributesPending && span.resource.waitForAsyncAttributes) {
200+
pendingResources ??= [];
201+
pendingResources.push(span.resource.waitForAsyncAttributes());
202+
}
203+
}
204+
205+
// Avoid scheduling a promise to make the behavior more predictable and easier to test
206+
if (pendingResources === null) {
207+
doExport();
208+
} else {
209+
Promise.all(pendingResources).then(doExport, err => {
210+
globalErrorHandler(err);
211+
reject(err);
212+
});
213+
}
214+
});
215+
});
216+
}
217+
218+
private _maybeStartTimer() {
219+
if (this._isExporting) return;
220+
const flush = () => {
221+
this._isExporting = true;
222+
this._flushOneBatch()
223+
.finally(() => {
224+
this._isExporting = false;
225+
if (this._finishedSpans.length > 0) {
226+
this._clearTimer();
227+
this._maybeStartTimer();
228+
}
229+
})
230+
.catch(e => {
231+
this._isExporting = false;
232+
globalErrorHandler(e);
233+
});
234+
};
235+
// we only wait if the queue doesn't have enough elements yet
236+
if (this._finishedSpans.length >= this._maxExportBatchSize) {
237+
return flush();
238+
}
239+
if (this._timer !== undefined) return;
240+
this._timer = setTimeout(() => flush(), this._scheduledDelayMillis);
241+
unrefTimer(this._timer);
242+
}
243+
244+
private _clearTimer() {
245+
if (this._timer !== undefined) {
246+
clearTimeout(this._timer);
247+
this._timer = undefined;
248+
}
249+
}
250+
251+
onShutdown(): void {}
252+
}

aws-distro-opentelemetry-node-autoinstrumentation/src/aws-opentelemetry-configurator.ts

Lines changed: 21 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,16 @@ import { AwsXRayRemoteSampler } from './sampler/aws-xray-remote-sampler';
5959
// This file is generated via `npm run compile`
6060
import { LIB_VERSION } from './version';
6161
import { OTLPUdpSpanExporter } from './otlp-udp-exporter';
62+
import { AwsBatchUnsampledSpanProcessor } from './aws-batch-unsampled-span-processor';
6263

6364
const APPLICATION_SIGNALS_ENABLED_CONFIG: string = 'OTEL_AWS_APPLICATION_SIGNALS_ENABLED';
6465
const APPLICATION_SIGNALS_EXPORTER_ENDPOINT_CONFIG: string = 'OTEL_AWS_APPLICATION_SIGNALS_EXPORTER_ENDPOINT';
6566
const METRIC_EXPORT_INTERVAL_CONFIG: string = 'OTEL_METRIC_EXPORT_INTERVAL';
6667
const DEFAULT_METRIC_EXPORT_INTERVAL_MILLIS: number = 60000;
6768
const AWS_LAMBDA_FUNCTION_NAME_CONFIG: string = 'AWS_LAMBDA_FUNCTION_NAME';
6869
const AWS_XRAY_DAEMON_ADDRESS_CONFIG: string = 'AWS_XRAY_DAEMON_ADDRESS';
69-
70+
const FORMAT_OTEL_SAMPLED_TRACES_BINARY_PREFIX = 'T1S';
71+
const FORMAT_OTEL_UNSAMPLED_TRACES_BINARY_PREFIX = 'T1U';
7072
/**
7173
* Aws Application Signals Config Provider creates a configuration object that can be provided to
7274
* the OTel NodeJS SDK for Auto Instrumentation with Application Signals Functionality.
@@ -225,6 +227,17 @@ export class AwsOpentelemetryConfigurator {
225227

226228
diag.info('AWS Application Signals enabled.');
227229

230+
// Register BatchUnsampledSpanProcessor to export unsampled traces in Lambda
231+
// when Application Signals enabled
232+
if (isLambdaEnvironment()) {
233+
spanProcessors.push(
234+
new AwsBatchUnsampledSpanProcessor(
235+
new OTLPUdpSpanExporter(getXrayDaemonEndpoint(), FORMAT_OTEL_UNSAMPLED_TRACES_BINARY_PREFIX)
236+
)
237+
);
238+
diag.info('Enabled batch unsampled span processor for Lambda environment.');
239+
}
240+
228241
let exportIntervalMillis: number = Number(process.env[METRIC_EXPORT_INTERVAL_CONFIG]);
229242
diag.debug(`AWS Application Signals Metrics export interval: ${exportIntervalMillis}`);
230243

@@ -412,7 +425,7 @@ export class AwsSpanProcessorProvider {
412425
return new OTLPProtoTraceExporter();
413426
case 'udp':
414427
diag.debug('Detected AWS Lambda environment and enabling UDPSpanExporter');
415-
return new OTLPUdpSpanExporter(process.env[AWS_XRAY_DAEMON_ADDRESS_CONFIG]);
428+
return new OTLPUdpSpanExporter(getXrayDaemonEndpoint(), FORMAT_OTEL_SAMPLED_TRACES_BINARY_PREFIX);
416429
default:
417430
diag.warn(`Unsupported OTLP traces protocol: ${protocol}. Using http/protobuf.`);
418431
return new OTLPProtoTraceExporter();
@@ -601,8 +614,13 @@ function getSamplerProbabilityFromEnv(environment: Required<ENVIRONMENT>): numbe
601614
return probability;
602615
}
603616

604-
export function isLambdaEnvironment() {
617+
function isLambdaEnvironment() {
605618
// detect if running in AWS Lambda environment
606619
return process.env[AWS_LAMBDA_FUNCTION_NAME_CONFIG] !== undefined;
607620
}
621+
622+
function getXrayDaemonEndpoint() {
623+
return process.env[AWS_XRAY_DAEMON_ADDRESS_CONFIG];
624+
}
625+
608626
// END The OpenTelemetry Authors code

aws-distro-opentelemetry-node-autoinstrumentation/src/otlp-udp-exporter.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import { SpanExporter, ReadableSpan } from '@opentelemetry/sdk-trace-base';
99

1010
const DEFAULT_ENDPOINT = '127.0.0.1:2000';
1111
const PROTOCOL_HEADER = '{"format":"json","version":1}\n';
12-
const FORMAT_OTEL_TRACES_BINARY_PREFIX = 'T1';
12+
const DEFAULT_FORMAT_OTEL_TRACES_BINARY_PREFIX = 'T1S';
1313

1414
export class UdpExporter {
1515
private _endpoint: string;
@@ -62,7 +62,7 @@ export class OTLPUdpSpanExporter implements SpanExporter {
6262
constructor(endpoint?: string, _signalPrefix?: string) {
6363
this._endpoint = endpoint || DEFAULT_ENDPOINT;
6464
this._udpExporter = new UdpExporter(this._endpoint);
65-
this._signalPrefix = _signalPrefix || FORMAT_OTEL_TRACES_BINARY_PREFIX;
65+
this._signalPrefix = _signalPrefix || DEFAULT_FORMAT_OTEL_TRACES_BINARY_PREFIX;
6666
}
6767

6868
export(spans: ReadableSpan[], resultCallback: (result: ExportResult) => void): void {

0 commit comments

Comments
 (0)