Feature/opentelemetry (#89)

* initial adds for otel tracing

* initial basic testing

* basic tracing for incoming calls

* linting

* add traceId to the webhook params

* trace webhook calls

* tracing: add new commands as tags when receiving async commands over websocket

* tracing new commands

* add summary for config verb

* trace async commands

* bugfix: undefined ref

* tracing: give time for final webhooks before closing root span

* tracing bugfix: span for background gather was not ended

* tracing - minor tag changes

* tracing - add span atttribute for reason call ended

* trace call status webhooks, add app version to trace output

* config: add support for automatically re-enabling

* env var to customize service name in tracing UI

* config: change to use 'sticky' attribute to re-enable bargein automatically

* fix warnings

* when adulting create a new root span

* when background gather triggers bargein via vad clear queue of tasks

* additional trace attributes for dial and refer

* fix dial tracing

* add better summary for dial

* fix prev commit

* add exponential backoff to WsRequestor reconnection logic

* add calling number to log metadata, as this will be frequently the key data given for troubleshooting

* add accountSid to log metadata

* make handshake timeout for ws connections configurable with default 1.5 secs

* rename env var

* fix bug prev checkin

* logging fixes

* consistent env naming
This commit is contained in:
Dave Horton
2022-03-28 15:38:28 -04:00
committed by GitHub
parent f1f83598ca
commit 6abfdafe05
24 changed files with 1852 additions and 87 deletions

View File

@@ -4,6 +4,7 @@ const debug = require('debug')('jambonz:feature-server');
const assert = require('assert');
const {TaskPreconditions} = require('../utils/constants');
const normalizeJambones = require('../utils/normalize-jambones');
const {trace} = require('@opentelemetry/api');
const specs = new Map();
const _specData = require('./specs');
for (const key in _specData) {specs.set(key, _specData[key]);}
@@ -71,6 +72,15 @@ class Task extends Emitter {
setImmediate(() => this.parentTask = null);
}
startSpan(name, attributes) {
const {srf} = require('../..');
const {tracer} = srf.locals.otel;
const span = tracer.startSpan(name, undefined, this.ctx);
if (attributes) span.setAttributes(attributes);
trace.setSpan(this.ctx, span);
return span;
}
/**
* when a subclass Task has completed its work, it should call this method
*/
@@ -111,29 +121,49 @@ class Task extends Emitter {
async performAction(results, expectResponse = true) {
if (this.actionHook) {
const params = results ? Object.assign(results, this.cs.callInfo.toJSON()) : this.cs.callInfo.toJSON();
const json = await this.cs.requestor.request('verb:hook', this.actionHook, params);
if (expectResponse && json && Array.isArray(json)) {
const makeTask = require('./make_task');
const tasks = normalizeJambones(this.logger, json).map((tdata) => makeTask(this.logger, tdata));
if (tasks && tasks.length > 0) {
this.logger.info({tasks: tasks}, `${this.name} replacing application with ${tasks.length} tasks`);
this.callSession.replaceApplication(tasks);
const span = this.startSpan('verb:hook', {'hook.url': this.actionHook});
span.setAttributes({'http.body': JSON.stringify(params)});
try {
const json = await this.cs.requestor.request('verb:hook', this.actionHook, params);
span.setAttributes({'http.statusCode': 200});
span.end();
if (expectResponse && json && Array.isArray(json)) {
const makeTask = require('./make_task');
const tasks = normalizeJambones(this.logger, json).map((tdata) => makeTask(this.logger, tdata));
if (tasks && tasks.length > 0) {
this.logger.info({tasks: tasks}, `${this.name} replacing application with ${tasks.length} tasks`);
this.callSession.replaceApplication(tasks);
}
}
} catch (err) {
span.setAttributes({'http.statusCode': err.statusCode});
span.end();
throw err;
}
}
}
async performHook(cs, hook, results) {
const json = await cs.requestor.request('verb:hook', hook, results);
if (json && Array.isArray(json)) {
const makeTask = require('./make_task');
const tasks = normalizeJambones(this.logger, json).map((tdata) => makeTask(this.logger, tdata));
if (tasks && tasks.length > 0) {
this.redirect(cs, tasks);
return true;
const span = this.startSpan('verb:hook', {'hook.url': hook});
span.setAttributes({'http.body': JSON.stringify(results)});
try {
const json = await cs.requestor.request('verb:hook', hook, results);
span.setAttributes({'http.statusCode': 200});
span.end();
if (json && Array.isArray(json)) {
const makeTask = require('./make_task');
const tasks = normalizeJambones(this.logger, json).map((tdata) => makeTask(this.logger, tdata));
if (tasks && tasks.length > 0) {
this.redirect(cs, tasks);
return true;
}
}
return false;
} catch (err) {
span.setAttributes({'http.statusCode': err.statusCode});
span.end();
throw err;
}
return false;
}
redirect(cs, tasks) {