Skip to content

Commit 73f6715

Browse files
[AudioContext] Add new logger for the composite and add log lines for audio context (#5939)
* Add new logger for the composite and add log lines for audio context * Change files * add warning for when the context is recreated * add trouble shooting doc about audio contexts * fix lint * add mock for tests
1 parent 3c3ab04 commit 73f6715

File tree

7 files changed

+103
-2
lines changed

7 files changed

+103
-2
lines changed
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"type": "patch",
3+
"area": "improvement",
4+
"workstream": "Logging",
5+
"comment": "Add new logger for the composite and add log lines for audio context",
6+
"packageName": "@azure/communication-react",
7+
"email": "dmceachern@microsoft.com",
8+
"dependentChangeType": "patch"
9+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"type": "patch",
3+
"area": "improvement",
4+
"workstream": "Logging",
5+
"comment": "Add new logger for the composite and add log lines for audio context",
6+
"packageName": "@azure/communication-react",
7+
"email": "dmceachern@microsoft.com",
8+
"dependentChangeType": "patch"
9+
}

common/config/jest/jestSetup.js

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@ afterAll(() => {
2020
});
2121

2222
window.AudioContext = jest.fn().mockImplementation(() => {
23-
return {};
23+
return {
24+
close: jest.fn().mockResolvedValue(undefined)
25+
};
2426
});
2527
// Add `ResizeObserver` to globals. Without this GridLayout jest tests fail with "ReferenceError: ResizeObserver is not defined"
2628
global.ResizeObserver = require('resize-observer-polyfill');

packages/react-composites/src/composites/common/AudioProvider.tsx

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
// Copyright (c) Microsoft Corporation.
22
// Licensed under the MIT License.
33

4+
import { _logEvent } from '@internal/acs-ui-common';
45
import React, { useContext, createContext, useState, useEffect } from 'react';
6+
import { compositeLogger, EventNames } from './logger';
57

68
/**
79
* @private
@@ -21,7 +23,23 @@ export const ACSAudioProvider = (props: ACSAudioProviderProps): JSX.Element => {
2123
const [stateAudioContext, setStateAudioContext] = useState<AudioContext | undefined>(undefined);
2224

2325
useEffect(() => {
26+
if (stateAudioContext) {
27+
_logEvent(compositeLogger, {
28+
name: EventNames.COMPOSITE_AUDIO_CONTEXT_RECREATED,
29+
level: 'warning',
30+
message: 'AudioContext recreated for composite.',
31+
data: { audioContextState: stateAudioContext.state }
32+
});
33+
} else {
34+
_logEvent(compositeLogger, {
35+
name: EventNames.COMPOSITE_AUDIO_CONTEXT_CREATED,
36+
level: 'info',
37+
message: 'AudioContext created for composite.',
38+
data: { audioContextState: audioContext.state }
39+
});
40+
}
2441
// Create the AudioContext only when the component is rendered
42+
2543
setStateAudioContext(audioContext);
2644
}, [audioContext]);
2745

packages/react-composites/src/composites/common/BaseComposite.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,13 +12,15 @@ import {
1212
useTheme
1313
} from '@fluentui/react';
1414
import { FluentThemeProvider, ParticipantMenuItemsCallback } from '@internal/react-components';
15-
import React, { createContext, useContext } from 'react';
15+
import React, { createContext, useContext, useEffect } from 'react';
1616
import { CompositeLocale, LocalizationProvider } from '../localization';
1717
import { AvatarPersonaDataCallback } from './AvatarPersona';
1818
import { CallCompositeIcons, CallWithChatCompositeIcons, ChatCompositeIcons, DEFAULT_COMPOSITE_ICONS } from './icons';
1919
import { globalLayerHostStyle } from './styles/GlobalHostLayer.styles';
2020
import { useId } from '@fluentui/react-hooks';
2121
import { ACSAudioProvider } from './AudioProvider';
22+
import { compositeLogger, EventNames } from './logger';
23+
import { _logEvent } from '@internal/acs-ui-common';
2224
/**
2325
* Properties common to all composites exported from this library.
2426
*
@@ -112,6 +114,23 @@ export const BaseProvider = (
112114
* We need to create one context for the AudioProvider to ensure that we only have one instance of the AudioContext.
113115
*/
114116
const compositeAudioContext = new AudioContext();
117+
118+
useEffect(() => {
119+
return () => {
120+
if (compositeAudioContext) {
121+
_logEvent(compositeLogger, {
122+
name: EventNames.COMPOSITE_AUDIO_CONTEXT_CLOSED,
123+
level: 'info',
124+
message: 'AudioContext is being closed.',
125+
data: {}
126+
});
127+
compositeAudioContext.close().catch((e) => {
128+
console.error('Failed to close AudioContext', e);
129+
});
130+
}
131+
}
132+
}, []);
133+
115134
// we use Customizer to override default LayerHost injected to <body />
116135
// which stop polluting global dom tree and increase compatibility with react-full-screen
117136
const CompositeElement = (
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
import { createClientLogger } from '@azure/logger';
5+
6+
/**
7+
* @private
8+
*/
9+
export const compositeLogger = createClientLogger('communication-react:composite');
10+
11+
/**
12+
* @private
13+
* Enum for event names used in the composite logger.
14+
* These events are used to track various actions and states within the composite.
15+
*/
16+
export enum EventNames {
17+
// Info
18+
COMPOSITE_INITIALIZED = 'COMPOSITE_INITIALIZED',
19+
COMPOSITE_RENDERED = 'COMPOSITE_RENDERED',
20+
COMPOSITE_CLOSED = 'COMPOSITE_CLOSED',
21+
COMPOSITE_AUDIO_CONTEXT_CREATED = 'COMPOSITE_AUDIO_CONTEXT_CREATED',
22+
COMPOSITE_AUDIO_CONTEXT_CLOSED = 'COMPOSITE_AUDIO_CONTEXT_CLOSED',
23+
COMPOSITE_AUDIO_CONTEXT_RECREATED = 'COMPOSITE_AUDIO_CONTEXT_RECREATED',
24+
25+
// Warning
26+
COMPOSITE_WARNING = 'COMPOSITE_WARNING',
27+
28+
// Error
29+
COMPOSITE_ERROR = 'COMPOSITE_ERROR'
30+
}

packages/storybook8/stories/Concepts/Troubleshooting/Docs.mdx

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,3 +123,17 @@ useEffect(() => {
123123
This `useEffect` will add an event listener for the `pageshow` event, this event when triggered
124124
will share if the page is persisted or cached. If the page is cached we will reload the app so the
125125
user can rejoin the call.
126+
127+
## AudioContext issues
128+
129+
With the nature of react and how it re-renders components it is possible that there will be multiple instances of [`AudioContexts`](https://developer.mozilla.org/en-US/docs/Web/API/AudioContext) created. These Audio Context provide the
130+
audio playback for the calls and for playing DTMF Tones in our composite experiences. If you are experiencing issues with audio playback or DTMF tones not being played, it is likely that there are multiple AudioContexts created.
131+
132+
In order to detect this we provide logs to detect when the AudioContext is created and closed. You can enable these logs by setting the log level to `info` in your application:
133+
134+
```ts
135+
import { setLogLevel } from '@azure/communication-react';
136+
setLogLevel('info');
137+
```
138+
139+
This will log when the AudioContext is created and closed. If you see multiple logs for the creation of the AudioContext, it is likely that there are multiple instances being created.

0 commit comments

Comments
 (0)