外观
监控与日志
字数
7482 字
预计阅读
29 分钟
后台有两组菜单,帮你了解系统的状况:
- 系统监控:系统现在怎么样。谁在线,服务器和数据库忙不忙,Redis 里存了什么;
- 系统管理 → 日志管理:过去发生了什么。谁做了什么操作,谁登录过,接口的访问记录和出错记录。
这些菜单默认只有超级管理员能看到。要给别人用,在 系统管理 → 角色管理 里给角色分配对应的菜单和按钮,权限点见权限一览。
Java 专属功能的替代
Java 后台常见的 Druid 数据监控和 JVM 面板依赖 Java 运行环境,这里分别换成了 MySQL 状态卡(数据监控)和 Node 运行时面板(在 服务监控 里)。
菜单一览
| 菜单 | 看什么 |
|---|---|
| 系统监控 → 在线用户 | 当前登录着的会话(包括第三方应用的),可以强制下线 |
| 系统监控 → 定时任务、调度日志 | 定时执行的任务和每次执行的记录,见定时任务 |
| 系统监控 → 服务监控 | 服务器的 CPU、内存、磁盘,以及 Node 进程 |
| 系统监控 → 缓存监控 | Redis 的运行状态和命令统计 |
| 系统监控 → 缓存列表 | 按类别查看、清理 Redis 里的数据 |
| 系统监控 → 数据监控 | MySQL 的运行状态和应用的连接池 |
| 系统管理 → 日志管理 → 操作日志 | 谁在什么时候做了什么业务操作 |
| 系统管理 → 日志管理 → 登录日志 | 登录、退出、被强退、被锁定 |
| 系统管理 → 日志管理 → API 访问日志 | 每个接口请求的记录 |
| 系统管理 → 日志管理 → API 错误日志 | 服务端出错时的异常信息和调用栈 |
在线用户
会话就是"一次登录"。同一个人在电脑和手机上各登录一次,就是两个会话。系统监控 → 在线用户 列出所有还没结束的会话。
- 列:用户名、部门、IP 地址、登录地点、客户端(电脑、手机,或者第三方应用的客户端标识)、登录时间、最近访问。浏览器、操作系统、过期时间、保持登录、User-Agent 默认隐藏,可以在"列设置"里打开;
- 筛选:用户名、IP 地址(包含即可)、客户端;可以按登录时间或最近访问排序。客户端的下拉框里有"电脑"和"手机",也可以直接输入第三方应用的客户端标识(要完全一致);
- 最近访问是这个会话最后一次发请求的时间;过期时间是不再续期的话,会话结束的时间;
- 令牌本身不会显示,因为服务端只保存令牌的哈希值(见安全基线 · 认证与会话);
- 已经结束的会话会自动从列表中消失。内置定时任务"清理过期在线会话"每 10 分钟清理一次残留的记录。
强制下线
| 按钮 | 作用 |
|---|---|
| 强退 | 结束这一个会话 |
| 强退该用户 | 结束这个人在所有设备上的全部会话,包括他授权给第三方应用的 |
| 批量强退 | 勾选一行或多行后出现,一次结束选中的会话(最多 200 个) |
- 被强退的人立即掉线:页面弹出"你已被管理员强制下线,请重新登录",然后回到登录页。这个通知通过实时推送发出,手机端也一样;
- 你自己当前的会话标着"当前会话",不能勾选,也不能强退(想退出请用退出登录)。"强退该用户"对你自己的账号也不可用,但你在其他设备上的会话可以单独强退;
- 每次强退都记一条操作日志(动作"强制退出")。被强退的人在登录日志里多一条"被强制退出",提示里写着是哪个管理员操作的。
数据范围
不是超级管理员的人,只能看到和强退自己数据范围内用户的会话。数据范围按部门划分,见权限与数据范围。超级管理员的会话受保护:不是超级管理员的人不能强退它们。
"登录地点"是根据 IP 地址查出来的,需要先下载 IP 数据文件(见快速开始)。只支持 IPv4,内网地址查不到,显示为空。
第三方应用的会话
本系统可以作为单点登录(OAuth2)的提供方(见单点登录(OAuth2))。第三方应用每次用授权码或客户端凭证换取令牌,都会产生一个会话,也列在这里;它换新令牌时不产生新会话。
- 客户端一列显示这个应用的客户端标识(比如
crm),也就是在 系统管理 → 客户端管理 里登记的标识。用户自己在电脑和手机上的会话显示"电脑""手机"; - IP 地址、浏览器、操作系统和 User-Agent,记的是第三方应用的服务器来换取令牌时的,不是用户的浏览器;"保持登录"为"否";
- 过期时间是这次授权到期的时间:登录时间(第三方应用换取令牌的时间)加上客户端的"刷新令牌有效期";客户端没有勾选刷新令牌,或者会话是用客户端凭证申请的,加上"访问令牌有效期"。第三方应用换新令牌也不会延长它;
- 用"客户端凭证"方式申请的会话不属于任何用户,用户名一栏是空的。这种会话只有超级管理员能看到和强退;
- 强退之后,这个会话的访问令牌和刷新令牌立即失效,第三方应用要让用户重新授权(客户端凭证的会话,重新申请令牌即可)。第三方应用没有页面可以弹提示,它下一次用令牌调用接口时会被拒绝,换新令牌也会失败。登录日志同样记一条"被强制退出",客户端是这个客户端标识;
- 要一次结束某个应用的全部会话,在客户端管理里停用或删除它,见系统管理 · 客户端管理。
服务监控
系统监控 → 服务监控 显示运行服务端的这台机器和 Node 进程的状态。页面打开时每 5 秒自动刷新,切到别的页面或者浏览器标签页被隐藏时暂停。页面右上角显示"更新于 …"。
最上面是三个仪表盘:
| 仪表盘 | 内容 |
|---|---|
| CPU | 使用率、型号、核心数、平均负载(最近 1、5、15 分钟;Windows 上始终为 0) |
| 内存 | 使用率、已用、总量、可用。"已用"只算进程实际占用的内存,不算系统随时可以收回的文件缓存 |
| Node.js 堆内存 | 堆已用占堆总量的比例,以及常驻内存。堆是存放 JavaScript 对象的内存,持续上涨可能是内存泄漏 |
仪表盘到 70% 变成警告色,到 90% 变成危险色。
下面还有:
- 操作系统:主机名、发行版、平台和架构、开机时长;
- 进程:Node.js 版本、V8 版本、进程 ID、进程运行时长。它和上面的堆内存一起,就是 JVM 面板的替代;
- 磁盘:每个挂载点的文件系统、类型、容量、已用和使用率。
缓存监控
Redis 是一个把数据放在内存里的数据库,本项目用它存会话、缓存和各种计数器(见 Redis 是做什么的)。系统监控 → 缓存监控 显示它的运行状态:
- 顶部数字:版本、运行模式、运行时长、客户端连接、已用内存、键数量、带过期键数、命中率(读缓存时找到数据的比例);
- 三张图:内存(已用内存占上限
maxmemory的比例,没设上限时按机器内存算)、每秒命令数、调用次数最多的 10 个命令; - 服务信息:按服务、客户端、内存、持久化、统计、CPU 分页,列出 Redis 自己报告的原始字段。
这一页打开时读取一次,点右上角的"刷新"按钮重新读取。
和别的项目共用 Redis 时
同一个 Redis 可能还有别的项目在用。所以"键数量"只统计本项目自己的库(环境变量 REDIS_DB),Redis 在服务器上的文件路径也不会显示。这一页不需要 Redis 的管理员权限。
缓存列表
系统监控 → 缓存列表 分三栏:
- 命名空间:项目把 Redis 的键按用途分类,每一类有固定的前缀,比如字典缓存是
dict:,这一类就叫一个命名空间。这里只列出开发者在代码里登记过的类别; - 键:点一个命名空间,列出它下面的键。最多列出 1000 个,超过时会提示。上方的输入框可以在列出的键里筛选;
- 值:点一个键,显示类型、剩余时间(秒,或"永不过期")和值。JSON 会格式化显示;列表、集合、哈希最多显示 100 项,字符串最多显示 65536 个字符。
键名省略了全局前缀 qw:(环境变量 REDIS_KEY_PREFIX)。
敏感的值不显示。 带"眼睛划掉"图标的命名空间,比如会话和各种令牌、验证码、参数、导入错误报告,只显示键名、类型和剩余时间,值的位置提示"该值包含敏感信息,不予显示。"。参数里可能有密钥,导入错误报告里是别人的数据。
清理缓存
| 操作 | 作用 |
|---|---|
| 命名空间旁的"清理" | 删除这一类下的全部键 |
| 键旁的"删除" | 删除这一个键 |
| 清理全部命名空间 | 删除所有允许清理的类别 |
- 只有这几类允许清理:验证码、短信限额、字典、参数、防重复提交、导入错误报告。字典和参数被删除后,应用会在需要时从数据库重新读取;其余几类是临时数据,删除后就没有了:已发出的图形验证码和验证通过后的凭证会失效,要重新验证;短信的发送间隔、发送次数和校验次数限制从零开始计算;导入错误报告无法再下载;
- 会话、令牌、登录失败计数、锁、任务锁等不能在这里删除。想让某人下线,用在线用户的强退;想解除登录锁定,用登录日志的"解锁账户";
- 清理字典和参数时,它们的版本号会加一。这样,另一个正在读取旧数据的请求就不会把旧值重新写回缓存;
- 这三个操作需要"删除"权限,每次都记一条操作日志。
数据监控
系统监控 → 数据监控 是 MySQL 的状态卡。它只读取一份固定的状态项和配置项,不显示 SQL 语句。
- 顶部数字:MySQL 版本、运行时长、语句总数、每秒查询数(平均,即语句总数除以运行时长)、慢查询数、接收字节、发送字节;
- 连接数:已连接线程占最大连接数(
max_connections)的比例,以及运行中线程; - InnoDB 缓冲池:缓冲池是 MySQL 放在内存里的数据页。显示已用页的比例、总页数、空闲页和命中率(读数据时不用读磁盘的比例,健康的数据库接近 100%)。缓冲池占满是正常现象,所以这个仪表盘不会变色;
- 连接池:应用和 MySQL 之间的连接池,显示当前连接 / 连接上限、空闲连接、排队请求。排队请求一直大于 0,说明连接不够用。数据库驱动读不到这些数字时,页面提示"数据库驱动未提供连接池状态。"。
和缓存监控一样,打开时读取一次,点"刷新"重新读取。
四种日志的共同点
| 操作日志 | 登录日志 | API 访问日志 | API 错误日志 | |
|---|---|---|---|---|
| 记录什么 | 业务操作 | 登录、退出、强退、锁定 | 接口请求 | 服务端错误(5xx) |
| 默认记录 | 新增、修改、删除等操作和导出 | 全部 | 只记非 GET 请求 | 全部 |
| 详情、导出 | ✓ | ✓ | ✓ | ✓ |
| 删除、清空 | ✓ | ✓ | — | — |
| 特有操作 | — | 解锁账户 | — | 标记处理状态 |
- 筛选:文字条件(操作人、IP、路径等)一般是"包含"匹配;追踪 ID、请求方法这类编号要完全一致,见各节。时间按日期范围筛选,结束日期包含当天全天;
- 详情:点列表里蓝色的那一列,右侧弹出抽屉显示全部字段。操作日志点"业务域",登录日志点"登录名",API 访问日志点"请求路径",API 错误日志点"异常类型"。需要"查看"权限;
- 导出:导出为 Excel,筛选和排序与列表相同,不分页。导出本身也记一条操作日志。数据分批读取,导出期间有新日志写入(比如这次导出自己的那条操作日志),也不会让导出的行重复或漏掉;
- 删除和清空:删除后不再显示,也无法在页面上恢复。数据库里的记录要等超过保留天数,才由定时任务真正删除,见保留时间和清理;
- 自动脱敏:请求参数里,字段名看起来像密码、令牌、密钥、验证码的,值会被替换成
***; - 地点:操作日志和登录日志会根据 IP 记下地点,规则和在线用户相同;
- 不影响业务:日志在后台异步写入。写入失败只在服务端日志里留一条错误,不会让业务操作失败。
操作日志
系统管理 → 日志管理 → 操作日志 记录"谁、在什么时候、做了什么、结果如何"。
哪些操作会被记录:所有新增、修改、删除类的接口,以及导出、导入、发布、审批等动作。开发者写接口时必须声明"记录"或者"明确跳过",漏写的话自动检查会失败(见开发指南 · 操作日志)。
未登录(401)和没有权限(403)的请求,在进入接口之前就被拦下了,不会记录。参数校验失败的请求会记录为失败。
| 字段 | 说明 |
|---|---|
| 业务域 | 哪个模块,比如 iam.user 就是用户管理 |
| 动作 | 新增、修改、删除、导出、强制退出、通过、驳回……(字典"操作动作") |
| 业务 ID | 被操作的记录编号 |
| 操作人、部门 | 当前登录的用户 |
| 请求方法、请求地址 | |
| IP 地址、操作地点、User-Agent | User-Agent 是浏览器自报的名称和版本 |
| 请求参数 | 最多 4 KB,已脱敏 |
| 返回结果 | 最多 2 KB,已脱敏 |
| 是否成功、失败原因 | 失败原因按查看者的语言显示,比如"超级管理员账号受保护,不能执行此操作" |
| 耗时(毫秒)、创建时间 | |
| 追踪 ID | 见追踪 ID |
失败原因怎样翻译
数据库里存的是错误的翻译键,比如 error.iam.user_protected。列表和详情读取时,才按查看者的语言翻译成文字。错误的参数没有保存,所以带占位符的消息不会填上实际的值,比如会显示"数据行数超过上限 {max} 行"。翻译键和错误码的对应见错误码。
筛选:业务域、动作、操作人、IP 地址、是否成功、创建时间。可以按耗时和创建时间排序。
按钮:导出、清空(删除全部)、批量删除(勾选后出现)、每行的删除。清空之后,会马上记下一条新的"清空"日志,所以总能查到是谁清空的。
登录日志
系统管理 → 日志管理 → 登录日志 记录和登录有关的每一件事,成功和失败都记。
| 类型 | 什么时候记 |
|---|---|
| 密码登录 | 用户名密码登录,成功或失败 |
| 短信登录 | 短信验证码登录 |
| 微信登录 | 微信小程序登录和绑定(默认关闭,见安全基线 · 微信小程序登录) |
| 退出登录 | 用户自己退出 |
| 被强制退出 | 被管理员在"在线用户"里强退 |
| 刷新令牌重放 | 一个已经用过的刷新令牌又被拿来用,整个会话被吊销(令牌可能被盗了) |
| 锁定 | 同一个"用户名 + IP"的失败次数达到上限、刚被锁定时;被锁定期间又尝试登录时;或者在锁屏解锁、修改密码、修改手机号时,当前密码输错太多次,这个人的所有会话被结束 |
记录内容:类型、登录名(用户输入的原样)、用户 ID、客户端、IP 地址、登录地点、浏览器、操作系统、是否成功、提示、追踪 ID、创建时间。列表默认显示其中一部分,其余在详情里看。
提示写明了具体原因,比如"密码错误""用户名不存在""用户已停用""IP 在登录黑名单中",按查看者的语言显示。
登录页不会说出真实原因
为了不让人借此探测账号是否存在,登录页对"用户名不存在"和"密码错误"的回应是一样的。真实原因只记在登录日志里,只有管理员能看到。
筛选:类型、登录名、IP 地址、是否成功、创建时间。
按钮:导出、解锁账户、清空、批量删除、每行的删除。
解锁账户
同一个"用户名 + IP"连续输错密码,会被锁定一段时间(默认 5 次锁 10 分钟,见安全基线 · 登录防护)。不想等的话:
- 在列表里勾选这个人的一条日志(只能选一条,否则按钮不可用);
- 点"解锁账户",确认。
解锁立即生效。它清除这个用户名在所有 IP 上的锁定,以及跨 IP 的失败计数(这个计数太高时会强制要求验证码)。按 IP 统计的失败计数不清除,因为它属于那个 IP,而不是这个账号。每次解锁都记一条操作日志。
API 访问日志
系统管理 → 日志管理 → API 访问日志 是更底层的记录:每个接口请求一行,不管它是什么业务。
记录哪些请求,由参数 audit.http_trace.mode 决定。在 系统管理 → 参数设置 中修改,立即生效:
| 值 | 记录 |
|---|---|
off | 不记录 |
write(默认) | 只记录非 GET 请求,也就是会修改数据的请求 |
all | 记录全部请求。请求量大时,这张表会增长得很快 |
填了其他值时按 write 处理。
下面这些请求不会记录:
- 排除的路径:参数
audit.http_trace.exclude_paths里列出的路径。每项写成"[方法] 路径前缀",用逗号或换行分隔,比如GET /api/monitor/表示以/api/monitor/开头的 GET 请求都不记录。默认值是/api/health, GET /api/monitor/, GET /api/messaging/bulletins/feed,分别是健康检查、监控页面的读取、顶栏消息铃铛读取公告; - 代码里标记为不记录的接口,比如监控页面每 5 秒一次的读取;
- 未登录(401)、没有权限(403)、被限流(429)的请求,它们在进入接口之前就被拦下了。参数校验失败(400)和接口出错的请求会记录。
记录内容:
- 谁:登录名、用户 ID、用户类型、客户端、IP 地址、User-Agent;
- 请求:请求方法、请求路径(不含查询参数)、查询参数、请求体(各最多 4 KB,已脱敏);
- 结果:HTTP 状态码、结果码(成功是
0,失败是错误码,比如A0440)、错误消息(按查看者的语言显示,规则和操作日志的失败原因一样); - 时间:请求时间、耗时;
- 追踪 ID。
筛选:请求路径、追踪 ID、登录名、请求方法、HTTP 状态码、结果码、IP 地址、请求时间。其中追踪 ID、请求方法、状态码和结果码要完全一致,其余是包含匹配。可以按 HTTP 状态码、请求时间和耗时排序。
这一页只能查看和导出,不能删除。
API 错误日志
系统管理 → 日志管理 → API 错误日志 收集服务端出错的请求,也就是 HTTP 状态码为 5xx("服务器内部错误"这一类)的请求。用户只看到一条通用的错误提示,真正的异常信息记在这里(见异常处理)。
- 不受访问日志开关影响:即使访问日志关闭,或者路径在排除列表里,出错的请求照样记录。只有代码里标记为不记录的接口(比如健康检查)例外;
- 记录内容:追踪 ID、用户 ID、登录名、请求方法、请求路径、查询参数、请求体、IP 地址、User-Agent、异常类型、异常消息、堆栈(只保留调用位置,最多 8 KB)。异常消息里可能带出的密码、参数值等会被脱敏;
- 筛选:追踪 ID、登录名、请求方法、请求路径、IP 地址、异常类型、处理状态、创建时间。其中追踪 ID 和请求方法要完全一致,其余是包含匹配。可以按创建时间排序。
处理状态
可以把错误当成待办来跟进:
| 状态 | 说明 |
|---|---|
| 待处理 | 新记录的状态 |
| 已处理 | 在待处理的记录上点"标记已处理",记下处理人和处理时间 |
| 已忽略 | 在待处理的记录上点"忽略",同样记下处理人和处理时间 |
已处理和已忽略的记录可以点"重新打开",回到待处理,处理人和处理时间被清空。这三个按钮需要"处理错误"权限,每次操作都记一条操作日志。
这一页没有删除按钮。
待处理的错误不会被自动清理
定时清理只删除已处理或已忽略、并且超过保留天数的错误日志。待处理的会一直留着,提醒你去看。
追踪 ID
追踪 ID(traceId)是每个请求的编号。同一个请求在各处留下的记录,带的是同一个编号:
- 出错时,接口返回的错误信息里有
traceId,响应头X-Request-Id也是它; - 服务端日志每一行的
reqId; - 四种日志里的"追踪 ID"。
用户报告"刚才出错了"时,可以这样查:
- 从错误提示或者浏览器开发者工具里拿到追踪 ID;
- 在 API 错误日志 或 API 访问日志 的"追踪 ID"里搜索(这两页可以按它筛选);
- 在 API 错误日志的详情里看异常消息和堆栈。
操作日志和登录日志的追踪 ID 在详情里显示。更多排查方法见排查问题。
保留时间和清理
日志不会无限增长。内置的定时任务"清理过期日志"(处理器 audit.purge)默认每天 3:30 执行一次,删除超过保留天数的记录。这里的 3:30 按服务器的时区计算,不受参数 core.default_timezone 影响。服务停机错过了执行时间的话,启动后会补执行一次。可以在 系统监控 → 定时任务 里修改执行时间,或者停用它。
保留天数由参数 audit.retention_days 决定,默认 180 天,取值范围 1–36500,填错时按 180 天处理。它对下面这些数据一起生效:
- 操作日志、登录日志、API 访问日志;
- API 错误日志中已处理和已忽略的(待处理的不删);
- 调度日志、站内信、邮件和短信的发送记录、短信验证码;
- 被删除超过这么多天的文件(文件本身和记录)。
在页面上删除或清空的日志,也是到这时候才从数据库里真正删除。每次执行后,调度日志的输出里会列出每张表删除了多少条。
权限一览
在 系统管理 → 角色管理 里给角色勾选菜单下的按钮:
| 菜单 | 按钮 | 权限点 |
|---|---|---|
| 在线用户 | 浏览、强退 | iam.session.browse、iam.session.kick |
| 服务监控 | 浏览 | monitor.server.browse |
| 缓存监控 | 浏览 | monitor.redis.browse |
| 缓存列表 | 浏览、删除 | monitor.cache.browse、monitor.cache.remove |
| 数据监控 | 浏览 | monitor.mysql.browse |
| 操作日志 | 浏览、查看、导出、删除 | audit.actionLog.browse、.view、.export、.remove |
| 登录日志 | 浏览、查看、导出、删除、解锁账户 | audit.signinLog.browse、.view、.export、.remove、.unlock |
| API 访问日志 | 浏览、查看、导出 | audit.httpTrace.browse、.view、.export |
| API 错误日志 | 浏览、查看、导出、处理错误 | audit.httpFault.browse、.view、.export、.handle |
日志页面的"删除"权限同时管着删除、批量删除和清空。
开发指南
- 操作日志:给接口加操作日志、隐藏敏感字段、让接口不进 API 访问日志
- 缓存:登记缓存命名空间,决定它在"缓存列表"里能不能清理、值是否隐藏
- 定时任务:内置的清理任务,以及怎样写自己的定时任务
- 参数设置:在代码里读取
audit.retention_days这类参数 - 异常处理:错误码,以及 500 错误怎样进入 API 错误日志
- 排查问题:用追踪 ID 和服务端日志定位问题
- 相关功能:安全基线、权限与数据范围、实时推送