Skip to content

Commit

Permalink
Remove console logs from Metro when native debugger console is available
Browse files Browse the repository at this point in the history
Summary:
Changelog: [Internal] Remove console logs from Metro when native Fusebox debugger console is available

React Native currently sends all `console.log` messages to Metro, which prints them to the terminal. This feature has been in place since 2019 (D15559151) but is pretty limited when compared to what's available in modern browsers. Most of the limitations can't really be fixed within the constraints of Metro's relatively simple terminal infrastructure.

With the new React Native debugger (codenamed "Fusebox") we aim to fundamentally elevate the debugging experience by shipping a well-tested version of Chrome DevTools with React Native. Chrome DevTools has a rich, interactive console, as well as a host of other debugging features we want developers to notice and use.

To that end, we plan to **strongly nudge users towards the Fusebox console from day 1**. Specifically, if we detect that Fusebox is available and is using an engine which implements CDP `console` support ( = Hermes only for now), we'll no longer send logs to Metro, and will instead display an explanatory message directing users to Fusebox.

Reviewed By: huntie

Differential Revision: D54829811

fbshipit-source-id: 2b1cdb666094f901ff4e7f42b123271be4ce7d10
  • Loading branch information
motiz88 authored and facebook-github-bot committed Mar 20, 2024
1 parent b79e488 commit b7c1bd4
Show file tree
Hide file tree
Showing 11 changed files with 66 additions and 2 deletions.
4 changes: 3 additions & 1 deletion packages/react-native/Libraries/Core/setUpDeveloperTools.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,9 @@ if (__DEV__) {
if (!Platform.isTesting) {
const HMRClient = require('../Utilities/HMRClient');

if (console._isPolyfilled) {
if (global.__FUSEBOX_HAS_FULL_CONSOLE_SUPPORT__) {
HMRClient.unstable_notifyFuseboxConsoleEnabled();
} else if (console._isPolyfilled) {
// We assume full control over the console and send JavaScript logs to Metro.
[
'trace',
Expand Down
28 changes: 28 additions & 0 deletions packages/react-native/Libraries/Utilities/HMRClient.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ let hmrUnavailableReason: string | null = null;
let currentCompileErrorMessage: string | null = null;
let didConnect: boolean = false;
let pendingLogs: Array<[LogLevel, $ReadOnlyArray<mixed>]> = [];
let pendingFuseboxConsoleNotification = false;

type LogLevel =
| 'trace'
Expand All @@ -51,6 +52,7 @@ export type HMRClientNativeInterface = {|
isEnabled: boolean,
scheme?: string,
): void,
unstable_notifyFuseboxConsoleEnabled(): void,
|};

/**
Expand Down Expand Up @@ -140,6 +142,29 @@ const HMRClient: HMRClientNativeInterface = {
}
},

unstable_notifyFuseboxConsoleEnabled() {
if (!hmrClient) {
pendingFuseboxConsoleNotification = true;
return;
}
hmrClient.send(
JSON.stringify({
type: 'log',
level: 'info',
data: [
'\n' +
'\x1b[7m' +
' \x1b[1mJavaScript logs have moved!\x1b[22m They will now appear in the debugger console. ' +
'Tip: Type \x1b[1mj\x1b[22m in the terminal to open the debugger (requires Google Chrome ' +
'or Microsoft Edge).' +
'\x1b[27m' +
'\n',
],
}),
);
pendingFuseboxConsoleNotification = false;
},

// Called once by the bridge on startup, even if Fast Refresh is off.
// It creates the HMR client but doesn't actually set up the socket yet.
setup(
Expand Down Expand Up @@ -316,6 +341,9 @@ function flushEarlyLogs(client: MetroHMRClient) {
pendingLogs.forEach(([level, data]) => {
HMRClient.log(level, data);
});
if (pendingFuseboxConsoleNotification) {
HMRClient.unstable_notifyFuseboxConsoleEnabled();
}
} finally {
pendingLogs.length = 0;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ const HMRClientProdShim: HMRClientNativeInterface = {
disable() {},
registerBundle() {},
log() {},
unstable_notifyFuseboxConsoleEnabled() {},
};

module.exports = HMRClientProdShim;
Original file line number Diff line number Diff line change
Expand Up @@ -8417,6 +8417,7 @@ export type HMRClientNativeInterface = {|
isEnabled: boolean,
scheme?: string
): void,
unstable_notifyFuseboxConsoleEnabled(): void,
|};
declare const HMRClient: HMRClientNativeInterface;
declare module.exports: HMRClient;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,10 @@ class HermesRuntimeTargetDelegate::Impl final : public RuntimeTargetDelegate {
HermesConsoleMessage{message.timestamp, type, std::move(message.args)});
}

bool supportsConsole() const override {
return true;
}

private:
HermesRuntimeTargetDelegate& delegate_;
std::shared_ptr<HermesRuntime> runtime_;
Expand Down Expand Up @@ -173,6 +177,10 @@ void HermesRuntimeTargetDelegate::addConsoleMessage(
impl_->addConsoleMessage(runtime, std::move(message));
}

bool HermesRuntimeTargetDelegate::supportsConsole() const {
return impl_->supportsConsole();
}

#ifdef HERMES_ENABLE_DEBUGGER
CDPDebugAPI& HermesRuntimeTargetDelegate::getCDPDebugAPI() {
return impl_->getCDPDebugAPI();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ class HermesRuntimeTargetDelegate : public RuntimeTargetDelegate {
void addConsoleMessage(jsi::Runtime& runtime, ConsoleMessage message)
override;

bool supportsConsole() const override;

private:
// We use the private implementation idiom to ensure this class has the same
// layout regardless of whether HERMES_ENABLE_DEBUGGER is defined. The net
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,8 @@ void FallbackRuntimeTargetDelegate::addConsoleMessage(
// TODO: Best-effort printing (without RemoteObjects)
}

bool FallbackRuntimeTargetDelegate::supportsConsole() const {
return false;
}

} // namespace facebook::react::jsinspector_modern
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ class FallbackRuntimeTargetDelegate : public RuntimeTargetDelegate {
void addConsoleMessage(jsi::Runtime& runtime, ConsoleMessage message)
override;

bool supportsConsole() const override;

private:
std::string engineDescription_;
};
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,12 @@ class RuntimeTargetDelegate {
virtual void addConsoleMessage(
jsi::Runtime& runtime,
ConsoleMessage message) = 0;

/**
* \returns true if the runtime supports reporting console API calls over CDP.
* \c addConsoleMessage MAY be called even if this method returns false.
*/
virtual bool supportsConsole() const = 0;
};

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -102,8 +102,10 @@ double getTimestampMs() {
} // namespace

void RuntimeTarget::installConsoleHandler() {
auto delegateSupportsConsole = delegate_.supportsConsole();
jsExecutor_([selfWeak = weak_from_this(),
selfExecutor = executorFromThis()](jsi::Runtime& runtime) {
selfExecutor = executorFromThis(),
delegateSupportsConsole](jsi::Runtime& runtime) {
jsi::Value consolePrototype = jsi::Value::null();
auto originalConsoleVal = runtime.global().getProperty(runtime, "console");
std::shared_ptr<jsi::Object> originalConsole;
Expand Down Expand Up @@ -441,6 +443,13 @@ void RuntimeTarget::installConsoleHandler() {
}

runtime.global().setProperty(runtime, "console", console);
if (delegateSupportsConsole) {
// NOTE: If the delegate doesn't report console support, we'll still
// install the console handler for consistency of the runtime environment,
// but not claim that it has full console support.
runtime.global().setProperty(
runtime, "__FUSEBOX_HAS_FULL_CONSOLE_SUPPORT__", true);
}
});
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,7 @@ class MockRuntimeTargetDelegate : public RuntimeTargetDelegate {
addConsoleMessage,
(jsi::Runtime & runtime, ConsoleMessage message),
(override));
MOCK_METHOD(bool, supportsConsole, (), (override, const));
};

class MockRuntimeAgentDelegate : public RuntimeAgentDelegate {
Expand Down

0 comments on commit b7c1bd4

Please sign in to comment.