add tracing context to all webhooks

This commit is contained in:
Dave Horton
2022-03-30 17:31:21 -04:00
parent 0975d866f3
commit 4284797e85
12 changed files with 62 additions and 20 deletions

View File

@@ -240,7 +240,6 @@ module.exports = function(srf, logger) {
span = obj.span; span = obj.span;
const b3 = rootSpan.getTracingPropagation(); const b3 = rootSpan.getTracingPropagation();
const httpHeaders = b3 && {b3}; const httpHeaders = b3 && {b3};
logger.info({b3, httpHeaders}, 'sending trace info (?)');
const json = await app.requestor.request('session:new', app.call_hook, params, httpHeaders); const json = await app.requestor.request('session:new', app.call_hook, params, httpHeaders);
app.tasks = normalizeJambones(logger, json).map((tdata) => makeTask(logger, tdata)); app.tasks = normalizeJambones(logger, json).map((tdata) => makeTask(logger, tdata));
span.setAttributes({ span.setAttributes({

View File

@@ -230,6 +230,10 @@ class CallSession extends Emitter {
return this.backgroundGatherTask; return this.backgroundGatherTask;
} }
get b3() {
return this.rootSpan?.getTracingPropagation();
}
async enableBotMode(gather, autoEnable) { async enableBotMode(gather, autoEnable) {
try { try {
const t = normalizeJambones(this.logger, [gather]); const t = normalizeJambones(this.logger, [gather]);
@@ -512,17 +516,20 @@ class CallSession extends Emitter {
async _lccCallHook(opts) { async _lccCallHook(opts) {
const webhooks = []; const webhooks = [];
let sd, tasks, childTasks; let sd, tasks, childTasks;
const b3 = this.b3;
const httpHeaders = b3 && {b3};
if (opts.call_hook || opts.child_call_hook) { if (opts.call_hook || opts.child_call_hook) {
if (opts.call_hook) { if (opts.call_hook) {
webhooks.push(this.requestor.request('session:redirect', opts.call_hook, this.callInfo.toJSON())); webhooks.push(this.requestor.request('session:redirect', opts.call_hook, this.callInfo.toJSON(), httpHeaders));
} }
if (opts.child_call_hook) { if (opts.child_call_hook) {
/* child call hook only allowed from a connected Dial state */ /* child call hook only allowed from a connected Dial state */
const task = this.currentTask; const task = this.currentTask;
sd = task.sd; sd = task.sd;
if (task && TaskName.Dial === task.name && sd) { if (task && TaskName.Dial === task.name && sd) {
webhooks.push(this.requestor.request('session:redirect', opts.child_call_hook, sd.callInfo.toJSON())); webhooks.push(this.requestor.request(
'session:redirect', opts.child_call_hook, sd.callInfo.toJSON(), httpHeaders));
} }
} }
const [tasks1, tasks2] = await Promise.all(webhooks); const [tasks1, tasks2] = await Promise.all(webhooks);
@@ -641,6 +648,8 @@ class CallSession extends Emitter {
async _lccWhisper(opts, callSid) { async _lccWhisper(opts, callSid) {
const {whisper} = opts; const {whisper} = opts;
let tasks; let tasks;
const b3 = this.b3;
const httpHeaders = b3 && {b3};
// this whole thing requires us to be in a Dial verb // this whole thing requires us to be in a Dial verb
const task = this.currentTask; const task = this.currentTask;
@@ -651,7 +660,7 @@ class CallSession extends Emitter {
// allow user to provide a url object, a url string, an array of tasks, or a single task // allow user to provide a url object, a url string, an array of tasks, or a single task
if (typeof whisper === 'string' || (typeof whisper === 'object' && whisper.url)) { if (typeof whisper === 'string' || (typeof whisper === 'object' && whisper.url)) {
// retrieve a url // retrieve a url
const json = await this.requestor(opts.call_hook, this.callInfo.toJSON()); const json = await this.requestor(opts.call_hook, this.callInfo.toJSON(), httpHeaders);
tasks = normalizeJambones(this.logger, json).map((tdata) => makeTask(this.logger, tdata)); tasks = normalizeJambones(this.logger, json).map((tdata) => makeTask(this.logger, tdata));
} }
else if (Array.isArray(whisper)) { else if (Array.isArray(whisper)) {
@@ -1264,7 +1273,9 @@ class CallSession extends Emitter {
const {span} = this.rootSpan.startChildSpan(`call-status:${this.callInfo.callStatus}`); const {span} = this.rootSpan.startChildSpan(`call-status:${this.callInfo.callStatus}`);
span.setAttributes(this.callInfo.toJSON()); span.setAttributes(this.callInfo.toJSON());
try { try {
this.notifier.request('call:status', this.call_status_hook, this.callInfo.toJSON()); const b3 = this.b3;
const httpHeaders = b3 && {b3};
this.notifier.request('call:status', this.call_status_hook, this.callInfo.toJSON(), httpHeaders);
span.end(); span.end();
} catch (err) { } catch (err) {
span.end(); span.end();

View File

@@ -529,7 +529,9 @@ class Conference extends Task {
async _playHook(cs, dlg, hook, allowed = [TaskName.Play, TaskName.Say, TaskName.Pause]) { async _playHook(cs, dlg, hook, allowed = [TaskName.Play, TaskName.Say, TaskName.Pause]) {
assert(!this._playSession); assert(!this._playSession);
const json = await cs.application.requestor.request('verb:hook', hook, cs.callInfo); const b3 = this.getTracingPropagation();
const httpHeaders = b3 && {b3};
const json = await cs.application.requestor.request('verb:hook', hook, cs.callInfo, httpHeaders);
const tasks = normalizeJambones(this.logger, json).map((tdata) => makeTask(this.logger, tdata)); const tasks = normalizeJambones(this.logger, json).map((tdata) => makeTask(this.logger, tdata));
const allowedTasks = tasks.filter((t) => allowed.includes(t.name)); const allowedTasks = tasks.filter((t) => allowed.includes(t.name));
@@ -582,11 +584,14 @@ class Conference extends Task {
_notifyConferenceEvent(cs, eventName, params = {}) { _notifyConferenceEvent(cs, eventName, params = {}) {
if (this.statusEvents.includes(eventName)) { if (this.statusEvents.includes(eventName)) {
const b3 = this.getTracingPropagation();
const httpHeaders = b3 && {b3};
params.event = eventName; params.event = eventName;
params.duration = (Date.now() - this.conferenceStartTime.getTime()) / 1000; params.duration = (Date.now() - this.conferenceStartTime.getTime()) / 1000;
if (!params.time) params.time = (new Date()).toISOString(); if (!params.time) params.time = (new Date()).toISOString();
if (!params.members && typeof this.participantCount === 'number') params.members = this.participantCount; if (!params.members && typeof this.participantCount === 'number') params.members = this.participantCount;
cs.application.requestor.request('verb:hook', this.statusHook, Object.assign(params, this.statusParams)) cs.application.requestor
.request('verb:hook', this.statusHook, Object.assign(params, this.statusParams, httpHeaders))
.catch((err) => this.logger.info(err, 'Conference:notifyConferenceEvent - error')); .catch((err) => this.logger.info(err, 'Conference:notifyConferenceEvent - error'));
} }
} }

View File

@@ -271,6 +271,9 @@ class TaskDial extends Task {
const referring_call_sid = isChild ? callInfo.callSid : cs.callSid; const referring_call_sid = isChild ? callInfo.callSid : cs.callSid;
const referred_call_sid = isChild ? callInfo.parentCallSid : this.sd.callSid; const referred_call_sid = isChild ? callInfo.parentCallSid : this.sd.callSid;
const b3 = this.getTracingPropagation();
const httpHeaders = b3 && {b3};
const to = parseUri(req.getParsedHeader('Refer-To').uri); const to = parseUri(req.getParsedHeader('Refer-To').uri);
const by = parseUri(req.getParsedHeader('Referred-By').uri); const by = parseUri(req.getParsedHeader('Referred-By').uri);
this.logger.info({to}, 'refer to parsed'); this.logger.info({to}, 'refer to parsed');
@@ -285,7 +288,7 @@ class TaskDial extends Task {
referring_call_sid, referring_call_sid,
referred_call_sid referred_call_sid
} }
}); }, httpHeaders);
res.send(202); res.send(202);
this.logger.info('DialTask:handleRefer - sent 202 Accepted'); this.logger.info('DialTask:handleRefer - sent 202 Accepted');
} catch (err) { } catch (err) {
@@ -345,8 +348,10 @@ class TaskDial extends Task {
const key = arr[1]; const key = arr[1];
const match = dtmfDetector.keyPress(key); const match = dtmfDetector.keyPress(key);
if (match) { if (match) {
const b3 = this.getTracingPropagation();
const httpHeaders = b3 && {b3};
this.logger.info({callSid}, `Dial:_onInfo triggered dtmf match: ${match}`); this.logger.info({callSid}, `Dial:_onInfo triggered dtmf match: ${match}`);
requestor.request('verb:hook', this.dtmfHook, {dtmf: match, ...callInfo.toJSON()}) requestor.request('verb:hook', this.dtmfHook, {dtmf: match, ...callInfo.toJSON(), httpHeaders})
.catch((err) => this.logger.info(err, 'Dial:_onDtmf - error')); .catch((err) => this.logger.info(err, 'Dial:_onDtmf - error'));
} }
} }

View File

@@ -453,7 +453,10 @@ class Dialogflow extends Task {
} }
async _performHook(cs, hook, results = {}) { async _performHook(cs, hook, results = {}) {
const json = await this.cs.requestor.request('verb:hook', hook, {...results, ...cs.callInfo.toJSON()}); const b3 = this.getTracingPropagation();
const httpHeaders = b3 && {b3};
const json = await this.cs.requestor.request('verb:hook', hook,
{...results, ...cs.callInfo.toJSON()}, httpHeaders);
if (json && Array.isArray(json)) { if (json && Array.isArray(json)) {
const makeTask = require('../make_task'); const makeTask = require('../make_task');
const tasks = normalizeJambones(this.logger, json).map((tdata) => makeTask(this.logger, tdata)); const tasks = normalizeJambones(this.logger, json).map((tdata) => makeTask(this.logger, tdata));

View File

@@ -394,7 +394,10 @@ class TaskGather extends Task {
this._killAudio(cs); this._killAudio(cs);
} }
if (this.partialResultHook) { if (this.partialResultHook) {
this.cs.requestor.request(this.partialResultHook, Object.assign({speech: evt}, this.cs.callInfo)); const b3 = this.getTracingPropagation();
const httpHeaders = b3 && {b3};
this.cs.requestor.request(this.partialResultHook, Object.assign({speech: evt},
this.cs.callInfo, httpHeaders));
} }
} }
} }

View File

@@ -289,7 +289,9 @@ class Lex extends Task {
} }
async _performHook(cs, hook, results) { async _performHook(cs, hook, results) {
const json = await this.cs.requestor.request('verb:hook', hook, results); const b3 = this.getTracingPropagation();
const httpHeaders = b3 && {b3};
const json = await this.cs.requestor.request('verb:hook', hook, results, httpHeaders);
if (json && Array.isArray(json)) { if (json && Array.isArray(json)) {
const makeTask = require('./make_task'); const makeTask = require('./make_task');
const tasks = normalizeJambones(this.logger, json).map((tdata) => makeTask(this.logger, tdata)); const tasks = normalizeJambones(this.logger, json).map((tdata) => makeTask(this.logger, tdata));

View File

@@ -48,7 +48,9 @@ class TaskRestDial extends Task {
cs.setDialog(dlg); cs.setDialog(dlg);
try { try {
const tasks = await cs.requestor.request('verb:hook', this.call_hook, cs.callInfo); const b3 = this.getTracingPropagation();
const httpHeaders = b3 && {b3};
const tasks = await cs.requestor.request('verb:hook', this.call_hook, cs.callInfo, httpHeaders);
if (tasks && Array.isArray(tasks)) { if (tasks && Array.isArray(tasks)) {
this.logger.debug({tasks: tasks}, `TaskRestDial: replacing application with ${tasks.length} tasks`); this.logger.debug({tasks: tasks}, `TaskRestDial: replacing application with ${tasks.length} tasks`);
cs.replaceApplication(normalizeJambones(this.logger, tasks).map((tdata) => makeTask(this.logger, tdata))); cs.replaceApplication(normalizeJambones(this.logger, tasks).map((tdata) => makeTask(this.logger, tdata)));

View File

@@ -76,7 +76,10 @@ class TaskSipRefer extends Task {
const status = arr[1]; const status = arr[1];
this.logger.debug(`TaskSipRefer:_handleNotify: call got status ${status}`); this.logger.debug(`TaskSipRefer:_handleNotify: call got status ${status}`);
if (this.eventHook) { if (this.eventHook) {
await cs.requestor.request('verb:hook', this.eventHook, {event: 'transfer-status', call_status: status}); const b3 = this.getTracingPropagation();
const httpHeaders = b3 && {b3};
await cs.requestor.request('verb:hook', this.eventHook,
{event: 'transfer-status', call_status: status}, httpHeaders);
} }
if (status >= 200) { if (status >= 200) {
this.referSpan.setAttributes({'refer.finalNotify': status}); this.referSpan.setAttributes({'refer.finalNotify': status});

View File

@@ -137,10 +137,12 @@ class Task extends Emitter {
async performAction(results, expectResponse = true) { async performAction(results, expectResponse = true) {
if (this.actionHook) { if (this.actionHook) {
const params = results ? Object.assign(results, this.cs.callInfo.toJSON()) : this.cs.callInfo.toJSON(); const params = results ? Object.assign(results, this.cs.callInfo.toJSON()) : this.cs.callInfo.toJSON();
const b3 = this.getTracingPropagation();
const httpHeaders = b3 && {b3};
const span = this.startSpan('verb:hook', {'hook.url': this.actionHook}); const span = this.startSpan('verb:hook', {'hook.url': this.actionHook});
span.setAttributes({'http.body': JSON.stringify(params)}); span.setAttributes({'http.body': JSON.stringify(params)});
try { try {
const json = await this.cs.requestor.request('verb:hook', this.actionHook, params); const json = await this.cs.requestor.request('verb:hook', this.actionHook, params, httpHeaders);
span.setAttributes({'http.statusCode': 200}); span.setAttributes({'http.statusCode': 200});
span.end(); span.end();
if (expectResponse && json && Array.isArray(json)) { if (expectResponse && json && Array.isArray(json)) {
@@ -160,10 +162,12 @@ class Task extends Emitter {
} }
async performHook(cs, hook, results) { async performHook(cs, hook, results) {
const b3 = this.getTracingPropagation();
const httpHeaders = b3 && {b3};
const span = this.startSpan('verb:hook', {'hook.url': hook}); const span = this.startSpan('verb:hook', {'hook.url': hook});
span.setAttributes({'http.body': JSON.stringify(results)}); span.setAttributes({'http.body': JSON.stringify(results)});
try { try {
const json = await cs.requestor.request('verb:hook', hook, results); const json = await cs.requestor.request('verb:hook', hook, results, httpHeaders);
span.setAttributes({'http.statusCode': 200}); span.setAttributes({'http.statusCode': 200});
span.end(); span.end();
if (json && Array.isArray(json)) { if (json && Array.isArray(json)) {

View File

@@ -254,7 +254,10 @@ class TaskTranscribe extends Task {
} }
if (this.transcriptionHook) { if (this.transcriptionHook) {
this.cs.requestor.request('verb:hook', this.transcriptionHook, Object.assign({speech: evt}, this.cs.callInfo)) const b3 = this.getTracingPropagation();
const httpHeaders = b3 && {b3};
this.cs.requestor.request('verb:hook', this.transcriptionHook,
Object.assign({speech: evt}, this.cs.callInfo), httpHeaders)
.catch((err) => this.logger.info(err, 'TranscribeTask:_onTranscription error')); .catch((err) => this.logger.info(err, 'TranscribeTask:_onTranscription error'));
} }
if (this.parentTask) { if (this.parentTask) {

View File

@@ -33,7 +33,7 @@ class WsRequestor extends BaseRequestor {
* @param {string} [hook.password] - if basic auth is protecting the endpoint * @param {string} [hook.password] - if basic auth is protecting the endpoint
* @param {object} [params] - request parameters * @param {object} [params] - request parameters
*/ */
async request(type, hook, params) { async request(type, hook, params, httpHeaders = {}) {
assert(HookMsgTypes.includes(type)); assert(HookMsgTypes.includes(type));
const url = hook.url || hook; const url = hook.url || hook;
@@ -48,7 +48,7 @@ class WsRequestor extends BaseRequestor {
if (this._isAbsoluteUrl(url) && url.startsWith('http')) { if (this._isAbsoluteUrl(url) && url.startsWith('http')) {
this.logger.debug({hook}, 'WsRequestor: sending a webhook (HTTP)'); this.logger.debug({hook}, 'WsRequestor: sending a webhook (HTTP)');
const requestor = new HttpRequestor(this.logger, this.account_sid, hook, this.secret); const requestor = new HttpRequestor(this.logger, this.account_sid, hook, this.secret);
return requestor.request(type, hook, params); return requestor.request(type, hook, params, httpHeaders);
} }
/* connect if necessary */ /* connect if necessary */
@@ -73,12 +73,14 @@ class WsRequestor extends BaseRequestor {
assert.ok(url, 'WsRequestor:request url was not provided'); assert.ok(url, 'WsRequestor:request url was not provided');
const msgid = short.generate(); const msgid = short.generate();
const b3 = httpHeaders?.b3 ? {b3: httpHeaders.b3} : {};
const obj = { const obj = {
type, type,
msgid, msgid,
call_sid: this.call_sid, call_sid: this.call_sid,
hook: type === 'verb:hook' ? url : undefined, hook: type === 'verb:hook' ? url : undefined,
data: {...payload} data: {...payload},
...b3
}; };
//this.logger.debug({obj}, `websocket: sending (${url})`); //this.logger.debug({obj}, `websocket: sending (${url})`);