For AI agents: the complete documentation index is available at /zh/llms.txt, the full documentation bundle is available at /zh/llms-full.txt, and this page is available as Markdown at /zh/guides/advanced-features/server-monitor/monitors.md.
  • 简体中文
  • Monitors

    Modern.js 是一个全栈框架,它可以同时支持客户端和服务端的开发。当 Modern.js 在服务端渲染页面时,框架会在运行时插入更多日志与指标,帮助开发者在线上运维时定位问题。

    但服务端代码运行在 Node.js 环境中,开发者无法直接通过浏览器控制台来排查问题,而不同的项目可能使用不同的日志库,或是将数据上报到不同的平台。框架无法覆盖所有的场景,因此需要有统一的方式,允许开发者自行管理所有的内置日志与指标。

    Monitors 是 Modern.js 提供的帮助开发者监控应用程序的运行情况的模块。它包含注册 Monitor 和分发监控事件两部分能力。当开发者中调用 Monitors 的某个 API 时,框架会对应的监控事件分发到所有注册的 Monitor 中。

    Note

    在 Modern.js 中内置了默认的 Monitor,开发者也可以注册自定义的 Monitor。

    Monitors 定义

    Monitors 模块的类型定义如下,其中 push 方法用于注册 Monitor,其他方法用于分发监控事件。

    export type LogLevel = 'error' | 'warn' | 'info' | 'debug' | 'trace';
    export interface LogEvent {
      type: 'log';
      payload: {
        level: LogLevel;
        message: string;
        args?: any[];
      };
    }
    
    export interface TimingEvent {
      type: 'timing';
      payload: {
        name: string;
        dur: number;
        desc?: string;
        args?: any[];
      };
    }
    
    export interface CounterEvent {
      type: 'counter';
      payload: {
        name: string;
        args?: any[];
      };
    }
    
    export type MonitorEvent = LogEvent | TimingEvent | CounterEvent;
    
    export type CoreMonitor = (event: MonitorEvent) => void;
    
    export interface Monitors {
      // 注册 Monitor
      push(monitor: CoreMonitor): void;
    
      // 日志事件
      error(message: string, ...args: any[]): void;
      warn(message: string, ...args: any[]): void;
      debug(message: string, ...args: any[]): void;
      info(message: string, ...args: any[]): void;
      trace(message: string, ...args: any[]): void;
    
      // 指标事件
      timing(name: string, dur: number, ...args: any[]): void;
      counter(name: string, ...args: any[]): void;
    }

    内置 Monitor

    Modern.js 内置了默认的 Monitor,不同的事件会触发内置 Monitor 不同的行为。详细内容查看:

    注册 Monitors

    开发者可以通过 Monitors 的 push API 注册自己的 Monitor,但只能在服务端中间件或服务端插件中注册,在 Data Loader、组件、init 函数中无法注册。

    server/modern.server.ts
    import {
      defineServerConfig,
      type MiddlewareHandler,
    } from '@modern-js/server-runtime';
    import type { MonitorEvent } from '@modern-js/types';
    
    const injectMonitorMiddleware: MiddlewareHandler = async (c, next) => {
      const monitors = c.get('monitors');
      const myMonitor = (event: MonitorEvent) => {
        if (event.type === 'log') {
          // 处理日志事件
          console.log(`[${event.payload.level}] ${event.payload.message}`);
        } else if (event.type === 'timing') {
          // 处理性能指标事件
          console.log(`Timing: ${event.payload.name} = ${event.payload.dur}ms`);
        } else if (event.type === 'counter') {
          // 处理计数事件
          console.log(`Counter: ${event.payload.name}`);
        }
      };
      monitors.push(myMonitor);
      await next();
    };
    
    export default defineServerConfig({
      middlewares: [
        {
          name: 'inject-monitor',
          handler: injectMonitorMiddleware,
        },
      ],
    });

    在服务端插件中注册 Monitor:

    server/plugins/my-monitor-plugin.ts
    import type { ServerPlugin } from '@modern-js/server-runtime';
    import type { MonitorEvent } from '@modern-js/types';
    
    const myMonitorPlugin = (): ServerPlugin => ({
      name: '@my-org/my-monitor-plugin',
      setup(api) {
        api.onPrepare(() => {
          const { middlewares } = api.getServerContext();
    
          // 定义 monitor,确保只创建一次
          const myMonitor = (event: MonitorEvent) => {
            if (event.type === 'log') {
              // 处理日志事件
              console.log(`[${event.payload.level}] ${event.payload.message}`);
            } else if (event.type === 'timing') {
              // 处理性能指标事件
              console.log(`Timing: ${event.payload.name} = ${event.payload.dur}ms`);
            } else if (event.type === 'counter') {
              // 处理计数事件
              console.log(`Counter: ${event.payload.name}`);
            }
          };
    
          // 使用标志确保 monitor 只注册一次
          let monitorRegistered = false;
    
          middlewares.push({
            name: 'inject-monitor',
            handler: async (c, next) => {
              const monitors = c.get('monitors');
              // 只在第一次请求时注册 monitor
              if (!monitorRegistered) {
                monitors.push(myMonitor);
                monitorRegistered = true;
              }
              await next();
            },
          });
        });
      },
    });
    
    export default myMonitorPlugin;

    然后在 server/modern.server.ts 中配置插件:

    server/modern.server.ts
    import { defineServerConfig } from '@modern-js/server-runtime';
    import myMonitorPlugin from './plugins/my-monitor-plugin';
    
    export default defineServerConfig({
      plugins: [myMonitorPlugin()],
    });

    调用 Monitors

    Modern.js 允许开发者在 Data Loader、组件中调用 Monitors。

    Tip

    只有在 Node.js 环境才可以调用 Monitors,在浏览器环境调用无任何效果。

    在 Data Loader 中,开发者可以这样使用:

    routes/page.data.ts
    import type { LoaderFunctionArgs } from '@modern-js/runtime/router';
    import { getMonitors } from '@modern-js/runtime';
    const loader = async ({ context }: LoaderFunctionArgs) => {
        const monitors = getMonitors();
        const start = Date.now();
        try {
            await fetch(...);
            monitors.timing('loader_fetch_timing', Date.now() - start);
        } catch(e) {
            monitors.error(e);
        }
    }

    在组件中调用 Monitors,需要判断当前运行环境是否为 Node.js:

    routes/page.tsx
    import { use } from 'react';
    import { RuntimeContext, getMonitors } from '@modern-js/runtime';
    const Page = () => {
      const context = use(RuntimeContext);
      if (process.env.MODERN_TARGET === 'node') {
        const monitors = getMonitors();
        monitors.info('Page rendered');
      }
      return <div>Hello World</div>;
    };
    export default Page;

    在中间件中,我们也可以调用 Monitors,但方式与在运行时代码中不同,需要通过 context 来获取:

    server/modern.server.ts
    import {
      defineServerConfig,
      type MiddlewareHandler,
    } from '@modern-js/server-runtime';
    
    export const handler: MiddlewareHandler = async (c, next) => {
      const monitors = c.get('monitors');
      const start = Date.now();
    
      await next();
    
      const end = Date.now();
      // 上报耗时
      monitors.timing('request_timing', end - start);
    };
    
    export default defineServerConfig({
      middlewares: [
        {
          name: 'request-timing',
          handler,
        },
      ],
    });