my-logs

Compare original and translation side by side

🇺🇸

Original

English
🇨🇳

Translation

Chinese

Logging Standards

日志记录标准

When writing or reviewing logging code, follow these principles.
编写或审核日志代码时,请遵循以下原则。

Library

库选择

  • TypeScript/Node.js: use
    pino
  • TypeScript/Node.js:使用
    pino

Log Format

日志格式

Show only three fields in the visible line: time, module, and message.
HH:mm:ss MODULE_NAME  message text here
  • Time: hours, minutes, seconds only — no milliseconds
  • Module: fixed-width string (pad shorter names with spaces) so messages align vertically
  • Message: human-readable text
在可见行中仅显示三个字段:时间模块消息
HH:mm:ss MODULE_NAME  message text here
  • 时间:仅显示时、分、秒 — 不显示毫秒
  • 模块:固定宽度字符串(较短名称用空格补全),使消息在垂直方向对齐
  • 消息:便于人类阅读的文本

Pino Configuration Example

Pino配置示例

typescript
import pino from "pino";

const MODULE_WIDTH = 12;

function createLogger(module: string) {
  return pino({
    transport: {
      target: "pino-pretty",
      options: {
        ignore: "pid,hostname",
        translateTime: "HH:MM:ss",
        messageFormat: `${module.padEnd(MODULE_WIDTH)}  {msg}`,
        customColors: getModuleColor(module),
      },
    },
  });
}
typescript
import pino from "pino";

const MODULE_WIDTH = 12;

function createLogger(module: string) {
  return pino({
    transport: {
      target: "pino-pretty",
      options: {
        ignore: "pid,hostname",
        translateTime: "HH:MM:ss",
        messageFormat: `${module.padEnd(MODULE_WIDTH)}  {msg}`,
        customColors: getModuleColor(module),
      },
    },
  });
}

Module Colors

模块颜色

Each module gets a deterministic color derived from its name via MurmurHash3. No manual color assignment — the hash picks from a fixed palette automatically. Results are cached so each module hashes only once.
typescript
// ANSI 256-color palette — 28 visually distinct colors
const COLORS = [
  196, // red
  202, // orange
  208, // dark orange
  214, // orange-gold
  220, // gold
  226, // yellow
  190, // yellow-green
  154, // chartreuse
  118, // bright green
  82,  // green
  48,  // spring green
  50,  // teal
  51,  // cyan
  45,  // turquoise
  39,  // deep sky blue
  33,  // dodger blue
  27,  // blue
  21,  // deep blue
  57,  // blue-violet
  93,  // purple
  129, // medium purple
  165, // magenta
  201, // hot pink
  213, // pink
  177, // violet
  141, // light purple
  105, // slate blue
  69,  // cornflower blue
] as const;

const colorCache = new Map<string, string>();

function murmurhash3(key: string, seed = 0): number {
  let h = seed ^ key.length;
  for (let i = 0; i < key.length; i++) {
    h ^= key.charCodeAt(i);
    h = Math.imul(h ^ (h >>> 16), 0x85ebca6b);
    h = Math.imul(h ^ (h >>> 13), 0xc2b2ae35);
    h ^= h >>> 16;
  }
  return h >>> 0;
}

function getModuleColor(module: string): number {
  let color = colorCache.get(module);
  if (!color) {
    color = `\x1b[38;5;${COLORS[murmurhash3(module) % COLORS.length]}m`;
    colorCache.set(module, color);
  }
  return color;
}
每个模块的颜色由其名称通过MurmurHash3算法确定,无需手动分配颜色 — 哈希会自动从固定调色板中选取。结果会被缓存,因此每个模块仅哈希一次。
typescript
// ANSI 256色调色板 — 28种视觉区分度高的颜色
const COLORS = [
  196, // red
  202, // orange
  208, // dark orange
  214, // orange-gold
  220, // gold
  226, // yellow
  190, // yellow-green
  154, // chartreuse
  118, // bright green
  82,  // green
  48,  // spring green
  50,  // teal
  51,  // cyan
  45,  // turquoise
  39,  // deep sky blue
  33,  // dodger blue
  27,  // blue
  21,  // deep blue
  57,  // blue-violet
  93,  // purple
  129, // medium purple
  165, // magenta
  201, // hot pink
  213, // pink
  177, // violet
  141, // light purple
  105, // slate blue
  69,  // cornflower blue
] as const;

const colorCache = new Map<string, string>();

function murmurhash3(key: string, seed = 0): number {
  let h = seed ^ key.length;
  for (let i = 0; i < key.length; i++) {
    h ^= key.charCodeAt(i);
    h = Math.imul(h ^ (h >>> 16), 0x85ebca6b);
    h = Math.imul(h ^ (h >>> 13), 0xc2b2ae35);
    h ^= h >>> 16;
  }
  return h >>> 0;
}

function getModuleColor(module: string): number {
  let color = colorCache.get(module);
  if (!color) {
    color = `\x1b[38;5;${COLORS[murmurhash3(module) % COLORS.length]}m`;
    colorCache.set(module, color);
  }
  return color;
}

Self-Contained Messages

自包含消息

Every log message must be understandable on its own — assume only the text part is visible (no structured fields are shown). Include all relevant identifiers directly in the message string.
Good — all IDs are in the text:
typescript
log.info(`order:create orderId=${orderId} userId=${userId} items=${items.length}`);
log.error(`payment:charge orderId=${orderId} stripeId=${stripeId} failed amount=${amount}`);
log.info(`user:login userId=${userId} email=${email} method=oauth`);
Bad — IDs hidden in structured fields that won't appear in the console:
typescript
log.info({ orderId, userId }, "order created");
log.error({ orderId, stripeId }, "payment failed");
每条日志消息必须能够独立被理解 — 假设仅文本部分可见(结构化字段不会显示)。请将所有相关标识符直接包含在消息字符串中。
规范示例 — 所有ID均包含在文本中:
typescript
log.info(`order:create orderId=${orderId} userId=${userId} items=${items.length}`);
log.error(`payment:charge orderId=${orderId} stripeId=${stripeId} failed amount=${amount}`);
log.info(`user:login userId=${userId} email=${email} method=oauth`);
不规范示例 — ID隐藏在不会显示在控制台的结构化字段中:
typescript
log.info({ orderId, userId }, "order created");
log.error({ orderId, stripeId }, "payment failed");

Prefix Notation

前缀命名法

Structure messages with prefix notation: place the most common/general word on the left, narrowing to the specific action on the right. This groups related messages visually when scanning logs.
domain:action  detail
Good — common prefix groups related lines together:
order:create   orderId=abc-123 userId=u-456 items=3
order:pay      orderId=abc-123 amount=59.99 method=card
order:ship     orderId=abc-123 carrier=ups tracking=1Z999
order:cancel   orderId=abc-123 reason=user-request

user:login     userId=u-456 email=j@example.com method=oauth
user:logout    userId=u-456 sessionDuration=3600s
user:update    userId=u-456 field=email

cache:hit      key=user:u-456 ttl=120s
cache:miss     key=product:p-789
cache:evict    key=session:s-012 reason=expired
Bad — specific word first scatters related entries:
createOrder  ...
payOrder     ...
loginUser    ...
shipOrder    ...
使用前缀命名法组织消息:将最通用的词放在左侧,逐步缩小到具体操作。这样在查看日志时,相关消息会在视觉上分组显示。
领域:操作 详细信息
规范示例 — 通用前缀将相关行分组在一起:
order:create   orderId=abc-123 userId=u-456 items=3
order:pay      orderId=abc-123 amount=59.99 method=card
order:ship     orderId=abc-123 carrier=ups tracking=1Z999
order:cancel   orderId=abc-123 reason=user-request

user:login     userId=u-456 email=j@example.com method=oauth
user:logout    userId=u-456 sessionDuration=3600s
user:update    userId=u-456 field=email

cache:hit      key=user:u-456 ttl=120s
cache:miss     key=product:p-789
cache:evict    key=session:s-012 reason=expired
不规范示例 — 具体词在前会分散相关条目:
createOrder  ...
payOrder     ...
loginUser    ...
shipOrder    ...

Summary

总结

  1. Use
    pino
    for TypeScript
  2. Display only
    HH:mm:ss
    , fixed-width module, and message
  3. Assign distinct colors per module
  4. Put all IDs in the message text — assume no structured fields are visible
  5. Use
    domain:action
    prefix notation with the general word first
  1. 针对TypeScript使用
    pino
  2. 仅显示
    HH:mm:ss
    、固定宽度模块名称和消息
  3. 为每个模块分配独特颜色
  4. 将所有ID放入消息文本中 — 假设结构化字段不可见
  5. 使用
    领域:操作
    前缀命名法,将通用词放在前面