More work on opentelemetry event reporting
Moastly a re-org to avoid new contexts over React component unmounts/ remounts.
This commit is contained in:
parent
22d2404370
commit
31450219c8
8 changed files with 93 additions and 38 deletions
|
@ -1,3 +1,19 @@
|
|||
/*
|
||||
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 { SpanExporter } from "@opentelemetry/sdk-trace-base";
|
||||
import { ReadableSpan } from "@opentelemetry/sdk-trace-base";
|
||||
import { ExportResult, ExportResultCode } from "@opentelemetry/core";
|
||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
|||
*/
|
||||
|
||||
import opentelemetry, { Context, Span } from "@opentelemetry/api";
|
||||
import { GroupCall, MatrixEvent } from "matrix-js-sdk";
|
||||
import { GroupCall, MatrixClient, MatrixEvent } from "matrix-js-sdk";
|
||||
import { VoipEvent } from "matrix-js-sdk/src/webrtc/call";
|
||||
|
||||
import { tracer } from "./otel";
|
||||
|
@ -27,7 +27,7 @@ function setNestedAttributesFromToDeviceEvent(span: Span, event: VoipEvent) {
|
|||
setSpanEventAttributesRecursive(
|
||||
span,
|
||||
event as unknown as Record<string, unknown>, // XXX Types
|
||||
"matrix.",
|
||||
"matrix.event.",
|
||||
0
|
||||
);
|
||||
}
|
||||
|
@ -64,28 +64,27 @@ export class OTelGroupCallMembership {
|
|||
private context: Context;
|
||||
private callMembershipSpan: Span;
|
||||
|
||||
constructor(groupCall: GroupCall) {
|
||||
const callIdContext = opentelemetry.context
|
||||
.active()
|
||||
.setValue(Symbol("confId"), groupCall.groupCallId);
|
||||
constructor(groupCall: GroupCall, client: MatrixClient) {
|
||||
// Create a new call based on the callIdContext. This context also has a span assigned to it.
|
||||
// Other spans can use this context to extract the parent span.
|
||||
// (When passing this context to startSpan the started span will use the span set in the context (in this case the callSpan) as the parent)
|
||||
|
||||
const myMember = groupCall.room.getMember(client.getUserId());
|
||||
this.context = opentelemetry.trace
|
||||
.setSpan(opentelemetry.context.active(), this.callMembershipSpan)
|
||||
.setValue(Symbol("confId"), groupCall.groupCallId)
|
||||
.setValue(Symbol("matrix.userId"), client.getUserId())
|
||||
.setValue(Symbol("matrix.displayName"), myMember.name);
|
||||
}
|
||||
|
||||
public onJoinCall() {
|
||||
// Create the main span that tracks the time we intend to be in the call
|
||||
this.callMembershipSpan = tracer.startSpan(
|
||||
"otel_groupCallMembershipSpan",
|
||||
undefined,
|
||||
callIdContext
|
||||
this.context
|
||||
);
|
||||
|
||||
// Create a new call based on the callIdContext. This context also has a span assigned to it.
|
||||
// Other spans can use this context to extract the parent span.
|
||||
// (When passing this context to startSpan the started span will use the span set in the context (in this case the callSpan) as the parent)
|
||||
this.context = opentelemetry.trace.setSpan(
|
||||
opentelemetry.context.active(),
|
||||
this.callMembershipSpan
|
||||
);
|
||||
}
|
||||
|
||||
public onJoinCall() {
|
||||
// Here we start a very short span. This is a hack to trigger the posthog exporter.
|
||||
// Only ended spans are processed by the exporter.
|
||||
// We want the exporter to know that a call has started
|
||||
|
@ -107,7 +106,7 @@ export class OTelGroupCallMembership {
|
|||
startCallSpan.end();
|
||||
|
||||
// and end the main span to indicate we've left
|
||||
this.callMembershipSpan.end();
|
||||
if (this.callMembershipSpan) this.callMembershipSpan.end();
|
||||
}
|
||||
|
||||
public onSendStateEvent(stateEvent: MatrixEvent) {}
|
||||
|
|
|
@ -1,4 +1,19 @@
|
|||
/* document-load.ts|js file - the code is the same for both the languages */
|
||||
/*
|
||||
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 {
|
||||
ConsoleSpanExporter,
|
||||
SimpleSpanProcessor,
|
||||
|
|
|
@ -354,20 +354,8 @@ function reducer(
|
|||
function useGroupCallState(
|
||||
client: MatrixClient,
|
||||
groupCall: GroupCall,
|
||||
showPollCallStats: boolean
|
||||
otelGroupCallMembership: OTelGroupCallMembership
|
||||
): InspectorContextState {
|
||||
const [otelMembership] = useState(
|
||||
() => new OTelGroupCallMembership(groupCall)
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
otelMembership.onJoinCall();
|
||||
|
||||
return () => {
|
||||
otelMembership.onLeaveCall();
|
||||
};
|
||||
}, [otelMembership]);
|
||||
|
||||
const [state, dispatch] = useReducer(reducer, {
|
||||
localUserId: client.getUserId(),
|
||||
localSessionId: client.getSessionId(),
|
||||
|
@ -403,7 +391,7 @@ function useGroupCallState(
|
|||
function onSendVoipEvent(event: VoipEvent) {
|
||||
dispatch({ type: CallEvent.SendVoipEvent, rawEvent: event });
|
||||
|
||||
otelMembership.onSendEvent(event);
|
||||
otelGroupCallMembership.onSendEvent(event);
|
||||
}
|
||||
|
||||
function onUndecryptableToDevice(event: MatrixEvent) {
|
||||
|
@ -437,7 +425,7 @@ function useGroupCallState(
|
|||
onUndecryptableToDevice
|
||||
);
|
||||
};
|
||||
}, [client, groupCall, otelMembership]);
|
||||
}, [client, groupCall, otelGroupCallMembership]);
|
||||
|
||||
return state;
|
||||
}
|
||||
|
@ -445,17 +433,19 @@ function useGroupCallState(
|
|||
interface GroupCallInspectorProps {
|
||||
client: MatrixClient;
|
||||
groupCall: GroupCall;
|
||||
otelGroupCallMembership: OTelGroupCallMembership;
|
||||
show: boolean;
|
||||
}
|
||||
|
||||
export function GroupCallInspector({
|
||||
client,
|
||||
groupCall,
|
||||
otelGroupCallMembership,
|
||||
show,
|
||||
}: GroupCallInspectorProps) {
|
||||
const [currentTab, setCurrentTab] = useState("sequence-diagrams");
|
||||
const [selectedUserId, setSelectedUserId] = useState<string>();
|
||||
const state = useGroupCallState(client, groupCall, show);
|
||||
const state = useGroupCallState(client, groupCall, otelGroupCallMembership);
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const [_, setState] = useContext(InspectorContext);
|
||||
|
|
|
@ -81,7 +81,8 @@ export function GroupCallView({
|
|||
screenshareFeeds,
|
||||
participants,
|
||||
unencryptedEventsFromUsers,
|
||||
} = useGroupCall(groupCall);
|
||||
otelGroupCallMembership,
|
||||
} = useGroupCall(groupCall, client);
|
||||
|
||||
const { t } = useTranslation();
|
||||
const { setAudioInput, setVideoInput } = useMediaHandler();
|
||||
|
@ -237,6 +238,7 @@ export function GroupCallView({
|
|||
onLeave={onLeave}
|
||||
isEmbedded={isEmbedded}
|
||||
hideHeader={hideHeader}
|
||||
otelGroupCallMembership={otelGroupCallMembership}
|
||||
/>
|
||||
);
|
||||
} else {
|
||||
|
@ -261,6 +263,7 @@ export function GroupCallView({
|
|||
roomIdOrAlias={roomIdOrAlias}
|
||||
unencryptedEventsFromUsers={unencryptedEventsFromUsers}
|
||||
hideHeader={hideHeader}
|
||||
otelGroupCallMembership={otelGroupCallMembership}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -73,6 +73,7 @@ import { TileDescriptor } from "../video-grid/TileDescriptor";
|
|||
import { AudioSink } from "../video-grid/AudioSink";
|
||||
import { useCallViewKeyboardShortcuts } from "../useCallViewKeyboardShortcuts";
|
||||
import { NewVideoGrid } from "../video-grid/NewVideoGrid";
|
||||
import { OTelGroupCallMembership } from "../otel/OTelGroupCallMembership";
|
||||
|
||||
const canScreenshare = "getDisplayMedia" in (navigator.mediaDevices ?? {});
|
||||
// There is currently a bug in Safari our our code with cloning and sending MediaStreams
|
||||
|
@ -100,6 +101,7 @@ interface Props {
|
|||
roomIdOrAlias: string;
|
||||
unencryptedEventsFromUsers: Set<string>;
|
||||
hideHeader: boolean;
|
||||
otelGroupCallMembership: OTelGroupCallMembership;
|
||||
}
|
||||
|
||||
export function InCallView({
|
||||
|
@ -122,6 +124,7 @@ export function InCallView({
|
|||
roomIdOrAlias,
|
||||
unencryptedEventsFromUsers,
|
||||
hideHeader,
|
||||
otelGroupCallMembership,
|
||||
}: Props) {
|
||||
const { t } = useTranslation();
|
||||
usePreventScroll();
|
||||
|
@ -429,6 +432,7 @@ export function InCallView({
|
|||
<GroupCallInspector
|
||||
client={client}
|
||||
groupCall={groupCall}
|
||||
otelGroupCallMembership={otelGroupCallMembership}
|
||||
show={showInspector}
|
||||
/>
|
||||
{rageshakeRequestModalState.isOpen && (
|
||||
|
|
|
@ -44,6 +44,7 @@ import { GroupCallInspector } from "./GroupCallInspector";
|
|||
import { OverflowMenu } from "./OverflowMenu";
|
||||
import { Size } from "../Avatar";
|
||||
import { ParticipantInfo } from "./useGroupCall";
|
||||
import { OTelGroupCallMembership } from "../otel/OTelGroupCallMembership";
|
||||
|
||||
function getPromptText(
|
||||
networkWaiting: boolean,
|
||||
|
@ -106,6 +107,7 @@ interface Props {
|
|||
onLeave: () => void;
|
||||
isEmbedded: boolean;
|
||||
hideHeader: boolean;
|
||||
otelGroupCallMembership: OTelGroupCallMembership;
|
||||
}
|
||||
|
||||
export const PTTCallView: React.FC<Props> = ({
|
||||
|
@ -119,6 +121,7 @@ export const PTTCallView: React.FC<Props> = ({
|
|||
onLeave,
|
||||
isEmbedded,
|
||||
hideHeader,
|
||||
otelGroupCallMembership,
|
||||
}) => {
|
||||
const { t } = useTranslation();
|
||||
const { modalState: inviteModalState, modalProps: inviteModalProps } =
|
||||
|
@ -192,6 +195,7 @@ export const PTTCallView: React.FC<Props> = ({
|
|||
<GroupCallInspector
|
||||
client={client}
|
||||
groupCall={groupCall}
|
||||
otelGroupCallMembership={otelGroupCallMembership}
|
||||
// Never shown in PTT mode, but must be present to collect call state
|
||||
// https://github.com/vector-im/element-call/issues/328
|
||||
show={false}
|
||||
|
|
|
@ -27,11 +27,13 @@ import { CallFeed, CallFeedEvent } from "matrix-js-sdk/src/webrtc/callFeed";
|
|||
import { RoomMember } from "matrix-js-sdk/src/models/room-member";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { IWidgetApiRequest } from "matrix-widget-api";
|
||||
import { MatrixClient } from "matrix-js-sdk";
|
||||
|
||||
import { usePageUnload } from "./usePageUnload";
|
||||
import { PosthogAnalytics } from "../analytics/PosthogAnalytics";
|
||||
import { TranslatedError, translatedError } from "../TranslatedError";
|
||||
import { ElementWidgetActions, ScreenshareStartData, widget } from "../widget";
|
||||
import { OTelGroupCallMembership } from "../otel/OTelGroupCallMembership";
|
||||
|
||||
export enum ConnectionState {
|
||||
EstablishingCall = "establishing call", // call hasn't been established yet
|
||||
|
@ -66,6 +68,7 @@ export interface UseGroupCallReturnType {
|
|||
participants: Map<RoomMember, Map<string, ParticipantInfo>>;
|
||||
hasLocalParticipant: boolean;
|
||||
unencryptedEventsFromUsers: Set<string>;
|
||||
otelGroupCallMembership: OTelGroupCallMembership;
|
||||
}
|
||||
|
||||
interface State {
|
||||
|
@ -84,6 +87,13 @@ interface State {
|
|||
hasLocalParticipant: boolean;
|
||||
}
|
||||
|
||||
// This is a bit of a hack, but we keep the opentelemetry tracker object at the file
|
||||
// level so that it doesn't pop in & out of existence as react mounts & unmounts
|
||||
// components. The right solution is probably for this to live in the js-sdk and have
|
||||
// the same lifetime as groupcalls themselves.
|
||||
let groupCallOTelMembership: OTelGroupCallMembership;
|
||||
let groupCallOTelMembershipGroupCallId: string;
|
||||
|
||||
function getParticipants(
|
||||
groupCall: GroupCall
|
||||
): Map<RoomMember, Map<string, ParticipantInfo>> {
|
||||
|
@ -112,7 +122,10 @@ function getParticipants(
|
|||
return participants;
|
||||
}
|
||||
|
||||
export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType {
|
||||
export function useGroupCall(
|
||||
groupCall: GroupCall,
|
||||
client: MatrixClient
|
||||
): UseGroupCallReturnType {
|
||||
const [
|
||||
{
|
||||
state,
|
||||
|
@ -146,6 +159,11 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType {
|
|||
hasLocalParticipant: false,
|
||||
});
|
||||
|
||||
if (groupCallOTelMembershipGroupCallId !== groupCall.groupCallId) {
|
||||
groupCallOTelMembership = new OTelGroupCallMembership(groupCall, client);
|
||||
groupCallOTelMembershipGroupCallId = groupCall.groupCallId;
|
||||
}
|
||||
|
||||
const [unencryptedEventsFromUsers, addUnencryptedEventUser] = useReducer(
|
||||
(state: Set<string>, newVal: string) => {
|
||||
return new Set(state).add(newVal);
|
||||
|
@ -383,9 +401,14 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType {
|
|||
console.error(error);
|
||||
updateState({ error });
|
||||
});
|
||||
|
||||
groupCallOTelMembership.onJoinCall();
|
||||
}, [groupCall, updateState]);
|
||||
|
||||
const leave = useCallback(() => groupCall.leave(), [groupCall]);
|
||||
const leave = useCallback(() => {
|
||||
groupCallOTelMembership.onLeaveCall();
|
||||
groupCall.leave();
|
||||
}, [groupCall]);
|
||||
|
||||
const toggleLocalVideoMuted = useCallback(() => {
|
||||
const toggleToMute = !groupCall.isLocalVideoMuted();
|
||||
|
@ -525,5 +548,6 @@ export function useGroupCall(groupCall: GroupCall): UseGroupCallReturnType {
|
|||
participants,
|
||||
hasLocalParticipant,
|
||||
unencryptedEventsFromUsers,
|
||||
otelGroupCallMembership: groupCallOTelMembership,
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue