Updated readme to match latest changes.

Not writes process/resource and additional attribute information
This commit is contained in:
2026-01-10 13:53:53 -06:00
parent ce23405757
commit 7371260442
2 changed files with 171 additions and 153 deletions

View File

@@ -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 ## 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: For example, using Jaeger UI:
![Tracing Screenshot](screenshots/IVA%20Tracing.png) ![Tracing Screenshot](screenshots/IVA%20Tracing.png)
## Code Snippets
### Start Span ### Start Span
```javascript ```javascript
(async () => { (async () => {
opentelemetry.startSpan("Global Flow") opentelemetry.startSpan("Global Flow");
})() })()
.catch((error) => { .catch((error) => {
console.error(error.message) console.error(error.message);
recognizedObject.answers.push('') recognizedObject.answers.push("");
recognizedObject.errorInfo = { recognizedObject.errorInfo = {
...recognizedObject.errorInfo, ...recognizedObject.errorInfo,
label: { label: {
data: error.toJSON ? error.toJSON() : {}, data: error.toJSON ? error.toJSON() : {},
message: error.message, message: error.message,
}, },
} };
}) })
.finally(() => { .finally(() => {
next() next();
}) });
``` ```
### End Span ### End Span
```javascript ```javascript
(async () => { (async () => {
opentelemetry.endSpan("Global Flow") opentelemetry.endSpan("Global Flow");
})() })()
.catch((error) => { .catch((error) => {
console.error(error.message) console.error(error.message);
recognizedObject.answers.push('') recognizedObject.answers.push("");
recognizedObject.errorInfo = { recognizedObject.errorInfo = {
...recognizedObject.errorInfo, ...recognizedObject.errorInfo,
label: { label: {
data: error.toJSON ? error.toJSON() : {}, data: error.toJSON ? error.toJSON() : {},
message: error.message, message: error.message,
}, },
} };
}) })
.finally(() => { .finally(() => {
next() next();
}) });
``` ```

View File

@@ -1,152 +1,160 @@
const { const {
recognizedObject: r = {}, recognizedObject: r = {},
traces = axios.create({ traces = axios.create({
baseURL: opentelemetry_settings.baseUrl, baseURL: opentelemetry_settings.baseUrl,
timeout: 16000, timeout: 16000,
}), }),
} = this; } = this;
function generateIdHex(numBytes = 8) { function generateIdHex(numBytes = 8) {
const bytes = crypto.getRandomValues(new Uint8Array(numBytes)); const bytes = crypto.getRandomValues(new Uint8Array(numBytes));
return Array.from(bytes) return Array.from(bytes)
.map(b => b.toString(16).padStart(2, '0')) .map((b) => b.toString(16).padStart(2, "0"))
.join(''); .join("");
} }
return { return {
async startSpan(name) { async startSpan(name) {
if (name == null) { if (name == null) {
throw new Error("Span name is required"); throw new Error("Span name is required");
} }
// Initialize telemetry object if it doesn't exist // Initialize telemetry object if it doesn't exist
if ("telemetry" in r == false) { if ("telemetry" in r == false) {
r.telemetry = []; r.telemetry = { spans: [] };
if (opentelemetry_settings.trace == "conversation") { if (opentelemetry_settings.trace == "conversation") {
r.telemetry.traceId = r.conversationId.replace(/-/gi, ""); r.telemetry.traceId = r.conversationId.replace(/-/gi, "");
} else { } else {
r.telemetry.traceId = generateIdHex(16); r.telemetry.traceId = generateIdHex(16);
} }
} }
try { try {
const span = { const span = {
name: name, name: name,
resourceSpans: [ resourceSpans: [
{ {
resource: { resource: {
attributes: [ attributes: [
{ {
key: "service.name", key: "service.name",
value: { value: {
stringValue: "ivastudio.verint.live", 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: { key: "channel",
name: r.conversationId, value: {
version: "1.0.0", stringValue: (
attributes: [ req.body?.metadata?.channel ??
{ req.body?.channel ??
key: "my.scope.attribute", "web"
value: { ).toLowerCase(),
stringValue: "some scope attribute", },
}, },
}, {
], key: "sessionId",
}, value: {
spans: [ stringValue: (
{ req.body?.metadata?.sessionId ?? "undefined"
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,
},
},
],
},
],
},
],
},
], ],
}; },
r.telemetry.unshift(span); ],
return span; },
} catch (e) { ],
throw e; };
} r.telemetry.spans.unshift(span);
}, return span;
async endSpan(name) { } catch (e) {
if (name == null) { throw e;
throw new Error("Span name is required"); }
},
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) { span.resourceSpans[0].scopeSpans[0].spans[0].endTimeUnixNano =
// nothing to end "" + Date.now() * 1000000;
return;
}
try { span.resourceSpans[0].scopeSpans[0].spans[0].attributes.push({
const spanIndex = r.telemetry.findIndex((span) => span.name === name); key: "answers",
value: {
stringValue: JSON.stringify(r.answers),
},
});
if (spanIndex == -1) { const response = await traces.post(`/v1/traces`, span, {
// nothing to end headers: {
return; "Content-Type": "application/json",
} },
});
const span = r.telemetry.splice(spanIndex, 1)[0]; return response.data;
} catch (e) {
if (r.telemetry.length >= 1) { throw e;
// 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;
}
},
}; };