Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
1 change: 1 addition & 0 deletions experimental/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ feat(configuration): parse config for rc 3 [#6304](https://github.com/open-telem

* fix(exporter-prometheus): add missing `@opentelemetry/semantic-conventions` dependency [#6330](https://github.com/open-telemetry/opentelemetry-js/pull/6330) @omizha
* fix(otlp-transformer): correctly handle Uint8Array attribute values when serializing to JSON [#6348](https://github.com/open-telemetry/opentelemetry-js/pull/6348) @pichlermarc
* fix(otlp-exporter-base): fix unwanted instrumentation of the fetch exports when context is not propagated [#6353](https://github.com/open-telemetry/opentelemetry-js/pull/6353) @david-luna

### :books: Documentation

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,10 +38,23 @@ class FetchTransport implements IExporterTransport {
async send(data: Uint8Array, timeoutMillis: number): Promise<ExportResponse> {
const abortController = new AbortController();
const timeout = setTimeout(() => abortController.abort(), timeoutMillis);
// Fetch API may be wrapped by an instrumentation like `@opentelemetry/instrumentation-fetch`.
// In that case the instrumentation would create a new Span for this request
// because the context manager cannot keep the context after `await` calls.
// This creates an indirect endless loop Export -> Span -> Export
// By using the `__original` function the instrumentation can't intercept the call
// and no Span will be created breaking the vicious cycle
let fetchApi = globalThis.fetch;
// @ts-expect-error -- fetch could be wrapped
if (typeof fetchApi.__original === 'function') {
// @ts-expect-error -- fetch could be wrapped
fetchApi = fetchApi.__original;
}

try {
const isBrowserEnvironment = !!globalThis.location;
const url = new URL(this._parameters.url);
const response = await fetch(url.href, {
const response = await fetchApi(url.href, {
method: 'POST',
headers: await this._parameters.headers(),
body: data,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,30 @@ describe('FetchTransport', function () {
});

describe('send', function () {
it('it uses global fetch API and is not affected by patching', function (done) {
// arrange
const fetchStub = sinon
.stub(globalThis, 'fetch')
.resolves(new Response('test response', { status: 200 }));
const transport = createFetchTransport(testTransportParameters);
// We patch fetch simulating what an instrumentation would do
const patchedStub = sinon.stub().callsFake(fetchStub);
globalThis.fetch = patchedStub;
(globalThis.fetch as any).__original = fetchStub;

//act
transport.send(testPayload, requestTimeout).then(response => {
// assert
try {
assert.strictEqual(response.status, 'success');
sinon.assert.notCalled(patchedStub);
sinon.assert.called(fetchStub);
} catch (e) {
done(e);
}
done();
}, done /* catch any rejections */);
});
it('returns success when request succeeds', function (done) {
// arrange
const fetchStub = sinon
Expand Down
Loading