From 8772ad671a3224f4a67decbbe1411b87c8a65898 Mon Sep 17 00:00:00 2001 From: Muthukumar <75667393+BenzeneAlcohol@users.noreply.github.com> Date: Wed, 17 Jul 2024 06:23:20 +0530 Subject: [PATCH] Allow uploading JSON-per-line OTLP data (#2380) OpenTelemetry specifies that JSON line files can contain valid JSON objects which are seperated with a newline character. The existing readJsonFile function couldn't handle those instances, this PR fixes that, along with test cases to verify. ## Which problem is this PR solving? Resolves #2225 ## Description of the changes A new try catch block was inserted, for files that weren't parsed by the usual JSON.parse, would be parsed by the catch block assuming the objects are seperated by newline (which is mentioned in opentelemetry) ## How was this change tested? Test cases, and manual testing ## Checklist - [ X ] I have read https://github.com/jaegertracing/jaeger/blob/master/CONTRIBUTING_GUIDELINES.md - [ X ] I have signed all commits - [ X ] I have added unit tests for the new functionality - [ X ] I have run lint and test steps successfully - for `jaeger`: `make lint test` - for `jaeger-ui`: `yarn lint` and `yarn test` --------- Signed-off-by: Muthukumar Signed-off-by: Yuri Shkuro Co-authored-by: Yuri Shkuro --- .../utils/fixtures/oltp2jaeger-multi-out.json | 524 ++++++++++++++++++ .../otlp2jaeger-multi-in-combined.json | 202 +++++++ .../fixtures/otlp2jaeger-multi-in.json.txt | 4 + .../jaeger-ui/src/utils/readJsonFile.test.js | 35 +- packages/jaeger-ui/src/utils/readJsonFile.tsx | 59 +- 5 files changed, 805 insertions(+), 19 deletions(-) create mode 100644 packages/jaeger-ui/src/utils/fixtures/oltp2jaeger-multi-out.json create mode 100644 packages/jaeger-ui/src/utils/fixtures/otlp2jaeger-multi-in-combined.json create mode 100644 packages/jaeger-ui/src/utils/fixtures/otlp2jaeger-multi-in.json.txt diff --git a/packages/jaeger-ui/src/utils/fixtures/oltp2jaeger-multi-out.json b/packages/jaeger-ui/src/utils/fixtures/oltp2jaeger-multi-out.json new file mode 100644 index 0000000000..6e37231af0 --- /dev/null +++ b/packages/jaeger-ui/src/utils/fixtures/oltp2jaeger-multi-out.json @@ -0,0 +1,524 @@ +{ + "data": [ + { + "traceID": "c620759e5d60fafb8ee0922b30e06bc6", + "spans": [ + { + "traceID": "c620759e5d60fafb8ee0922b30e06bc6", + "spanID": "26f5ef9dbb885479", + "operationName": "ohboy.do", + "references": [ + { + "refType": "CHILD_OF", + "traceID": "c620759e5d60fafb8ee0922b30e06bc6", + "spanID": "49b8e9efa1e8a3b1", + "span": { + "traceID": "c620759e5d60fafb8ee0922b30e06bc6", + "spanID": "49b8e9efa1e8a3b1", + "operationName": "info", + "references": [ + { + "refType": "CHILD_OF", + "traceID": "c620759e5d60fafb8ee0922b30e06bc6", + "spanID": "eff3153be6b1fb93", + "span": { + "traceID": "c620759e5d60fafb8ee0922b30e06bc6", + "spanID": "eff3153be6b1fb93", + "operationName": "main", + "references": [], + "startTime": 1711138246940000, + "duration": 176321, + "tags": [ + { + "key": "otel.library.name", + "type": "string", + "value": "widgets" + }, + { + "key": "otel.library.version", + "type": "string", + "value": "1.2.3" + }, + { + "key": "span.kind", + "type": "string", + "value": "internal" + } + ], + "logs": [], + "processID": "p1", + "warnings": [], + "process": { + "serviceName": "example-trace", + "tags": [ + { + "key": "telemetry.sdk.language", + "type": "string", + "value": "nodejs" + }, + { + "key": "telemetry.sdk.name", + "type": "string", + "value": "opentelemetry" + }, + { + "key": "telemetry.sdk.version", + "type": "string", + "value": "1.22.0" + } + ] + }, + "relativeStartTime": 0, + "depth": 0, + "hasChildren": true, + "childSpanIds": ["49b8e9efa1e8a3b1"] + } + } + ], + "startTime": 1711138246943000, + "duration": 173150, + "tags": [ + { + "key": "otel.library.name", + "type": "string", + "value": "widgets" + }, + { + "key": "otel.library.version", + "type": "string", + "value": "1.2.3" + }, + { + "key": "span.kind", + "type": "string", + "value": "internal" + } + ], + "logs": [], + "processID": "p1", + "warnings": [], + "process": { + "serviceName": "example-trace", + "tags": [ + { + "key": "telemetry.sdk.language", + "type": "string", + "value": "nodejs" + }, + { + "key": "telemetry.sdk.name", + "type": "string", + "value": "opentelemetry" + }, + { + "key": "telemetry.sdk.version", + "type": "string", + "value": "1.22.0" + } + ] + }, + "relativeStartTime": 3000, + "depth": 1, + "hasChildren": true, + "childSpanIds": ["e76628f4e6dde174", "26f5ef9dbb885479"] + } + } + ], + "startTime": 1711138246978000, + "duration": 46460, + "tags": [ + { + "key": "otel.library.name", + "type": "string", + "value": "ohboy" + }, + { + "key": "span.kind", + "type": "string", + "value": "internal" + } + ], + "logs": [], + "processID": "p1", + "warnings": [], + "process": { + "serviceName": "example-trace", + "tags": [ + { + "key": "telemetry.sdk.language", + "type": "string", + "value": "nodejs" + }, + { + "key": "telemetry.sdk.name", + "type": "string", + "value": "opentelemetry" + }, + { + "key": "telemetry.sdk.version", + "type": "string", + "value": "1.22.0" + } + ] + }, + "relativeStartTime": 38000, + "depth": 2, + "hasChildren": false, + "childSpanIds": [] + }, + { + "traceID": "c620759e5d60fafb8ee0922b30e06bc6", + "spanID": "e76628f4e6dde174", + "operationName": "ohboy.do", + "references": [ + { + "refType": "CHILD_OF", + "traceID": "c620759e5d60fafb8ee0922b30e06bc6", + "spanID": "49b8e9efa1e8a3b1", + "span": { + "traceID": "c620759e5d60fafb8ee0922b30e06bc6", + "spanID": "49b8e9efa1e8a3b1", + "operationName": "info", + "references": [ + { + "refType": "CHILD_OF", + "traceID": "c620759e5d60fafb8ee0922b30e06bc6", + "spanID": "eff3153be6b1fb93", + "span": { + "traceID": "c620759e5d60fafb8ee0922b30e06bc6", + "spanID": "eff3153be6b1fb93", + "operationName": "main", + "references": [], + "startTime": 1711138246940000, + "duration": 176321, + "tags": [ + { + "key": "otel.library.name", + "type": "string", + "value": "widgets" + }, + { + "key": "otel.library.version", + "type": "string", + "value": "1.2.3" + }, + { + "key": "span.kind", + "type": "string", + "value": "internal" + } + ], + "logs": [], + "processID": "p1", + "warnings": [], + "process": { + "serviceName": "example-trace", + "tags": [ + { + "key": "telemetry.sdk.language", + "type": "string", + "value": "nodejs" + }, + { + "key": "telemetry.sdk.name", + "type": "string", + "value": "opentelemetry" + }, + { + "key": "telemetry.sdk.version", + "type": "string", + "value": "1.22.0" + } + ] + }, + "relativeStartTime": 0, + "depth": 0, + "hasChildren": true, + "childSpanIds": ["49b8e9efa1e8a3b1"] + } + } + ], + "startTime": 1711138246943000, + "duration": 173150, + "tags": [ + { + "key": "otel.library.name", + "type": "string", + "value": "widgets" + }, + { + "key": "otel.library.version", + "type": "string", + "value": "1.2.3" + }, + { + "key": "span.kind", + "type": "string", + "value": "internal" + } + ], + "logs": [], + "processID": "p1", + "warnings": [], + "process": { + "serviceName": "example-trace", + "tags": [ + { + "key": "telemetry.sdk.language", + "type": "string", + "value": "nodejs" + }, + { + "key": "telemetry.sdk.name", + "type": "string", + "value": "opentelemetry" + }, + { + "key": "telemetry.sdk.version", + "type": "string", + "value": "1.22.0" + } + ] + }, + "relativeStartTime": 3000, + "depth": 1, + "hasChildren": true, + "childSpanIds": ["e76628f4e6dde174", "26f5ef9dbb885479"] + } + } + ], + "startTime": 1711138247025000, + "duration": 71355, + "tags": [ + { + "key": "otel.library.name", + "type": "string", + "value": "ohboy" + }, + { + "key": "span.kind", + "type": "string", + "value": "internal" + } + ], + "logs": [], + "processID": "p1", + "warnings": [], + "process": { + "serviceName": "example-trace", + "tags": [ + { + "key": "telemetry.sdk.language", + "type": "string", + "value": "nodejs" + }, + { + "key": "telemetry.sdk.name", + "type": "string", + "value": "opentelemetry" + }, + { + "key": "telemetry.sdk.version", + "type": "string", + "value": "1.22.0" + } + ] + }, + "relativeStartTime": 85000, + "depth": 2, + "hasChildren": false, + "childSpanIds": [] + }, + { + "traceID": "c620759e5d60fafb8ee0922b30e06bc6", + "spanID": "49b8e9efa1e8a3b1", + "operationName": "info", + "references": [ + { + "refType": "CHILD_OF", + "traceID": "c620759e5d60fafb8ee0922b30e06bc6", + "spanID": "eff3153be6b1fb93", + "span": { + "traceID": "c620759e5d60fafb8ee0922b30e06bc6", + "spanID": "eff3153be6b1fb93", + "operationName": "main", + "references": [], + "startTime": 1711138246940000, + "duration": 176321, + "tags": [ + { + "key": "otel.library.name", + "type": "string", + "value": "widgets" + }, + { + "key": "otel.library.version", + "type": "string", + "value": "1.2.3" + }, + { + "key": "span.kind", + "type": "string", + "value": "internal" + } + ], + "logs": [], + "processID": "p1", + "warnings": [], + "process": { + "serviceName": "example-trace", + "tags": [ + { + "key": "telemetry.sdk.language", + "type": "string", + "value": "nodejs" + }, + { + "key": "telemetry.sdk.name", + "type": "string", + "value": "opentelemetry" + }, + { + "key": "telemetry.sdk.version", + "type": "string", + "value": "1.22.0" + } + ] + }, + "relativeStartTime": 0, + "depth": 0, + "hasChildren": true, + "childSpanIds": ["49b8e9efa1e8a3b1"] + } + } + ], + "startTime": 1711138246943000, + "duration": 173150, + "tags": [ + { + "key": "otel.library.name", + "type": "string", + "value": "widgets" + }, + { + "key": "otel.library.version", + "type": "string", + "value": "1.2.3" + }, + { + "key": "span.kind", + "type": "string", + "value": "internal" + } + ], + "logs": [], + "processID": "p1", + "warnings": [], + "process": { + "serviceName": "example-trace", + "tags": [ + { + "key": "telemetry.sdk.language", + "type": "string", + "value": "nodejs" + }, + { + "key": "telemetry.sdk.name", + "type": "string", + "value": "opentelemetry" + }, + { + "key": "telemetry.sdk.version", + "type": "string", + "value": "1.22.0" + } + ] + }, + "relativeStartTime": 3000, + "depth": 1, + "hasChildren": true, + "childSpanIds": ["e76628f4e6dde174", "26f5ef9dbb885479"] + }, + { + "traceID": "c620759e5d60fafb8ee0922b30e06bc6", + "spanID": "eff3153be6b1fb93", + "operationName": "main", + "references": [], + "startTime": 1711138246940000, + "duration": 176321, + "tags": [ + { + "key": "otel.library.name", + "type": "string", + "value": "widgets" + }, + { + "key": "otel.library.version", + "type": "string", + "value": "1.2.3" + }, + { + "key": "span.kind", + "type": "string", + "value": "internal" + } + ], + "logs": [], + "processID": "p1", + "warnings": [], + "process": { + "serviceName": "example-trace", + "tags": [ + { + "key": "telemetry.sdk.language", + "type": "string", + "value": "nodejs" + }, + { + "key": "telemetry.sdk.name", + "type": "string", + "value": "opentelemetry" + }, + { + "key": "telemetry.sdk.version", + "type": "string", + "value": "1.22.0" + } + ] + }, + "relativeStartTime": 0, + "depth": 0, + "hasChildren": true, + "childSpanIds": ["49b8e9efa1e8a3b1"] + } + ], + "processes": { + "p1": { + "serviceName": "example-trace", + "tags": [ + { + "key": "telemetry.sdk.language", + "type": "string", + "value": "nodejs" + }, + { + "key": "telemetry.sdk.name", + "type": "string", + "value": "opentelemetry" + }, + { + "key": "telemetry.sdk.version", + "type": "string", + "value": "1.22.0" + } + ] + } + }, + "warnings": null + } + ], + "total": 0, + "limit": 0, + "offset": 0, + "errors": null +} diff --git a/packages/jaeger-ui/src/utils/fixtures/otlp2jaeger-multi-in-combined.json b/packages/jaeger-ui/src/utils/fixtures/otlp2jaeger-multi-in-combined.json new file mode 100644 index 0000000000..eb2bc1ae7c --- /dev/null +++ b/packages/jaeger-ui/src/utils/fixtures/otlp2jaeger-multi-in-combined.json @@ -0,0 +1,202 @@ +{ + "resourceSpans": [ + { + "resource": { + "attributes": [ + { + "key": "service.name", + "value": { + "stringValue": "example-trace" + } + }, + { + "key": "telemetry.sdk.language", + "value": { + "stringValue": "nodejs" + } + }, + { + "key": "telemetry.sdk.name", + "value": { + "stringValue": "opentelemetry" + } + }, + { + "key": "telemetry.sdk.version", + "value": { + "stringValue": "1.22.0" + } + } + ] + }, + "scopeSpans": [ + { + "scope": { + "name": "ohboy" + }, + "spans": [ + { + "traceId": "c620759e5d60fafb8ee0922b30e06bc6", + "spanId": "26f5ef9dbb885479", + "parentSpanId": "49b8e9efa1e8a3b1", + "name": "ohboy.do", + "kind": 1, + "startTimeUnixNano": "1711138246978000000", + "endTimeUnixNano": "1711138247024460625", + "status": {} + } + ] + } + ] + }, + { + "resource": { + "attributes": [ + { + "key": "service.name", + "value": { + "stringValue": "example-trace" + } + }, + { + "key": "telemetry.sdk.language", + "value": { + "stringValue": "nodejs" + } + }, + { + "key": "telemetry.sdk.name", + "value": { + "stringValue": "opentelemetry" + } + }, + { + "key": "telemetry.sdk.version", + "value": { + "stringValue": "1.22.0" + } + } + ] + }, + "scopeSpans": [ + { + "scope": { + "name": "ohboy" + }, + "spans": [ + { + "traceId": "c620759e5d60fafb8ee0922b30e06bc6", + "spanId": "e76628f4e6dde174", + "parentSpanId": "49b8e9efa1e8a3b1", + "name": "ohboy.do", + "kind": 1, + "startTimeUnixNano": "1711138247025000000", + "endTimeUnixNano": "1711138247096355250", + "status": {} + } + ] + } + ] + }, + { + "resource": { + "attributes": [ + { + "key": "service.name", + "value": { + "stringValue": "example-trace" + } + }, + { + "key": "telemetry.sdk.language", + "value": { + "stringValue": "nodejs" + } + }, + { + "key": "telemetry.sdk.name", + "value": { + "stringValue": "opentelemetry" + } + }, + { + "key": "telemetry.sdk.version", + "value": { + "stringValue": "1.22.0" + } + } + ] + }, + "scopeSpans": [ + { + "scope": { + "name": "widgets", + "version": "1.2.3" + }, + "spans": [ + { + "traceId": "c620759e5d60fafb8ee0922b30e06bc6", + "spanId": "49b8e9efa1e8a3b1", + "parentSpanId": "eff3153be6b1fb93", + "name": "info", + "kind": 1, + "startTimeUnixNano": "1711138246943000000", + "endTimeUnixNano": "1711138247116150875", + "status": {} + } + ] + } + ] + }, + { + "resource": { + "attributes": [ + { + "key": "service.name", + "value": { + "stringValue": "example-trace" + } + }, + { + "key": "telemetry.sdk.language", + "value": { + "stringValue": "nodejs" + } + }, + { + "key": "telemetry.sdk.name", + "value": { + "stringValue": "opentelemetry" + } + }, + { + "key": "telemetry.sdk.version", + "value": { + "stringValue": "1.22.0" + } + } + ] + }, + "scopeSpans": [ + { + "scope": { + "name": "widgets", + "version": "1.2.3" + }, + "spans": [ + { + "traceId": "c620759e5d60fafb8ee0922b30e06bc6", + "spanId": "eff3153be6b1fb93", + "parentSpanId": "", + "name": "main", + "kind": 1, + "startTimeUnixNano": "1711138246940000000", + "endTimeUnixNano": "1711138247116321958", + "status": {} + } + ] + } + ] + } + ] +} diff --git a/packages/jaeger-ui/src/utils/fixtures/otlp2jaeger-multi-in.json.txt b/packages/jaeger-ui/src/utils/fixtures/otlp2jaeger-multi-in.json.txt new file mode 100644 index 0000000000..5c92bedab2 --- /dev/null +++ b/packages/jaeger-ui/src/utils/fixtures/otlp2jaeger-multi-in.json.txt @@ -0,0 +1,4 @@ +{"resourceSpans":[{"resource":{"attributes":[{"key":"service.name","value":{"stringValue":"example-trace"}},{"key":"telemetry.sdk.language","value":{"stringValue":"nodejs"}},{"key":"telemetry.sdk.name","value":{"stringValue":"opentelemetry"}},{"key":"telemetry.sdk.version","value":{"stringValue":"1.22.0"}}]},"scopeSpans":[{"scope":{"name":"ohboy"},"spans":[{"traceId":"c620759e5d60fafb8ee0922b30e06bc6","spanId":"26f5ef9dbb885479","parentSpanId":"49b8e9efa1e8a3b1","name":"ohboy.do","kind":1,"startTimeUnixNano":"1711138246978000000","endTimeUnixNano":"1711138247024460625","status":{}}]}]}]} +{"resourceSpans":[{"resource":{"attributes":[{"key":"service.name","value":{"stringValue":"example-trace"}},{"key":"telemetry.sdk.language","value":{"stringValue":"nodejs"}},{"key":"telemetry.sdk.name","value":{"stringValue":"opentelemetry"}},{"key":"telemetry.sdk.version","value":{"stringValue":"1.22.0"}}]},"scopeSpans":[{"scope":{"name":"ohboy"},"spans":[{"traceId":"c620759e5d60fafb8ee0922b30e06bc6","spanId":"e76628f4e6dde174","parentSpanId":"49b8e9efa1e8a3b1","name":"ohboy.do","kind":1,"startTimeUnixNano":"1711138247025000000","endTimeUnixNano":"1711138247096355250","status":{}}]}]}]} +{"resourceSpans":[{"resource":{"attributes":[{"key":"service.name","value":{"stringValue":"example-trace"}},{"key":"telemetry.sdk.language","value":{"stringValue":"nodejs"}},{"key":"telemetry.sdk.name","value":{"stringValue":"opentelemetry"}},{"key":"telemetry.sdk.version","value":{"stringValue":"1.22.0"}}]},"scopeSpans":[{"scope":{"name":"widgets","version":"1.2.3"},"spans":[{"traceId":"c620759e5d60fafb8ee0922b30e06bc6","spanId":"49b8e9efa1e8a3b1","parentSpanId":"eff3153be6b1fb93","name":"info","kind":1,"startTimeUnixNano":"1711138246943000000","endTimeUnixNano":"1711138247116150875","status":{}}]}]}]} +{"resourceSpans":[{"resource":{"attributes":[{"key":"service.name","value":{"stringValue":"example-trace"}},{"key":"telemetry.sdk.language","value":{"stringValue":"nodejs"}},{"key":"telemetry.sdk.name","value":{"stringValue":"opentelemetry"}},{"key":"telemetry.sdk.version","value":{"stringValue":"1.22.0"}}]},"scopeSpans":[{"scope":{"name":"widgets","version":"1.2.3"},"spans":[{"traceId":"c620759e5d60fafb8ee0922b30e06bc6","spanId":"eff3153be6b1fb93","parentSpanId":"","name":"main","kind":1,"startTimeUnixNano":"1711138246940000000","endTimeUnixNano":"1711138247116321958","status":{}}]}]}]} diff --git a/packages/jaeger-ui/src/utils/readJsonFile.test.js b/packages/jaeger-ui/src/utils/readJsonFile.test.js index 865cd33241..7c910a8493 100644 --- a/packages/jaeger-ui/src/utils/readJsonFile.test.js +++ b/packages/jaeger-ui/src/utils/readJsonFile.test.js @@ -17,12 +17,29 @@ import lodash from 'lodash'; import readJsonFile from './readJsonFile'; import JaegerAPI from '../api/jaeger'; +let OTLPTrace; +let jaegerTrace; +let OTLPTraceMulti; +let jaegerTraceMulti; + +beforeAll(() => { + OTLPTrace = JSON.parse(fs.readFileSync('src/utils/fixtures/otlp2jaeger-in.json', 'utf-8')); + jaegerTrace = JSON.parse(fs.readFileSync('src/utils/fixtures/otlp2jaeger-out.json', 'utf-8')); + OTLPTraceMulti = JSON.parse( + fs.readFileSync('src/utils/fixtures/otlp2jaeger-multi-in-combined.json', 'utf-8') + ); + jaegerTraceMulti = JSON.parse(fs.readFileSync('src/utils/fixtures/oltp2jaeger-multi-out.json', 'utf-8')); +}); + jest.spyOn(JaegerAPI, 'transformOTLP').mockImplementation(APICallRequest => { - const OTLPTrace = JSON.parse(fs.readFileSync('src/utils/fixtures/otlp2jaeger-in.json', 'utf-8')); - const jaegerTrace = JSON.parse(fs.readFileSync('src/utils/fixtures/otlp2jaeger-out.json', 'utf-8')); if (lodash.isEqual(APICallRequest, OTLPTrace)) { return Promise.resolve(jaegerTrace); } + + if (lodash.isEqual(APICallRequest, OTLPTraceMulti)) { + return Promise.resolve(jaegerTraceMulti); + } + // This defines case where API call errors out even after detecting a `resourceSpan` in the request return Promise.reject(); }); @@ -53,8 +70,8 @@ describe('fileReader.readJsonFile', () => { }); it('loads JSON data (OTLP), successfully', () => { - const inObj = JSON.parse(fs.readFileSync('src/utils/fixtures/otlp2jaeger-in.json', 'utf-8')); - const outObj = JSON.parse(fs.readFileSync('src/utils/fixtures/otlp2jaeger-out.json', 'utf-8')); + const inObj = OTLPTrace; + const outObj = jaegerTrace; const file = new File([JSON.stringify(inObj)], 'foo.json'); const p = readJsonFile({ file }); return expect(p).resolves.toMatchObject(outObj); @@ -67,9 +84,17 @@ describe('fileReader.readJsonFile', () => { return expect(p).rejects.toMatchObject(expect.any(Error)); }); - it('rejects on malform JSON', () => { + it('rejects malformed JSON', () => { const file = new File(['not-json'], 'foo.json'); const p = readJsonFile({ file }); return expect(p).rejects.toMatchObject(expect.any(Error)); }); + + it('loads JSON-per-line data', () => { + const expectedOutput = jaegerTraceMulti; + const fileContent = fs.readFileSync('src/utils/fixtures/otlp2jaeger-multi-in.json.txt', 'utf-8'); + const file = new File([fileContent], 'multi.json', { type: 'application/json' }); + const p = readJsonFile({ file }); + return expect(p).resolves.toMatchObject(expectedOutput); + }); }); diff --git a/packages/jaeger-ui/src/utils/readJsonFile.tsx b/packages/jaeger-ui/src/utils/readJsonFile.tsx index be404c56be..d680c0df33 100644 --- a/packages/jaeger-ui/src/utils/readJsonFile.tsx +++ b/packages/jaeger-ui/src/utils/readJsonFile.tsx @@ -14,6 +14,22 @@ import JaegerAPI from '../api/jaeger'; +function tryParseMultiLineInput(input: string): any[] { + const jsonStrings = input.split('\n').filter((line: string) => line.trim() !== ''); + const parsedObjects: any[] = []; + + jsonStrings.forEach((jsonString: string, index: number) => { + try { + const traceObj = JSON.parse(jsonString.trim()); + parsedObjects.push(traceObj); + } catch (error) { + throw new Error(`Error parsing JSON at line ${index + 1}: ${(error as Error).message}`); + } + }); + + return parsedObjects; +} + export default function readJsonFile(fileList: { file: File }): Promise { return new Promise((resolve, reject) => { const reader = new FileReader(); @@ -22,21 +38,36 @@ export default function readJsonFile(fileList: { file: File }): Promise reject(new Error('Invalid result type')); return; } + let traceObj; try { - const traceObj = JSON.parse(reader.result); - if ('resourceSpans' in traceObj) { - JaegerAPI.transformOTLP(traceObj) - .then((result: string) => { - resolve(result); - }) - .catch(() => { - reject(new Error(`Error converting traces to OTLP`)); - }); - } else { - resolve(traceObj); + traceObj = JSON.parse(reader.result); + } catch (error) { + try { + traceObj = tryParseMultiLineInput(reader.result); + } catch (error) { + reject(error); + return; } - } catch (error: unknown) { - reject(new Error(`Error parsing JSON: ${(error as Error).message}`)); + } + if (Array.isArray(traceObj) && traceObj.every(obj => 'resourceSpans' in obj)) { + const mergedResourceSpans = traceObj.reduce((acc, obj) => { + acc.push(...obj.resourceSpans); + return acc; + }, []); + + traceObj = { resourceSpans: mergedResourceSpans }; + } + + if ('resourceSpans' in traceObj) { + JaegerAPI.transformOTLP(traceObj) + .then((result: string) => { + resolve(result); + }) + .catch(() => { + reject(new Error('Error converting traces to OTLP')); + }); + } else { + resolve(traceObj); } }; reader.onerror = () => { @@ -48,7 +79,7 @@ export default function readJsonFile(fileList: { file: File }): Promise }; try { reader.readAsText(fileList.file); - } catch (error: unknown) { + } catch (error) { reject(new Error(`Error reading the JSON file: ${(error as Error).message}`)); } });