Skip to content

Commit 10034e0

Browse files
committed
feat(otlp-exporter-base): add custom fetch option to OTLP HTTP exporters
1 parent f6e1c1d commit 10034e0

File tree

16 files changed

+231
-34
lines changed

16 files changed

+231
-34
lines changed

experimental/CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ For notes on migrating to 2.x / 0.200.x see [the upgrade guide](doc/upgrade-to-2
1515

1616
### :rocket: Features
1717

18+
* feat(otlp-exporter-base): add custom `fetch` option to OTLP HTTP exporters
1819
feat(configuration): parse config for rc 3 [#6304](https://github.com/open-telemetry/opentelemetry-js/pull/6304) @maryliag
1920

2021
### :bug: Bug Fixes

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

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,13 +32,14 @@ import { OTLPLogExporter } from '@opentelemetry/exporter-logs-otlp-http';
3232

3333
// exporter options. see all options in OTLPExporterConfigBase
3434
const collectorOptions = {
35+
fetch: customFetch, // an optional custom fetch implementation - default is globalThis.fetch
3536
url: '<opentelemetry-collector-url>', // url is optional and can be omitted - default is http://localhost:4318/v1/logs
3637
headers: {}, // an optional object containing custom headers to be sent with each request
3738
concurrencyLimit: 1, // an optional limit on pending requests
3839
};
3940
const logExporter = new OTLPLogExporter(collectorOptions);
4041
const loggerProvider = new LoggerProvider({
41-
processors: [new BatchRecordProcessor(logExporter)]
42+
processors: [new BatchRecordProcessor(logExporter)],
4243
});
4344

4445
const logger = loggerProvider.getLogger('default', '1.0.0');
@@ -67,7 +68,7 @@ const collectorOptions = {
6768
};
6869
const logExporter = new OTLPLogExporter(collectorOptions);
6970
const loggerProvider = new LoggerProvider({
70-
processors: [new BatchRecordProcessor(logExporter)]
71+
processors: [new BatchRecordProcessor(logExporter)],
7172
});
7273

7374
const logger = loggerProvider.getLogger('default', '1.0.0');

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

Lines changed: 16 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ import {
3131
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http';
3232

3333
const collectorOptions = {
34+
fetch: customFetch, // an optional custom fetch implementation - default is globalThis.fetch
3435
url: '<opentelemetry-collector-url>', // url is optional and can be omitted - default is http://localhost:4318/v1/traces
3536
headers: {}, // an optional object containing custom headers to be sent with each request
3637
concurrencyLimit: 10, // an optional limit on pending requests
@@ -48,24 +49,29 @@ const provider = new WebTracerProvider({
4849
scheduledDelayMillis: 500,
4950
// How long the export can run before it is cancelled
5051
exportTimeoutMillis: 30000,
51-
})
52-
]
52+
}),
53+
],
5354
});
5455

5556
provider.register();
56-
5757
```
5858

5959
## Traces in Node - JSON over http
6060

6161
```js
62-
const { NodeTracerProvider, BatchSpanProcessor } = require('@opentelemetry/sdk-trace-node');
63-
const { OTLPTraceExporter } = require('@opentelemetry/exporter-trace-otlp-http');
62+
const {
63+
NodeTracerProvider,
64+
BatchSpanProcessor,
65+
} = require('@opentelemetry/sdk-trace-node');
66+
const {
67+
OTLPTraceExporter,
68+
} = require('@opentelemetry/exporter-trace-otlp-http');
6469

6570
const collectorOptions = {
71+
fetch: customFetch, // an optional custom fetch implementation - default is globalThis.fetch
6672
url: '<opentelemetry-collector-url>', // url is optional and can be omitted - default is http://localhost:4318/v1/traces
6773
headers: {
68-
foo: 'bar'
74+
foo: 'bar',
6975
}, // an optional object containing custom headers to be sent with each request will only work with http
7076
concurrencyLimit: 10, // an optional limit on pending requests
7177
};
@@ -78,12 +84,11 @@ const provider = new NodeTracerProvider({
7884
maxQueueSize: 1000,
7985
// The interval between two consecutive exports
8086
scheduledDelayMillis: 30000,
81-
})
82-
]
87+
}),
88+
],
8389
});
8490

8591
provider.register();
86-
8792
```
8893

8994
## GRPC
@@ -138,7 +143,7 @@ To override the default timeout duration, use the following options:
138143
timeoutMillis: 15000,
139144
url: '<opentelemetry-collector-url>', // url is optional and can be omitted - default is http://localhost:4318/v1/traces
140145
headers: {
141-
foo: 'bar'
146+
foo: 'bar',
142147
}, // an optional object containing custom headers to be sent with each request will only work with http
143148
concurrencyLimit: 10, // an optional limit on pending requests
144149
};
@@ -161,7 +166,7 @@ This retry policy has the following configuration, which there is currently no w
161166

162167
This retry policy first checks if the response has a `'Retry-After'` header. If there is a `'Retry-After'` header, the exporter will wait the amount specified in the `'Retry-After'` header before retrying. If there is no `'Retry-After'` header, the exporter will use an exponential backoff with jitter retry strategy.
163168

164-
> The exporter will retry exporting within the [exporter timeout configuration](#exporter-timeout-configuration) time.
169+
> The exporter will retry exporting within the [exporter timeout configuration](#exporter-timeout-configuration) time.
165170
166171
## Running opentelemetry-collector locally to see the traces
167172

experimental/packages/opentelemetry-exporter-metrics-otlp-http/README.md

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -26,10 +26,14 @@ the [Collector Trace Exporter for web and node][trace-exporter-url].
2626
The OTLPMetricExporter in Web expects the endpoint to end in `/v1/metrics`.
2727

2828
```js
29-
import { MeterProvider, PeriodicExportingMetricReader } from '@opentelemetry/sdk-metrics';
29+
import {
30+
MeterProvider,
31+
PeriodicExportingMetricReader,
32+
} from '@opentelemetry/sdk-metrics';
3033
import { OTLPMetricExporter } from '@opentelemetry/exporter-metrics-otlp-http';
3134

3235
const collectorOptions = {
36+
fetch: customFetch, // an optional custom fetch implementation - default is globalThis.fetch
3337
url: '<opentelemetry-collector-url>', // url is optional and can be omitted - default is http://localhost:4318/v1/metrics
3438
headers: {}, // an optional object containing custom headers to be sent with each request
3539
concurrencyLimit: 1, // an optional limit on pending requests
@@ -47,15 +51,21 @@ const meterProvider = new MeterProvider({
4751
// Now, start recording data
4852
const meter = meterProvider.getMeter('example-meter');
4953
const counter = meter.createCounter('metric_name');
50-
counter.add(10, { 'key': 'value' });
54+
counter.add(10, { key: 'value' });
5155
```
5256

5357
## Metrics in Node
5458

5559
```js
56-
const { MeterProvider, PeriodicExportingMetricReader } = require('@opentelemetry/sdk-metrics');
57-
const { OTLPMetricExporter } = require('@opentelemetry/exporter-metrics-otlp-http');
60+
const {
61+
MeterProvider,
62+
PeriodicExportingMetricReader,
63+
} = require('@opentelemetry/sdk-metrics');
64+
const {
65+
OTLPMetricExporter,
66+
} = require('@opentelemetry/exporter-metrics-otlp-http');
5867
const collectorOptions = {
68+
fetch: customFetch, // an optional custom fetch implementation - default is globalThis.fetch
5969
url: '<opentelemetry-collector-url>', // url is optional and can be omitted - default is http://localhost:4318/v1/metrics
6070
concurrencyLimit: 1, // an optional limit on pending requests
6171
};
@@ -72,8 +82,7 @@ const meterProvider = new MeterProvider({
7282
// Now, start recording data
7383
const meter = meterProvider.getMeter('example-meter');
7484
const counter = meter.createCounter('metric_name');
75-
counter.add(10, { 'key': 'value' });
76-
85+
counter.add(10, { key: 'value' });
7786
```
7887

7988
## Environment Variable Configuration

experimental/packages/otlp-exporter-base/src/configuration/convert-legacy-browser-http-options.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ export function convertLegacyBrowserHttpOptions(
3939
timeoutMillis: config.timeoutMillis,
4040
headers: convertLegacyHeaders(config),
4141
concurrencyLimit: config.concurrencyLimit,
42+
fetch: config.fetch,
4243
},
4344
{}, // no fallback for browser case
4445
getHttpConfigurationDefaults(requiredHeaders, signalResourcePath)

experimental/packages/otlp-exporter-base/src/configuration/convert-legacy-node-http-options.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ export function convertLegacyHttpOptions(
7171
compression: config.compression,
7272
agentFactory: convertLegacyAgentOptions(config),
7373
userAgent: config.userAgent,
74+
fetch: config.fetch,
7475
},
7576
getNodeHttpConfigurationFromEnvironment(
7677
signalIdentifier,

experimental/packages/otlp-exporter-base/src/configuration/legacy-base-configuration.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,24 @@ export interface OTLPExporterConfigBase {
4242
*/
4343
headers?: Record<string, string> | HeadersFactory;
4444
url?: string;
45+
/**
46+
* Custom fetch implementation to use for HTTP requests.
47+
*
48+
* @remarks
49+
* This option allows you to provide a custom fetch function that will be used
50+
* instead of the global `fetch`.
51+
*
52+
* The function must have the same signature as the global `fetch` function.
53+
*
54+
* @example <caption>Using a custom fetch with request logging:</caption>
55+
* fetch: async (input, init) => {
56+
* console.log('Making request to:', input);
57+
* return globalThis.fetch(input, init);
58+
* }
59+
*
60+
* @default globalThis.fetch
61+
*/
62+
fetch?: (input: RequestInfo, init: RequestInit) => Promise<Response>;
4563
concurrencyLimit?: number;
4664
/** Maximum time the OTLP exporter will wait for each batch export.
4765
* The default value is 10000ms. */

experimental/packages/otlp-exporter-base/src/configuration/otlp-http-configuration.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { validateAndNormalizeHeaders } from '../util';
2424
export type HeadersFactory = () => Promise<Record<string, string>>;
2525

2626
export interface OtlpHttpConfiguration extends OtlpSharedConfiguration {
27+
fetch: (input: RequestInfo, init: RequestInit) => Promise<Response>;
2728
url: string;
2829
headers: HeadersFactory;
2930
}
@@ -97,6 +98,10 @@ export function mergeOtlpHttpConfigurationWithDefaults(
9798
validateUserProvidedUrl(userProvidedConfiguration.url) ??
9899
fallbackConfiguration.url ??
99100
defaultConfiguration.url,
101+
fetch:
102+
userProvidedConfiguration.fetch ??
103+
fallbackConfiguration.fetch ??
104+
defaultConfiguration.fetch,
100105
};
101106
}
102107

@@ -108,5 +113,6 @@ export function getHttpConfigurationDefaults(
108113
...getSharedConfigurationDefaults(),
109114
headers: async () => requiredHeaders,
110115
url: 'http://localhost:4318/' + signalResourcePath,
116+
fetch: globalThis.fetch,
111117
};
112118
}

experimental/packages/otlp-exporter-base/src/transport/fetch-transport.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import {
2424
import { HeadersFactory } from '../configuration/otlp-http-configuration';
2525

2626
export interface FetchTransportParameters {
27+
fetch: (input: RequestInfo, init: RequestInit) => Promise<Response>;
2728
url: string;
2829
headers: HeadersFactory;
2930
}
@@ -41,7 +42,7 @@ class FetchTransport implements IExporterTransport {
4142
try {
4243
const isBrowserEnvironment = !!globalThis.location;
4344
const url = new URL(this._parameters.url);
44-
const response = await fetch(url.href, {
45+
const response = await this._parameters.fetch(url.href, {
4546
method: 'POST',
4647
headers: await this._parameters.headers(),
4748
body: data,
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright The OpenTelemetry Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import * as assert from 'assert';
18+
import { convertLegacyBrowserHttpOptions } from '../../../src/configuration/convert-legacy-browser-http-options';
19+
20+
describe('convertLegacyBrowserHttpOptions', function () {
21+
it('should pass along custom fetch function', function () {
22+
const customFetch = async () => new Response();
23+
const options = convertLegacyBrowserHttpOptions(
24+
{
25+
fetch: customFetch,
26+
},
27+
'v1/signal',
28+
{}
29+
);
30+
31+
assert.strictEqual(options.fetch, customFetch);
32+
});
33+
34+
it('should use default fetch when not provided', function () {
35+
const options = convertLegacyBrowserHttpOptions({}, 'v1/signal', {});
36+
37+
assert.strictEqual(options.fetch, globalThis.fetch);
38+
});
39+
});

0 commit comments

Comments
 (0)