Skip to content

Commit

Permalink
feat: unify server monitors usage, add logger for data loader (#6818)
Browse files Browse the repository at this point in the history
  • Loading branch information
zllkjc authored Feb 11, 2025
1 parent 721d9c1 commit 02ca983
Show file tree
Hide file tree
Showing 13 changed files with 67 additions and 81 deletions.
8 changes: 8 additions & 0 deletions .changeset/great-wolves-flash.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
---
'@modern-js/runtime': patch
'@modern-js/prod-server': patch
'@modern-js/server-core': patch
---

feat: unify server monitors usage, add error logger for data loader
feat: 统一 server 监控的使用方式,为 data loader 添加错误日志
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import { createLoaderManager } from '../loader/loaderManager';
import { createRoot } from '../react';
import type { SSRServerContext } from '../types';
import { CHUNK_CSS_PLACEHOLDER } from './constants';
import { SSRErrors } from './tracer';
import { getSSRConfigByEntry, getSSRMode } from './utils';

export type { RequestHandlerConfig as HandleRequestConfig } from '@modern-js/app-tools';
Expand Down Expand Up @@ -234,6 +235,14 @@ export const createRequestHandler: CreateRequestHandler = async (
context.ssrContext?.response.status(context.routerContext?.statusCode);
}

// log error by monitors when data loader throw error
const errors = Object.values(
(context.routerContext?.errors || {}) as Record<string, Error>,
);
if (errors.length > 0) {
options.onError(errors[0], SSRErrors.LOADER_ERROR);
}

context.initialData = initialData;

const redirectResponse = getRedirectResponse(initialData);
Expand Down
18 changes: 6 additions & 12 deletions packages/runtime/plugin-runtime/src/core/server/stream/shared.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { OnError } from '@modern-js/app-tools';
import { run } from '@modern-js/runtime-utils/node';
import { time } from '@modern-js/runtime-utils/time';
import { parseHeaders } from '@modern-js/runtime-utils/universal/request';
Expand All @@ -11,12 +12,7 @@ import type { RuntimeContext } from '../../context';
import { wrapRuntimeContextProvider } from '../../react/wrapper';
import type { HandleRequestConfig } from '../requestHandler';
import type { RenderStreaming, SSRConfig } from '../shared';
import {
SSRErrors,
SSRTimings,
createOnError,
createOnTiming,
} from '../tracer';
import { SSRErrors, SSRTimings } from '../tracer';
import { getSSRConfigByEntry } from '../utils';

export type CreateReadableStreamFromElementOptions = {
Expand All @@ -33,7 +29,7 @@ export type CreateReadableStreamFromElementOptions = {
onShellReady?: () => void;
onShellError?: (error: unknown) => void;
onAllReady?: () => void;
onError?: (error: unknown) => void;
onError: OnError;
};

export type CreateReadableStreamFromElement = (
Expand Down Expand Up @@ -79,9 +75,7 @@ export function createRenderStreaming(
return run(headersData, async () => {
const end = time();
const { runtimeContext, config, resource } = options;

const onError = createOnError(options.onError);
const onTiming = createOnTiming(options.onTiming);
const { onError, onTiming } = options;

const { htmlTemplate, entryName } = resource;

Expand Down Expand Up @@ -124,10 +118,10 @@ export function createRenderStreaming(
onTiming(SSRTimings.RENDER_HTML, cost);
},
onShellError(error) {
onError(SSRErrors.RENDER_SHELL, error);
onError(error, SSRErrors.RENDER_SHELL);
},
onError(error) {
onError(SSRErrors.RENDER_STREAM, error);
onError(error, SSRErrors.RENDER_STREAM);
},
},
);
Expand Down
18 changes: 5 additions & 13 deletions packages/runtime/plugin-runtime/src/core/server/string/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { OnError, OnTiming } from '@modern-js/app-tools';
import { run } from '@modern-js/runtime-utils/node';
import type { StaticHandlerContext } from '@modern-js/runtime-utils/remix-router';
import { time } from '@modern-js/runtime-utils/time';
Expand All @@ -15,13 +16,7 @@ import {
} from '../constants';
import { createReplaceHelemt } from '../helmet';
import { type BuildHtmlCb, type RenderString, buildHtml } from '../shared';
import {
SSRErrors,
SSRTimings,
type Tracer,
createOnError,
createOnTiming,
} from '../tracer';
import { SSRErrors, SSRTimings, type Tracer } from '../tracer';
import { getSSRConfigByEntry, safeReplace } from '../utils';
import { LoadableCollector } from './loadable';
import { prefetch } from './prefetch';
Expand All @@ -39,10 +34,7 @@ export const renderString: RenderString = async (
return run(headersData, async () => {
const { resource, runtimeContext, config, onError, onTiming } = options;

const tracer: Tracer = {
onError: createOnError(onError),
onTiming: createOnTiming(onTiming),
};
const tracer: Tracer = { onError, onTiming };

const routerContext = runtimeContext.routerContext as StaticHandlerContext;

Expand Down Expand Up @@ -74,7 +66,7 @@ export const renderString: RenderString = async (
chunkSet.renderLevel = RenderLevel.SERVER_PREFETCH;
} catch (e) {
chunkSet.renderLevel = RenderLevel.CLIENT_RENDER;
tracer.onError(SSRErrors.PRERENDER, e);
tracer.onError(e, SSRErrors.PRERENDER);
}

const collectors = [
Expand Down Expand Up @@ -146,7 +138,7 @@ async function generateHtml(
onTiming(SSRTimings.RENDER_HTML, cost);
} catch (e) {
chunkSet.renderLevel = RenderLevel.CLIENT_RENDER;
onError(SSRErrors.RENDER_HTML, e);
onError(e, SSRErrors.RENDER_HTML);
}

// collectors do effect
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ export const prefetch = async (
// tracker.trackTiming(SSRTimings.PRERENDER, cost);
} catch (e) {
const error = e as Error;
onError(SSRErrors.PRERENDER, error);
onError(error, SSRErrors.PRERENDER);

// re-throw the error
throw e;
Expand All @@ -81,7 +81,7 @@ export const prefetch = async (

onTiming(SSRTimings.USE_LOADER, cost);
} catch (e) {
onError(SSRErrors.USE_LOADER, e);
onError(e, SSRErrors.USE_LOADER);

// re-throw the error
throw e;
Expand All @@ -90,7 +90,7 @@ export const prefetch = async (
Object.keys(loadersData).forEach(id => {
const data = loadersData[id];
if (data._error) {
onError(SSRErrors.USE_LOADER, data._error);
onError(data._error, SSRErrors.USE_LOADER);
delete data._error;
}
});
Expand Down
24 changes: 3 additions & 21 deletions packages/runtime/plugin-runtime/src/core/server/tracer.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import type { OnError, OnTiming } from '@modern-js/app-tools';

export enum SSRTimings {
PRERENDER = 'ssr-prerender',
RENDER_HTML = 'ssr-render-html',
Expand All @@ -11,30 +13,10 @@ export enum SSRErrors {
RENDER_HTML = 'App Render To HTML',
RENDER_STREAM = 'An error occurs during streaming SSR',
RENDER_SHELL = 'An error occurs during streaming render shell',
LOADER_ERROR = 'App error occurs during data loader',
}

export type Tracer = {
onError: OnError;
onTiming: OnTiming;
};

export type OnError = (key: SSRErrors, e: unknown) => void;

export function createOnError(onError?: (e: unknown) => void): OnError {
return (key, e) => {
const error = e instanceof Error ? e : new Error('Unexpected Server Error');

(error as any).name = key;

onError?.(e);
};
}
export type OnTiming = (key: SSRTimings, cost: number) => void;

export function createOnTiming(
onTiming?: (name: string, dur: number) => void,
): OnTiming {
return (key, cost) => {
onTiming?.(key, cost);
};
}
5 changes: 3 additions & 2 deletions packages/runtime/plugin-runtime/src/core/types.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import type { OnError, OnTiming } from '@modern-js/app-tools';
import type { BaseSSRServerContext } from '@modern-js/types';
import type { RenderLevel } from './constants';
import type { LoaderResult } from './loader/loaderManager';
Expand Down Expand Up @@ -68,8 +69,8 @@ export type SSRServerContext = Pick<
};
htmlModifiers: BuildHtmlCb[];
loaderFailureMode?: 'clientRender' | 'errorBoundary';
onError?: (e: unknown) => void;
onTiming?: (name: string, dur: number) => void;
onError: OnError;
onTiming: OnTiming;
useJsonScript?: boolean;
};

Expand Down
39 changes: 19 additions & 20 deletions packages/server/core/src/plugins/render/render.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import type { IncomingMessage } from 'http';
import type { Logger, Metrics, Reporter, ServerRoute } from '@modern-js/types';
import type { ServerRoute } from '@modern-js/types';
import { cutNameByHyphen } from '@modern-js/utils/universal';
import type { Router } from 'hono/router';
import { TrieRouter } from 'hono/router/trie-router';
import { REPLACE_REG, X_MODERNJS_RENDER } from '../../constants';
import { X_MODERNJS_RENDER } from '../../constants';
import type {
CacheConfig,
FallbackReason,
Expand All @@ -18,11 +18,9 @@ import {
createErrorHtml,
getPathname,
getRuntimeEnv,
onError as onErrorFn,
parseHeaders,
parseQuery,
sortRoutes,
transformResponse,
} from '../../utils';
import { dataHandler } from './dataHandler';
import { renderRscHandler } from './renderRscHandler';
Expand All @@ -41,6 +39,11 @@ interface CreateRenderOptions {
nonce?: string;
}

type FallbackWrapper = (
reason: FallbackReason,
err?: unknown,
) => ReturnType<OnFallback>;

const DYNAMIC_ROUTE_REG = /\/:./;

function getRouter(routes: ServerRoute[]): Router<ServerRoute> {
Expand Down Expand Up @@ -113,7 +116,7 @@ export async function createRender({
cacheConfig,
forceCSR,
config,
onFallback: onFallbackFn,
onFallback,
}: CreateRenderOptions): Promise<Render> {
const router = getRouter(routes);

Expand Down Expand Up @@ -146,9 +149,9 @@ export async function createRender({
const fallbackHeader = `x-${cutNameByHyphen(framework)}-ssr-fallback`;
let fallbackReason = null;

const onFallback = async (reason: FallbackReason, error?: unknown) => {
const fallbackWrapper: FallbackWrapper = async (reason, error?) => {
fallbackReason = reason;
return onFallbackFn?.(reason, { logger, reporter, metrics }, error);
return onFallback?.(reason, { logger, reporter, metrics }, error);
};

if (!routeInfo) {
Expand Down Expand Up @@ -176,15 +179,15 @@ export async function createRender({
routeInfo.isSSR,
forceCSR,
nodeReq,
onFallback,
fallbackWrapper,
);

const headerData = parseHeaders(req);

const onError = (e: unknown) => {
const onError = (e: unknown, key?: string) => {
monitors?.error(
`SSR Error - ${
e instanceof Error ? e.name : e
key || (e instanceof Error ? e.name : e)
}, error = %s, req.url = %s, req.headers = %o`,
e instanceof Error ? e.stack || e.message : e,
forMatchpathname,
Expand All @@ -196,11 +199,6 @@ export async function createRender({
monitors?.timing(name, dur, 'SSR');
};

const onBoundError = async (e: unknown) => {
onErrorFn(ErrorDigest.ERENDER, e as string | Error, monitors, req);
await onFallback?.('error', e);
};

const renderOptions: SSRRenderOptions & {
serverRoutes: ServerRoute[];
} = {
Expand Down Expand Up @@ -232,7 +230,7 @@ export async function createRender({
case 'data':
response =
(await dataHandler(req, renderOptions)) ||
(await renderHandler(req, renderOptions, 'ssr', onBoundError));
(await renderHandler(req, renderOptions, 'ssr', fallbackWrapper));
break;
case 'rsc-tree':
response = await renderRscHandler(req, renderOptions);
Expand All @@ -246,7 +244,7 @@ export async function createRender({
req,
renderOptions,
renderMode,
onBoundError,
fallbackWrapper,
);
break;
default:
Expand All @@ -264,7 +262,7 @@ async function renderHandler(
request: Request,
options: SSRRenderOptions,
mode: 'ssr' | 'csr',
onError: (e: unknown) => Promise<void>,
fallbackWrapper: FallbackWrapper,
) {
let response: Response | null = null;

Expand Down Expand Up @@ -315,7 +313,8 @@ async function renderHandler(
try {
response = await ssrRender(request, options);
} catch (e) {
await onError(e);
options.onError(e as Error, ErrorDigest.ERENDER);
await fallbackWrapper('error', e);
response = csrRender(options.html);
}
} else {
Expand All @@ -340,7 +339,7 @@ async function getRenderMode(
isSSR?: boolean,
forceCSR?: boolean,
nodeReq?: IncomingMessage,
onFallback?: (reason: FallbackReason, err?: unknown) => Promise<void>,
onFallback?: FallbackWrapper,
): Promise<'ssr' | 'csr' | 'data' | 'rsc-action' | 'rsc-tree'> {
const query = parseQuery(req);
if (req.headers.get('x-rsc-action')) {
Expand Down
4 changes: 2 additions & 2 deletions packages/server/core/src/plugins/render/ssrRender.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ export interface SSRRenderOptions {
cacheConfig?: CacheConfig;
nodeReq?: IncomingMessage;

onError?: OnError;
onTiming?: OnTiming;
onError: OnError;
onTiming: OnTiming;
}

const SERVER_RUNTIME_ENTRY = 'requestHandler';
Expand Down
1 change: 1 addition & 0 deletions packages/server/core/src/types/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import type {
} from '@modern-js/types';
import type { ServerManifest } from './server';

// TODO: combine some field with RequestHandlerOptions
export interface RenderOptions {
loaderContext?: Map<string, unknown>;

Expand Down
6 changes: 3 additions & 3 deletions packages/server/core/src/types/requestHandler.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ export type RequestHandlerConfig = {

export type LoaderContext = Map<string, any>;

export type OnError = (err: unknown) => void;
export type OnError = (err: unknown, key?: string) => void;

export type OnTiming = (name: string, dur: number) => void;

Expand Down Expand Up @@ -64,8 +64,8 @@ export type RequestHandlerOptions = {
/** @deprecated */
metrics?: Metrics;

onError?: OnError;
onTiming?: OnTiming;
onError: OnError;
onTiming: OnTiming;
};

export type RequestHandler = (
Expand Down
2 changes: 1 addition & 1 deletion packages/server/core/src/utils/error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export const createErrorHtml = (status: number) => {
export enum ErrorDigest {
ENOTF = 'Page could not be found',
EINTER = 'Internal server error',
ERENDER = 'SSR render failed',
ERENDER = 'SSR render fallback',
// INIT: 'Server init error',
// WARMUP: 'SSR warmup failed',
// EMICROINJ: 'Get micro-frontend info failed',
Expand Down
Loading

0 comments on commit 02ca983

Please sign in to comment.