From 73712604425c12cecd7e3046d6914b4e138b0778 Mon Sep 17 00:00:00 2001 From: "Peter.Morton" Date: Sat, 10 Jan 2026 13:53:53 -0600 Subject: [PATCH] Updated readme to match latest changes. Not writes process/resource and additional attribute information --- open-telemetry/README.md | 44 +-- .../GlobalVariable/opentelemetry.js | 280 +++++++++--------- 2 files changed, 171 insertions(+), 153 deletions(-) diff --git a/open-telemetry/README.md b/open-telemetry/README.md index 622efe3..989cae8 100644 --- a/open-telemetry/README.md +++ b/open-telemetry/README.md @@ -1,57 +1,67 @@ -# Open Telemetery Example +# Open Telemetry Example + +Writes Telemetry Tracing information to the endpoint provided using the [OLTP JSON over HTTP specification](https://github.com/open-telemetry/opentelemetry-proto/blob/main/docs/specification.md#json-protobuf-encoding) ## Example Usage -Use the _code block_ widget to start and end spans. Spans can be nested to form a stack, ending a span will always end the last span added to the stack. +Use the _code block_ widget to start and end spans. -### Visualization +Spans can be nested to form a stack, with the parent span being the most recent +active span added to the stack. You can have multiple spans using the same name, +and ending a span by name will always end the most recent span. + +Spans will only be written to the opentelemetry endpoint once the _endSpan_ +method is called. + +## Visualization For example, using Jaeger UI: ![Tracing Screenshot](screenshots/IVA%20Tracing.png) +## Code Snippets + ### Start Span ```javascript (async () => { - opentelemetry.startSpan("Global Flow") + opentelemetry.startSpan("Global Flow"); })() .catch((error) => { - console.error(error.message) - recognizedObject.answers.push('') + console.error(error.message); + recognizedObject.answers.push(""); recognizedObject.errorInfo = { ...recognizedObject.errorInfo, label: { data: error.toJSON ? error.toJSON() : {}, message: error.message, }, - } + }; }) .finally(() => { - next() - }) - + next(); + }); ``` ### End Span ```javascript (async () => { - opentelemetry.endSpan("Global Flow") + opentelemetry.endSpan("Global Flow"); })() .catch((error) => { - console.error(error.message) - recognizedObject.answers.push('') + console.error(error.message); + recognizedObject.answers.push(""); recognizedObject.errorInfo = { ...recognizedObject.errorInfo, label: { data: error.toJSON ? error.toJSON() : {}, message: error.message, }, - } + }; }) .finally(() => { - next() - }) + next(); + }); +``` -``` \ No newline at end of file diff --git a/open-telemetry/_studio_dependencies/GlobalVariable/opentelemetry.js b/open-telemetry/_studio_dependencies/GlobalVariable/opentelemetry.js index a0d0992..7f35c45 100644 --- a/open-telemetry/_studio_dependencies/GlobalVariable/opentelemetry.js +++ b/open-telemetry/_studio_dependencies/GlobalVariable/opentelemetry.js @@ -1,152 +1,160 @@ const { - recognizedObject: r = {}, - traces = axios.create({ - baseURL: opentelemetry_settings.baseUrl, - timeout: 16000, - }), + recognizedObject: r = {}, + traces = axios.create({ + baseURL: opentelemetry_settings.baseUrl, + timeout: 16000, + }), } = this; function generateIdHex(numBytes = 8) { - const bytes = crypto.getRandomValues(new Uint8Array(numBytes)); - return Array.from(bytes) - .map(b => b.toString(16).padStart(2, '0')) - .join(''); + const bytes = crypto.getRandomValues(new Uint8Array(numBytes)); + return Array.from(bytes) + .map((b) => b.toString(16).padStart(2, "0")) + .join(""); } return { - async startSpan(name) { - if (name == null) { - throw new Error("Span name is required"); - } + async startSpan(name) { + if (name == null) { + throw new Error("Span name is required"); + } - // Initialize telemetry object if it doesn't exist - if ("telemetry" in r == false) { - r.telemetry = []; - if (opentelemetry_settings.trace == "conversation") { - r.telemetry.traceId = r.conversationId.replace(/-/gi, ""); - } else { - r.telemetry.traceId = generateIdHex(16); - } - } + // Initialize telemetry object if it doesn't exist + if ("telemetry" in r == false) { + r.telemetry = { spans: [] }; + if (opentelemetry_settings.trace == "conversation") { + r.telemetry.traceId = r.conversationId.replace(/-/gi, ""); + } else { + r.telemetry.traceId = generateIdHex(16); + } + } - try { - const span = { - name: name, - resourceSpans: [ - { - resource: { - attributes: [ - { - key: "service.name", - value: { - stringValue: "ivastudio.verint.live", - }, - }, - ], + try { + const span = { + name: name, + resourceSpans: [ + { + resource: { + attributes: [ + { + key: "service.name", + value: { + stringValue: "ivastudio.verint.live", + }, + }, + { + key: "conversationId", + value: { + stringValue: r.conversationId, + }, + }, + { + key: "workspaceId", + value: { + stringValue: r.workspaceId, + }, + }, + ], + }, + scopeSpans: [ + { + spans: [ + { + traceId: r.telemetry.traceId, + spanId: generateIdHex(), + name: name, + startTimeUnixNano: "" + Date.now() * 1000000, + kind: 2, + attributes: [ + { + key: "input", + value: { + stringValue: r.input, }, - scopeSpans: [ - { - scope: { - name: r.conversationId, - version: "1.0.0", - attributes: [ - { - key: "my.scope.attribute", - value: { - stringValue: "some scope attribute", - }, - }, - ], - }, - spans: [ - { - traceId: r.telemetry.traceId, - spanId: generateIdHex(), - name: name, - startTimeUnixNano: "" + Date.now() * 1000000, - kind: 2, - attributes: [ - { - key: "conversationId", - value: { - stringValue: r.conversationId, - }, - }, - { - key: "workspaceId", - value: { - stringValue: r.workspaceId, - }, - }, - { - key: "input", - value: { - stringValue: r.input, - }, - }, - ], - }, - ], - }, - ], - }, + }, + { + key: "channel", + value: { + stringValue: ( + req.body?.metadata?.channel ?? + req.body?.channel ?? + "web" + ).toLowerCase(), + }, + }, + { + key: "sessionId", + value: { + stringValue: ( + req.body?.metadata?.sessionId ?? "undefined" + ), + }, + }, + ], + }, ], - }; - r.telemetry.unshift(span); - return span; - } catch (e) { - throw e; - } - }, - async endSpan(name) { - if (name == null) { - throw new Error("Span name is required"); + }, + ], + }, + ], + }; + r.telemetry.spans.unshift(span); + return span; + } catch (e) { + throw e; + } + }, + async endSpan(name) { + if (name == null) { + throw new Error("Span name is required"); + } + + if ("telemetry" in r == false) { + // nothing to end + return; + } + + try { + const spanIndex = r.telemetry.spans.findIndex( + (span) => span.name === name + ); + + if (spanIndex == -1) { + // nothing to end + return; + } + + const span = r.telemetry.spans.splice(spanIndex, 1)[0]; + + if (r.telemetry.spans.length >= 1) { + // Assume span now at index is the parent + const parentSpan = r.telemetry.spans[spanIndex]; + if (parentSpan) { + span.resourceSpans[0].scopeSpans[0].spans[0].parentSpanId = + parentSpan.resourceSpans[0].scopeSpans[0].spans[0].spanId; + } else { + console.debug(name + " has no parentSpan"); } + } - if ("telemetry" in r == false) { - // nothing to end - return; - } + span.resourceSpans[0].scopeSpans[0].spans[0].endTimeUnixNano = + "" + Date.now() * 1000000; - try { - const spanIndex = r.telemetry.findIndex((span) => span.name === name); + span.resourceSpans[0].scopeSpans[0].spans[0].attributes.push({ + key: "answers", + value: { + stringValue: JSON.stringify(r.answers), + }, + }); - if (spanIndex == -1) { - // nothing to end - return; - } - - const span = r.telemetry.splice(spanIndex, 1)[0]; - - if (r.telemetry.length >= 1) { - // Assume span now at index is the parent - const parentSpan = r.telemetry[spanIndex]; - if (parentSpan) { - span.resourceSpans[0].scopeSpans[0].spans[0].parentSpanId = - parentSpan.resourceSpans[0].scopeSpans[0].spans[0].spanId; - } else { - console.debug(name + " has no parentSpan"); - } - } - - span.resourceSpans[0].scopeSpans[0].spans[0].endTimeUnixNano = - "" + Date.now() * 1000000; - - span.resourceSpans[0].scopeSpans[0].spans[0].attributes.push({ - key: "answers", - value: { - stringValue: JSON.stringify(r.answers), - }, - }); - - const response = await traces.post(`/v1/traces`, span, { - headers: { - "Content-Type": "application/json", - }, - }); - return response.data; - } catch (e) { - throw e; - } - }, + const response = await traces.post(`/v1/traces`, span, { + headers: { + "Content-Type": "application/json", + }, + }); + return response.data; + } catch (e) { + throw e; + } + }, };