mirror of
https://github.com/vector-im/element-call.git
synced 2026-07-18 18:59:23 +00:00
Extended logging for mute states
This commit is contained in:
@@ -168,6 +168,15 @@ export interface ResolvedConfigOptions extends ConfigOptions {
|
|||||||
enable_video: boolean;
|
enable_video: boolean;
|
||||||
};
|
};
|
||||||
app_prompt: boolean;
|
app_prompt: boolean;
|
||||||
|
logging?: {
|
||||||
|
livekit_log_level?:
|
||||||
|
| "trace"
|
||||||
|
| "debug"
|
||||||
|
| "info"
|
||||||
|
| "warn"
|
||||||
|
| "error"
|
||||||
|
| "silent";
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export const DEFAULT_CONFIG: ResolvedConfigOptions = {
|
export const DEFAULT_CONFIG: ResolvedConfigOptions = {
|
||||||
@@ -186,4 +195,7 @@ export const DEFAULT_CONFIG: ResolvedConfigOptions = {
|
|||||||
enable_video: true,
|
enable_video: true,
|
||||||
},
|
},
|
||||||
app_prompt: true,
|
app_prompt: true,
|
||||||
|
logging: {
|
||||||
|
livekit_log_level: "info",
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
25
src/main.tsx
25
src/main.tsx
@@ -16,6 +16,7 @@ import { createRoot } from "react-dom/client";
|
|||||||
import "./index.css";
|
import "./index.css";
|
||||||
import { logger } from "matrix-js-sdk/lib/logger";
|
import { logger } from "matrix-js-sdk/lib/logger";
|
||||||
import {
|
import {
|
||||||
|
LogLevel,
|
||||||
setLogExtension as setLKLogExtension,
|
setLogExtension as setLKLogExtension,
|
||||||
setLogLevel as setLKLogLevel,
|
setLogLevel as setLKLogLevel,
|
||||||
} from "livekit-client";
|
} from "livekit-client";
|
||||||
@@ -25,6 +26,7 @@ import { init as initRageshake } from "./settings/rageshake";
|
|||||||
import { Initializer } from "./initializer";
|
import { Initializer } from "./initializer";
|
||||||
import { AppViewModel } from "./state/AppViewModel";
|
import { AppViewModel } from "./state/AppViewModel";
|
||||||
import { globalScope } from "./state/ObservableScope";
|
import { globalScope } from "./state/ObservableScope";
|
||||||
|
import { Config } from "./config/Config.ts";
|
||||||
|
|
||||||
window.setLKLogLevel = setLKLogLevel;
|
window.setLKLogLevel = setLKLogLevel;
|
||||||
|
|
||||||
@@ -32,6 +34,29 @@ initRageshake().catch((e) => {
|
|||||||
logger.error("Failed to initialize rageshake", e);
|
logger.error("Failed to initialize rageshake", e);
|
||||||
});
|
});
|
||||||
setLKLogLevel("info");
|
setLKLogLevel("info");
|
||||||
|
|
||||||
|
// Initialize config and set LiveKit log level accordingly
|
||||||
|
// dev/test deployments can set the log level more verbose via config to help debugging
|
||||||
|
Config.init()
|
||||||
|
.then(() => {
|
||||||
|
const LKLogsMapping = {
|
||||||
|
trace: LogLevel.trace,
|
||||||
|
debug: LogLevel.debug,
|
||||||
|
info: LogLevel.info,
|
||||||
|
warn: LogLevel.warn,
|
||||||
|
error: LogLevel.error,
|
||||||
|
silent: LogLevel.silent,
|
||||||
|
};
|
||||||
|
// const logLevelConfig = Config.get().logging?.livekit_log_level;
|
||||||
|
// DO NOT COMMIT: temporarily hardcode until we add this to config options
|
||||||
|
const logLevelConfig = "debug";
|
||||||
|
|
||||||
|
setLKLogLevel(LKLogsMapping[logLevelConfig ?? "info"] ?? LogLevel.info);
|
||||||
|
})
|
||||||
|
.catch((e) => {
|
||||||
|
logger.error("Failed to initialize config for livekit log level", e);
|
||||||
|
});
|
||||||
|
|
||||||
setLKLogExtension((level, msg, context) => {
|
setLKLogExtension((level, msg, context) => {
|
||||||
// we pass a synthetic logger name of "livekit" to the rageshake to make it easier to read
|
// we pass a synthetic logger name of "livekit" to the rageshake to make it easier to read
|
||||||
global.mx_rage_logger.log(level, "livekit", msg, context);
|
global.mx_rage_logger.log(level, "livekit", msg, context);
|
||||||
|
|||||||
@@ -477,9 +477,7 @@ export function createCallViewModel$(
|
|||||||
mediaDevices,
|
mediaDevices,
|
||||||
muteStates,
|
muteStates,
|
||||||
trackProcessorState$,
|
trackProcessorState$,
|
||||||
logger.getChild(
|
logger.getChild(`[${connection.transport.livekit_service_url}]`),
|
||||||
"[Publisher" + connection.transport.livekit_service_url + "]",
|
|
||||||
),
|
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
connectionManager: connectionManager,
|
connectionManager: connectionManager,
|
||||||
|
|||||||
@@ -45,14 +45,16 @@ import {
|
|||||||
* The Publisher is also responsible for creating the media tracks.
|
* The Publisher is also responsible for creating the media tracks.
|
||||||
*/
|
*/
|
||||||
export class Publisher {
|
export class Publisher {
|
||||||
|
private readonly logger: Logger;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new Publisher.
|
* Creates a new Publisher.
|
||||||
* @param scope - The observable scope to use for managing the publisher.
|
* @param scope - The observable scope to use for managing the publisher.
|
||||||
* @param connection - The connection to use for publishing.
|
* @param connection - The connection to use for publishing.
|
||||||
* @param devices - The media devices to use for audio and video input.
|
* @param devices - The media devices to use for audio and video input.
|
||||||
* @param muteStates - The mute states for audio and video.
|
* @param muteStates - The mute states for audio and video.
|
||||||
* @param e2eeLivekitOptions - The E2EE options to use for the LiveKit room. Use to share the same key provider across connections!.
|
|
||||||
* @param trackerProcessorState$ - The processor state for the video track processor (e.g. background blur).
|
* @param trackerProcessorState$ - The processor state for the video track processor (e.g. background blur).
|
||||||
|
* @param logger - the parent logger
|
||||||
*/
|
*/
|
||||||
public constructor(
|
public constructor(
|
||||||
private scope: ObservableScope,
|
private scope: ObservableScope,
|
||||||
@@ -60,8 +62,9 @@ export class Publisher {
|
|||||||
devices: MediaDevices,
|
devices: MediaDevices,
|
||||||
private readonly muteStates: MuteStates,
|
private readonly muteStates: MuteStates,
|
||||||
trackerProcessorState$: Behavior<ProcessorState>,
|
trackerProcessorState$: Behavior<ProcessorState>,
|
||||||
private logger: Logger,
|
logger: Logger,
|
||||||
) {
|
) {
|
||||||
|
this.logger = logger.getChild(`[Publisher]`);
|
||||||
this.logger.info("Create LiveKit room");
|
this.logger.info("Create LiveKit room");
|
||||||
const { controlledAudioDevices } = getUrlParams();
|
const { controlledAudioDevices } = getUrlParams();
|
||||||
|
|
||||||
@@ -149,6 +152,7 @@ export class Publisher {
|
|||||||
|
|
||||||
private _publishing$ = new BehaviorSubject<boolean>(false);
|
private _publishing$ = new BehaviorSubject<boolean>(false);
|
||||||
public publishing$ = this.scope.behavior(this._publishing$);
|
public publishing$ = this.scope.behavior(this._publishing$);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
* @returns
|
* @returns
|
||||||
@@ -233,6 +237,7 @@ export class Publisher {
|
|||||||
* Stops all tracks that are currently running
|
* Stops all tracks that are currently running
|
||||||
*/
|
*/
|
||||||
public stopTracks(): void {
|
public stopTracks(): void {
|
||||||
|
this.logger.debug("stopTracks called");
|
||||||
this.tracks$.value.forEach((t) => t.stop());
|
this.tracks$.value.forEach((t) => t.stop());
|
||||||
this._tracks$.next([]);
|
this._tracks$.next([]);
|
||||||
}
|
}
|
||||||
@@ -337,6 +342,7 @@ export class Publisher {
|
|||||||
private observeMuteStates(scope: ObservableScope): void {
|
private observeMuteStates(scope: ObservableScope): void {
|
||||||
const lkRoom = this.connection.livekitRoom;
|
const lkRoom = this.connection.livekitRoom;
|
||||||
this.muteStates.audio.setHandler(async (desired) => {
|
this.muteStates.audio.setHandler(async (desired) => {
|
||||||
|
this.logger.debug(`Syncing LiveKit audio mute state to ${desired}`);
|
||||||
try {
|
try {
|
||||||
await lkRoom.localParticipant.setMicrophoneEnabled(desired);
|
await lkRoom.localParticipant.setMicrophoneEnabled(desired);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -345,6 +351,7 @@ export class Publisher {
|
|||||||
return lkRoom.localParticipant.isMicrophoneEnabled;
|
return lkRoom.localParticipant.isMicrophoneEnabled;
|
||||||
});
|
});
|
||||||
this.muteStates.video.setHandler(async (desired) => {
|
this.muteStates.video.setHandler(async (desired) => {
|
||||||
|
this.logger.debug(`Syncing LiveKit video mute state to ${desired}`);
|
||||||
try {
|
try {
|
||||||
await lkRoom.localParticipant.setCameraEnabled(desired);
|
await lkRoom.localParticipant.setCameraEnabled(desired);
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ describe("MuteState", () => {
|
|||||||
} as unknown as MediaDevice<DeviceLabel, SelectedDevice>;
|
} as unknown as MediaDevice<DeviceLabel, SelectedDevice>;
|
||||||
|
|
||||||
const muteState = new MuteState(
|
const muteState = new MuteState(
|
||||||
|
"test-mutestate",
|
||||||
testScope,
|
testScope,
|
||||||
deviceStub,
|
deviceStub,
|
||||||
constant(true),
|
constant(true),
|
||||||
|
|||||||
@@ -52,12 +52,14 @@ export class MuteState<Label, Selected> {
|
|||||||
private readonly handler$ = new BehaviorSubject(defaultHandler);
|
private readonly handler$ = new BehaviorSubject(defaultHandler);
|
||||||
|
|
||||||
public setHandler(handler: Handler): void {
|
public setHandler(handler: Handler): void {
|
||||||
|
logger.debug(`MuteState[${this.description}]: setting handler`);
|
||||||
if (this.handler$.value !== defaultHandler)
|
if (this.handler$.value !== defaultHandler)
|
||||||
throw new Error("Multiple mute state handlers are not supported");
|
throw new Error("Multiple mute state handlers are not supported");
|
||||||
this.handler$.next(handler);
|
this.handler$.next(handler);
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsetHandler(): void {
|
public unsetHandler(): void {
|
||||||
|
logger.debug(`MuteState[${this.description}]: removing handler`);
|
||||||
this.handler$.next(defaultHandler);
|
this.handler$.next(defaultHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -77,16 +79,19 @@ export class MuteState<Label, Selected> {
|
|||||||
this.enabledByDefault$,
|
this.enabledByDefault$,
|
||||||
(canControlDevices, enabledByDefault) => {
|
(canControlDevices, enabledByDefault) => {
|
||||||
logger.info(
|
logger.info(
|
||||||
`MuteState: canControlDevices: ${canControlDevices}, enabled by default: ${enabledByDefault}`,
|
`MuteState[${this.description}]: canControlDevices: ${canControlDevices}, enabled by default: ${enabledByDefault}`,
|
||||||
);
|
);
|
||||||
if (!canControlDevices) {
|
if (!canControlDevices) {
|
||||||
logger.info(
|
logger.info(
|
||||||
`MuteState: devices connected: ${canControlDevices}, disabling`,
|
`MuteState[${this.description}]: devices connected: ${canControlDevices}, disabling`,
|
||||||
);
|
);
|
||||||
// We need to sync the mute state with the handler
|
// We need to sync the mute state with the handler
|
||||||
// to ensure nothing is beeing published.
|
// to ensure nothing is beeing published.
|
||||||
this.handler$.value(false).catch((err) => {
|
this.handler$.value(false).catch((err) => {
|
||||||
logger.error("MuteState-disable: handler error", err);
|
logger.error(
|
||||||
|
"MuteState[${this.description}] disable: handler error",
|
||||||
|
err,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
return { enabled$: of(false), set: null, toggle: null };
|
return { enabled$: of(false), set: null, toggle: null };
|
||||||
}
|
}
|
||||||
@@ -102,12 +107,18 @@ export class MuteState<Label, Selected> {
|
|||||||
let syncing = false;
|
let syncing = false;
|
||||||
|
|
||||||
const sync = async (): Promise<void> => {
|
const sync = async (): Promise<void> => {
|
||||||
if (enabled === latestDesired) syncing = false;
|
if (enabled === latestDesired) {
|
||||||
else {
|
syncing = false;
|
||||||
|
} else {
|
||||||
const previouslyEnabled = enabled;
|
const previouslyEnabled = enabled;
|
||||||
enabled = await firstValueFrom(
|
enabled = await firstValueFrom(
|
||||||
this.handler$.pipe(
|
this.handler$.pipe(
|
||||||
switchMap(async (handler) => handler(latestDesired)),
|
switchMap(async (handler) => {
|
||||||
|
logger.debug(
|
||||||
|
`MuteState[${this.description}]: syncing to ${latestDesired}`,
|
||||||
|
);
|
||||||
|
return handler(latestDesired);
|
||||||
|
}),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
if (enabled === previouslyEnabled) {
|
if (enabled === previouslyEnabled) {
|
||||||
@@ -117,7 +128,10 @@ export class MuteState<Label, Selected> {
|
|||||||
syncing = true;
|
syncing = true;
|
||||||
sync().catch((err) => {
|
sync().catch((err) => {
|
||||||
// TODO: better error handling
|
// TODO: better error handling
|
||||||
logger.error("MuteState: handler error", err);
|
logger.error(
|
||||||
|
"MuteState[${this.description}]: handler error",
|
||||||
|
err,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,7 +143,10 @@ export class MuteState<Label, Selected> {
|
|||||||
syncing = true;
|
syncing = true;
|
||||||
sync().catch((err) => {
|
sync().catch((err) => {
|
||||||
// TODO: better error handling
|
// TODO: better error handling
|
||||||
logger.error("MuteState: handler error", err);
|
logger.error(
|
||||||
|
"MuteState[${this.description}]: handler error",
|
||||||
|
err,
|
||||||
|
);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -158,6 +175,8 @@ export class MuteState<Label, Selected> {
|
|||||||
);
|
);
|
||||||
|
|
||||||
public constructor(
|
public constructor(
|
||||||
|
// A description for logging purposes
|
||||||
|
private readonly description: string,
|
||||||
private readonly scope: ObservableScope,
|
private readonly scope: ObservableScope,
|
||||||
private readonly device: MediaDevice<Label, Selected>,
|
private readonly device: MediaDevice<Label, Selected>,
|
||||||
private readonly joined$: Observable<boolean>,
|
private readonly joined$: Observable<boolean>,
|
||||||
@@ -189,6 +208,7 @@ export class MuteStates {
|
|||||||
);
|
);
|
||||||
|
|
||||||
public readonly audio = new MuteState(
|
public readonly audio = new MuteState(
|
||||||
|
"audio-mutestate",
|
||||||
this.scope,
|
this.scope,
|
||||||
this.mediaDevices.audioInput,
|
this.mediaDevices.audioInput,
|
||||||
this.joined$,
|
this.joined$,
|
||||||
@@ -196,6 +216,7 @@ export class MuteStates {
|
|||||||
constant(false),
|
constant(false),
|
||||||
);
|
);
|
||||||
public readonly video = new MuteState(
|
public readonly video = new MuteState(
|
||||||
|
"video-mutestate",
|
||||||
this.scope,
|
this.scope,
|
||||||
this.mediaDevices.videoInput,
|
this.mediaDevices.videoInput,
|
||||||
this.joined$,
|
this.joined$,
|
||||||
|
|||||||
Reference in New Issue
Block a user