Add end-to-end audio observability

This reports via OpenTelemetry when particular participants are speaking, as an easy way to observe the delivery of audio in calls.
This commit is contained in:
Robin Townsend
2023-03-22 14:23:26 -04:00
parent 48493a96e1
commit 313ebe258e
4 changed files with 76 additions and 29 deletions

View File

@@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
import opentelemetry, { Span, Attributes } from "@opentelemetry/api";
import opentelemetry, { Span, Attributes, Context } from "@opentelemetry/api";
import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions";
import {
GroupCall,
@@ -73,8 +73,10 @@ function flattenVoipEventRecursive(
*/
export class OTelGroupCallMembership {
private callMembershipSpan?: Span;
private callMembershipContext?: Context;
private myUserId: string;
private myMember: RoomMember;
private readonly speakingSpans = new Map<RoomMember, Map<string, Span>>();
constructor(private groupCall: GroupCall, client: MatrixClient) {
this.myUserId = client.getUserId();
@@ -101,7 +103,7 @@ export class OTelGroupCallMembership {
this.myMember.name
);
opentelemetry.trace.setSpan(
this.callMembershipContext = opentelemetry.trace.setSpan(
opentelemetry.context.active(),
this.callMembershipSpan
);
@@ -110,10 +112,11 @@ export class OTelGroupCallMembership {
}
public onLeaveCall() {
this.callMembershipSpan?.addEvent("matrix.leaveCall");
// and end the main span to indicate we've left
if (this.callMembershipSpan) this.callMembershipSpan.end();
this.callMembershipSpan!.addEvent("matrix.leaveCall");
// and end the span to indicate we've left
this.callMembershipSpan!.end();
this.callMembershipSpan = undefined;
this.callMembershipContext = undefined;
}
public onUpdateRoomState(event: MatrixEvent) {
@@ -177,4 +180,34 @@ export class OTelGroupCallMembership {
"matrix.screensharing.enabled": newValue,
});
}
public onSpeaking(member: RoomMember, deviceId: string, speaking: boolean) {
if (speaking) {
// Ensure that there's an audio activity span for this speaker
let deviceMap = this.speakingSpans.get(member);
if (deviceMap === undefined) {
deviceMap = new Map();
this.speakingSpans.set(member, deviceMap);
}
if (!deviceMap.has(deviceId)) {
const span = ElementCallOpenTelemetry.instance.tracer.startSpan(
"matrix.audioActivity",
undefined,
this.callMembershipContext
);
span.setAttribute("matrix.userId", member.userId);
span.setAttribute("matrix.displayName", member.rawDisplayName);
deviceMap.set(deviceId, span);
}
} else {
// End the audio activity span for this speaker, if any
const deviceMap = this.speakingSpans.get(member);
deviceMap?.get(deviceId)?.end();
deviceMap?.delete(deviceId);
if (deviceMap?.size === 0) this.speakingSpans.delete(member);
}
}
}