Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(otlp-exporter-base)!: use transport interface in web exporters #4895

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
2 changes: 2 additions & 0 deletions experimental/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ All notable changes to experimental packages in this project will be documented
* feat(exporter-*-otlp*)!: remove environment-variable specific code from browser exporters
* (user-facing) removes the ability to configure browser exporters by using `process.env` polyfills
* feat(sdk-node)!: Automatically configure logs exporter [#4740](https://github.com/open-telemetry/opentelemetry-js/pull/4740)
* feat(exporter-*-otlp-*)!: use transport interface in browser exporters [#4895](https://github.com/open-telemetry/opentelemetry-js/pull/4895) @pichlermarc
* (user-facing) protected `headers` property was intended for internal use has been removed from all exporters

### :rocket: (Enhancement)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -150,22 +150,34 @@ describe('OTLPTraceExporter - web', () => {

collectorTraceExporter.export(spans, () => {});

setTimeout(() => {
const response: any = spyLoggerDebug.args[2][0];
assert.strictEqual(response, 'sendBeacon - can send');
assert.strictEqual(spyLoggerError.args.length, 0);
queueMicrotask(() => {
try {
const response: any = spyLoggerDebug.args[2][0];
assert.strictEqual(response, 'SendBeacon success');
assert.strictEqual(spyLoggerError.args.length, 0);

done();
done();
} catch (e) {
done(e);
}
});
});

it('should log the error message', done => {
stubBeacon.returns(false);

collectorTraceExporter.export(spans, result => {
assert.deepStrictEqual(result.code, ExportResultCode.FAILED);
assert.ok(result.error?.message.includes('cannot send'));
done();
try {
assert.deepStrictEqual(result.code, ExportResultCode.FAILED);
assert.ok(
result.error,
'Expected Error, but no Error was present on the result'
);
assert.match(result.error?.message, /SendBeacon failed/);
done();
} catch (e) {
done(e);
}
});
});
});
Expand All @@ -179,8 +191,8 @@ describe('OTLPTraceExporter - web', () => {
clock = sinon.useFakeTimers();

(window.navigator as any).sendBeacon = false;
collectorTraceExporter = new OTLPTraceExporter(collectorExporterConfig);
server = sinon.fakeServer.create();
collectorTraceExporter = new OTLPTraceExporter(collectorExporterConfig);
});
afterEach(() => {
server.restore();
Expand All @@ -189,15 +201,15 @@ describe('OTLPTraceExporter - web', () => {
it('should successfully send the spans using XMLHttpRequest', done => {
collectorTraceExporter.export(spans, () => {});

queueMicrotask(() => {
queueMicrotask(async () => {
const request = server.requests[0];
assert.strictEqual(request.method, 'POST');
assert.strictEqual(request.url, 'http://foo.bar.com');

const body = request.requestBody;
const body = request.requestBody as Blob;
const decoder = new TextDecoder();
const json = JSON.parse(
decoder.decode(body)
decoder.decode(await body.arrayBuffer())
) as IExportTraceServiceRequest;
const span1 = json.resourceSpans?.[0].scopeSpans?.[0].spans?.[0];

Expand Down Expand Up @@ -235,28 +247,36 @@ describe('OTLPTraceExporter - web', () => {
queueMicrotask(() => {
const request = server.requests[0];
request.respond(200);
const response: any = spyLoggerDebug.args[2][0];
assert.strictEqual(response, 'xhr success');
assert.strictEqual(spyLoggerError.args.length, 0);
assert.strictEqual(stubBeacon.callCount, 0);

clock.restore();
done();
try {
const response: any = spyLoggerDebug.args[2][0];
assert.strictEqual(response, 'XHR success');
assert.strictEqual(spyLoggerError.args.length, 0);
assert.strictEqual(stubBeacon.callCount, 0);
clock.restore();
done();
} catch (e) {
done(e);
}
});
});

it('should log the error message', done => {
collectorTraceExporter.export(spans, result => {
assert.deepStrictEqual(result.code, ExportResultCode.FAILED);
assert.ok(result.error?.message.includes('Failed to export'));
try {
assert.deepStrictEqual(result.code, ExportResultCode.FAILED);
assert.deepStrictEqual(
result.error?.message,
'XHR request failed with non-retryable status'
);
} catch (e) {
done(e);
}
done();
});

queueMicrotask(() => {
const request = server.requests[0];
request.respond(400);
clock.restore();
done();
});
});

Expand Down Expand Up @@ -421,17 +441,20 @@ describe('OTLPTraceExporter - web', () => {
it('should log the timeout request error message', done => {
const responseSpy = sinon.spy();
collectorTraceExporter.export(spans, responseSpy);
clock.tick(10000);
clock.tick(20000);
clock.restore();

setTimeout(() => {
const result = responseSpy.args[0][0] as core.ExportResult;
assert.strictEqual(result.code, core.ExportResultCode.FAILED);
const error = result.error as OTLPExporterError;
assert.ok(error !== undefined);
assert.strictEqual(error.message, 'Request Timeout');

done();
try {
const result = responseSpy.args[0][0] as core.ExportResult;
assert.strictEqual(result.code, core.ExportResultCode.FAILED);
const error = result.error as OTLPExporterError;
assert.ok(error !== undefined);
assert.strictEqual(error.message, 'XHR request timed out');
done();
} catch (e) {
done(e);
}
});
});
});
Expand All @@ -455,15 +478,19 @@ describe('OTLPTraceExporter - web', () => {

setTimeout(() => {
// Expect 4 failures
assert.strictEqual(failures.length, 4);
failures.forEach(([result]) => {
assert.strictEqual(result.code, ExportResultCode.FAILED);
assert.strictEqual(
result.error!.message,
'Concurrent export limit reached'
);
});
done();
try {
assert.strictEqual(failures.length, 4);
failures.forEach(([result]) => {
assert.strictEqual(result.code, ExportResultCode.FAILED);
assert.strictEqual(
result.error!.message,
'Concurrent export limit reached'
);
});
done();
} catch (e) {
done(e);
}
});
});
});
Expand Down Expand Up @@ -514,49 +541,63 @@ describe('export with retry - real http request destroyed', () => {
(window.navigator as any).sendBeacon = false;
collectorTraceExporter = new OTLPTraceExporter(collectorExporterConfig);
});
it('should log the timeout request error message when retrying with exponential backoff with jitter', done => {
it('should log the retryable request error message when retrying with exponential backoff with jitter', done => {
spans = [];
spans.push(Object.assign({}, mockedReadableSpan));

let retry = 0;
let calls = 0;
server.respondWith(
'http://localhost:4318/v1/traces',
function (xhr: any) {
retry++;
calls++;
xhr.respond(503);
}
);

collectorTraceExporter.export(spans, result => {
assert.strictEqual(result.code, core.ExportResultCode.FAILED);
const error = result.error as OTLPExporterError;
assert.ok(error !== undefined);
assert.strictEqual(error.message, 'Request Timeout');
assert.strictEqual(retry, 1);
done();
try {
assert.strictEqual(result.code, core.ExportResultCode.FAILED);
const error = result.error as OTLPExporterError;
assert.ok(error !== undefined);
assert.strictEqual(
error.message,
'Export failed with retryable status'
);
assert.strictEqual(calls, 6);
done();
} catch (e) {
done(e);
}
});
}).timeout(3000);

it('should log the timeout request error message when retry-after header is set to 3 seconds', done => {
spans = [];
spans.push(Object.assign({}, mockedReadableSpan));

let retry = 0;
let calls = 0;
server.respondWith(
'http://localhost:4318/v1/traces',
function (xhr: any) {
retry++;
xhr.respond(503, { 'Retry-After': 3 });
calls++;
xhr.respond(503, { 'Retry-After': 0.1 });
}
);

collectorTraceExporter.export(spans, result => {
assert.strictEqual(result.code, core.ExportResultCode.FAILED);
const error = result.error as OTLPExporterError;
assert.ok(error !== undefined);
assert.strictEqual(error.message, 'Request Timeout');
assert.strictEqual(retry, 1);
done();
try {
assert.strictEqual(result.code, core.ExportResultCode.FAILED);
const error = result.error as OTLPExporterError;
assert.ok(error !== undefined);
assert.strictEqual(
error.message,
'Export failed with retryable status'
);
assert.strictEqual(calls, 6);
done();
} catch (e) {
done(e);
}
});
}).timeout(3000);
it('should log the timeout request error message when retry-after header is a date', done => {
Expand All @@ -569,18 +610,25 @@ describe('export with retry - real http request destroyed', () => {
function (xhr: any) {
retry++;
const d = new Date();
d.setSeconds(d.getSeconds() + 1);
d.setSeconds(d.getSeconds() + 0.1);
xhr.respond(503, { 'Retry-After': d });
}
);

collectorTraceExporter.export(spans, result => {
assert.strictEqual(result.code, core.ExportResultCode.FAILED);
const error = result.error as OTLPExporterError;
assert.ok(error !== undefined);
assert.strictEqual(error.message, 'Request Timeout');
assert.strictEqual(retry, 2);
done();
try {
assert.strictEqual(result.code, core.ExportResultCode.FAILED);
const error = result.error as OTLPExporterError;
assert.ok(error !== undefined);
assert.strictEqual(
error.message,
'Export failed with retryable status'
);
assert.strictEqual(retry, 6);
done();
} catch (e) {
done(e);
}
});
}).timeout(3000);
it('should log the timeout request error message when retry-after header is a date with long delay', done => {
Expand All @@ -599,12 +647,19 @@ describe('export with retry - real http request destroyed', () => {
);

collectorTraceExporter.export(spans, result => {
assert.strictEqual(result.code, core.ExportResultCode.FAILED);
const error = result.error as OTLPExporterError;
assert.ok(error !== undefined);
assert.strictEqual(error.message, 'Request Timeout');
assert.strictEqual(retry, 1);
done();
try {
assert.strictEqual(result.code, core.ExportResultCode.FAILED);
const error = result.error as OTLPExporterError;
assert.ok(error !== undefined);
assert.strictEqual(
error.message,
'Export failed with retryable status'
);
assert.strictEqual(retry, 1);
done();
} catch (e) {
done(e);
}
});
}).timeout(3000);
});
Expand Down
Loading