Skip to content

Commit f2c42c3

Browse files
authored
Merge pull request #4088 from element-hq/toger5/logger-getChild-fix
fix getChild before rageshake setup
2 parents 58a8b73 + 1370933 commit f2c42c3

14 files changed

Lines changed: 132 additions & 31 deletions

.oxlintrc.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@
3434
"/*\nCopyright %%CURRENT_YEAR%% Element Creations Ltd.\n\nSPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial\nPlease see LICENSE in the repository root for full details.\n*/\n\n"
3535
],
3636
"element-call/no-observablescope-leak": "error",
37+
"element-call/no-top-level-logger-get-child": "error",
3738
"jsdoc/empty-tags": "error",
3839
"jsdoc/check-property-names": "error",
3940
"jsdoc/require-param-description": "warn",

eslint/NoTopLevelLoggerGetChild.js

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
/*
2+
Copyright 2026 Element Creations Ltd.
3+
4+
SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
5+
Please see LICENSE in the repository root for full details.
6+
*/
7+
8+
import { ESLintUtils } from "@typescript-eslint/utils";
9+
10+
/**
11+
* Node types that introduce a new non-module scope. A getChild() call nested
12+
* inside any of these is considered "not at the top level".
13+
*/
14+
const FUNCTION_OR_CLASS_TYPES = new Set([
15+
"FunctionDeclaration",
16+
"FunctionExpression",
17+
"ArrowFunctionExpression",
18+
"ClassBody",
19+
]);
20+
21+
const rule = ESLintUtils.RuleCreator(
22+
() => "https://github.com/element-hq/element-call",
23+
)({
24+
name: "no-top-level-logger-get-child",
25+
meta: {
26+
type: "problem",
27+
docs: {
28+
description:
29+
"Disallow calling logger.getChild() at the top level of a module." +
30+
"`getChild` has to be called after the rageshake logger `init()`." +
31+
"If it is called at the top level the child logger will never be setup for rageshakes.",
32+
},
33+
messages: {
34+
noTopLevelGetChild:
35+
"Do not call logger.getChild() at the top level of a module; move it inside a function or class instead that gets called after rageshake logger `init()` is called.",
36+
},
37+
schema: [],
38+
},
39+
create(context) {
40+
// Tracks the local binding names that refer to the logger imported from
41+
// 'matrix-js-sdk/lib/logger', e.g. both `logger` and `rootLogger` in:
42+
// import { logger } from "matrix-js-sdk/lib/logger";
43+
// import { logger as rootLogger } from "matrix-js-sdk/lib/logger";
44+
const loggerNames = new Set();
45+
46+
return {
47+
ImportDeclaration(node) {
48+
if (node.source.value !== "matrix-js-sdk/lib/logger") return;
49+
for (const specifier of node.specifiers) {
50+
if (
51+
specifier.type === "ImportSpecifier" &&
52+
specifier.imported.name === "logger"
53+
) {
54+
loggerNames.add(specifier.local.name);
55+
}
56+
}
57+
},
58+
59+
CallExpression(node) {
60+
// Must be a non-computed member expression call: something.getChild(...)
61+
if (
62+
node.callee.type !== "MemberExpression" ||
63+
node.callee.computed ||
64+
node.callee.property.type !== "Identifier" ||
65+
node.callee.property.name !== "getChild"
66+
)
67+
return;
68+
69+
// The receiver must be one of the tracked logger names.
70+
const object = node.callee.object;
71+
if (object.type !== "Identifier" || !loggerNames.has(object.name))
72+
return;
73+
74+
// Flag the call only when it is at module top level — i.e. there is no
75+
// enclosing function or class body anywhere in the ancestor chain.
76+
const ancestors = context.sourceCode.getAncestors(node);
77+
const isTopLevel = !ancestors.some((a) =>
78+
FUNCTION_OR_CLASS_TYPES.has(a.type),
79+
);
80+
81+
if (isTopLevel) {
82+
context.report({
83+
messageId: "noTopLevelGetChild",
84+
node,
85+
});
86+
}
87+
},
88+
};
89+
},
90+
});
91+
92+
export default rule;

eslint/index.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,5 +2,7 @@ module.exports = {
22
rules: {
33
"copyright-header": require("./CopyrightHeader").default,
44
"no-observablescope-leak": require("./NoObservableScopeLeak").default,
5+
"no-top-level-logger-get-child": require("./NoTopLevelLoggerGetChild")
6+
.default,
57
},
68
};

sdk/helper.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,8 @@ import { scan } from "rxjs";
1515
import { type WidgetHelpers } from "../src/widget";
1616
import { type LivekitRoomItem } from "../src/state/CallViewModel/CallViewModel";
1717

18-
export const logger = rootLogger.getChild("[MatrixRTCSdk]");
19-
2018
export const tryMakeSticky = (widget: WidgetHelpers): void => {
19+
const logger = rootLogger.getChild("[MatrixRTCSdk]");
2120
logger.info("try making sticky MatrixRTCSdk");
2221
void widget.api
2322
.setAlwaysOnScreen(true)

sdk/main.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,8 @@ import { getUrlParams } from "../src/UrlParams";
5252
import { MuteStates } from "../src/state/MuteStates";
5353
import { MediaDevices } from "../src/state/MediaDevices";
5454
import { E2eeType } from "../src/e2ee/e2eeType";
55-
import { currentAndPrev, logger, TEXT_LK_TOPIC, tryMakeSticky } from "./helper";
55+
import { currentAndPrev, TEXT_LK_TOPIC, tryMakeSticky } from "./helper";
56+
import { logger as rootLogger } from "matrix-js-sdk/lib/logger";
5657
import {
5758
ElementWidgetActions,
5859
widget as _widget,
@@ -104,6 +105,7 @@ export async function createMatrixRTCSdk(
104105
id: string = "",
105106
sticky: boolean = false,
106107
): Promise<MatrixRTCSdk> {
108+
const logger = rootLogger.getChild("[MatrixRTCSdk]");
107109
const scope = new ObservableScope();
108110

109111
// widget client

src/e2ee/matrixKeyProvider.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ import {
1010
type MatrixRTCSession,
1111
MatrixRTCSessionEvent,
1212
} from "matrix-js-sdk/lib/matrixrtc";
13-
import { logger as rootLogger } from "matrix-js-sdk/lib/logger";
13+
import { logger as rootLogger, type Logger } from "matrix-js-sdk/lib/logger";
1414
import { type CallMembershipIdentityParts } from "matrix-js-sdk/lib/matrixrtc/EncryptionManager";
15-
const logger = rootLogger.getChild("[MatrixKeyProvider]");
1615

1716
export class MatrixKeyProvider extends BaseKeyProvider {
1817
private rtcSession?: MatrixRTCSession;
19-
18+
private logger: Logger;
2019
public constructor() {
2120
super({ ratchetWindowSize: 10, keyringSize: 256 });
21+
this.logger = rootLogger.getChild("[MatrixKeyProvider]");
2222
}
2323

2424
public setRTCSession(rtcSession: MatrixRTCSession): void {
@@ -60,12 +60,12 @@ export class MatrixKeyProvider extends BaseKeyProvider {
6060
encryptionKeyIndex,
6161
);
6262

63-
logger.debug(
63+
this.logger.debug(
6464
`Sent new key to livekit room=${this.rtcSession?.room.roomId} participantId=${rtcBackendIdentity} (before hash: ${membershipParts.userId}:${membershipParts.deviceId}) encryptionKeyIndex=${encryptionKeyIndex}`,
6565
);
6666
},
6767
(e) => {
68-
logger.error(
68+
this.logger.error(
6969
`Failed to create key material from buffer for livekit room=${this.rtcSession?.room.roomId} participantId before hash=${membershipParts.userId}:${membershipParts.deviceId} encryptionKeyIndex=${encryptionKeyIndex}`,
7070
e,
7171
);

src/livekit/MatrixAudioRenderer.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import {
1414
AudioTrack,
1515
type AudioTrackProps,
1616
} from "@livekit/components-react";
17-
import { logger } from "matrix-js-sdk/lib/logger";
17+
import { logger as rootLogger } from "matrix-js-sdk/lib/logger";
1818

1919
import { useEarpieceAudioConfig } from "../MediaDevicesContext";
2020
import { useReactiveState } from "../useReactiveState";
@@ -40,7 +40,6 @@ export interface MatrixAudioRendererProps {
4040
muted?: boolean;
4141
}
4242

43-
const prefixedLogger = logger.getChild("[MatrixAudioRenderer]");
4443
/**
4544
* Takes care of handling remote participants’ audio tracks and makes sure that microphones and screen share are audible.
4645
*
@@ -60,6 +59,7 @@ export function LivekitRoomAudioRenderer({
6059
validIdentities,
6160
muted,
6261
}: MatrixAudioRendererProps): ReactNode {
62+
const logger = rootLogger.getChild("[MatrixAudioRenderer]");
6363
const tracks = useTracks(
6464
[
6565
Track.Source.Microphone,
@@ -80,7 +80,7 @@ export function LivekitRoomAudioRenderer({
8080
if (!isValid) {
8181
// TODO make sure to also skip the warn logging for the local identity
8282
// Log that there is an invalid identity, that means that someone is publishing audio that is not expected to be in the call.
83-
prefixedLogger.warn(
83+
logger.warn(
8484
`Audio track ${ref.participant.identity} from ${url} has no matching matrix call member`,
8585
`current members: ${validIdentities.join()}`,
8686
`track will not get rendered`,

src/room/InCallView.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,6 @@ declare module "react" {
9494
}
9595
}
9696

97-
const logger = rootLogger.getChild("[InCallView]");
98-
9997
export interface ActiveCallProps extends Omit<
10098
InCallViewProps,
10199
"vm" | "livekitRoom" | "connState" | "footerVm"
@@ -116,7 +114,7 @@ export const ActiveCall: FC<ActiveCallProps> = (props) => {
116114
const mediaDevices = useMediaDevices();
117115
const trackProcessorState$ = useTrackProcessorObservable$();
118116
useEffect(() => {
119-
logger.info("START CALL VIEW SCOPE");
117+
rootLogger.info("START CALL VIEW SCOPE");
120118
const scope = new ObservableScope();
121119
const reactionsReader = new ReactionsReader(scope, props.rtcSession);
122120
const { autoLeaveWhenOthersLeft, waitForCallPickup, sendNotificationType } =
@@ -218,6 +216,7 @@ export const InCallView: FC<InCallViewProps> = ({
218216
muteStates,
219217
onShareClick,
220218
}) => {
219+
const logger = rootLogger.getChild("[InCallView]");
221220
const { t } = useTranslation();
222221
const { sendReaction, toggleRaisedHand } = useReactionsSender();
223222

src/settings/rageshake.ts

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -467,7 +467,7 @@ declare global {
467467
// eslint-disable-next-line no-var, camelcase
468468
var mx_rage_initStoragePromise: Promise<void> | undefined;
469469
}
470-
470+
export let rageshakeLogger: Logger;
471471
/**
472472
* Configure rage shaking support for sending bug reports.
473473
* Modifies globals.
@@ -477,7 +477,8 @@ export async function init(): Promise<void> {
477477
global.mx_rage_logger = new ConsoleLogger();
478478

479479
// configure loglevel based loggers:
480-
setLogExtension(logger, global.mx_rage_logger.log);
480+
rageshakeLogger = logger;
481+
setLogExtension(rageshakeLogger, global.mx_rage_logger.log);
481482

482483
// intercept console logging so that we can get matrix_sdk logs:
483484
// this is nasty, but no logging hooks are provided

src/state/CallViewModel/CallNotificationLifecycle.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,8 +39,6 @@ import { type Behavior } from "../Behavior";
3939
import { type Epoch, type ObservableScope } from "../ObservableScope";
4040
import { type RoomMemberMap } from "./remoteMembers/MatrixMemberMetadata";
4141

42-
const logger = rootLogger.getChild("[CallNotificationLifecycle]");
43-
4442
export type AutoLeaveReason = "allOthersLeft" | "timeout" | "decline";
4543

4644
export interface RingAttempt {
@@ -114,6 +112,7 @@ export function createCallNotificationLifecycle$({
114112
*/
115113
autoLeave$: Observable<AutoLeaveReason>;
116114
} {
115+
const logger = rootLogger.getChild("[CallNotificationLifecycle]");
117116
let ringAttempts$: Observable<RingAttempt> = NEVER;
118117
if (options.waitForCallPickup)
119118
ringAttempts$ = sentCallNotification$.pipe(

0 commit comments

Comments
 (0)