Add webrtc metric to OTel (#974)
* stats: Add summery report --------- Co-authored-by: David Baker <dave@matrix.org>
This commit is contained in:
parent
28196a2e9d
commit
390442a4c3
7 changed files with 478 additions and 26 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#042f2ed76c501c10dde98a31732fd92d862e2187",
|
"matrix-js-sdk": "github:matrix-org/matrix-js-sdk#fe79a6fa7ca50fc7d078e11826b5539bb0822c45",
|
||||||
"matrix-widget-api": "^1.3.1",
|
"matrix-widget-api": "^1.3.1",
|
||||||
"mermaid": "^8.13.8",
|
"mermaid": "^8.13.8",
|
||||||
"normalize.css": "^8.0.1",
|
"normalize.css": "^8.0.1",
|
||||||
|
|
|
@ -32,9 +32,17 @@ import {
|
||||||
CallsByUserAndDevice,
|
CallsByUserAndDevice,
|
||||||
GroupCallError,
|
GroupCallError,
|
||||||
GroupCallEvent,
|
GroupCallEvent,
|
||||||
|
GroupCallStatsReport,
|
||||||
} from "matrix-js-sdk/src/webrtc/groupCall";
|
} from "matrix-js-sdk/src/webrtc/groupCall";
|
||||||
|
import {
|
||||||
|
ConnectionStatsReport,
|
||||||
|
ByteSentStatsReport,
|
||||||
|
SummaryStatsReport,
|
||||||
|
} 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
|
||||||
|
@ -91,16 +99,26 @@ interface CallTrackingInfo {
|
||||||
export class OTelGroupCallMembership {
|
export class OTelGroupCallMembership {
|
||||||
private callMembershipSpan?: Span;
|
private callMembershipSpan?: Span;
|
||||||
private groupCallContext?: Context;
|
private groupCallContext?: Context;
|
||||||
private myUserId: string;
|
private myUserId = "unknown";
|
||||||
private myDeviceId: string;
|
private myDeviceId: string;
|
||||||
private myMember: RoomMember;
|
private myMember?: RoomMember;
|
||||||
private callsByCallId = new Map<string, CallTrackingInfo>();
|
private callsByCallId = new Map<string, CallTrackingInfo>();
|
||||||
|
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.myDeviceId = client.getDeviceId();
|
if (clientId) {
|
||||||
this.myMember = groupCall.room.getMember(client.getUserId());
|
this.myUserId = clientId;
|
||||||
|
const myMember = groupCall.room.getMember(clientId);
|
||||||
|
if (myMember) {
|
||||||
|
this.myMember = myMember;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.myDeviceId = client.getDeviceId() || "unknown";
|
||||||
|
this.statsReportSpan = { span: undefined, stats: [] };
|
||||||
this.groupCall.on(GroupCallEvent.CallsChanged, this.onCallsChanged);
|
this.groupCall.on(GroupCallEvent.CallsChanged, this.onCallsChanged);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -125,7 +143,7 @@ export class OTelGroupCallMembership {
|
||||||
this.callMembershipSpan.setAttribute("matrix.deviceId", this.myDeviceId);
|
this.callMembershipSpan.setAttribute("matrix.deviceId", this.myDeviceId);
|
||||||
this.callMembershipSpan.setAttribute(
|
this.callMembershipSpan.setAttribute(
|
||||||
"matrix.displayName",
|
"matrix.displayName",
|
||||||
this.myMember.name
|
this.myMember ? this.myMember.name : "unknown-name"
|
||||||
);
|
);
|
||||||
|
|
||||||
this.groupCallContext = opentelemetry.trace.setSpan(
|
this.groupCallContext = opentelemetry.trace.setSpan(
|
||||||
|
@ -308,4 +326,80 @@ export class OTelGroupCallMembership {
|
||||||
"sender.userId": event.getSender(),
|
"sender.userId": event.getSender(),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public onConnectionStatsReport(
|
||||||
|
statsReport: GroupCallStatsReport<ConnectionStatsReport>
|
||||||
|
) {
|
||||||
|
const type = OTelStatsReportType.ConnectionReport;
|
||||||
|
const data =
|
||||||
|
ObjectFlattener.flattenConnectionStatsReportObject(statsReport);
|
||||||
|
this.buildStatsEventSpan({ type, data });
|
||||||
|
}
|
||||||
|
|
||||||
|
public onByteSentStatsReport(
|
||||||
|
statsReport: GroupCallStatsReport<ByteSentStatsReport>
|
||||||
|
) {
|
||||||
|
const type = OTelStatsReportType.ByteSentReport;
|
||||||
|
const data = ObjectFlattener.flattenByteSentStatsReportObject(statsReport);
|
||||||
|
this.buildStatsEventSpan({ type, data });
|
||||||
|
}
|
||||||
|
|
||||||
|
public onSummaryStatsReport(
|
||||||
|
statsReport: GroupCallStatsReport<SummaryStatsReport>
|
||||||
|
) {
|
||||||
|
const type = OTelStatsReportType.SummaryReport;
|
||||||
|
const data = ObjectFlattener.flattenSummaryStatsReportObject(statsReport);
|
||||||
|
this.buildStatsEventSpan({ type, data });
|
||||||
|
}
|
||||||
|
|
||||||
|
private buildStatsEventSpan(event: OTelStatsReportEvent): void {
|
||||||
|
// @ TODO: fix this - Because on multiple calls we receive multiple stats report spans.
|
||||||
|
// This could be break if stats arrived in same time from different call objects.
|
||||||
|
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.stats.push(event);
|
||||||
|
// if received all three types of stats close this
|
||||||
|
if (this.statsReportSpan.stats.length === 3) {
|
||||||
|
this.statsReportSpan.span.end();
|
||||||
|
this.statsReportSpan = { span: undefined, stats: [] };
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface OTelStatsReportEvent {
|
||||||
|
type: OTelStatsReportType;
|
||||||
|
data: Attributes;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum OTelStatsReportType {
|
||||||
|
ConnectionReport = "matrix.stats.connection",
|
||||||
|
ByteSentReport = "matrix.stats.byteSent",
|
||||||
|
SummaryReport = "matrix.stats.summary",
|
||||||
}
|
}
|
||||||
|
|
97
src/otel/ObjectFlattener.ts
Normal file
97
src/otel/ObjectFlattener.ts
Normal file
|
@ -0,0 +1,97 @@
|
||||||
|
/*
|
||||||
|
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,
|
||||||
|
SummaryStatsReport,
|
||||||
|
} 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
static flattenSummaryStatsReportObject(
|
||||||
|
statsReport: GroupCallStatsReport<SummaryStatsReport>
|
||||||
|
) {
|
||||||
|
const flatObject = {};
|
||||||
|
ObjectFlattener.flattenObjectRecursive(
|
||||||
|
statsReport.report,
|
||||||
|
flatObject,
|
||||||
|
"matrix.stats.summary.",
|
||||||
|
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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -48,7 +48,7 @@ export class ElementCallOpenTelemetry {
|
||||||
return sharedInstance;
|
return sharedInstance;
|
||||||
}
|
}
|
||||||
|
|
||||||
constructor(collectorUrl: string) {
|
constructor(collectorUrl: string | undefined) {
|
||||||
const otlpExporter = new OTLPTraceExporter({
|
const otlpExporter = new OTLPTraceExporter({
|
||||||
url: collectorUrl,
|
url: collectorUrl,
|
||||||
});
|
});
|
||||||
|
|
|
@ -22,12 +22,19 @@ 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,
|
||||||
|
SummaryStatsReport,
|
||||||
|
} 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";
|
||||||
|
@ -337,6 +344,24 @@ export function useGroupCall(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function onConnectionStatsReport(
|
||||||
|
report: GroupCallStatsReport<ConnectionStatsReport>
|
||||||
|
): void {
|
||||||
|
groupCallOTelMembership?.onConnectionStatsReport(report);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onByteSentStatsReport(
|
||||||
|
report: GroupCallStatsReport<ByteSentStatsReport>
|
||||||
|
): void {
|
||||||
|
groupCallOTelMembership?.onByteSentStatsReport(report);
|
||||||
|
}
|
||||||
|
|
||||||
|
function onSummaryStatsReport(
|
||||||
|
report: GroupCallStatsReport<SummaryStatsReport>
|
||||||
|
): void {
|
||||||
|
groupCallOTelMembership?.onSummaryStatsReport(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(
|
||||||
|
@ -353,6 +378,18 @@ 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
|
||||||
|
);
|
||||||
|
|
||||||
|
groupCall.on(GroupCallStatsReportEvent.SummaryStats, onSummaryStatsReport);
|
||||||
|
|
||||||
updateState({
|
updateState({
|
||||||
error: null,
|
error: null,
|
||||||
state: groupCall.state,
|
state: groupCall.state,
|
||||||
|
@ -399,6 +436,18 @@ 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.removeListener(
|
||||||
|
GroupCallStatsReportEvent.SummaryStats,
|
||||||
|
onSummaryStatsReport
|
||||||
|
);
|
||||||
leaveCall();
|
leaveCall();
|
||||||
};
|
};
|
||||||
}, [groupCall, updateState, leaveCall]);
|
}, [groupCall, updateState, leaveCall]);
|
||||||
|
|
215
test/otel/ObjectFlattener-test.ts
Normal file
215
test/otel/ObjectFlattener-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,
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
31
yarn.lock
31
yarn.lock
|
@ -1822,9 +1822,9 @@
|
||||||
integrity sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw==
|
integrity sha512-zMM9Ds+SawiUkakS7y94Ymqx+S0ORzpG3frZirN3l+UlXUmSUR7hF4wxCVqW+ei94JzV5kt0uXBcoOEAuiydrw==
|
||||||
|
|
||||||
"@matrix-org/matrix-sdk-crypto-js@^0.1.0-alpha.5":
|
"@matrix-org/matrix-sdk-crypto-js@^0.1.0-alpha.5":
|
||||||
version "0.1.0-alpha.5"
|
version "0.1.0-alpha.6"
|
||||||
resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-js/-/matrix-sdk-crypto-js-0.1.0-alpha.5.tgz#60ede2c43b9d808ba8cf46085a3b347b290d9658"
|
resolved "https://registry.yarnpkg.com/@matrix-org/matrix-sdk-crypto-js/-/matrix-sdk-crypto-js-0.1.0-alpha.6.tgz#c0bdb9ab0d30179b8ef744d1b4010b0ad0ab9c3a"
|
||||||
integrity sha512-2KjAgWNGfuGLNjJwsrs6gGX157vmcTfNrA4u249utgnMPbJl7QwuUqh1bGxQ0PpK06yvZjgPlkna0lTbuwtuQw==
|
integrity sha512-7hMffzw7KijxDyyH/eUyTfrLeCQHuyU3kaPOKGhcl3DZ3vx7bCncqjGMGTnxNPoP23I6gosvKSbO+3wYOT24Xg==
|
||||||
|
|
||||||
"@matrix-org/olm@https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz":
|
"@matrix-org/olm@https://gitlab.matrix.org/api/v4/projects/27/packages/npm/@matrix-org/olm/-/@matrix-org/olm-3.2.14.tgz":
|
||||||
version "3.2.14"
|
version "3.2.14"
|
||||||
|
@ -5726,7 +5726,12 @@ content-disposition@0.5.4:
|
||||||
dependencies:
|
dependencies:
|
||||||
safe-buffer "5.2.1"
|
safe-buffer "5.2.1"
|
||||||
|
|
||||||
content-type@^1.0.4, content-type@~1.0.4:
|
content-type@^1.0.4:
|
||||||
|
version "1.0.5"
|
||||||
|
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.5.tgz#8b773162656d1d1086784c8f23a54ce6d73d7918"
|
||||||
|
integrity sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==
|
||||||
|
|
||||||
|
content-type@~1.0.4:
|
||||||
version "1.0.4"
|
version "1.0.4"
|
||||||
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
|
resolved "https://registry.yarnpkg.com/content-type/-/content-type-1.0.4.tgz#e138cc75e040c727b1966fe5e5f8c9aee256fe3b"
|
||||||
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
|
integrity sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==
|
||||||
|
@ -10413,9 +10418,9 @@ log-symbols@^4.1.0:
|
||||||
is-unicode-supported "^0.1.0"
|
is-unicode-supported "^0.1.0"
|
||||||
|
|
||||||
loglevel@^1.7.1:
|
loglevel@^1.7.1:
|
||||||
version "1.8.0"
|
version "1.8.1"
|
||||||
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.0.tgz#e7ec73a57e1e7b419cb6c6ac06bf050b67356114"
|
resolved "https://registry.yarnpkg.com/loglevel/-/loglevel-1.8.1.tgz#5c621f83d5b48c54ae93b6156353f555963377b4"
|
||||||
integrity sha512-G6A/nJLRgWOuuwdNuA6koovfEV1YpqqAG4pRUlFaz3jj2QNZ8M4vBqnVA+HBTmU/AMNUtlOsMmSpF6NyOjztbA==
|
integrity sha512-tCRIJM51SHjAayKwC+QAg8hT8vg6z7GSgLJKGvzuPb1Wc+hLzqtuVLxp6/HzSPOozuK+8ErAhy7U/sVzw8Dgfg==
|
||||||
|
|
||||||
long@^2.4.0:
|
long@^2.4.0:
|
||||||
version "2.4.0"
|
version "2.4.0"
|
||||||
|
@ -10545,9 +10550,9 @@ matrix-events-sdk@0.0.1:
|
||||||
resolved "https://registry.yarnpkg.com/matrix-events-sdk/-/matrix-events-sdk-0.0.1.tgz#c8c38911e2cb29023b0bbac8d6f32e0de2c957dd"
|
resolved "https://registry.yarnpkg.com/matrix-events-sdk/-/matrix-events-sdk-0.0.1.tgz#c8c38911e2cb29023b0bbac8d6f32e0de2c957dd"
|
||||||
integrity sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA==
|
integrity sha512-1QEOsXO+bhyCroIe2/A5OwaxHvBm7EsSQ46DEDn8RBIfQwN5HWBpFvyWWR4QY0KHPPnnJdI99wgRiAl7Ad5qaA==
|
||||||
|
|
||||||
"matrix-js-sdk@github:matrix-org/matrix-js-sdk#042f2ed76c501c10dde98a31732fd92d862e2187":
|
"matrix-js-sdk@github:matrix-org/matrix-js-sdk#fe79a6fa7ca50fc7d078e11826b5539bb0822c45":
|
||||||
version "24.0.0"
|
version "24.0.0"
|
||||||
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/042f2ed76c501c10dde98a31732fd92d862e2187"
|
resolved "https://codeload.github.com/matrix-org/matrix-js-sdk/tar.gz/fe79a6fa7ca50fc7d078e11826b5539bb0822c45"
|
||||||
dependencies:
|
dependencies:
|
||||||
"@babel/runtime" "^7.12.5"
|
"@babel/runtime" "^7.12.5"
|
||||||
"@matrix-org/matrix-sdk-crypto-js" "^0.1.0-alpha.5"
|
"@matrix-org/matrix-sdk-crypto-js" "^0.1.0-alpha.5"
|
||||||
|
@ -10570,14 +10575,6 @@ matrix-widget-api@^1.3.1:
|
||||||
"@types/events" "^3.0.0"
|
"@types/events" "^3.0.0"
|
||||||
events "^3.2.0"
|
events "^3.2.0"
|
||||||
|
|
||||||
matrix-widget-api@^1.3.1:
|
|
||||||
version "1.3.1"
|
|
||||||
resolved "https://registry.yarnpkg.com/matrix-widget-api/-/matrix-widget-api-1.3.1.tgz#e38f404c76bb15c113909505c1c1a5b4d781c2f5"
|
|
||||||
integrity sha512-+rN6vGvnXm+fn0uq9r2KWSL/aPtehD6ObC50jYmUcEfgo8CUpf9eUurmjbRlwZkWq3XHXFuKQBUCI9UzqWg37Q==
|
|
||||||
dependencies:
|
|
||||||
"@types/events" "^3.0.0"
|
|
||||||
events "^3.2.0"
|
|
||||||
|
|
||||||
md5.js@^1.3.4:
|
md5.js@^1.3.4:
|
||||||
version "1.3.5"
|
version "1.3.5"
|
||||||
resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
|
resolved "https://registry.yarnpkg.com/md5.js/-/md5.js-1.3.5.tgz#b5d07b8e3216e3e27cd728d72f70d1e6a342005f"
|
||||||
|
|
Loading…
Add table
Reference in a new issue