docu: Add webrtc metric to OTel
This commit is contained in:
parent
40f5c53c05
commit
66c3d05ae9
5 changed files with 421 additions and 7 deletions
|
@ -53,7 +53,7 @@
|
||||||
"i18next-browser-languagedetector": "^6.1.8",
|
"i18next-browser-languagedetector": "^6.1.8",
|
||||||
"i18next-http-backend": "^1.4.4",
|
"i18next-http-backend": "^1.4.4",
|
||||||
"lodash": "^4.17.21",
|
"lodash": "^4.17.21",
|
||||||
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#23837266fca5ee799b51a722f7b8eefb2f5ac140",
|
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#2cd38e91eee1f5b16a9be0caba6ff19486b95f31",
|
||||||
"matrix-widget-api": "^1.0.0",
|
"matrix-widget-api": "^1.0.0",
|
||||||
"mermaid": "^8.13.8",
|
"mermaid": "^8.13.8",
|
||||||
"normalize.css": "^8.0.1",
|
"normalize.css": "^8.0.1",
|
||||||
|
|
|
@ -23,8 +23,15 @@ import {
|
||||||
RoomMember,
|
RoomMember,
|
||||||
} from "matrix-js-sdk";
|
} from "matrix-js-sdk";
|
||||||
import { VoipEvent } from "matrix-js-sdk/src/webrtc/call";
|
import { VoipEvent } from "matrix-js-sdk/src/webrtc/call";
|
||||||
|
import { GroupCallStatsReport } from "matrix-js-sdk/src/webrtc/groupCall";
|
||||||
|
import {
|
||||||
|
ConnectionStatsReport,
|
||||||
|
ByteSentStatsReport,
|
||||||
|
} from "matrix-js-sdk/src/webrtc/stats/statsReport";
|
||||||
|
import { setSpan } from "@opentelemetry/api/build/esm/trace/context-utils";
|
||||||
|
|
||||||
import { ElementCallOpenTelemetry } from "./otel";
|
import { ElementCallOpenTelemetry } from "./otel";
|
||||||
|
import { ObjectFlattener } from "./ObjectFlattener";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flattens out an object into a single layer with components
|
* Flattens out an object into a single layer with components
|
||||||
|
@ -73,12 +80,24 @@ function flattenVoipEventRecursive(
|
||||||
*/
|
*/
|
||||||
export class OTelGroupCallMembership {
|
export class OTelGroupCallMembership {
|
||||||
private callMembershipSpan?: Span;
|
private callMembershipSpan?: Span;
|
||||||
private myUserId: string;
|
private myUserId = "unknown";
|
||||||
private myMember: RoomMember;
|
private myMember?: RoomMember;
|
||||||
|
private statsReportSpan: {
|
||||||
|
span: Span | undefined;
|
||||||
|
stats: OTelStatsReportEvent[];
|
||||||
|
};
|
||||||
|
|
||||||
constructor(private groupCall: GroupCall, client: MatrixClient) {
|
constructor(private groupCall: GroupCall, client: MatrixClient) {
|
||||||
this.myUserId = client.getUserId();
|
const clientId = client.getUserId();
|
||||||
this.myMember = groupCall.room.getMember(client.getUserId());
|
if (clientId) {
|
||||||
|
this.myUserId = clientId;
|
||||||
|
const myMember = groupCall.room.getMember(clientId);
|
||||||
|
if (myMember) {
|
||||||
|
this.myMember = myMember;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.statsReportSpan = { span: undefined, stats: [] };
|
||||||
|
|
||||||
ElementCallOpenTelemetry.instance.provider.resource.attributes[
|
ElementCallOpenTelemetry.instance.provider.resource.attributes[
|
||||||
SemanticResourceAttributes.SERVICE_NAME
|
SemanticResourceAttributes.SERVICE_NAME
|
||||||
|
@ -98,7 +117,7 @@ export class OTelGroupCallMembership {
|
||||||
this.callMembershipSpan.setAttribute("matrix.userId", this.myUserId);
|
this.callMembershipSpan.setAttribute("matrix.userId", this.myUserId);
|
||||||
this.callMembershipSpan.setAttribute(
|
this.callMembershipSpan.setAttribute(
|
||||||
"matrix.displayName",
|
"matrix.displayName",
|
||||||
this.myMember.name
|
this.myMember ? this.myMember.name : "unknown-name"
|
||||||
);
|
);
|
||||||
|
|
||||||
opentelemetry.trace.setSpan(
|
opentelemetry.trace.setSpan(
|
||||||
|
@ -113,7 +132,7 @@ export class OTelGroupCallMembership {
|
||||||
this.callMembershipSpan?.addEvent("matrix.leaveCall");
|
this.callMembershipSpan?.addEvent("matrix.leaveCall");
|
||||||
|
|
||||||
// and end the main span to indicate we've left
|
// and end the main span to indicate we've left
|
||||||
if (this.callMembershipSpan) this.callMembershipSpan.end();
|
this.callMembershipSpan?.end();
|
||||||
}
|
}
|
||||||
|
|
||||||
public onUpdateRoomState(event: MatrixEvent) {
|
public onUpdateRoomState(event: MatrixEvent) {
|
||||||
|
@ -177,4 +196,65 @@ export class OTelGroupCallMembership {
|
||||||
"matrix.screensharing.enabled": newValue,
|
"matrix.screensharing.enabled": newValue,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public onConnectionStatsReport(
|
||||||
|
statsReport: GroupCallStatsReport<ConnectionStatsReport>
|
||||||
|
) {
|
||||||
|
const type = OTelStatsReportType.ConnectionStatsReport;
|
||||||
|
const data =
|
||||||
|
ObjectFlattener.flattenConnectionStatsReportObject(statsReport);
|
||||||
|
this.buildStatsEventSpan({ type, data });
|
||||||
|
}
|
||||||
|
|
||||||
|
public onByteSentStatsReport(
|
||||||
|
statsReport: GroupCallStatsReport<ByteSentStatsReport>
|
||||||
|
) {
|
||||||
|
const type = OTelStatsReportType.ByteSentStatsReport;
|
||||||
|
const data = ObjectFlattener.flattenByteSentStatsReportObject(statsReport);
|
||||||
|
this.buildStatsEventSpan({ type, data });
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildStatsEventSpan(event: OTelStatsReportEvent): void {
|
||||||
|
if (this.statsReportSpan.span === undefined && this.callMembershipSpan) {
|
||||||
|
const ctx = setSpan(
|
||||||
|
opentelemetry.context.active(),
|
||||||
|
this.callMembershipSpan
|
||||||
|
);
|
||||||
|
this.statsReportSpan.span =
|
||||||
|
ElementCallOpenTelemetry.instance.tracer.startSpan(
|
||||||
|
"matrix.groupCallMembership.statsReport",
|
||||||
|
undefined,
|
||||||
|
ctx
|
||||||
|
);
|
||||||
|
this.statsReportSpan.span.setAttribute(
|
||||||
|
"matrix.confId",
|
||||||
|
this.groupCall.groupCallId
|
||||||
|
);
|
||||||
|
this.statsReportSpan.span.setAttribute("matrix.userId", this.myUserId);
|
||||||
|
this.statsReportSpan.span.setAttribute(
|
||||||
|
"matrix.displayName",
|
||||||
|
this.myMember ? this.myMember.name : "unknown-name"
|
||||||
|
);
|
||||||
|
|
||||||
|
this.statsReportSpan.span.addEvent(event.type, event.data);
|
||||||
|
this.statsReportSpan.stats.push(event);
|
||||||
|
} else if (
|
||||||
|
this.statsReportSpan.span !== undefined &&
|
||||||
|
this.callMembershipSpan
|
||||||
|
) {
|
||||||
|
this.statsReportSpan.span.addEvent(event.type, event.data);
|
||||||
|
this.statsReportSpan.span.end();
|
||||||
|
this.statsReportSpan = { span: undefined, stats: [] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OTelStatsReportEvent {
|
||||||
|
type: OTelStatsReportType;
|
||||||
|
data: Attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OTelStatsReportType {
|
||||||
|
ConnectionStatsReport = "matrix.stats.connection",
|
||||||
|
ByteSentStatsReport = "matrix.stats.byteSent",
|
||||||
}
|
}
|
||||||
|
|
83
src/otel/ObjectFlattener.ts
Normal file
83
src/otel/ObjectFlattener.ts
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
/*
|
||||||
|
Copyright 2023 New Vector Ltd
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
import { Attributes } from "@opentelemetry/api";
|
||||||
|
import { GroupCallStatsReport } from "matrix-js-sdk/src/webrtc/groupCall";
|
||||||
|
import {
|
||||||
|
ByteSentStatsReport,
|
||||||
|
ConnectionStatsReport,
|
||||||
|
} from "matrix-js-sdk/src/webrtc/stats/statsReport";
|
||||||
|
|
||||||
|
export class ObjectFlattener {
|
||||||
|
public static flattenConnectionStatsReportObject(
|
||||||
|
statsReport: GroupCallStatsReport<ConnectionStatsReport>
|
||||||
|
): Attributes {
|
||||||
|
const flatObject = {};
|
||||||
|
ObjectFlattener.flattenObjectRecursive(
|
||||||
|
statsReport.report,
|
||||||
|
flatObject,
|
||||||
|
"matrix.stats.conn.",
|
||||||
|
0
|
||||||
|
);
|
||||||
|
return flatObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static flattenByteSentStatsReportObject(
|
||||||
|
statsReport: GroupCallStatsReport<ByteSentStatsReport>
|
||||||
|
): Attributes {
|
||||||
|
const flatObject = {};
|
||||||
|
ObjectFlattener.flattenObjectRecursive(
|
||||||
|
statsReport.report,
|
||||||
|
flatObject,
|
||||||
|
"matrix.stats.bytesSent.",
|
||||||
|
0
|
||||||
|
);
|
||||||
|
return flatObject;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static flattenObjectRecursive(
|
||||||
|
obj: Object,
|
||||||
|
flatObject: Attributes,
|
||||||
|
prefix: string,
|
||||||
|
depth: number
|
||||||
|
): void {
|
||||||
|
if (depth > 10)
|
||||||
|
throw new Error(
|
||||||
|
"Depth limit exceeded: aborting VoipEvent recursion. Prefix is " +
|
||||||
|
prefix
|
||||||
|
);
|
||||||
|
let entries;
|
||||||
|
if (obj instanceof Map) {
|
||||||
|
entries = obj.entries();
|
||||||
|
} else {
|
||||||
|
entries = Object.entries(obj);
|
||||||
|
}
|
||||||
|
for (const [k, v] of entries) {
|
||||||
|
if (["string", "number", "boolean"].includes(typeof v) || v === null) {
|
||||||
|
let value;
|
||||||
|
value = v === null ? "null" : v;
|
||||||
|
value = typeof v === "number" && Number.isNaN(v) ? "NaN" : value;
|
||||||
|
flatObject[prefix + k] = value;
|
||||||
|
} else if (typeof v === "object") {
|
||||||
|
ObjectFlattener.flattenObjectRecursive(
|
||||||
|
v,
|
||||||
|
flatObject,
|
||||||
|
prefix + k + ".",
|
||||||
|
depth + 1
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -22,12 +22,18 @@ import {
|
||||||
GroupCallErrorCode,
|
GroupCallErrorCode,
|
||||||
GroupCallUnknownDeviceError,
|
GroupCallUnknownDeviceError,
|
||||||
GroupCallError,
|
GroupCallError,
|
||||||
|
GroupCallStatsReportEvent,
|
||||||
|
GroupCallStatsReport,
|
||||||
} from "matrix-js-sdk/src/webrtc/groupCall";
|
} from "matrix-js-sdk/src/webrtc/groupCall";
|
||||||
import { CallFeed, CallFeedEvent } from "matrix-js-sdk/src/webrtc/callFeed";
|
import { CallFeed, CallFeedEvent } from "matrix-js-sdk/src/webrtc/callFeed";
|
||||||
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { IWidgetApiRequest } from "matrix-widget-api";
|
import { IWidgetApiRequest } from "matrix-widget-api";
|
||||||
import { MatrixClient } from "matrix-js-sdk";
|
import { MatrixClient } from "matrix-js-sdk";
|
||||||
|
import {
|
||||||
|
ByteSentStatsReport,
|
||||||
|
ConnectionStatsReport,
|
||||||
|
} from "matrix-js-sdk/src/webrtc/stats/statsReport";
|
||||||
|
|
||||||
import { usePageUnload } from "./usePageUnload";
|
import { usePageUnload } from "./usePageUnload";
|
||||||
import { PosthogAnalytics } from "../analytics/PosthogAnalytics";
|
import { PosthogAnalytics } from "../analytics/PosthogAnalytics";
|
||||||
|
@ -330,6 +336,18 @@ export function useGroupCall(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onConnectionStatsReport(
|
||||||
|
report: GroupCallStatsReport<ConnectionStatsReport>
|
||||||
|
): void {
|
||||||
|
groupCallOTelMembership?.onConnectionStatsReport(report);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onByteSentStatsReport(
|
||||||
|
report: GroupCallStatsReport<ByteSentStatsReport>
|
||||||
|
): void {
|
||||||
|
groupCallOTelMembership?.onByteSentStatsReport(report);
|
||||||
|
}
|
||||||
|
|
||||||
groupCall.on(GroupCallEvent.GroupCallStateChanged, onGroupCallStateChanged);
|
groupCall.on(GroupCallEvent.GroupCallStateChanged, onGroupCallStateChanged);
|
||||||
groupCall.on(GroupCallEvent.UserMediaFeedsChanged, onUserMediaFeedsChanged);
|
groupCall.on(GroupCallEvent.UserMediaFeedsChanged, onUserMediaFeedsChanged);
|
||||||
groupCall.on(
|
groupCall.on(
|
||||||
|
@ -346,6 +364,16 @@ export function useGroupCall(
|
||||||
groupCall.on(GroupCallEvent.ParticipantsChanged, onParticipantsChanged);
|
groupCall.on(GroupCallEvent.ParticipantsChanged, onParticipantsChanged);
|
||||||
groupCall.on(GroupCallEvent.Error, onError);
|
groupCall.on(GroupCallEvent.Error, onError);
|
||||||
|
|
||||||
|
groupCall.on(
|
||||||
|
GroupCallStatsReportEvent.ConnectionStats,
|
||||||
|
onConnectionStatsReport
|
||||||
|
);
|
||||||
|
|
||||||
|
groupCall.on(
|
||||||
|
GroupCallStatsReportEvent.ByteSentStats,
|
||||||
|
onByteSentStatsReport
|
||||||
|
);
|
||||||
|
|
||||||
updateState({
|
updateState({
|
||||||
error: null,
|
error: null,
|
||||||
state: groupCall.state,
|
state: groupCall.state,
|
||||||
|
@ -392,6 +420,14 @@ export function useGroupCall(
|
||||||
onParticipantsChanged
|
onParticipantsChanged
|
||||||
);
|
);
|
||||||
groupCall.removeListener(GroupCallEvent.Error, onError);
|
groupCall.removeListener(GroupCallEvent.Error, onError);
|
||||||
|
groupCall.removeListener(
|
||||||
|
GroupCallStatsReportEvent.ConnectionStats,
|
||||||
|
onConnectionStatsReport
|
||||||
|
);
|
||||||
|
groupCall.removeListener(
|
||||||
|
GroupCallStatsReportEvent.ByteSentStats,
|
||||||
|
onByteSentStatsReport
|
||||||
|
);
|
||||||
groupCall.leave();
|
groupCall.leave();
|
||||||
};
|
};
|
||||||
}, [groupCall, updateState]);
|
}, [groupCall, updateState]);
|
||||||
|
|
215
test/otel/ObjectFlattene-test.ts
Normal file
215
test/otel/ObjectFlattene-test.ts
Normal file
|
@ -0,0 +1,215 @@
|
||||||
|
import { ObjectFlattener } from "../../src/otel/ObjectFlattener";
|
||||||
|
|
||||||
|
/*
|
||||||
|
Copyright 2023 New Vector Ltd
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
describe("ObjectFlattener", () => {
|
||||||
|
const statsReport = {
|
||||||
|
report: {
|
||||||
|
bandwidth: { upload: 426, download: 0 },
|
||||||
|
bitrate: {
|
||||||
|
upload: 426,
|
||||||
|
download: 0,
|
||||||
|
audio: {
|
||||||
|
upload: 124,
|
||||||
|
download: 0,
|
||||||
|
},
|
||||||
|
video: {
|
||||||
|
upload: 302,
|
||||||
|
download: 0,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
packetLoss: {
|
||||||
|
total: 0,
|
||||||
|
download: 0,
|
||||||
|
upload: 0,
|
||||||
|
},
|
||||||
|
framerate: {
|
||||||
|
local: new Map([
|
||||||
|
["LOCAL_AUDIO_TRACK_ID", 0],
|
||||||
|
["LOCAL_VIDEO_TRACK_ID", 30],
|
||||||
|
]),
|
||||||
|
remote: new Map([
|
||||||
|
["REMOTE_AUDIO_TRACK_ID", 0],
|
||||||
|
["REMOTE_VIDEO_TRACK_ID", 60],
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
resolution: {
|
||||||
|
local: new Map([
|
||||||
|
["LOCAL_AUDIO_TRACK_ID", { height: -1, width: -1 }],
|
||||||
|
["LOCAL_VIDEO_TRACK_ID", { height: 460, width: 780 }],
|
||||||
|
]),
|
||||||
|
remote: new Map([
|
||||||
|
["REMOTE_AUDIO_TRACK_ID", { height: -1, width: -1 }],
|
||||||
|
["REMOTE_VIDEO_TRACK_ID", { height: 960, width: 1080 }],
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
codec: {
|
||||||
|
local: new Map([
|
||||||
|
["LOCAL_AUDIO_TRACK_ID", "opus"],
|
||||||
|
["LOCAL_VIDEO_TRACK_ID", "v8"],
|
||||||
|
]),
|
||||||
|
remote: new Map([
|
||||||
|
["REMOTE_AUDIO_TRACK_ID", "opus"],
|
||||||
|
["REMOTE_VIDEO_TRACK_ID", "v9"],
|
||||||
|
]),
|
||||||
|
},
|
||||||
|
transport: [
|
||||||
|
{
|
||||||
|
ip: "ff11::5fa:abcd:999c:c5c5:50000",
|
||||||
|
type: "udp",
|
||||||
|
localIp: "2aaa:9999:2aaa:999:8888:2aaa:2aaa:7777:50000",
|
||||||
|
isFocus: true,
|
||||||
|
localCandidateType: "host",
|
||||||
|
remoteCandidateType: "host",
|
||||||
|
networkType: "ethernet",
|
||||||
|
rtt: NaN,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
ip: "10.10.10.2:22222",
|
||||||
|
type: "tcp",
|
||||||
|
localIp: "10.10.10.100:33333",
|
||||||
|
isFocus: true,
|
||||||
|
localCandidateType: "srfx",
|
||||||
|
remoteCandidateType: "srfx",
|
||||||
|
networkType: "ethernet",
|
||||||
|
rtt: null,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
};
|
||||||
|
describe("on flattenObjectRecursive", () => {
|
||||||
|
it("should flatter an Map object", () => {
|
||||||
|
const flatObject = {};
|
||||||
|
ObjectFlattener.flattenObjectRecursive(
|
||||||
|
statsReport.report.resolution,
|
||||||
|
flatObject,
|
||||||
|
"matrix.stats.conn.resolution.",
|
||||||
|
0
|
||||||
|
);
|
||||||
|
expect(flatObject).toEqual({
|
||||||
|
"matrix.stats.conn.resolution.local.LOCAL_AUDIO_TRACK_ID.height": -1,
|
||||||
|
"matrix.stats.conn.resolution.local.LOCAL_AUDIO_TRACK_ID.width": -1,
|
||||||
|
|
||||||
|
"matrix.stats.conn.resolution.local.LOCAL_VIDEO_TRACK_ID.height": 460,
|
||||||
|
"matrix.stats.conn.resolution.local.LOCAL_VIDEO_TRACK_ID.width": 780,
|
||||||
|
|
||||||
|
"matrix.stats.conn.resolution.remote.REMOTE_AUDIO_TRACK_ID.height": -1,
|
||||||
|
"matrix.stats.conn.resolution.remote.REMOTE_AUDIO_TRACK_ID.width": -1,
|
||||||
|
|
||||||
|
"matrix.stats.conn.resolution.remote.REMOTE_VIDEO_TRACK_ID.height": 960,
|
||||||
|
"matrix.stats.conn.resolution.remote.REMOTE_VIDEO_TRACK_ID.width": 1080,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it("should flatter an Array object", () => {
|
||||||
|
const flatObject = {};
|
||||||
|
ObjectFlattener.flattenObjectRecursive(
|
||||||
|
statsReport.report.transport,
|
||||||
|
flatObject,
|
||||||
|
"matrix.stats.conn.transport.",
|
||||||
|
0
|
||||||
|
);
|
||||||
|
expect(flatObject).toEqual({
|
||||||
|
"matrix.stats.conn.transport.0.ip": "ff11::5fa:abcd:999c:c5c5:50000",
|
||||||
|
"matrix.stats.conn.transport.0.type": "udp",
|
||||||
|
"matrix.stats.conn.transport.0.localIp":
|
||||||
|
"2aaa:9999:2aaa:999:8888:2aaa:2aaa:7777:50000",
|
||||||
|
"matrix.stats.conn.transport.0.isFocus": true,
|
||||||
|
"matrix.stats.conn.transport.0.localCandidateType": "host",
|
||||||
|
"matrix.stats.conn.transport.0.remoteCandidateType": "host",
|
||||||
|
"matrix.stats.conn.transport.0.networkType": "ethernet",
|
||||||
|
"matrix.stats.conn.transport.0.rtt": "NaN",
|
||||||
|
"matrix.stats.conn.transport.1.ip": "10.10.10.2:22222",
|
||||||
|
"matrix.stats.conn.transport.1.type": "tcp",
|
||||||
|
"matrix.stats.conn.transport.1.localIp": "10.10.10.100:33333",
|
||||||
|
"matrix.stats.conn.transport.1.isFocus": true,
|
||||||
|
"matrix.stats.conn.transport.1.localCandidateType": "srfx",
|
||||||
|
"matrix.stats.conn.transport.1.remoteCandidateType": "srfx",
|
||||||
|
"matrix.stats.conn.transport.1.networkType": "ethernet",
|
||||||
|
"matrix.stats.conn.transport.1.rtt": "null",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("on flattenConnectionStatsReportObject", () => {
|
||||||
|
it("should flatten a Report to otel Attributes Object", () => {
|
||||||
|
expect(
|
||||||
|
ObjectFlattener.flattenConnectionStatsReportObject(statsReport)
|
||||||
|
).toEqual({
|
||||||
|
"matrix.stats.conn.bandwidth.download": 0,
|
||||||
|
"matrix.stats.conn.bandwidth.upload": 426,
|
||||||
|
"matrix.stats.conn.bitrate.audio.download": 0,
|
||||||
|
"matrix.stats.conn.bitrate.audio.upload": 124,
|
||||||
|
"matrix.stats.conn.bitrate.download": 0,
|
||||||
|
"matrix.stats.conn.bitrate.upload": 426,
|
||||||
|
"matrix.stats.conn.bitrate.video.download": 0,
|
||||||
|
"matrix.stats.conn.bitrate.video.upload": 302,
|
||||||
|
"matrix.stats.conn.codec.local.LOCAL_AUDIO_TRACK_ID": "opus",
|
||||||
|
"matrix.stats.conn.codec.local.LOCAL_VIDEO_TRACK_ID": "v8",
|
||||||
|
"matrix.stats.conn.codec.remote.REMOTE_AUDIO_TRACK_ID": "opus",
|
||||||
|
"matrix.stats.conn.codec.remote.REMOTE_VIDEO_TRACK_ID": "v9",
|
||||||
|
"matrix.stats.conn.framerate.local.LOCAL_AUDIO_TRACK_ID": 0,
|
||||||
|
"matrix.stats.conn.framerate.local.LOCAL_VIDEO_TRACK_ID": 30,
|
||||||
|
"matrix.stats.conn.framerate.remote.REMOTE_AUDIO_TRACK_ID": 0,
|
||||||
|
"matrix.stats.conn.framerate.remote.REMOTE_VIDEO_TRACK_ID": 60,
|
||||||
|
"matrix.stats.conn.packetLoss.download": 0,
|
||||||
|
"matrix.stats.conn.packetLoss.total": 0,
|
||||||
|
"matrix.stats.conn.packetLoss.upload": 0,
|
||||||
|
"matrix.stats.conn.resolution.local.LOCAL_AUDIO_TRACK_ID.height": -1,
|
||||||
|
"matrix.stats.conn.resolution.local.LOCAL_AUDIO_TRACK_ID.width": -1,
|
||||||
|
"matrix.stats.conn.resolution.local.LOCAL_VIDEO_TRACK_ID.height": 460,
|
||||||
|
"matrix.stats.conn.resolution.local.LOCAL_VIDEO_TRACK_ID.width": 780,
|
||||||
|
"matrix.stats.conn.resolution.remote.REMOTE_AUDIO_TRACK_ID.height": -1,
|
||||||
|
"matrix.stats.conn.resolution.remote.REMOTE_AUDIO_TRACK_ID.width": -1,
|
||||||
|
"matrix.stats.conn.resolution.remote.REMOTE_VIDEO_TRACK_ID.height": 960,
|
||||||
|
"matrix.stats.conn.resolution.remote.REMOTE_VIDEO_TRACK_ID.width": 1080,
|
||||||
|
"matrix.stats.conn.transport.0.ip": "ff11::5fa:abcd:999c:c5c5:50000",
|
||||||
|
"matrix.stats.conn.transport.0.type": "udp",
|
||||||
|
"matrix.stats.conn.transport.0.localIp":
|
||||||
|
"2aaa:9999:2aaa:999:8888:2aaa:2aaa:7777:50000",
|
||||||
|
"matrix.stats.conn.transport.0.isFocus": true,
|
||||||
|
"matrix.stats.conn.transport.0.localCandidateType": "host",
|
||||||
|
"matrix.stats.conn.transport.0.remoteCandidateType": "host",
|
||||||
|
"matrix.stats.conn.transport.0.networkType": "ethernet",
|
||||||
|
"matrix.stats.conn.transport.0.rtt": "NaN",
|
||||||
|
"matrix.stats.conn.transport.1.ip": "10.10.10.2:22222",
|
||||||
|
"matrix.stats.conn.transport.1.type": "tcp",
|
||||||
|
"matrix.stats.conn.transport.1.localIp": "10.10.10.100:33333",
|
||||||
|
"matrix.stats.conn.transport.1.isFocus": true,
|
||||||
|
"matrix.stats.conn.transport.1.localCandidateType": "srfx",
|
||||||
|
"matrix.stats.conn.transport.1.remoteCandidateType": "srfx",
|
||||||
|
"matrix.stats.conn.transport.1.networkType": "ethernet",
|
||||||
|
"matrix.stats.conn.transport.1.rtt": "null",
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe("on flattenByteSendStatsReportObject", () => {
|
||||||
|
const byteSent = {
|
||||||
|
report: new Map([
|
||||||
|
["4aa92608-04c6-428e-8312-93e17602a959", 132093],
|
||||||
|
["a08e4237-ee30-4015-a932-b676aec894b1", 913448],
|
||||||
|
]),
|
||||||
|
};
|
||||||
|
it("should flatten a Report to otel Attributes Object", () => {
|
||||||
|
expect(
|
||||||
|
ObjectFlattener.flattenByteSentStatsReportObject(byteSent)
|
||||||
|
).toEqual({
|
||||||
|
"matrix.stats.bytesSent.4aa92608-04c6-428e-8312-93e17602a959": 132093,
|
||||||
|
"matrix.stats.bytesSent.a08e4237-ee30-4015-a932-b676aec894b1": 913448,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
Loading…
Add table
Reference in a new issue