---
title: "地图评分"
description: "evlog map 如何将规则结果转换为分数：每个入口点的权重、项目平均分、等级阈值，以及路由如何被标记。"
canonical_url: "https://evlog.zhcndoc.com/cli/scoring"
---
# 地图评分

> evlog map 如何将规则结果转换为分数：每个入口点的权重、项目平均分、等级阈值，以及路由如何被标记。

分数用于与自身进行比较。你可以在修复问题时观察它的变化，也可以据此设置 pull request 的门禁，而不是将其作为与其他项目比较的基准。

## 一个入口点

每个入口点从 100 分开始，按其未通过的每一项 **要求** 扣除相应权重，最低降到 0 分。

<table>
<thead>
  <tr>
    <th>
      要求
    </th>
    
    <th>
      权重
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        wide-event
      </code>
    </td>
    
    <td>
      40
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        audit
      </code>
    </td>
    
    <td>
      25
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        structured-errors
      </code>
    </td>
    
    <td>
      20
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        page-error-handling
      </code>
    </td>
    
    <td>
      20
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        context
      </code>
    </td>
    
    <td>
      15
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        error-handling
      </code>
    </td>
    
    <td>
      15
    </td>
  </tr>
</tbody>
</table>

通过的规则不会扣分。报告 `n/a` 的规则也不会扣分，因为它从未适用，所以不会算在入口点头上。[机会](https://evlog.zhcndoc.com/cli/rules#opportunities)完全没有权重，也不会出现在这里。

你[通过注释禁用的检查](https://evlog.zhcndoc.com/cli/rules#disabling-a-check)也会标记为 `n/a`，因此不会扣分；报告会统计禁用了多少项，因为部分得分来自已禁用检查时，必须明确说明这一点。

以 playground 里的登录处理器为例：

```text [evlog map server/api/auth/login.post.ts]
CHECKS
✓ useLogger  每个请求都发出了 wide event
✓ log.set    使用 log.set() 关联了 context
✗ log.audit  敏感操作但没有审计轨迹
```

`structured-errors` 和 `error-handling` 为 `n/a`：该处理器既不抛出异常，也不捕获异常。一个要求未通过，因此得分为 `100 − 25 = 75`。

<callout color="neutral" icon="i-lucide-info">

权重之和故意超过 100。一个未通过所有规则的处理器会降到 0 分，而优先修复顺序依然有意义：`wide-event` 的 40 分高于任何两个较小规则的权重之和。

</callout>

这样，无论从哪个方向看，算术都清晰易懂。下面是 playground 中最差的入口点，它从 `20` 逐步升到 `100`，每一行修复都恰好等于其满足的规则权重：

<map-score-climb>



</map-score-climb>

## 项目分数如何计算

全局分数是每个入口点的平均值，并根据盲区会给你造成多大损失来加权：

<table>
<thead>
  <tr>
    <th>
      入口点
    </th>
    
    <th>
      权重
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      标记为资金或认证相关
    </td>
    
    <td>
      ×2
    </td>
  </tr>
  
  <tr>
    <td>
      页面
    </td>
    
    <td>
      ×0.5
    </td>
  </tr>
  
  <tr>
    <td>
      其他所有
    </td>
    
    <td>
      ×1
    </td>
  </tr>
</tbody>
</table>

页面的权重更低，因为吞掉 fetch 错误的页面会带来比可观测性缺口更差的用户体验，而且它们通常也是应用中数量最多的文件。敏感处理器的权重加倍，因为那正是你需要事件的地方。

三个入口点：一个得分为 75 的敏感处理器、一个得分为 100 的普通处理器，以及一个得分为 50 的页面：

```text
(75 × 2) + (100 × 1) + (50 × 0.5)   275
───────────────────────────────── = ───── = 79
        2 + 1 + 0.5                  3.5
```

没有可评分入口点的项目目前得分为 100。这是一个空结果，并不代表埋点已完整完成。在解读分数之前，请检查检测到的框架和入口点数量。

## 如何分配等级

<table>
<thead>
  <tr>
    <th>
      分数
    </th>
    
    <th>
      等级
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      90–100
    </td>
    
    <td>
      <code>
        优秀
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      70–89
    </td>
    
    <td>
      <code>
        良好
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      50–69
    </td>
    
    <td>
      <code>
        需要改进
      </code>
    </td>
  </tr>
  
  <tr>
    <td>
      0–49
    </td>
    
    <td>
      <code>
        有风险
      </code>
    </td>
  </tr>
</tbody>
</table>

## 如何对文件进行分类

除了分数之外，每个入口点还会被分类，这决定了 JSON 中 `summary` 块的统计方式，也决定了报告中的覆盖率行如何构建。

<table>
<thead>
  <tr>
    <th>
      类别
    </th>
    
    <th>
      何时
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        instrumented
      </code>
    </td>
    
    <td>
      同时传递了 <code>
        wide-event
      </code>
      
       和 <code>
        context
      </code>
      
       的处理程序，或者能够处理其 fetch 错误的页面
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        partial
      </code>
    </td>
    
    <td>
      只传递了两者之一的处理程序
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        dark
      </code>
    </td>
    
    <td>
      两者都未传递的处理程序，或者吞掉 fetch 错误的页面
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        exempt
      </code>
    </td>
    
    <td>
      没有需要埋点的内容——evlog 自身的基础设施，或者没有发起任何 fetch 的页面。它与你自己的入口点分开统计，而不是作为一个缺口来统计
    </td>
  </tr>
</tbody>
</table>

`dark` 表示分析器未识别出所需的 evlog 埋点或页面错误处理。其他日志记录器或不受支持的抽象仍可能产生有用的证据。在将其分类视为没有日志之前，请检查代码。

## 标记涉及资金、认证或 PII 的内容

敏感度决定了一个入口点是否提升为双倍权重，并添加 `audit` 要求，所以准确了解它是如何判定的很重要。

### 资金：`$`

- **导入** `stripe`、`@stripe/stripe-js`、`paddle-sdk` 或 `@lemonsqueezy/lemonsqueezy.js`
- **路径** 包含 `checkout`、`payment`、`billing`、`invoice`、`refund`、`subscription`、`charge` 或 `payout`

### 认证：`A`

- **导入** `better-auth`、`next-auth`、`lucia`、`@auth/core` 或 `@auth/nextjs`
- **路径** 包含 `auth`、`oauth`、`login`、`logout`、`signin`、`signup`、`register`、`password`、`token`、`session`、`mfa` 或 `otp`

### PII：`@`

字段匹配 `email`、`phone`、`address`、`ssn` 或 `iban`，并且在同一个处理程序中存在写入调用（`create`、`update`、`insert`、`upsert`）。

金融和认证属于 `high` 敏感度：双倍权重，并且需要审计轨迹。PII 属于 `medium`：没有额外要求，但会在报告中标记。

### 匹配是如何工作的

有两个决定避免了噪音。

**导入是从 AST 中读取的。** 只有当某个包确实被导入时才计数，所以 `// TODO: drop stripe` 注释不会让某条路由处理金融相关内容。

**路径术语匹配完整单词**，同时允许复数形式。`/api/authors` 不是认证路由，而 `/api/refunds` 是资金路由。这一点比看起来更重要：被错误标记的路由会被分配一个没有理由满足的 25 分要求，并在平均分中按双倍计算，这是让整个分数失去可信度的最快方式。

当你检查某个入口点时，每个原因都会完整显示，所以错误调用只需一行就能发现：

```text [evlog map server/api/auth/login.post.ts]
FLAGGED SENSITIVE BECAUSE
▍ auth: path says "auth"
```

## 诚实地解读分数

- **高分并不等于良好的可观测性。** 它只说明结构已经具备：每个入口点都有事件，附带上下文，错误可以解释，敏感操作有审计。你附加的上下文是否正是你将来需要的上下文，这是静态分析器无法判断的。[最佳实践 →](https://evlog.zhcndoc.com/reference/best-practices)
- **比较的是运行结果，而不是项目。** 一个有 200 条路由、得分 70 的应用，状态比一个只有 6 条路由、得分同样为 70 的应用更好。
- **重点关注 dark 和 `资金与认证**，而不是总分。这两个地方正是下一次事故最可能潜伏的地方。

## 接下来

- [CI](https://evlog.zhcndoc.com/cli/ci)：将分数转换为通过／失败检查
- [规则](https://evlog.zhcndoc.com/cli/rules)：每个要求实际检查的内容。

---

- [规则](https://evlog.zhcndoc.com/cli/rules)
- [CI](https://evlog.zhcndoc.com/cli/ci)


## Sitemap

See the full [sitemap](https://evlog.zhcndoc.com/sitemap.md) for all pages.
