Better heirarchy and option to do conversation tracing vs transaction.

This commit is contained in:
2026-01-09 23:11:07 -06:00
parent a9a8b14c73
commit 6b86c348fe
2 changed files with 158 additions and 112 deletions

View File

@@ -8,45 +8,44 @@ Use the _code block_ widget to start and end spans. Spans can be nested to form
```javascript ```javascript
(async () => { (async () => {
await latencySpan().endSpan(recognizedObject.conversationId); opentelemetry.startSpan("Global Flow")
const span = await opentelemetry().startSpan("Global Flow");
console.log(span);
})() })()
.catch((error) => { .catch((error) => {
console.log(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 () => {
await opentelemetry().endSpan(); opentelemetry.endSpan("Global Flow")
})() })()
.catch((error) => { .catch((error) => {
console.log(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,105 +1,152 @@
const traces = axios.create({ const {
baseURL: opentelemetry_settings.baseUrl, recognizedObject: r = {},
timeout: 16000, 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('');
}
return { return {
async startSpan(name) { async startSpan(name) {
try { if (name == null) {
if (!conversationData.spans) { throw new Error("Span name is required");
conversationData.spans = [] }
}
const spanName = name ? name : 'default'
const span = { // Initialize telemetry object if it doesn't exist
resourceSpans: [ if ("telemetry" in r == false) {
{ r.telemetry = [];
resource: { if (opentelemetry_settings.trace == "conversation") {
attributes: [ r.telemetry.traceId = r.conversationId.replace(/-/gi, "");
{ } else {
key: 'service.name', r.telemetry.traceId = generateIdHex(16);
value: { }
stringValue: 'ivastudio.verint.live', }
},
}, try {
], const span = {
}, name: name,
scopeSpans: [ resourceSpans: [
{
scope: {
name: recognizedObject.conversationId,
version: '1.0.0',
attributes: [
{ {
key: 'my.scope.attribute', resource: {
value: { attributes: [
stringValue: 'some scope attribute', {
}, key: "service.name",
value: {
stringValue: "ivastudio.verint.live",
},
},
],
},
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,
},
},
],
},
],
},
],
}, },
],
},
spans: [
{
// traceId: recognizedObject.req.headers['x-b3-traceid'],
traceId: recognizedObject.conversationId.replace(/-/gi, ''),
spanId: recognizedObject.req.headers['x-b3-spanid'],
// parentSpanId: recognizedObject.req.headers['x-b3-parentspanid'],
name: spanName,
startTimeUnixNano: '' + Date.now() * 1000000,
kind: 2,
attributes: [
{
key: 'conversationId',
value: {
stringValue: recognizedObject.conversationId,
},
},
{
key: 'workspaceId',
value: {
stringValue: recognizedObject.workspaceId,
},
},
{
key: 'input',
value: {
stringValue: recognizedObject.input,
},
},
],
},
], ],
}, };
], r.telemetry.unshift(span);
}, return span;
], } catch (e) {
} throw e;
conversationData.spans.push(span) }
return span },
} catch (e) { async endSpan(name) {
throw e if (name == null) {
} throw new Error("Span name is required");
}, }
async endSpan() {
try {
const data = conversationData.spans.pop()
data.resourceSpans[0].scopeSpans[0].spans[0].endTimeUnixNano = '' + Date.now() * 1000000 if ("telemetry" in r == false) {
// nothing to end
return;
}
data.resourceSpans[0].scopeSpans[0].spans[0].attributes.push({ try {
key: 'answers', const spanIndex = r.telemetry.findIndex((span) => span.name === name);
value: {
stringValue: JSON.stringify(recognizedObject.answers),
},
})
const response = await traces.post(`/v1/traces`, data, { if (spanIndex == -1) {
headers: { // nothing to end
'Content-Type': 'application/json', return;
}, }
})
return response.data const span = r.telemetry.splice(spanIndex, 1)[0];
} catch (e) {
throw e 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;
}
},
};