Enable/disable opentelemetry based on config/user preference
Add config to set collector URL, obey the same analytics setting as posthog. Also refactor into a class to make it easier to manage.
This commit is contained in:
parent
359e055314
commit
3d6ae3fbc3
5 changed files with 98 additions and 33 deletions
|
@ -36,6 +36,13 @@ export interface ConfigOptions {
|
||||||
submit_url: string;
|
submit_url: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Controls whether to to send OpenTelemetry debugging data to collector
|
||||||
|
*/
|
||||||
|
opentelemetry?: {
|
||||||
|
collector_url: string;
|
||||||
|
};
|
||||||
|
|
||||||
// Describes the default homeserver to use. The same format as Element Web
|
// Describes the default homeserver to use. The same format as Element Web
|
||||||
// (without identity servers as we don't use them).
|
// (without identity servers as we don't use them).
|
||||||
default_server_config?: {
|
default_server_config?: {
|
||||||
|
|
|
@ -24,7 +24,7 @@ import {
|
||||||
} 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 { provider, tracer } from "./otel";
|
import { ElementCallOpenTelemetry } from "./otel";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flattens out an object into a single layer with components
|
* Flattens out an object into a single layer with components
|
||||||
|
@ -80,14 +80,17 @@ export class OTelGroupCallMembership {
|
||||||
this.myUserId = client.getUserId();
|
this.myUserId = client.getUserId();
|
||||||
this.myMember = groupCall.room.getMember(client.getUserId());
|
this.myMember = groupCall.room.getMember(client.getUserId());
|
||||||
|
|
||||||
provider.resource.attributes[
|
ElementCallOpenTelemetry.instance.provider.resource.attributes[
|
||||||
SemanticResourceAttributes.SERVICE_NAME
|
SemanticResourceAttributes.SERVICE_NAME
|
||||||
] = `element-call-${this.myUserId}-${client.getDeviceId()}`;
|
] = `element-call-${this.myUserId}-${client.getDeviceId()}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
public onJoinCall() {
|
public onJoinCall() {
|
||||||
// Create the main span that tracks the time we intend to be in the call
|
// Create the main span that tracks the time we intend to be in the call
|
||||||
this.callMembershipSpan = tracer.startSpan("matrix.groupCallMembership");
|
this.callMembershipSpan =
|
||||||
|
ElementCallOpenTelemetry.instance.tracer.startSpan(
|
||||||
|
"matrix.groupCallMembership"
|
||||||
|
);
|
||||||
this.callMembershipSpan.setAttribute(
|
this.callMembershipSpan.setAttribute(
|
||||||
"matrix.confId",
|
"matrix.confId",
|
||||||
this.groupCall.groupCallId
|
this.groupCall.groupCallId
|
||||||
|
|
|
@ -20,32 +20,79 @@ import {
|
||||||
} from "@opentelemetry/sdk-trace-base";
|
} from "@opentelemetry/sdk-trace-base";
|
||||||
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
import { OTLPTraceExporter } from "@opentelemetry/exporter-trace-otlp-http";
|
||||||
import { WebTracerProvider } from "@opentelemetry/sdk-trace-web";
|
import { WebTracerProvider } from "@opentelemetry/sdk-trace-web";
|
||||||
import opentelemetry from "@opentelemetry/api";
|
import opentelemetry, { Tracer } from "@opentelemetry/api";
|
||||||
import { Resource } from "@opentelemetry/resources";
|
import { Resource } from "@opentelemetry/resources";
|
||||||
import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions";
|
import { SemanticResourceAttributes } from "@opentelemetry/semantic-conventions";
|
||||||
|
import { logger } from "@sentry/utils";
|
||||||
|
|
||||||
import { PosthogSpanExporter } from "../analytics/OtelPosthogExporter";
|
import { PosthogSpanExporter } from "../analytics/OtelPosthogExporter";
|
||||||
|
import { Anonymity } from "../analytics/PosthogAnalytics";
|
||||||
|
import { Config } from "../config/Config";
|
||||||
|
import { getSetting, settingsBus } from "../settings/useSetting";
|
||||||
|
|
||||||
const SERVICE_NAME = "element-call";
|
const SERVICE_NAME_BASE = "element-call";
|
||||||
|
|
||||||
const otlpExporter = new OTLPTraceExporter();
|
let sharedInstance: ElementCallOpenTelemetry;
|
||||||
const consoleExporter = new ConsoleSpanExporter();
|
|
||||||
const posthogExporter = new PosthogSpanExporter();
|
|
||||||
|
|
||||||
// This is how we can make Jaeger show a reaonsable service in the dropdown on the left.
|
export class ElementCallOpenTelemetry {
|
||||||
const providerConfig = {
|
private _provider: WebTracerProvider;
|
||||||
resource: new Resource({
|
private _tracer: Tracer;
|
||||||
[SemanticResourceAttributes.SERVICE_NAME]: SERVICE_NAME,
|
private _anonymity: Anonymity;
|
||||||
}),
|
|
||||||
};
|
|
||||||
export const provider = new WebTracerProvider(providerConfig);
|
|
||||||
|
|
||||||
provider.addSpanProcessor(new SimpleSpanProcessor(otlpExporter));
|
static get instance(): ElementCallOpenTelemetry {
|
||||||
provider.addSpanProcessor(new SimpleSpanProcessor(posthogExporter));
|
return sharedInstance;
|
||||||
provider.addSpanProcessor(new SimpleSpanProcessor(consoleExporter));
|
}
|
||||||
opentelemetry.trace.setGlobalTracerProvider(provider);
|
|
||||||
|
|
||||||
// This is not the serviceName shown in jaeger
|
constructor(collectorUrl: string) {
|
||||||
export const tracer = opentelemetry.trace.getTracer(
|
const otlpExporter = new OTLPTraceExporter({
|
||||||
"my-element-call-otl-tracer"
|
url: collectorUrl,
|
||||||
);
|
});
|
||||||
|
const consoleExporter = new ConsoleSpanExporter();
|
||||||
|
const posthogExporter = new PosthogSpanExporter();
|
||||||
|
|
||||||
|
// This is how we can make Jaeger show a reaonsable service in the dropdown on the left.
|
||||||
|
const providerConfig = {
|
||||||
|
resource: new Resource({
|
||||||
|
[SemanticResourceAttributes.SERVICE_NAME]: `${SERVICE_NAME_BASE}-unauthenticated`,
|
||||||
|
}),
|
||||||
|
};
|
||||||
|
this._provider = new WebTracerProvider(providerConfig);
|
||||||
|
|
||||||
|
this._provider.addSpanProcessor(new SimpleSpanProcessor(otlpExporter));
|
||||||
|
this._provider.addSpanProcessor(new SimpleSpanProcessor(posthogExporter));
|
||||||
|
this._provider.addSpanProcessor(new SimpleSpanProcessor(consoleExporter));
|
||||||
|
opentelemetry.trace.setGlobalTracerProvider(this._provider);
|
||||||
|
|
||||||
|
this._tracer = opentelemetry.trace.getTracer(
|
||||||
|
// This is not the serviceName shown in jaeger
|
||||||
|
"my-element-call-otl-tracer"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
public get tracer(): Tracer {
|
||||||
|
return this._tracer;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get provider(): WebTracerProvider {
|
||||||
|
return this._provider;
|
||||||
|
}
|
||||||
|
|
||||||
|
public get anonymity(): Anonymity {
|
||||||
|
return this._anonymity;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function recheckOTelEnabledStatus(optInAnalayticsEnabled: boolean): void {
|
||||||
|
if (optInAnalayticsEnabled && !sharedInstance) {
|
||||||
|
logger.info("Starting OpenTelemetry debug reporting");
|
||||||
|
sharedInstance = new ElementCallOpenTelemetry(
|
||||||
|
Config.get().opentelemetry?.collector_url
|
||||||
|
);
|
||||||
|
} else if (!optInAnalayticsEnabled && sharedInstance) {
|
||||||
|
logger.info("Stopping OpenTelemetry debug reporting");
|
||||||
|
sharedInstance = undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
settingsBus.on("opt-in-analytics", recheckOTelEnabledStatus);
|
||||||
|
recheckOTelEnabledStatus(getSetting("opt-in-analytics", false));
|
||||||
|
|
|
@ -383,7 +383,7 @@ function useGroupCallState(
|
||||||
memberStateEvents,
|
memberStateEvents,
|
||||||
});
|
});
|
||||||
|
|
||||||
otelGroupCallMembership.onUpdateRoomState(event);
|
otelGroupCallMembership?.onUpdateRoomState(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onReceivedVoipEvent(event: MatrixEvent) {
|
function onReceivedVoipEvent(event: MatrixEvent) {
|
||||||
|
@ -393,7 +393,7 @@ function useGroupCallState(
|
||||||
function onSendVoipEvent(event: VoipEvent) {
|
function onSendVoipEvent(event: VoipEvent) {
|
||||||
dispatch({ type: CallEvent.SendVoipEvent, rawEvent: event });
|
dispatch({ type: CallEvent.SendVoipEvent, rawEvent: event });
|
||||||
|
|
||||||
otelGroupCallMembership.onSendEvent(event);
|
otelGroupCallMembership?.onSendEvent(event);
|
||||||
}
|
}
|
||||||
|
|
||||||
function onUndecryptableToDevice(event: MatrixEvent) {
|
function onUndecryptableToDevice(event: MatrixEvent) {
|
||||||
|
|
|
@ -28,12 +28,14 @@ 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 { logger } from "@sentry/utils";
|
||||||
|
|
||||||
import { usePageUnload } from "./usePageUnload";
|
import { usePageUnload } from "./usePageUnload";
|
||||||
import { PosthogAnalytics } from "../analytics/PosthogAnalytics";
|
import { PosthogAnalytics } from "../analytics/PosthogAnalytics";
|
||||||
import { TranslatedError, translatedError } from "../TranslatedError";
|
import { TranslatedError, translatedError } from "../TranslatedError";
|
||||||
import { ElementWidgetActions, ScreenshareStartData, widget } from "../widget";
|
import { ElementWidgetActions, ScreenshareStartData, widget } from "../widget";
|
||||||
import { OTelGroupCallMembership } from "../otel/OTelGroupCallMembership";
|
import { OTelGroupCallMembership } from "../otel/OTelGroupCallMembership";
|
||||||
|
import { ElementCallOpenTelemetry } from "../otel/otel";
|
||||||
|
|
||||||
export enum ConnectionState {
|
export enum ConnectionState {
|
||||||
EstablishingCall = "establishing call", // call hasn't been established yet
|
EstablishingCall = "establishing call", // call hasn't been established yet
|
||||||
|
@ -172,8 +174,14 @@ export function useGroupCall(
|
||||||
});
|
});
|
||||||
|
|
||||||
if (groupCallOTelMembershipGroupCallId !== groupCall.groupCallId) {
|
if (groupCallOTelMembershipGroupCallId !== groupCall.groupCallId) {
|
||||||
groupCallOTelMembership = new OTelGroupCallMembership(groupCall, client);
|
// If the user disables analytics, this will stay around until they leave the call
|
||||||
groupCallOTelMembershipGroupCallId = groupCall.groupCallId;
|
// so analytics will be disabled once they leave.
|
||||||
|
if (ElementCallOpenTelemetry.instance) {
|
||||||
|
groupCallOTelMembership = new OTelGroupCallMembership(groupCall, client);
|
||||||
|
groupCallOTelMembershipGroupCallId = groupCall.groupCallId;
|
||||||
|
} else {
|
||||||
|
groupCallOTelMembership = undefined;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const [unencryptedEventsFromUsers, addUnencryptedEventUser] = useReducer(
|
const [unencryptedEventsFromUsers, addUnencryptedEventUser] = useReducer(
|
||||||
|
@ -414,18 +422,18 @@ export function useGroupCall(
|
||||||
updateState({ error });
|
updateState({ error });
|
||||||
});
|
});
|
||||||
|
|
||||||
groupCallOTelMembership.onJoinCall();
|
groupCallOTelMembership?.onJoinCall();
|
||||||
}, [groupCall, updateState]);
|
}, [groupCall, updateState]);
|
||||||
|
|
||||||
const leave = useCallback(() => {
|
const leave = useCallback(() => {
|
||||||
groupCallOTelMembership.onLeaveCall();
|
groupCallOTelMembership?.onLeaveCall();
|
||||||
groupCall.leave();
|
groupCall.leave();
|
||||||
}, [groupCall]);
|
}, [groupCall]);
|
||||||
|
|
||||||
const toggleLocalVideoMuted = useCallback(() => {
|
const toggleLocalVideoMuted = useCallback(() => {
|
||||||
const toggleToMute = !groupCall.isLocalVideoMuted();
|
const toggleToMute = !groupCall.isLocalVideoMuted();
|
||||||
groupCall.setLocalVideoMuted(toggleToMute);
|
groupCall.setLocalVideoMuted(toggleToMute);
|
||||||
groupCallOTelMembership.onToggleLocalVideoMuted(toggleToMute);
|
groupCallOTelMembership?.onToggleLocalVideoMuted(toggleToMute);
|
||||||
// TODO: These explict posthog calls should be unnecessary now with the posthog otel exporter?
|
// TODO: These explict posthog calls should be unnecessary now with the posthog otel exporter?
|
||||||
PosthogAnalytics.instance.eventMuteCamera.track(
|
PosthogAnalytics.instance.eventMuteCamera.track(
|
||||||
toggleToMute,
|
toggleToMute,
|
||||||
|
@ -436,7 +444,7 @@ export function useGroupCall(
|
||||||
const setMicrophoneMuted = useCallback(
|
const setMicrophoneMuted = useCallback(
|
||||||
(setMuted) => {
|
(setMuted) => {
|
||||||
groupCall.setMicrophoneMuted(setMuted);
|
groupCall.setMicrophoneMuted(setMuted);
|
||||||
groupCallOTelMembership.onSetMicrophoneMuted(setMuted);
|
groupCallOTelMembership?.onSetMicrophoneMuted(setMuted);
|
||||||
PosthogAnalytics.instance.eventMuteMicrophone.track(
|
PosthogAnalytics.instance.eventMuteMicrophone.track(
|
||||||
setMuted,
|
setMuted,
|
||||||
groupCall.groupCallId
|
groupCall.groupCallId
|
||||||
|
@ -447,12 +455,12 @@ export function useGroupCall(
|
||||||
|
|
||||||
const toggleMicrophoneMuted = useCallback(() => {
|
const toggleMicrophoneMuted = useCallback(() => {
|
||||||
const toggleToMute = !groupCall.isMicrophoneMuted();
|
const toggleToMute = !groupCall.isMicrophoneMuted();
|
||||||
groupCallOTelMembership.onToggleMicrophoneMuted(toggleToMute);
|
groupCallOTelMembership?.onToggleMicrophoneMuted(toggleToMute);
|
||||||
setMicrophoneMuted(toggleToMute);
|
setMicrophoneMuted(toggleToMute);
|
||||||
}, [groupCall, setMicrophoneMuted]);
|
}, [groupCall, setMicrophoneMuted]);
|
||||||
|
|
||||||
const toggleScreensharing = useCallback(async () => {
|
const toggleScreensharing = useCallback(async () => {
|
||||||
groupCallOTelMembership.onToggleScreensharing(!groupCall.isScreensharing);
|
groupCallOTelMembership?.onToggleScreensharing(!groupCall.isScreensharing);
|
||||||
|
|
||||||
if (!groupCall.isScreensharing()) {
|
if (!groupCall.isScreensharing()) {
|
||||||
// toggling on
|
// toggling on
|
||||||
|
|
Loading…
Reference in a new issue