Skip to content

Commit 479321c

Browse files
svetlanabrennanFlarnarauno56vmarchauddyladan
authored
feat(otlp-exporter): add timeout env var (#2738)
Co-authored-by: Gerhard Stöbich <deb2001-github@yahoo.de> Co-authored-by: Rauno Viskus <Rauno56@users.noreply.github.com> Co-authored-by: Valentin Marchaud <contact@vmarchaud.fr> Co-authored-by: Daniel Dyla <dyladan@users.noreply.github.com>
1 parent ac578e9 commit 479321c

22 files changed

Lines changed: 825 additions & 134 deletions

File tree

experimental/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,7 @@ All notable changes to experimental packages in this project will be documented
6363
* feat(proto): add @opentelemetry/otlp-transformer package with hand-rolled transformation #2746 @dyladan
6464
* feat(sdk-metrics-base): shutdown and forceflush on MeterProvider #2890 @legendecas
6565
* feat(sdk-metrics-base): return the same meter for identical input to getMeter #2901 @legendecas
66+
* feat(otlp-exporter): add [OTEL_EXPORTER_OTLP_TIMEOUT](https://github.com/open-telemetry/opentelemetry-specification/blob/main/specification/protocol/exporter.md#configuration-options) env var to otlp exporters #2738 @svetlanabrennan
6667
* feat(sdk-metrics-base): hoist async instrument callback invocations #2822 @legendecas
6768

6869
### :bug: (Bug Fix)

experimental/packages/exporter-trace-otlp-grpc/README.md

Lines changed: 31 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,27 @@ provider.register();
108108

109109
Note, that this will only work if TLS is also configured on the server.
110110

111+
## Exporter Timeout Configuration
112+
113+
The OTLPTraceExporter has a timeout configuration option which is the maximum time, in milliseconds, the OTLP exporter will wait for each batch export. The default value is 10000ms.
114+
115+
+ To override the default timeout duration, provide `timeoutMillis` to OTLPTraceExporter with `collectorOptions`:
116+
117+
```js
118+
const collectorOptions = {
119+
timeoutMillis: 15000,
120+
// url is optional and can be omitted - default is localhost:4317
121+
url: '<collector-hostname>:<port>',
122+
metadata, // // an optional grpc.Metadata object to be sent with each request
123+
};
124+
125+
const exporter = new OTLPTraceExporter(collectorOptions);
126+
```
127+
128+
> Providing `timeoutMillis` with `collectorOptions` takes precedence and overrides timeout set with environment variables.
129+
130+
## Exporter Compression Configuration
131+
111132
By default no compression will be used. To use compression, set it programmatically in `collectorOptions` or with environment variables. Supported compression options: `gzip` and `none`.
112133

113134
```js
@@ -126,13 +147,13 @@ const exporter = new OTLPTraceExporter(collectorOptions);
126147
127148
## Environment Variable Configuration
128149

129-
Set compression with environment variables.
130-
131-
```shell
132-
OTEL_EXPORTER_OTLP_TRACES_COMPRESSION=gzip
133-
```
134-
135-
> Compression set programatically in `collectorOptions` takes precedence over compression set with environment variables. `OTEL_EXPORTER_OTLP_TRACES_COMPRESSION` takes precedence and overrides `OTEL_EXPORTER_OTLP_COMPRESSION`.
150+
| Environment variable | Description |
151+
|----------------------|-------------|
152+
| OTEL_EXPORTER_OTLP_TRACES_TIMEOUT | The maximum waiting time, in milliseconds, allowed to send each OTLP trace batch. Default is 10000. |
153+
| OTEL_EXPORTER_OTLP_TIMEOUT | The maximum waiting time, in milliseconds, allowed to send each OTLP trace and metric batch. Default is 10000. |
154+
| OTEL_EXPORTER_OTLP_TRACES_COMPRESSION | The compression type to use on OTLP trace requests. Options include gzip. By default no compression will be used. |
155+
| OTEL_EXPORTER_OTLP_COMPRESSION | The compression type to use on OTLP trace, metric, and log requests. Options include gzip. By default no compression will be used. |
156+
> The per-signal environment variables (`OTEL_EXPORTER_OTLP_TRACES_TIMEOUT`) takes precedence and non-per-signal environment variable (`OTEL_EXPORTER_OTLP_TIMEOUT`).
136157
137158
## Running opentelemetry-collector locally to see the traces
138159

@@ -141,9 +162,9 @@ OTEL_EXPORTER_OTLP_TRACES_COMPRESSION=gzip
141162

142163
## Useful links
143164

144-
- For more information on OpenTelemetry, visit: <https://opentelemetry.io/>
145-
- For more about OpenTelemetry JavaScript: <https://github.com/open-telemetry/opentelemetry-js>
146-
- For help or feedback on this project, join us in [GitHub Discussions][discussions-url]
165+
+ For more information on OpenTelemetry, visit: <https://opentelemetry.io/>
166+
+ For more about OpenTelemetry JavaScript: <https://github.com/open-telemetry/opentelemetry-js>
167+
+ For help or feedback on this project, join us in [GitHub Discussions][discussions-url]
147168

148169
## License
149170

experimental/packages/exporter-trace-otlp-grpc/test/OTLPTraceExporter.test.ts

Lines changed: 29 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ import {
3434
ensureResourceIsCorrect,
3535
mockedReadableSpan,
3636
} from './traceHelper';
37+
import * as core from '@opentelemetry/core';
3738
import { CompressionAlgorithm } from '@opentelemetry/otlp-exporter-base';
3839
import { GrpcCompressionAlgorithm } from '@opentelemetry/otlp-grpc-exporter-base';
3940
import { IExportTraceServiceRequest, IResourceSpans } from '@opentelemetry/otlp-transformer';
@@ -197,7 +198,34 @@ const testCollectorExporter = (params: TestParams) =>
197198
ensureMetadataIsCorrect(reqMetadata, params?.metadata);
198199

199200
done();
200-
}, 200);
201+
}, 500);
202+
});
203+
it('should log deadline exceeded error', done => {
204+
const credentials = params.useTLS
205+
? grpc.credentials.createSsl(
206+
fs.readFileSync('./test/certs/ca.crt'),
207+
fs.readFileSync('./test/certs/client.key'),
208+
fs.readFileSync('./test/certs/client.crt')
209+
)
210+
: undefined;
211+
212+
const collectorExporterWithTimeout = new OTLPTraceExporter({
213+
url: 'grpcs://' + address,
214+
credentials,
215+
metadata: params.metadata,
216+
timeoutMillis: 100,
217+
});
218+
219+
const responseSpy = sinon.spy();
220+
const spans = [Object.assign({}, mockedReadableSpan)];
221+
collectorExporterWithTimeout.export(spans, responseSpy);
222+
223+
setTimeout(() => {
224+
const result = responseSpy.args[0][0] as core.ExportResult;
225+
assert.strictEqual(result.code, core.ExportResultCode.FAILED);
226+
assert.strictEqual(responseSpy.args[0][0].error.details, 'Deadline exceeded');
227+
done();
228+
}, 300);
201229
});
202230
});
203231
describe('export - with gzip compression', () => {

experimental/packages/exporter-trace-otlp-http/README.md

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -107,16 +107,48 @@ OTEL_EXPORTER_OTLP_METRICS_ENDPOINT=https://metric-service:4318/v1/metrics
107107
108108
For more details, see [OpenTelemetry Specification on Protocol Exporter][opentelemetry-spec-protocol-exporter].
109109

110+
## Exporter Timeout Configuration
111+
112+
The OTLPTraceExporter has a timeout configuration option which is the maximum time, in milliseconds, the OTLP exporter will wait for each batch export. The default value is 10000ms.
113+
114+
To override the default timeout duration, use the following options:
115+
116+
+ Set with environment variables:
117+
118+
| Environment variable | Description |
119+
|----------------------|-------------|
120+
| OTEL_EXPORTER_OTLP_TRACES_TIMEOUT | The maximum waiting time, in milliseconds, allowed to send each OTLP trace batch. Default is 10000. |
121+
| OTEL_EXPORTER_OTLP_TIMEOUT | The maximum waiting time, in milliseconds, allowed to send each OTLP trace and metric batch. Default is 10000. |
122+
123+
> `OTEL_EXPORTER_OTLP_TRACES_TIMEOUT` takes precedence and overrides `OTEL_EXPORTER_OTLP_TIMEOUT`.
124+
125+
+ Provide `timeoutMillis` to OTLPTraceExporter with `collectorOptions`:
126+
127+
```js
128+
const collectorOptions = {
129+
timeoutMillis: 15000,
130+
url: '<opentelemetry-collector-url>', // url is optional and can be omitted - default is http://localhost:4318/v1/traces
131+
headers: {
132+
foo: 'bar'
133+
}, // an optional object containing custom headers to be sent with each request will only work with http
134+
concurrencyLimit: 10, // an optional limit on pending requests
135+
};
136+
137+
const exporter = new OTLPTraceExporter(collectorOptions);
138+
```
139+
140+
> Providing `timeoutMillis` with `collectorOptions` takes precedence and overrides timeout set with environment variables.
141+
110142
## Running opentelemetry-collector locally to see the traces
111143

112144
1. Go to `examples/otlp-exporter-node`
113145
2. Follow the instructions there to inspect traces.
114146

115147
## Useful links
116148

117-
- For more information on OpenTelemetry, visit: <https://opentelemetry.io/>
118-
- For more about OpenTelemetry JavaScript: <https://github.com/open-telemetry/opentelemetry-js>
119-
- For help or feedback on this project, join us in [GitHub Discussions][discussions-url]
149+
+ For more information on OpenTelemetry, visit: <https://opentelemetry.io/>
150+
+ For more about OpenTelemetry JavaScript: <https://github.com/open-telemetry/opentelemetry-js>
151+
+ For help or feedback on this project, join us in [GitHub Discussions][discussions-url]
120152

121153
## License
122154

experimental/packages/exporter-trace-otlp-http/test/browser/CollectorTraceExporter.test.ts

Lines changed: 47 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,21 @@
1414
* limitations under the License.
1515
*/
1616

17+
import * as core from '@opentelemetry/core';
1718
import { diag, DiagLogger, DiagLogLevel } from '@opentelemetry/api';
1819
import { ExportResultCode } from '@opentelemetry/core';
1920
import { ReadableSpan } from '@opentelemetry/sdk-trace-base';
2021
import * as assert from 'assert';
2122
import * as sinon from 'sinon';
2223
import { OTLPTraceExporter } from '../../src/platform/browser/index';
23-
2424
import {
2525
ensureSpanIsCorrect,
2626
ensureExportTraceServiceRequestIsSet,
2727
ensureWebResourceIsCorrect,
2828
ensureHeadersContain,
2929
mockedReadableSpan,
3030
} from '../traceHelper';
31-
import { OTLPExporterConfigBase } from '@opentelemetry/otlp-exporter-base';
31+
import { OTLPExporterConfigBase, OTLPExporterError } from '@opentelemetry/otlp-exporter-base';
3232
import { IExportTraceServiceRequest } from '@opentelemetry/otlp-transformer';
3333

3434
describe('OTLPTraceExporter - web', () => {
@@ -176,7 +176,12 @@ describe('OTLPTraceExporter - web', () => {
176176

177177
describe('when "sendBeacon" is NOT available', () => {
178178
let server: any;
179+
let clock: sinon.SinonFakeTimers;
179180
beforeEach(() => {
181+
// fakeTimers is used to replace the next setTimeout which is
182+
// located in sendWithXhr function called by the export method
183+
clock = sinon.useFakeTimers();
184+
180185
(window.navigator as any).sendBeacon = false;
181186
collectorTraceExporter = new OTLPTraceExporter(
182187
collectorExporterConfig
@@ -191,7 +196,7 @@ describe('OTLPTraceExporter - web', () => {
191196
collectorTraceExporter.export(spans, () => {
192197
});
193198

194-
setTimeout(() => {
199+
queueMicrotask(() => {
195200
const request = server.requests[0];
196201
assert.strictEqual(request.method, 'POST');
197202
assert.strictEqual(request.url, 'http://foo.bar.com');
@@ -211,9 +216,9 @@ describe('OTLPTraceExporter - web', () => {
211216
ensureWebResourceIsCorrect(resource);
212217

213218
assert.strictEqual(stubBeacon.callCount, 0);
214-
215219
ensureExportTraceServiceRequestIsSet(json);
216220

221+
clock.restore();
217222
done();
218223
});
219224
});
@@ -236,15 +241,15 @@ describe('OTLPTraceExporter - web', () => {
236241
collectorTraceExporter.export(spans, () => {
237242
});
238243

239-
setTimeout(() => {
244+
queueMicrotask(() => {
240245
const request = server.requests[0];
241246
request.respond(200);
242-
243247
const response: any = spyLoggerDebug.args[2][0];
244248
assert.strictEqual(response, 'xhr success');
245249
assert.strictEqual(spyLoggerError.args.length, 0);
246-
247250
assert.strictEqual(stubBeacon.callCount, 0);
251+
252+
clock.restore();
248253
done();
249254
});
250255
});
@@ -256,21 +261,24 @@ describe('OTLPTraceExporter - web', () => {
256261
done();
257262
});
258263

259-
setTimeout(() => {
264+
queueMicrotask(() => {
260265
const request = server.requests[0];
261266
request.respond(400);
267+
clock.restore();
268+
done();
262269
});
263270
});
264271

265272
it('should send custom headers', done => {
266273
collectorTraceExporter.export(spans, () => {
267274
});
268275

269-
setTimeout(() => {
276+
queueMicrotask(() => {
270277
const request = server.requests[0];
271278
request.respond(200);
272279

273280
assert.strictEqual(stubBeacon.callCount, 0);
281+
clock.restore();
274282
done();
275283
});
276284
});
@@ -373,7 +381,12 @@ describe('OTLPTraceExporter - web', () => {
373381
});
374382

375383
describe('when "sendBeacon" is available', () => {
384+
let clock: sinon.SinonFakeTimers;
376385
beforeEach(() => {
386+
// fakeTimers is used to replace the next setTimeout which is
387+
// located in sendWithXhr function called by the export method
388+
clock = sinon.useFakeTimers();
389+
377390
collectorTraceExporter = new OTLPTraceExporter(
378391
collectorExporterConfig
379392
);
@@ -382,20 +395,26 @@ describe('OTLPTraceExporter - web', () => {
382395
collectorTraceExporter.export(spans, () => {
383396
});
384397

385-
setTimeout(() => {
398+
queueMicrotask(() => {
386399
const [{ requestHeaders }] = server.requests;
387400

388401
ensureHeadersContain(requestHeaders, customHeaders);
389402
assert.strictEqual(stubBeacon.callCount, 0);
390403
assert.strictEqual(stubOpen.callCount, 0);
391404

405+
clock.restore();
392406
done();
393407
});
394408
});
395409
});
396410

397411
describe('when "sendBeacon" is NOT available', () => {
412+
let clock: sinon.SinonFakeTimers;
398413
beforeEach(() => {
414+
// fakeTimers is used to replace the next setTimeout which is
415+
// located in sendWithXhr function called by the export method
416+
clock = sinon.useFakeTimers();
417+
399418
(window.navigator as any).sendBeacon = false;
400419
collectorTraceExporter = new OTLPTraceExporter(
401420
collectorExporterConfig
@@ -406,13 +425,30 @@ describe('OTLPTraceExporter - web', () => {
406425
collectorTraceExporter.export(spans, () => {
407426
});
408427

409-
setTimeout(() => {
428+
queueMicrotask(() => {
410429
const [{ requestHeaders }] = server.requests;
411430

412431
ensureHeadersContain(requestHeaders, customHeaders);
413432
assert.strictEqual(stubBeacon.callCount, 0);
414433
assert.strictEqual(stubOpen.callCount, 0);
415434

435+
clock.restore();
436+
done();
437+
});
438+
});
439+
it('should log the timeout request error message', done => {
440+
const responseSpy = sinon.spy();
441+
collectorTraceExporter.export(spans, responseSpy);
442+
clock.tick(10000);
443+
clock.restore();
444+
445+
setTimeout(() => {
446+
const result = responseSpy.args[0][0] as core.ExportResult;
447+
assert.strictEqual(result.code, core.ExportResultCode.FAILED);
448+
const error = result.error as OTLPExporterError;
449+
assert.ok(error !== undefined);
450+
assert.strictEqual(error.message, 'Request Timeout');
451+
416452
done();
417453
});
418454
});

0 commit comments

Comments
 (0)