From de3f5934fb45a908b94e836ebba7c2a1379613c0 Mon Sep 17 00:00:00 2001 From: Muthukumar Date: Sat, 13 Jul 2024 12:55:37 +0530 Subject: [PATCH 1/7] Resolves #2225 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. Signed-off-by: Muthukumar --- .../utils/fixtures/otlp2jaeger-multi-in.json | 4 ++ .../jaeger-ui/src/utils/readJsonFile.test.js | 13 +++++ packages/jaeger-ui/src/utils/readJsonFile.tsx | 51 ++++++++++++++----- 3 files changed, 54 insertions(+), 14 deletions(-) create mode 100644 packages/jaeger-ui/src/utils/fixtures/otlp2jaeger-multi-in.json diff --git a/packages/jaeger-ui/src/utils/fixtures/otlp2jaeger-multi-in.json b/packages/jaeger-ui/src/utils/fixtures/otlp2jaeger-multi-in.json new file mode 100644 index 0000000000..5c92bedab2 --- /dev/null +++ b/packages/jaeger-ui/src/utils/fixtures/otlp2jaeger-multi-in.json @@ -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..a479ed6eae 100644 --- a/packages/jaeger-ui/src/utils/readJsonFile.test.js +++ b/packages/jaeger-ui/src/utils/readJsonFile.test.js @@ -72,4 +72,17 @@ describe('fileReader.readJsonFile', () => { const p = readJsonFile({ file }); return expect(p).rejects.toMatchObject(expect.any(Error)); }); + + it('loads multiple JSON data', () => { + let fileContent; + try { + fileContent = fs.readFileSync('src/utils/fixtures/otlp2jaeger-multi-in.json', 'utf-8'); + } catch (err) { + console.error('Error reading file:', err); + throw err; + } + const file = new File([fileContent], 'multi.json', { type: 'application/json' }); + const p = readJsonFile({ file }); + expect(p).resolves.toBeDefined(); + }); }); diff --git a/packages/jaeger-ui/src/utils/readJsonFile.tsx b/packages/jaeger-ui/src/utils/readJsonFile.tsx index be404c56be..f4173b1814 100644 --- a/packages/jaeger-ui/src/utils/readJsonFile.tsx +++ b/packages/jaeger-ui/src/utils/readJsonFile.tsx @@ -23,20 +23,43 @@ export default function readJsonFile(fileList: { file: File }): Promise return; } 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); + const resourceSpansArray: any[] = []; + let combinedTraceObj: { resourceSpans: any[] } = { resourceSpans: [] }; + + try { + // Attempt to parse the entire content as a single JSON object + const traceObj = JSON.parse(reader.result); + if ('resourceSpans' in traceObj) { + resourceSpansArray.push(...traceObj.resourceSpans); + } else { + resolve(traceObj); + } + } catch (error) { + // If parsing fails, handle multi-line JSON objects + const jsonStrings = reader.result.split('\n').filter(line => line.trim() !== ''); + jsonStrings.forEach(jsonString => { + try { + const traceObj = JSON.parse(jsonString.trim()); + if ('resourceSpans' in traceObj) { + resourceSpansArray.push(...traceObj.resourceSpans); + } + } catch (error) { + throw new Error(`Error parsing JSON: ${(error as Error).message}`); + } + }); } - } catch (error: unknown) { - reject(new Error(`Error parsing JSON: ${(error as Error).message}`)); + + combinedTraceObj = { resourceSpans: resourceSpansArray }; + + JaegerAPI.transformOTLP(combinedTraceObj) + .then((result: string) => { + resolve(result); + }) + .catch(() => { + reject(new Error('Error converting traces to OTLP')); + }); + } catch (error) { + reject(new Error(`Error processing JSON: ${(error as Error).message}`)); } }; reader.onerror = () => { @@ -48,7 +71,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}`)); } }); From 01079d59282f27dfb1f0c40ce4a350143067cbdb Mon Sep 17 00:00:00 2001 From: Muthukumar Date: Mon, 15 Jul 2024 00:07:37 +0530 Subject: [PATCH 2/7] Resolves #2225 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. Signed-off-by: Muthukumar --- packages/jaeger-ui/src/utils/readJsonFile.tsx | 65 +++++++++++-------- 1 file changed, 39 insertions(+), 26 deletions(-) diff --git a/packages/jaeger-ui/src/utils/readJsonFile.tsx b/packages/jaeger-ui/src/utils/readJsonFile.tsx index f4173b1814..996a90462f 100644 --- a/packages/jaeger-ui/src/utils/readJsonFile.tsx +++ b/packages/jaeger-ui/src/utils/readJsonFile.tsx @@ -14,6 +14,24 @@ import JaegerAPI from '../api/jaeger'; +function tryParseMultiLineInput(input: any) { + const jsonStrings: any[] = input.split('\n').filter((line: string) => line.trim() !== ''); + const resourceSpansArray: any[] = []; + + jsonStrings.forEach((jsonString: string) => { + try { + const traceObj = JSON.parse(jsonString.trim()); + if ('resourceSpans' in traceObj) { + resourceSpansArray.push(...traceObj.resourceSpans); + } + } catch (error) { + throw new Error(`Error parsing JSON: ${(error as Error).message}`); + } + }); + + return { resourceSpans: resourceSpansArray }; +} + export default function readJsonFile(fileList: { file: File }): Promise { return new Promise((resolve, reject) => { const reader = new FileReader(); @@ -22,44 +40,39 @@ export default function readJsonFile(fileList: { file: File }): Promise reject(new Error('Invalid result type')); return; } + let traceObj; try { - const resourceSpansArray: any[] = []; - let combinedTraceObj: { resourceSpans: any[] } = { resourceSpans: [] }; - + traceObj = JSON.parse(reader.result); + } catch (error) { + // try to interpret input as JSON-per-line try { - // Attempt to parse the entire content as a single JSON object - const traceObj = JSON.parse(reader.result); - if ('resourceSpans' in traceObj) { - resourceSpansArray.push(...traceObj.resourceSpans); - } else { - resolve(traceObj); - } + traceObj = tryParseMultiLineInput(reader.result); } catch (error) { - // If parsing fails, handle multi-line JSON objects - const jsonStrings = reader.result.split('\n').filter(line => line.trim() !== ''); - jsonStrings.forEach(jsonString => { - try { - const traceObj = JSON.parse(jsonString.trim()); - if ('resourceSpans' in traceObj) { - resourceSpansArray.push(...traceObj.resourceSpans); - } - } catch (error) { - throw new Error(`Error parsing JSON: ${(error as Error).message}`); - } - }); + reject(new Error(`Error processing JSON: ${(error as Error).message}`)); + return; } + } + // 1. check if array and transform to valid OTLP + if (Array.isArray(traceObj) && traceObj.every(obj => 'resourceSpans' in obj)) { + const mergedResourceSpans = traceObj.reduce((acc, obj) => { + acc.push(...obj.resourceSpans); + return acc; + }, []); - combinedTraceObj = { resourceSpans: resourceSpansArray }; + traceObj = { resourceSpans: mergedResourceSpans }; + } - JaegerAPI.transformOTLP(combinedTraceObj) + // 2. check if OTLP and transform to Jaeger model + if ('resourceSpans' in traceObj) { + JaegerAPI.transformOTLP(traceObj) .then((result: string) => { resolve(result); }) .catch(() => { reject(new Error('Error converting traces to OTLP')); }); - } catch (error) { - reject(new Error(`Error processing JSON: ${(error as Error).message}`)); + } else { + resolve(traceObj); } }; reader.onerror = () => { From 1e8eda9d7bad45ac7091dc32bc422d85b9599f9d Mon Sep 17 00:00:00 2001 From: Muthukumar Date: Mon, 15 Jul 2024 00:38:16 +0530 Subject: [PATCH 3/7] Fixes #2225 Signed-off-by: Muthukumar --- ...ulti-in.json => otlp2jaeger-multi-in.json.txt} | 0 packages/jaeger-ui/src/utils/readJsonFile.test.js | 2 +- packages/jaeger-ui/src/utils/readJsonFile.tsx | 15 +++++---------- 3 files changed, 6 insertions(+), 11 deletions(-) rename packages/jaeger-ui/src/utils/fixtures/{otlp2jaeger-multi-in.json => otlp2jaeger-multi-in.json.txt} (100%) diff --git a/packages/jaeger-ui/src/utils/fixtures/otlp2jaeger-multi-in.json b/packages/jaeger-ui/src/utils/fixtures/otlp2jaeger-multi-in.json.txt similarity index 100% rename from packages/jaeger-ui/src/utils/fixtures/otlp2jaeger-multi-in.json rename to packages/jaeger-ui/src/utils/fixtures/otlp2jaeger-multi-in.json.txt diff --git a/packages/jaeger-ui/src/utils/readJsonFile.test.js b/packages/jaeger-ui/src/utils/readJsonFile.test.js index a479ed6eae..b2d5f457bf 100644 --- a/packages/jaeger-ui/src/utils/readJsonFile.test.js +++ b/packages/jaeger-ui/src/utils/readJsonFile.test.js @@ -76,7 +76,7 @@ describe('fileReader.readJsonFile', () => { it('loads multiple JSON data', () => { let fileContent; try { - fileContent = fs.readFileSync('src/utils/fixtures/otlp2jaeger-multi-in.json', 'utf-8'); + fileContent = fs.readFileSync('src/utils/fixtures/otlp2jaeger-multi-in.json.txt', 'utf-8'); } catch (err) { console.error('Error reading file:', err); throw err; diff --git a/packages/jaeger-ui/src/utils/readJsonFile.tsx b/packages/jaeger-ui/src/utils/readJsonFile.tsx index 996a90462f..e506845ca2 100644 --- a/packages/jaeger-ui/src/utils/readJsonFile.tsx +++ b/packages/jaeger-ui/src/utils/readJsonFile.tsx @@ -14,22 +14,20 @@ import JaegerAPI from '../api/jaeger'; -function tryParseMultiLineInput(input: any) { - const jsonStrings: any[] = input.split('\n').filter((line: string) => line.trim() !== ''); - const resourceSpansArray: any[] = []; +function tryParseMultiLineInput(input: string): any[] { + const jsonStrings = input.split('\n').filter((line: string) => line.trim() !== ''); + const parsedObjects: any[] = []; jsonStrings.forEach((jsonString: string) => { try { const traceObj = JSON.parse(jsonString.trim()); - if ('resourceSpans' in traceObj) { - resourceSpansArray.push(...traceObj.resourceSpans); - } + parsedObjects.push(traceObj); } catch (error) { throw new Error(`Error parsing JSON: ${(error as Error).message}`); } }); - return { resourceSpans: resourceSpansArray }; + return parsedObjects; } export default function readJsonFile(fileList: { file: File }): Promise { @@ -44,7 +42,6 @@ export default function readJsonFile(fileList: { file: File }): Promise try { traceObj = JSON.parse(reader.result); } catch (error) { - // try to interpret input as JSON-per-line try { traceObj = tryParseMultiLineInput(reader.result); } catch (error) { @@ -52,7 +49,6 @@ export default function readJsonFile(fileList: { file: File }): Promise return; } } - // 1. check if array and transform to valid OTLP if (Array.isArray(traceObj) && traceObj.every(obj => 'resourceSpans' in obj)) { const mergedResourceSpans = traceObj.reduce((acc, obj) => { acc.push(...obj.resourceSpans); @@ -62,7 +58,6 @@ export default function readJsonFile(fileList: { file: File }): Promise traceObj = { resourceSpans: mergedResourceSpans }; } - // 2. check if OTLP and transform to Jaeger model if ('resourceSpans' in traceObj) { JaegerAPI.transformOTLP(traceObj) .then((result: string) => { From cdbc9257283fa3dd59362b25ce9d21297028315e Mon Sep 17 00:00:00 2001 From: Muthukumar Date: Mon, 15 Jul 2024 11:07:57 +0530 Subject: [PATCH 4/7] Added a better error message Signed-off-by: Muthukumar --- packages/jaeger-ui/src/utils/readJsonFile.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/jaeger-ui/src/utils/readJsonFile.tsx b/packages/jaeger-ui/src/utils/readJsonFile.tsx index e506845ca2..2eb599a58e 100644 --- a/packages/jaeger-ui/src/utils/readJsonFile.tsx +++ b/packages/jaeger-ui/src/utils/readJsonFile.tsx @@ -18,12 +18,12 @@ function tryParseMultiLineInput(input: string): any[] { const jsonStrings = input.split('\n').filter((line: string) => line.trim() !== ''); const parsedObjects: any[] = []; - jsonStrings.forEach((jsonString: string) => { + jsonStrings.forEach((jsonString: string, index: number) => { try { const traceObj = JSON.parse(jsonString.trim()); parsedObjects.push(traceObj); } catch (error) { - throw new Error(`Error parsing JSON: ${(error as Error).message}`); + throw new Error(`Error parsing JSON at line ${index+1}: ${(error as Error).message}`); } }); From d01daef47524706b42e873b1d9abdd1cfa8ef8de Mon Sep 17 00:00:00 2001 From: Yuri Shkuro Date: Mon, 15 Jul 2024 10:46:26 -0400 Subject: [PATCH 5/7] clean-up Signed-off-by: Yuri Shkuro --- packages/jaeger-ui/src/utils/readJsonFile.test.js | 12 +++--------- packages/jaeger-ui/src/utils/readJsonFile.tsx | 4 ++-- 2 files changed, 5 insertions(+), 11 deletions(-) diff --git a/packages/jaeger-ui/src/utils/readJsonFile.test.js b/packages/jaeger-ui/src/utils/readJsonFile.test.js index b2d5f457bf..8fba9ce4c2 100644 --- a/packages/jaeger-ui/src/utils/readJsonFile.test.js +++ b/packages/jaeger-ui/src/utils/readJsonFile.test.js @@ -67,20 +67,14 @@ 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 multiple JSON data', () => { - let fileContent; - try { - fileContent = fs.readFileSync('src/utils/fixtures/otlp2jaeger-multi-in.json.txt', 'utf-8'); - } catch (err) { - console.error('Error reading file:', err); - throw err; - } + it('loads JSON-per-line data', () => { + let 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 }); expect(p).resolves.toBeDefined(); diff --git a/packages/jaeger-ui/src/utils/readJsonFile.tsx b/packages/jaeger-ui/src/utils/readJsonFile.tsx index 2eb599a58e..d680c0df33 100644 --- a/packages/jaeger-ui/src/utils/readJsonFile.tsx +++ b/packages/jaeger-ui/src/utils/readJsonFile.tsx @@ -23,7 +23,7 @@ function tryParseMultiLineInput(input: string): any[] { 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}`); + throw new Error(`Error parsing JSON at line ${index + 1}: ${(error as Error).message}`); } }); @@ -45,7 +45,7 @@ export default function readJsonFile(fileList: { file: File }): Promise try { traceObj = tryParseMultiLineInput(reader.result); } catch (error) { - reject(new Error(`Error processing JSON: ${(error as Error).message}`)); + reject(error); return; } } From 2954b575cd820499b57df0af64cfc2f54f79b6a5 Mon Sep 17 00:00:00 2001 From: Yuri Shkuro Date: Mon, 15 Jul 2024 10:52:04 -0400 Subject: [PATCH 6/7] delint Signed-off-by: Yuri Shkuro --- packages/jaeger-ui/src/utils/readJsonFile.test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/jaeger-ui/src/utils/readJsonFile.test.js b/packages/jaeger-ui/src/utils/readJsonFile.test.js index 8fba9ce4c2..8a3c6d54eb 100644 --- a/packages/jaeger-ui/src/utils/readJsonFile.test.js +++ b/packages/jaeger-ui/src/utils/readJsonFile.test.js @@ -74,7 +74,7 @@ describe('fileReader.readJsonFile', () => { }); it('loads JSON-per-line data', () => { - let fileContent = fs.readFileSync('src/utils/fixtures/otlp2jaeger-multi-in.json.txt', 'utf-8'); + 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 }); expect(p).resolves.toBeDefined(); From 913906532bdc15224daf7737068f9fcf2f75ffbb Mon Sep 17 00:00:00 2001 From: Muthukumar Date: Wed, 17 Jul 2024 00:19:53 +0530 Subject: [PATCH 7/7] Added input and output files Mock is used instead of transform function in test cases. Signed-off-by: Muthukumar --- .../utils/fixtures/oltp2jaeger-multi-out.json | 524 ++++++++++++++++++ .../otlp2jaeger-multi-in-combined.json | 202 +++++++ .../jaeger-ui/src/utils/readJsonFile.test.js | 28 +- 3 files changed, 749 insertions(+), 5 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 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/readJsonFile.test.js b/packages/jaeger-ui/src/utils/readJsonFile.test.js index 8a3c6d54eb..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); @@ -74,9 +91,10 @@ describe('fileReader.readJsonFile', () => { }); 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 }); - expect(p).resolves.toBeDefined(); + return expect(p).resolves.toMatchObject(expectedOutput); }); });