chore: add performance logger (#4896)
Add more performance log to debug loading problem
This commit is contained in:
@@ -58,4 +58,9 @@ export class DebugLogger {
|
|||||||
this._debug.log = console[level].bind(console);
|
this._debug.log = console[level].bind(console);
|
||||||
this._debug(`[${level.toUpperCase()}] ${message}`, ...args);
|
this._debug(`[${level.toUpperCase()}] ${message}`, ...args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
namespace(extra: string) {
|
||||||
|
const currentNamespace = this._debug.namespace;
|
||||||
|
return new DebugLogger(`${currentNamespace}:${extra}`);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,8 +14,10 @@ import { RouterProvider } from 'react-router-dom';
|
|||||||
|
|
||||||
import { CloudSessionProvider } from './providers/session-provider';
|
import { CloudSessionProvider } from './providers/session-provider';
|
||||||
import { router } from './router';
|
import { router } from './router';
|
||||||
|
import { performanceLogger, performanceRenderLogger } from './shared';
|
||||||
import createEmotionCache from './utils/create-emotion-cache';
|
import createEmotionCache from './utils/create-emotion-cache';
|
||||||
|
|
||||||
|
const performanceI18nLogger = performanceLogger.namespace('i18n');
|
||||||
const cache = createEmotionCache();
|
const cache = createEmotionCache();
|
||||||
|
|
||||||
const DevTools = lazy(() =>
|
const DevTools = lazy(() =>
|
||||||
@@ -37,16 +39,23 @@ const future = {
|
|||||||
|
|
||||||
async function loadLanguage() {
|
async function loadLanguage() {
|
||||||
if (environment.isBrowser) {
|
if (environment.isBrowser) {
|
||||||
|
performanceI18nLogger.info('start');
|
||||||
|
|
||||||
const { createI18n, setUpLanguage } = await import('@affine/i18n');
|
const { createI18n, setUpLanguage } = await import('@affine/i18n');
|
||||||
const i18n = createI18n();
|
const i18n = createI18n();
|
||||||
document.documentElement.lang = i18n.language;
|
document.documentElement.lang = i18n.language;
|
||||||
|
|
||||||
|
performanceI18nLogger.info('set up');
|
||||||
await setUpLanguage(i18n);
|
await setUpLanguage(i18n);
|
||||||
|
performanceI18nLogger.info('done');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const languageLoadingPromise = loadLanguage().catch(console.error);
|
const languageLoadingPromise = loadLanguage().catch(console.error);
|
||||||
|
|
||||||
export const App = memo(function App() {
|
export const App = memo(function App() {
|
||||||
|
performanceRenderLogger.info('App');
|
||||||
|
|
||||||
use(languageLoadingPromise);
|
use(languageLoadingPromise);
|
||||||
return (
|
return (
|
||||||
<CacheProvider value={cache}>
|
<CacheProvider value={cache}>
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ import { nanoid } from 'nanoid';
|
|||||||
import { applyUpdate, Doc as YDoc, encodeStateAsUpdate } from 'yjs';
|
import { applyUpdate, Doc as YDoc, encodeStateAsUpdate } from 'yjs';
|
||||||
|
|
||||||
import { WorkspaceAdapters } from '../adapters/workspace';
|
import { WorkspaceAdapters } from '../adapters/workspace';
|
||||||
|
import { performanceLogger } from '../shared';
|
||||||
|
|
||||||
|
const performanceSetupLogger = performanceLogger.namespace('setup');
|
||||||
|
|
||||||
async function tryMigration() {
|
async function tryMigration() {
|
||||||
const value = localStorage.getItem('jotai-workspaces');
|
const value = localStorage.getItem('jotai-workspaces');
|
||||||
@@ -148,6 +151,7 @@ export function createFirstAppData(store: ReturnType<typeof createStore>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export async function setup(store: ReturnType<typeof createStore>) {
|
export async function setup(store: ReturnType<typeof createStore>) {
|
||||||
|
performanceSetupLogger.info('start');
|
||||||
store.set(
|
store.set(
|
||||||
workspaceAdaptersAtom,
|
workspaceAdaptersAtom,
|
||||||
WorkspaceAdapters as Record<
|
WorkspaceAdapters as Record<
|
||||||
@@ -156,11 +160,15 @@ export async function setup(store: ReturnType<typeof createStore>) {
|
|||||||
>
|
>
|
||||||
);
|
);
|
||||||
|
|
||||||
console.log('setup global');
|
performanceSetupLogger.info('setup global');
|
||||||
setupGlobal();
|
setupGlobal();
|
||||||
|
|
||||||
|
performanceSetupLogger.info('try migration');
|
||||||
await tryMigration();
|
await tryMigration();
|
||||||
|
|
||||||
|
performanceSetupLogger.info('get root workspace meta');
|
||||||
// do not read `rootWorkspacesMetadataAtom` before migration
|
// do not read `rootWorkspacesMetadataAtom` before migration
|
||||||
await store.get(rootWorkspacesMetadataAtom);
|
await store.get(rootWorkspacesMetadataAtom);
|
||||||
console.log('setup done');
|
|
||||||
|
performanceSetupLogger.info('done');
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,18 +5,28 @@ import { StrictMode, Suspense } from 'react';
|
|||||||
import { createRoot } from 'react-dom/client';
|
import { createRoot } from 'react-dom/client';
|
||||||
|
|
||||||
import { bootstrapPluginSystem } from './bootstrap/register-plugins';
|
import { bootstrapPluginSystem } from './bootstrap/register-plugins';
|
||||||
|
import { performanceLogger } from './shared';
|
||||||
|
|
||||||
|
const performanceMainLogger = performanceLogger.namespace('main');
|
||||||
async function main() {
|
async function main() {
|
||||||
|
performanceMainLogger.info('start');
|
||||||
const { setup } = await import('./bootstrap/setup');
|
const { setup } = await import('./bootstrap/setup');
|
||||||
|
|
||||||
const rootStore = getCurrentStore();
|
const rootStore = getCurrentStore();
|
||||||
|
performanceMainLogger.info('setup start');
|
||||||
await setup(rootStore);
|
await setup(rootStore);
|
||||||
|
performanceMainLogger.info('setup done');
|
||||||
|
|
||||||
bootstrapPluginSystem(rootStore).catch(err => {
|
bootstrapPluginSystem(rootStore).catch(err => {
|
||||||
console.error('Failed to bootstrap plugin system', err);
|
console.error('Failed to bootstrap plugin system', err);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
performanceMainLogger.info('import app');
|
||||||
const { App } = await import('./app');
|
const { App } = await import('./app');
|
||||||
const root = document.getElementById('app');
|
const root = document.getElementById('app');
|
||||||
assertExists(root);
|
assertExists(root);
|
||||||
|
|
||||||
|
performanceMainLogger.info('render app');
|
||||||
createRoot(root).render(
|
createRoot(root).render(
|
||||||
<StrictMode>
|
<StrictMode>
|
||||||
<Suspense fallback={<WorkspaceFallback key="AppLoading" />}>
|
<Suspense fallback={<WorkspaceFallback key="AppLoading" />}>
|
||||||
|
|||||||
@@ -40,6 +40,7 @@ import { WorkspaceHeader } from '../../components/workspace-header';
|
|||||||
import { useBlockSuiteMetaHelper } from '../../hooks/affine/use-block-suite-meta-helper';
|
import { useBlockSuiteMetaHelper } from '../../hooks/affine/use-block-suite-meta-helper';
|
||||||
import { useTrashModalHelper } from '../../hooks/affine/use-trash-modal-helper';
|
import { useTrashModalHelper } from '../../hooks/affine/use-trash-modal-helper';
|
||||||
import { useCurrentWorkspace } from '../../hooks/current/use-current-workspace';
|
import { useCurrentWorkspace } from '../../hooks/current/use-current-workspace';
|
||||||
|
import { performanceRenderLogger } from '../../shared';
|
||||||
import * as styles from './all-page.css';
|
import * as styles from './all-page.css';
|
||||||
import { EmptyPageList } from './page-list-empty';
|
import { EmptyPageList } from './page-list-empty';
|
||||||
import { useFilteredPageMetas } from './pages';
|
import { useFilteredPageMetas } from './pages';
|
||||||
@@ -302,5 +303,7 @@ export const AllPage = () => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Component = () => {
|
export const Component = () => {
|
||||||
|
performanceRenderLogger.info('AllPage');
|
||||||
|
|
||||||
return <AllPage />;
|
return <AllPage />;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -29,6 +29,7 @@ import { WorkspaceHeader } from '../../components/workspace-header';
|
|||||||
import { useRegisterBlocksuiteEditorCommands } from '../../hooks/affine/use-register-blocksuite-editor-commands';
|
import { useRegisterBlocksuiteEditorCommands } from '../../hooks/affine/use-register-blocksuite-editor-commands';
|
||||||
import { useCurrentWorkspace } from '../../hooks/current/use-current-workspace';
|
import { useCurrentWorkspace } from '../../hooks/current/use-current-workspace';
|
||||||
import { useNavigateHelper } from '../../hooks/use-navigate-helper';
|
import { useNavigateHelper } from '../../hooks/use-navigate-helper';
|
||||||
|
import { performanceRenderLogger } from '../../shared';
|
||||||
|
|
||||||
const DetailPageImpl = (): ReactElement => {
|
const DetailPageImpl = (): ReactElement => {
|
||||||
const { openPage, jumpToSubPath } = useNavigateHelper();
|
const { openPage, jumpToSubPath } = useNavigateHelper();
|
||||||
@@ -145,5 +146,7 @@ export const loader: LoaderFunction = async args => {
|
|||||||
};
|
};
|
||||||
|
|
||||||
export const Component = () => {
|
export const Component = () => {
|
||||||
|
performanceRenderLogger.info('DetailPage');
|
||||||
|
|
||||||
return <DetailPage />;
|
return <DetailPage />;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -15,10 +15,17 @@ import {
|
|||||||
} from 'react-router-dom';
|
} from 'react-router-dom';
|
||||||
|
|
||||||
import { WorkspaceLayout } from '../../layouts/workspace-layout';
|
import { WorkspaceLayout } from '../../layouts/workspace-layout';
|
||||||
|
import { performanceLogger, performanceRenderLogger } from '../../shared';
|
||||||
|
|
||||||
|
const workspaceLoaderLogger = performanceLogger.namespace('workspace_loader');
|
||||||
|
|
||||||
export const loader: LoaderFunction = async args => {
|
export const loader: LoaderFunction = async args => {
|
||||||
|
workspaceLoaderLogger.info('start');
|
||||||
|
|
||||||
const rootStore = getCurrentStore();
|
const rootStore = getCurrentStore();
|
||||||
const meta = await rootStore.get(rootWorkspacesMetadataAtom);
|
const meta = await rootStore.get(rootWorkspacesMetadataAtom);
|
||||||
|
workspaceLoaderLogger.info('meta loaded');
|
||||||
|
|
||||||
const currentMetadata = meta.find(({ id }) => id === args.params.workspaceId);
|
const currentMetadata = meta.find(({ id }) => id === args.params.workspaceId);
|
||||||
if (!currentMetadata) {
|
if (!currentMetadata) {
|
||||||
return redirect('/404');
|
return redirect('/404');
|
||||||
@@ -32,6 +39,8 @@ export const loader: LoaderFunction = async args => {
|
|||||||
}
|
}
|
||||||
if (currentMetadata.flavour === WorkspaceFlavour.AFFINE_CLOUD) {
|
if (currentMetadata.flavour === WorkspaceFlavour.AFFINE_CLOUD) {
|
||||||
const [workspaceAtom] = getBlockSuiteWorkspaceAtom(currentMetadata.id);
|
const [workspaceAtom] = getBlockSuiteWorkspaceAtom(currentMetadata.id);
|
||||||
|
workspaceLoaderLogger.info('get cloud workspace atom');
|
||||||
|
|
||||||
const workspace = await rootStore.get(workspaceAtom);
|
const workspace = await rootStore.get(workspaceAtom);
|
||||||
return (() => {
|
return (() => {
|
||||||
const blockVersions = workspace.meta.blockVersions;
|
const blockVersions = workspace.meta.blockVersions;
|
||||||
@@ -46,10 +55,14 @@ export const loader: LoaderFunction = async args => {
|
|||||||
return false;
|
return false;
|
||||||
})();
|
})();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
workspaceLoaderLogger.info('done');
|
||||||
return null;
|
return null;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const Component = (): ReactElement => {
|
export const Component = (): ReactElement => {
|
||||||
|
performanceRenderLogger.info('WorkspaceLayout');
|
||||||
|
|
||||||
const incompatible = useLoaderData();
|
const incompatible = useLoaderData();
|
||||||
return (
|
return (
|
||||||
<WorkspaceLayout incompatible={!!incompatible}>
|
<WorkspaceLayout incompatible={!!incompatible}>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { DebugLogger } from '@affine/debug';
|
||||||
import type { WorkspaceRegistry } from '@affine/env/workspace';
|
import type { WorkspaceRegistry } from '@affine/env/workspace';
|
||||||
import { Workspace as BlockSuiteWorkspace } from '@blocksuite/store';
|
import { Workspace as BlockSuiteWorkspace } from '@blocksuite/store';
|
||||||
|
|
||||||
@@ -26,3 +27,6 @@ export const pathGenerator = {
|
|||||||
} satisfies {
|
} satisfies {
|
||||||
[Path in WorkspaceSubPath]: (workspaceId: string) => string;
|
[Path in WorkspaceSubPath]: (workspaceId: string) => string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const performanceLogger = new DebugLogger('performance');
|
||||||
|
export const performanceRenderLogger = performanceLogger.namespace('render');
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { DebugLogger } from '@affine/debug';
|
||||||
import { createIndexeddbStorage } from '@blocksuite/store';
|
import { createIndexeddbStorage } from '@blocksuite/store';
|
||||||
import {
|
import {
|
||||||
createIndexedDBDatasource,
|
createIndexedDBDatasource,
|
||||||
@@ -12,6 +13,7 @@ import { createCloudBlobStorage } from '../blob/cloud-blob-storage';
|
|||||||
import { createAffineDataSource } from '.';
|
import { createAffineDataSource } from '.';
|
||||||
import { CRUD } from './crud';
|
import { CRUD } from './crud';
|
||||||
|
|
||||||
|
const performanceLogger = new DebugLogger('performance:sync');
|
||||||
let abortController: AbortController | undefined;
|
let abortController: AbortController | undefined;
|
||||||
|
|
||||||
const downloadRootFromIndexedDB = async (
|
const downloadRootFromIndexedDB = async (
|
||||||
@@ -29,9 +31,13 @@ const downloadRootFromIndexedDB = async (
|
|||||||
};
|
};
|
||||||
|
|
||||||
export async function startSync() {
|
export async function startSync() {
|
||||||
|
performanceLogger.info('start');
|
||||||
|
|
||||||
abortController = new AbortController();
|
abortController = new AbortController();
|
||||||
const signal = abortController.signal;
|
const signal = abortController.signal;
|
||||||
const workspaces = await CRUD.list();
|
const workspaces = await CRUD.list();
|
||||||
|
performanceLogger.info('CRUD list');
|
||||||
|
|
||||||
const syncDocPromises = workspaces.map(workspace =>
|
const syncDocPromises = workspaces.map(workspace =>
|
||||||
downloadRootFromIndexedDB(
|
downloadRootFromIndexedDB(
|
||||||
workspace.id,
|
workspace.id,
|
||||||
@@ -40,6 +46,8 @@ export async function startSync() {
|
|||||||
)
|
)
|
||||||
);
|
);
|
||||||
await Promise.all(syncDocPromises);
|
await Promise.all(syncDocPromises);
|
||||||
|
performanceLogger.info('all sync promise');
|
||||||
|
|
||||||
const syncPromises = workspaces.map(workspace => {
|
const syncPromises = workspaces.map(workspace => {
|
||||||
const remoteDataSource = createAffineDataSource(
|
const remoteDataSource = createAffineDataSource(
|
||||||
workspace.id,
|
workspace.id,
|
||||||
@@ -104,6 +112,7 @@ export async function startSync() {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
await Promise.all([...syncPromises, ...syncBlobPromises]);
|
await Promise.all([...syncPromises, ...syncBlobPromises]);
|
||||||
|
performanceLogger.info('sync done');
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function stopSync() {
|
export async function stopSync() {
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { DebugLogger } from '@affine/debug';
|
||||||
import type { WorkspaceAdapter } from '@affine/env/workspace';
|
import type { WorkspaceAdapter } from '@affine/env/workspace';
|
||||||
import { WorkspaceFlavour } from '@affine/env/workspace';
|
import { WorkspaceFlavour } from '@affine/env/workspace';
|
||||||
import type { BlockHub } from '@blocksuite/blocks';
|
import type { BlockHub } from '@blocksuite/blocks';
|
||||||
@@ -12,6 +13,8 @@ import { z } from 'zod';
|
|||||||
|
|
||||||
import { getOrCreateWorkspace } from './manager';
|
import { getOrCreateWorkspace } from './manager';
|
||||||
|
|
||||||
|
const performanceJotaiLogger = new DebugLogger('performance:jotai');
|
||||||
|
|
||||||
const rootWorkspaceMetadataV1Schema = z.object({
|
const rootWorkspaceMetadataV1Schema = z.object({
|
||||||
id: z.string(),
|
id: z.string(),
|
||||||
flavour: z.nativeEnum(WorkspaceFlavour),
|
flavour: z.nativeEnum(WorkspaceFlavour),
|
||||||
@@ -80,6 +83,8 @@ type FetchMetadata = (
|
|||||||
* @internal
|
* @internal
|
||||||
*/
|
*/
|
||||||
const fetchMetadata: FetchMetadata = async (get, { signal }) => {
|
const fetchMetadata: FetchMetadata = async (get, { signal }) => {
|
||||||
|
performanceJotaiLogger.info('fetch metadata start');
|
||||||
|
|
||||||
const WorkspaceAdapters = get(workspaceAdaptersAtom);
|
const WorkspaceAdapters = get(workspaceAdaptersAtom);
|
||||||
assertExists(WorkspaceAdapters, 'workspace adapter should be defined');
|
assertExists(WorkspaceAdapters, 'workspace adapter should be defined');
|
||||||
const metadata: RootWorkspaceMetadata[] = [];
|
const metadata: RootWorkspaceMetadata[] = [];
|
||||||
@@ -120,6 +125,7 @@ const fetchMetadata: FetchMetadata = async (get, { signal }) => {
|
|||||||
once: true,
|
once: true,
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
performanceJotaiLogger.info('migration done');
|
||||||
}
|
}
|
||||||
|
|
||||||
metadata.push(...loadFromLocalStorage());
|
metadata.push(...loadFromLocalStorage());
|
||||||
@@ -131,11 +137,15 @@ const fetchMetadata: FetchMetadata = async (get, { signal }) => {
|
|||||||
);
|
);
|
||||||
|
|
||||||
for (const Adapter of Adapters) {
|
for (const Adapter of Adapters) {
|
||||||
|
performanceJotaiLogger.info('%s adapter', Adapter.flavour);
|
||||||
|
|
||||||
const { CRUD, flavour: currentFlavour } = Adapter;
|
const { CRUD, flavour: currentFlavour } = Adapter;
|
||||||
if (
|
if (
|
||||||
Adapter.Events['app:access'] &&
|
Adapter.Events['app:access'] &&
|
||||||
!(await Adapter.Events['app:access']())
|
!(await Adapter.Events['app:access']())
|
||||||
) {
|
) {
|
||||||
|
performanceJotaiLogger.info('%s app:access', Adapter.flavour);
|
||||||
|
|
||||||
// skip the adapter if the user doesn't have access to it
|
// skip the adapter if the user doesn't have access to it
|
||||||
const removed = metadata.filter(
|
const removed = metadata.filter(
|
||||||
meta => meta.flavour === currentFlavour
|
meta => meta.flavour === currentFlavour
|
||||||
@@ -148,6 +158,7 @@ const fetchMetadata: FetchMetadata = async (get, { signal }) => {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const item = await CRUD.list();
|
const item = await CRUD.list();
|
||||||
|
performanceJotaiLogger.info('%s CRUD list', Adapter.flavour);
|
||||||
// remove the metadata that is not in the list
|
// remove the metadata that is not in the list
|
||||||
// because we treat the workspace adapter as the source of truth
|
// because we treat the workspace adapter as the source of truth
|
||||||
{
|
{
|
||||||
@@ -179,6 +190,7 @@ const fetchMetadata: FetchMetadata = async (get, { signal }) => {
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('list data error:', e);
|
console.error('list data error:', e);
|
||||||
}
|
}
|
||||||
|
performanceJotaiLogger.info('%s service:start', Adapter.flavour);
|
||||||
Adapter.Events['service:start']?.();
|
Adapter.Events['service:start']?.();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -195,7 +207,7 @@ const fetchMetadata: FetchMetadata = async (get, { signal }) => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
const result = Array.from(metadataMap.values());
|
const result = Array.from(metadataMap.values());
|
||||||
console.info('metadata', result);
|
performanceJotaiLogger.info('fetch metadata done', result);
|
||||||
return result;
|
return result;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user