Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add experimental DebugTracing logger for internal use #18531

Merged
merged 17 commits into from
Apr 16, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,56 @@ describe('ReactDOMServerIntegration', () => {
resetModules();
});

// Test pragmas don't support itRenders abstraction
if (
__EXPERIMENTAL__ &&
require('shared/ReactFeatureFlags').enableDebugTracing
) {
describe('React.unstable_DebugTracingMode', () => {
beforeEach(() => {
spyOnDevAndProd(console, 'log');
});

itRenders('with one child', async render => {
const e = await render(
<React.unstable_DebugTracingMode>
<div>text1</div>
</React.unstable_DebugTracingMode>,
);
const parent = e.parentNode;
expect(parent.childNodes[0].tagName).toBe('DIV');
});

itRenders('mode with several children', async render => {
const Header = props => {
return <p>header</p>;
};
const Footer = props => {
return (
<React.unstable_DebugTracingMode>
<h2>footer</h2>
<h3>about</h3>
</React.unstable_DebugTracingMode>
);
};
const e = await render(
<React.unstable_DebugTracingMode>
<div>text1</div>
<span>text2</span>
<Header />
<Footer />
</React.unstable_DebugTracingMode>,
);
const parent = e.parentNode;
expect(parent.childNodes[0].tagName).toBe('DIV');
expect(parent.childNodes[1].tagName).toBe('SPAN');
expect(parent.childNodes[2].tagName).toBe('P');
expect(parent.childNodes[3].tagName).toBe('H2');
expect(parent.childNodes[4].tagName).toBe('H3');
});
});
}

describe('React.StrictMode', () => {
itRenders('a strict mode with one child', async render => {
const e = await render(
Expand Down
2 changes: 2 additions & 0 deletions packages/react-dom/src/server/ReactPartialRenderer.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
} from 'shared/ReactFeatureFlags';

import {
REACT_DEBUG_TRACING_MODE_TYPE,
REACT_FORWARD_REF_TYPE,
REACT_FRAGMENT_TYPE,
REACT_STRICT_MODE_TYPE,
Expand Down Expand Up @@ -1002,6 +1003,7 @@ class ReactDOMServerRenderer {
}

switch (elementType) {
case REACT_DEBUG_TRACING_MODE_TYPE:
case REACT_STRICT_MODE_TYPE:
case REACT_PROFILER_TYPE:
case REACT_SUSPENSE_LIST_TYPE:
Expand Down
226 changes: 226 additions & 0 deletions packages/react-reconciler/src/DebugTracing.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,226 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

import type {Wakeable} from 'shared/ReactTypes';

import {enableDebugTracing} from 'shared/ReactFeatureFlags';

const nativeConsole: Object = console;
let nativeConsoleLog: null | Function = null;

const pendingGroupArgs: Array<any> = [];
let printedGroupIndex: number = -1;

function group(...groupArgs): void {
pendingGroupArgs.push(groupArgs);

if (nativeConsoleLog === null) {
nativeConsoleLog = nativeConsole.log;
nativeConsole.log = log;
}
}

function groupEnd(): void {
pendingGroupArgs.pop();
while (printedGroupIndex >= pendingGroupArgs.length) {
nativeConsole.groupEnd();
printedGroupIndex--;
}

if (pendingGroupArgs.length === 0) {
nativeConsole.log = nativeConsoleLog;
nativeConsoleLog = null;
}
}

function log(...logArgs): void {
if (printedGroupIndex < pendingGroupArgs.length - 1) {
for (let i = printedGroupIndex + 1; i < pendingGroupArgs.length; i++) {
const groupArgs = pendingGroupArgs[i];
nativeConsole.group(...groupArgs);
}
printedGroupIndex = pendingGroupArgs.length - 1;
}
if (typeof nativeConsoleLog === 'function') {
nativeConsoleLog(...logArgs);
} else {
nativeConsole.log(...logArgs);
}
}

const REACT_LOGO_STYLE =
'background-color: #20232a; color: #61dafb; padding: 0 2px;';

export function logCommitStarted(priorityLabel: string): void {
if (__DEV__) {
if (enableDebugTracing) {
group(
`%c⚛️%c commit%c (priority: ${priorityLabel})`,
REACT_LOGO_STYLE,
'',
'font-weight: normal;',
);
}
}
}

export function logCommitStopped(): void {
if (__DEV__) {
if (enableDebugTracing) {
groupEnd();
}
}
}

const PossiblyWeakMap = typeof WeakMap === 'function' ? WeakMap : Map;
// $FlowFixMe: Flow cannot handle polymorphic WeakMaps
const wakeableIDs: WeakMap<Wakeable, number> = new PossiblyWeakMap();
let wakeableID: number = 0;
function getWakeableID(wakeable: Wakeable): number {
if (!wakeableIDs.has(wakeable)) {
wakeableIDs.set(wakeable, wakeableID++);
}
return ((wakeableIDs.get(wakeable): any): number);
}

export function logComponentSuspended(
componentName: string,
wakeable: Wakeable,
): void {
if (__DEV__) {
if (enableDebugTracing) {
const id = getWakeableID(wakeable);
const display = (wakeable: any).displayName || wakeable;
log(
`%c⚛️%c ${componentName} suspended`,
REACT_LOGO_STYLE,
'color: #80366d; font-weight: bold;',
id,
display,
);
wakeable.then(
() => {
log(
`%c⚛️%c ${componentName} resolved`,
REACT_LOGO_STYLE,
'color: #80366d; font-weight: bold;',
id,
display,
);
},
() => {
log(
`%c⚛️%c ${componentName} rejected`,
REACT_LOGO_STYLE,
'color: #80366d; font-weight: bold;',
id,
display,
);
},
);
}
}
}

export function logLayoutEffectsStarted(priorityLabel: string): void {
if (__DEV__) {
if (enableDebugTracing) {
group(
`%c⚛️%c layout effects%c (priority: ${priorityLabel})`,
REACT_LOGO_STYLE,
'',
'font-weight: normal;',
);
}
}
}

export function logLayoutEffectsStopped(): void {
if (__DEV__) {
if (enableDebugTracing) {
groupEnd();
}
}
}

export function logPassiveEffectsStarted(priorityLabel: string): void {
if (__DEV__) {
if (enableDebugTracing) {
group(
`%c⚛️%c passive effects%c (priority: ${priorityLabel})`,
REACT_LOGO_STYLE,
'',
'font-weight: normal;',
);
}
}
}

export function logPassiveEffectsStopped(): void {
if (__DEV__) {
if (enableDebugTracing) {
groupEnd();
}
}
}

export function logRenderStarted(priorityLabel: string): void {
if (__DEV__) {
if (enableDebugTracing) {
group(
`%c⚛️%c render%c (priority: ${priorityLabel})`,
REACT_LOGO_STYLE,
'',
'font-weight: normal;',
);
}
}
}

export function logRenderStopped(): void {
if (__DEV__) {
if (enableDebugTracing) {
groupEnd();
}
}
}

export function logForceUpdateScheduled(
componentName: string,
priorityLabel: string,
): void {
if (__DEV__) {
if (enableDebugTracing) {
log(
`%c⚛️%c ${componentName} forced update %c(priority: ${priorityLabel})`,
REACT_LOGO_STYLE,
'color: #db2e1f; font-weight: bold;',
'',
);
}
}
}

export function logStateUpdateScheduled(
componentName: string,
priorityLabel: string,
payloadOrAction: any,
): void {
if (__DEV__) {
if (enableDebugTracing) {
log(
`%c⚛️%c ${componentName} updated state %c(priority: ${priorityLabel})`,
REACT_LOGO_STYLE,
'color: #01a252; font-weight: bold;',
'',
payloadOrAction,
);
}
}
}
6 changes: 6 additions & 0 deletions packages/react-reconciler/src/ReactFiber.new.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,15 @@ import {NoWork} from './ReactFiberExpirationTime.new';
import {
NoMode,
ConcurrentMode,
DebugTracingMode,
ProfileMode,
StrictMode,
BlockingMode,
} from './ReactTypeOfMode';
import {
REACT_FORWARD_REF_TYPE,
REACT_FRAGMENT_TYPE,
REACT_DEBUG_TRACING_MODE_TYPE,
REACT_STRICT_MODE_TYPE,
REACT_PROFILER_TYPE,
REACT_PROVIDER_TYPE,
Expand Down Expand Up @@ -488,6 +490,10 @@ export function createFiberFromTypeAndProps(
expirationTime,
key,
);
case REACT_DEBUG_TRACING_MODE_TYPE:
fiberTag = Mode;
mode |= DebugTracingMode;
break;
case REACT_STRICT_MODE_TYPE:
fiberTag = Mode;
mode |= StrictMode;
Expand Down
6 changes: 6 additions & 0 deletions packages/react-reconciler/src/ReactFiber.old.js
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,15 @@ import {NoWork} from './ReactFiberExpirationTime.old';
import {
NoMode,
ConcurrentMode,
DebugTracingMode,
ProfileMode,
StrictMode,
BlockingMode,
} from './ReactTypeOfMode';
import {
REACT_FORWARD_REF_TYPE,
REACT_FRAGMENT_TYPE,
REACT_DEBUG_TRACING_MODE_TYPE,
REACT_STRICT_MODE_TYPE,
REACT_PROFILER_TYPE,
REACT_PROVIDER_TYPE,
Expand Down Expand Up @@ -488,6 +490,10 @@ export function createFiberFromTypeAndProps(
expirationTime,
key,
);
case REACT_DEBUG_TRACING_MODE_TYPE:
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it mean that if I somehow end up with undefined type in a stable release, after this change it would start matching REACT_DEBUG_TRACING_MODE_TYPE (which would also be undefined in stable), and thus I won't get to the catch-all default case that throws? This seems bad.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

REACT_DEBUG_TRACING_MODE_TYPE isn't undefined in stable, just React.DebugTracingMode.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(I backed out the conditional symbol export)

fiberTag = Mode;
mode |= DebugTracingMode;
break;
case REACT_STRICT_MODE_TYPE:
fiberTag = Mode;
mode |= StrictMode;
Expand Down
Loading