From aa28b6312d5904abcf6aed96d27e899e2dfc7b7c Mon Sep 17 00:00:00 2001 From: pijiang <419471640@qq.com> Date: Thu, 15 Oct 2026 14:42:50 +0800 Subject: [PATCH 1/4] docs: fix DataWorks console steps and simplify zh annotations --- .../alert-sources/aliyun-dataworks-op.mdx | 21 ++--- .../alert-sources/aliyun-dataworks-op.mdx | 82 ++++++------------- 2 files changed, 31 insertions(+), 72 deletions(-) diff --git a/en/on-call/integration/alert-integration/alert-sources/aliyun-dataworks-op.mdx b/en/on-call/integration/alert-integration/alert-sources/aliyun-dataworks-op.mdx index dd3e26ba9..e7d6bc592 100644 --- a/en/on-call/integration/alert-integration/alert-sources/aliyun-dataworks-op.mdx +++ b/en/on-call/integration/alert-integration/alert-sources/aliyun-dataworks-op.mdx @@ -45,25 +45,16 @@ Choose this option when you need to route alerts to different collaboration spac ## In Alibaba Cloud DataWorks -**Step 1: Create a custom webhook** -
1. Log in to the Alibaba Cloud console and open the **DataWorks** product -2. Go to **DataStudio** or **Operation Center** and open the **Intelligent Monitoring** page -3. Open the alarm receiver configuration (the custom webhook entry) and create a new webhook -4. Set the request URL to the Flashduty integration push URL copied above; the request method is POST -5. After saving, you can click **Send test message**. DataWorks pushes a connectivity test message (`message.content`); Flashduty returns success without creating an alert event - -
- -**Step 2: Enable the webhook in monitoring rules** - -
+2. Go to **Data Development and O&M** → **Operation Center** +3. In the left navigation, choose **Monitoring and Alerting** → **Rule Management** +4. Open an existing rule, or create a new one +5. In the rule's **Alarm Behavior** section, select **WebHook** as the alarm method, and paste the Flashduty integration push URL copied above into the webhook address field +6. Save the rule. Alerts triggered by this rule are pushed to Flashduty in real time -1. In **Intelligent Monitoring**, open the **custom rules** (instance status alerts, resource group alerts), **baseline management**, or **event management** page -2. Create or edit a rule, and select the custom webhook created above as an alarm channel -3. Save the rule. Alerts triggered by this rule are pushed to Flashduty in real time +> After entering the URL, you can click **Send test message** to verify connectivity. The test message only verifies the push URL; Flashduty returns success without creating an alert event.
diff --git a/zh/on-call/integration/alert-integration/alert-sources/aliyun-dataworks-op.mdx b/zh/on-call/integration/alert-integration/alert-sources/aliyun-dataworks-op.mdx index b589640d4..32e81ffb5 100644 --- a/zh/on-call/integration/alert-integration/alert-sources/aliyun-dataworks-op.mdx +++ b/zh/on-call/integration/alert-integration/alert-sources/aliyun-dataworks-op.mdx @@ -45,25 +45,16 @@ keywords: ["告警集成", "阿里云 DataWorks 集成", "DataWorks 智能监控 ## 在阿里云 DataWorks -**步骤 1:创建自定义 Webhook** -
1. 登录阿里云控制台,进入 **DataWorks** 产品 -2. 进入 **数据开发(DataStudio)** 或 **运维中心**,打开 **智能监控** 页面 -3. 进入 **报警接收配置**(或 **自定义 webhook** 配置入口),新建一个 Webhook -4. `请求地址` 填写 Flashduty 集成的推送地址(上一步复制),`请求方法` 为 POST -5. 保存后,可点击 **发送测试消息**,DataWorks 会推送一条连通性测试消息(`message.content`),Flashduty 收到后返回成功但不产生告警事件 - -
- -**步骤 2:在监控规则中启用 Webhook** - -
+2. 进入 **数据开发与运维** → **运维中心** +3. 在左侧导航选择 **监控告警** → **规则管理** +4. 打开一个已有规则,或新建规则 +5. 在规则的 **报警行为** 区域,报警方式勾选 **WebHook**,并在 **Webhook地址** 中填写 Flashduty 集成的推送地址(上一步复制) +6. 保存规则,完成。此后该规则触发的告警会实时推送到 Flashduty -1. 进入 **智能监控** 的 **自定义规则**(周期实例状态告警、资源组告警)、**基线管理** 或 **事件管理** 页面 -2. 创建或编辑规则,在 **报警方式** 中勾选上一步创建的自定义 Webhook -3. 保存规则,完成。此后该规则触发的告警会实时推送到 Flashduty +> 填写地址后可点击 **发送测试消息** 验证连通性;测试消息仅用于验证推送地址,Flashduty 收到后返回成功但不产生告警事件。
@@ -75,44 +66,44 @@ DataWorks 智能监控自定义 Webhook 推送的消息统一包裹在 `message` | message.title | 告警类型 | 说明 | | ------------- | -------- | ---- | | `DataWorks_Remind` | 自定义告警 | 周期实例状态告警(remind_unit 为 Task、Baseline、Project、BizProcess)与资源组告警(ScheduleResourceGroup、DiResourceGroup) | -| `DataWorks_Baseline_Alert` | 基线告警 | 基线预警(dangerous)与破线(overSla) | -| `DataWorks_Event_Remind` | 事件告警 | 任务出错(ERROR)与变慢(SLOW) | +| `DataWorks_Baseline_Alert` | 基线告警 | 基线预警与破线 | +| `DataWorks_Event_Remind` | 事件告警 | 任务出错与变慢 | -连通性测试消息(`message.content`)仅用于验证推送地址,不会产生告警事件。 +连通性测试消息仅用于验证推送地址,不会产生告警事件。 ## 严重程度对照
-DataWorks 自定义告警 `remind_type` 到 Flashduty 告警等级(Severity)映射关系: +DataWorks 自定义告警 `remind_type` 到 Flashduty 告警等级映射关系: | DataWorks remind_type | Flashduty | 状态 | | --------------------- | --------- | ---- | -| error(出错) | Critical | 严重 | -| exhausted error(自动重跑仍出错) | Critical | 严重 | -| uncompleted(未完成) | Warning | 警告 | -| cycle uncompleted(周期未完成) | Warning | 警告 | -| run over time(运行超时) | Warning | 警告 | -| resGroup use ratio(资源组利用率) | Warning | 警告 | -| the number of waiting task in resGroup(资源组等待资源实例数) | Warning | 警告 | -| completed(完成) | Info | 提醒 | +| error | Critical | 严重 | +| exhausted error | Critical | 严重 | +| uncompleted | Warning | 警告 | +| cycle uncompleted | Warning | 警告 | +| run over time | Warning | 警告 | +| resGroup use ratio | Warning | 警告 | +| the number of waiting task in resGroup | Warning | 警告 | +| completed | Info | 提醒 | | 空值或未知值 | Warning | 警告 | 基线告警 `status` 映射关系: | DataWorks status | Flashduty | 状态 | | ---------------- | --------- | ---- | -| overSla(破线) | Critical | 严重 | -| dangerous(预警) | Warning | 警告 | +| overSla | Critical | 严重 | +| dangerous | Warning | 警告 | | 空值或未知值 | Warning | 警告 | 事件告警 `event_type` 映射关系: | DataWorks event_type | Flashduty | 状态 | | -------------------- | --------- | ---- | -| ERROR(出错) | Critical | 严重 | -| SLOW(变慢) | Warning | 警告 | +| ERROR | Critical | 严重 | +| SLOW | Warning | 警告 | | 空值或未知值 | Warning | 警告 |
@@ -120,44 +111,21 @@ DataWorks 自定义告警 `remind_type` 到 Flashduty 告警等级(Severity) ## Alert Key 与告警合并
-DataWorks 智能监控 Webhook 属于**一次性事件**(one-shot):官方协议只定义告警通知,**不恢复**、无更新语义,也没有提供厂商级别的去重 ID 或指纹字段。因此本集成不会自动恢复(关闭)任何告警,所有事件均以触发状态投递。 +DataWorks 智能监控 Webhook 属于**一次性事件**:官方协议只定义告警通知,**不恢复**、无更新语义,也没有提供厂商级别的去重 ID 或指纹字段。因此本集成不会自动恢复(关闭)任何告警,所有事件均以触发状态投递。 每次 webhook 投递都会生成一个全新的随机 UUID 作为 Alert Key,即每条推送都会创建一条独立的告警事件,重复触发不会自动合并。如需降噪,请在 Flashduty 中配置告警聚合或收敛策略。
-## 字段标签对照 -
- -推送消息中的主要字段会以标签(Labels)形式保留在告警事件中,可用于路由、静默与模板渲染: - -| 标签 | 来源字段 | 说明 | -| ---- | -------- | ---- | -| alert_type | title | 告警类型(DataWorks_Remind 等) | -| remind_name | remind_name | 规则名称 | -| remind_unit | remind_unit | 监控粒度(Task、ScheduleResourceGroup 等) | -| remind_type | remind_type | 触发条件原始值 | -| occur_time | occur_time | 发生时间原文 | -| remind_founder | remind_founder | 规则创建人 | -| more_information | more_information | 运维中心周期实例链接 | -| task_size / number_of_alerts | 同名字段 | 任务数量 / 第几次告警 | -| node_id_list / node_name_list / node_owner_list | 同名字段 | 节点 ID / 名称 / 责任人列表(英文逗号分隔,最多 5 个) | -| resource_group_name / threshold / duration | 同名字段 | 资源组名 / 阈值 / 持续时间(分钟),资源组监控特有 | -| baseline_name / baseline_owners | 同名字段 | 基线名 / 基线责任人 | -| cycle_number / key_instance / sla_time / buffer / status | 同名字段 | 周期号 / 关键实例 / 承诺时间 / 余量(分钟)/ 基线状态 | -| task_name / task_owner_name / event_type / event_name | 同名字段 | 任务名 / 责任人 / 事件类型 / 事件名称 | - -
- ## 常见问题
**为什么告警不会自动恢复?** -DataWorks 智能监控 Webhook 协议没有定义恢复(resolve)消息,属于一次性通知。如需关闭告警,请在 Flashduty 控制台手动处理,或为规则配置告警收敛策略。 +DataWorks 智能监控 Webhook 协议没有定义恢复消息,属于一次性通知。如需关闭告警,请在 Flashduty 控制台手动处理,或为规则配置告警收敛策略。 **发送测试消息后为什么看不到告警?** -DataWorks 的连通性测试消息(`message.content`)仅用于验证推送地址可达,Flashduty 收到后返回成功但不生成告警事件。请通过真实触发规则来验证端到端链路。 +DataWorks 的连通性测试消息仅用于验证推送地址可达,Flashduty 收到后返回成功但不生成告警事件。请通过真实触发规则来验证端到端链路。
From 1d8f96b16a334a931ba45ada4aa79625bd91495b Mon Sep 17 00:00:00 2001 From: Ulric Qin Date: Thu, 15 Oct 2026 03:36:37 -0700 Subject: [PATCH 2/4] docs(monitors): document public folder management APIs --- api-reference/monitors.openapi.en.json | 565 ++++++++++++++++++++++++- api-reference/monitors.openapi.zh.json | 565 ++++++++++++++++++++++++- api-reference/openapi.en.json | 565 ++++++++++++++++++++++++- api-reference/openapi.zh.json | 565 ++++++++++++++++++++++++- docs.json | 34 +- 5 files changed, 2288 insertions(+), 6 deletions(-) diff --git a/api-reference/monitors.openapi.en.json b/api-reference/monitors.openapi.en.json index 7a93a559a..c64821fe2 100644 --- a/api-reference/monitors.openapi.en.json +++ b/api-reference/monitors.openapi.en.json @@ -5116,7 +5116,7 @@ "Monitors/Rule folders" ], "x-mint": { - "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **1,000 requests/minute**; **50 requests/second** per account |\n| Permissions | **Alerting Rules Read** or **Node Permissions Read** (`monit`) |\n\n## Usage\n\n- The response is a flat list of every readable folder, not a tree: rebuild the hierarchy from `parent_id` and `parent_path`, and search client-side.\n- Dashboards and alert rules both live in this folder tree, so the same IDs appear as `folder_id` on dashboard payloads.\n- Folder writes (`create`, `update`, `move`, `delete`) are still JWT-only and are not part of the public API.", + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **1,000 requests/minute**; **50 requests/second** per account |\n| Permissions | **Alerting Rules Read** or **Node Permissions Read** (`monit`) |\n\n## Usage\n\n- The response is a flat list of every readable folder, not a tree: rebuild the hierarchy from `parent_id` and `parent_path`, and search client-side.\n- Dashboards and alert rules both live in this folder tree, so the same IDs appear as `folder_id` on dashboard payloads.", "href": "/en/api-reference/monitors/rule-folders/monit-folder-read-list", "metadata": { "sidebarTitle": "List monitor folders" @@ -5182,6 +5182,435 @@ } } } + }, + "/monit/folder/health": { + "post": { + "operationId": "monit-folder-read-health", + "summary": "Get monitor folder health", + "description": "Get health summaries for visible folders, including issues in their descendants.", + "tags": [ + "Monitors/Rule folders" + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **30 requests/minute**; **2 requests/second** per account |\n| Permissions | **Alerting Rules Read**, **Alerting Rules Manage**, **Node Permissions Read**, or **Node Permissions Manage** (`monit`) |\n\n## Usage\n\n- Issues include active alerts and alert-rule runtime problems; issues propagate to visible ancestors.\n- This operation scans folder rules and runtime state. Avoid frequent polling.", + "href": "/en/api-reference/monitors/rule-folders/monit-folder-read-health", + "metadata": { + "sidebarTitle": "Get monitor folder health" + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/FolderHealthOutput" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "items": [ + { + "folder_id": 12, + "has_issue": false + } + ] + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/monit/folder/create": { + "post": { + "operationId": "monit-folder-write-create", + "summary": "Create monitor folder", + "description": "Create a monitor folder at the root or under an accessible parent folder.", + "tags": [ + "Monitors/Rule folders" + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Alerting Rules Manage** or **Node Permissions Manage** (`monit`) |\n\n## Usage\n\n- Use `parent_id: 0` or omit it to create a root folder. The parent must be accessible.\n- Every call is recorded in the account audit log. Don't put secrets in request fields.", + "href": "/en/api-reference/monitors/rule-folders/monit-folder-write-create", + "metadata": { + "sidebarTitle": "Create monitor folder" + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/FolderItem" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "id": 12, + "account_id": 10023, + "parent_id": 10, + "parent_path": "10", + "team_id": 0, + "name": "Checkout", + "note": "Checkout service folders", + "creator_id": 10023, + "creator_name": "Ada Lovelace", + "updater_id": 10088, + "updater_name": "Grace Hopper", + "created_at": 1791000000, + "updated_at": 1791100800 + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderCreateRequest" + }, + "example": { + "name": "Checkout", + "note": "Checkout service folders", + "parent_id": 10, + "team_id": 0 + } + } + } + } + } + }, + "/monit/folder/update": { + "post": { + "operationId": "monit-folder-write-update", + "summary": "Update monitor folder", + "description": "Replace the name, note, and team assignment of an accessible monitor folder.", + "tags": [ + "Monitors/Rule folders" + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Alerting Rules Manage** or **Node Permissions Manage** (`monit`) |\n\n## Usage\n\n- Send both `id` and `name`. This is a replacement of editable fields: omitting `note` clears it, and omitting `team_id` resets it to 0.\n- Use the move operation to change the parent folder.\n- Every call is recorded in the account audit log. Don't put secrets in request fields.", + "href": "/en/api-reference/monitors/rule-folders/monit-folder-write-update", + "metadata": { + "sidebarTitle": "Update monitor folder" + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/FolderItem" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "id": 12, + "account_id": 10023, + "parent_id": 10, + "parent_path": "10", + "team_id": 0, + "name": "Checkout production", + "note": "Production services", + "creator_id": 10023, + "creator_name": "Ada Lovelace", + "updater_id": 10088, + "updater_name": "Grace Hopper", + "created_at": 1791000000, + "updated_at": 1791100800 + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderUpdateRequest" + }, + "example": { + "id": 12, + "name": "Checkout production", + "note": "Production services", + "team_id": 0 + } + } + } + } + } + }, + "/monit/folder/move": { + "post": { + "operationId": "monit-folder-write-move", + "summary": "Move monitor folder", + "description": "Move a monitor folder and its descendants under another folder or to the root.", + "tags": [ + "Monitors/Rule folders" + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Alerting Rules Manage** or **Node Permissions Manage** (`monit`) |\n\n## Usage\n\n- You must have access to both the source and destination folders.\n- `dest_parent_id` is required; send 0 explicitly to move to the root. Moving under the folder itself or a descendant is rejected.\n- Descendant paths are updated in the same transaction.\n- Every call is recorded in the account audit log. Don't put secrets in request fields.", + "href": "/en/api-reference/monitors/rule-folders/monit-folder-write-move", + "metadata": { + "sidebarTitle": "Move monitor folder" + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "Empty object." + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": {} + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderMoveRequest" + }, + "example": { + "id": 12, + "dest_parent_id": 0 + } + } + } + } + } + }, + "/monit/folder/delete": { + "post": { + "operationId": "monit-folder-write-delete", + "summary": "Delete monitor folder", + "description": "Delete an accessible monitor folder after removing its children, rules, and dashboards.", + "tags": [ + "Monitors/Rule folders" + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Alerting Rules Manage** or **Node Permissions Manage** (`monit`) |\n\n## Usage\n\n- Only empty folders can be deleted. Child folders, alert rules, and dashboards block deletion with HTTP 409 and error code `FolderNotEmpty`.\n- Every call is recorded in the account audit log. Don't put secrets in request fields.", + "href": "/en/api-reference/monitors/rule-folders/monit-folder-write-delete", + "metadata": { + "sidebarTitle": "Delete monitor folder" + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "Empty object." + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": {} + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + }, + "409": { + "description": "Folder is not empty.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "request_id": { + "type": "string", + "description": "Request ID." + }, + "error": { + "type": "object", + "description": "Error details.", + "properties": { + "code": { + "type": "string", + "description": "FolderNotEmpty: remove the folder contents before retrying.", + "enum": [ + "FolderNotEmpty" + ] + }, + "message": { + "type": "string", + "description": "Error message." + } + }, + "required": [ + "code", + "message" + ] + } + }, + "required": [ + "request_id", + "error" + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "FolderNotEmpty", + "message": "Folder contains dashboards" + } + } + } + } + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderDeleteRequest" + }, + "example": { + "id": 12 + } + } + } + } + } } }, "components": { @@ -12205,6 +12634,140 @@ "created_at", "updated_at" ] + }, + "FolderCreateRequest": { + "type": "object", + "description": "Monitor folder request.", + "properties": { + "name": { + "type": "string", + "description": "Folder name; cannot contain a period, slash, or comma.", + "minLength": 1 + }, + "note": { + "type": "string", + "description": "Free-form note; omitted or empty clears the note on update." + }, + "parent_id": { + "type": "integer", + "description": "Parent folder ID; 0 or omitted creates a root folder.", + "format": "uint64", + "minimum": 0 + }, + "team_id": { + "type": "integer", + "description": "Team ID; 0 means account-wide. Omitted on update resets to 0.", + "format": "uint64", + "minimum": 0 + } + }, + "required": [ + "name" + ] + }, + "FolderUpdateRequest": { + "type": "object", + "description": "Monitor folder request.", + "properties": { + "id": { + "type": "integer", + "description": "Folder ID.", + "format": "uint64", + "minimum": 1 + }, + "name": { + "type": "string", + "description": "Folder name; cannot contain a period, slash, or comma.", + "minLength": 1 + }, + "note": { + "type": "string", + "description": "Free-form note; omitted or empty clears the note on update." + }, + "team_id": { + "type": "integer", + "description": "Team ID; 0 means account-wide. Omitted on update resets to 0.", + "format": "uint64", + "minimum": 0 + } + }, + "required": [ + "id", + "name" + ] + }, + "FolderMoveRequest": { + "type": "object", + "description": "Monitor folder request.", + "properties": { + "id": { + "type": "integer", + "description": "Folder ID.", + "format": "uint64", + "minimum": 1 + }, + "dest_parent_id": { + "type": "integer", + "description": "Destination parent folder ID; required even when 0 to move to the root.", + "format": "uint64", + "minimum": 0 + } + }, + "required": [ + "id", + "dest_parent_id" + ] + }, + "FolderDeleteRequest": { + "type": "object", + "description": "Monitor folder request.", + "properties": { + "id": { + "type": "integer", + "description": "Folder ID.", + "format": "uint64", + "minimum": 1 + } + }, + "required": [ + "id" + ] + }, + "FolderHealthItem": { + "type": "object", + "description": "Health summary for a visible folder.", + "properties": { + "folder_id": { + "type": "integer", + "description": "Folder ID.", + "format": "uint64", + "minimum": 0 + }, + "has_issue": { + "type": "boolean", + "description": "Whether this folder or a visible descendant has active alerts or rule runtime issues." + } + }, + "required": [ + "folder_id", + "has_issue" + ] + }, + "FolderHealthOutput": { + "type": "object", + "description": "Health summaries for visible folders.", + "properties": { + "items": { + "type": "array", + "description": "Folder health summaries.", + "items": { + "$ref": "#/components/schemas/FolderHealthItem" + } + } + }, + "required": [ + "items" + ] } } } diff --git a/api-reference/monitors.openapi.zh.json b/api-reference/monitors.openapi.zh.json index 0b51d7df7..e51b3412b 100644 --- a/api-reference/monitors.openapi.zh.json +++ b/api-reference/monitors.openapi.zh.json @@ -5116,7 +5116,7 @@ "Monitors/规则分组" ], "x-mint": { - "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | **1,000 requests/minute**; **50 requests/second** per account |\n| 权限要求 | **告警规则查看** 或 **节点权限查看**(`monit`) |\n\n## 使用说明\n\n- 响应是可读目录的扁平列表而非树:请用 `parent_id` 与 `parent_path` 重建层级,并在客户端自行搜索。\n- 仪表盘与告警规则共用这棵目录树,因此同一批 ID 会作为仪表盘载荷中的 `folder_id` 出现。\n- 目录写操作(`create`、`update`、`move`、`delete`)仍仅支持 JWT,不在公开 API 范围内。", + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | **1,000 requests/minute**; **50 requests/second** per account |\n| 权限要求 | **告警规则查看** 或 **节点权限查看**(`monit`) |\n\n## 使用说明\n\n- 响应是可读目录的扁平列表而非树:请用 `parent_id` 与 `parent_path` 重建层级,并在客户端自行搜索。\n- 仪表盘与告警规则共用这棵目录树,因此同一批 ID 会作为仪表盘载荷中的 `folder_id` 出现。", "href": "/zh/api-reference/monitors/rule-folders/monit-folder-read-list", "metadata": { "sidebarTitle": "查询监控节点列表" @@ -5182,6 +5182,435 @@ } } } + }, + "/monit/folder/health": { + "post": { + "operationId": "monit-folder-read-health", + "summary": "获取监控目录健康摘要", + "description": "获取可见目录的健康摘要,包含后代目录中的问题。", + "tags": [ + "Monitors/规则分组" + ], + "x-mint": { + "content": "## 限制\n\n| 项目 | 值 |\n| ------ | ----- |\n| 频率限制 | 每账户 **30 次/分钟**;**2 次/秒** |\n| 权限 | **告警规则查看**、**告警规则管理**、**节点权限查看**或**节点权限管理**(`monit`) |\n\n## 使用说明\n\n- 问题包括活跃告警和告警规则运行问题,并向可见祖先目录汇总。\n- 此操作扫描目录规则和运行状态,请避免频繁轮询。", + "href": "/zh/api-reference/monitors/rule-folders/monit-folder-read-health", + "metadata": { + "sidebarTitle": "获取监控目录健康摘要" + } + }, + "responses": { + "200": { + "description": "成功", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/FolderHealthOutput" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "items": [ + { + "folder_id": 12, + "has_issue": false + } + ] + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/monit/folder/create": { + "post": { + "operationId": "monit-folder-write-create", + "summary": "创建监控目录", + "description": "在顶层或有权访问的父目录下创建监控目录。", + "tags": [ + "Monitors/规则分组" + ], + "x-mint": { + "content": "## 限制\n\n| 项目 | 值 |\n| ------ | ----- |\n| 频率限制 | 每账户 **300 次/分钟**;**20 次/秒** |\n| 权限 | **告警规则管理**或**节点权限管理**(`monit`) |\n\n## 使用说明\n\n- 传入 `parent_id: 0` 或省略该字段以创建顶层目录。您必须有权访问父目录。\n- 每次调用均记录账户审计日志。请勿在请求字段中传入密钥。", + "href": "/zh/api-reference/monitors/rule-folders/monit-folder-write-create", + "metadata": { + "sidebarTitle": "创建监控目录" + } + }, + "responses": { + "200": { + "description": "成功", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/FolderItem" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "id": 12, + "account_id": 10023, + "parent_id": 10, + "parent_path": "10", + "team_id": 0, + "name": "Checkout", + "note": "Checkout service folders", + "creator_id": 10023, + "creator_name": "Ada Lovelace", + "updater_id": 10088, + "updater_name": "Grace Hopper", + "created_at": 1791000000, + "updated_at": 1791100800 + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderCreateRequest" + }, + "example": { + "name": "Checkout", + "note": "Checkout service folders", + "parent_id": 10, + "team_id": 0 + } + } + } + } + } + }, + "/monit/folder/update": { + "post": { + "operationId": "monit-folder-write-update", + "summary": "更新监控目录", + "description": "替换有权访问的监控目录的名称、备注和团队归属。", + "tags": [ + "Monitors/规则分组" + ], + "x-mint": { + "content": "## 限制\n\n| 项目 | 值 |\n| ------ | ----- |\n| 频率限制 | 每账户 **300 次/分钟**;**20 次/秒** |\n| 权限 | **告警规则管理**或**节点权限管理**(`monit`) |\n\n## 使用说明\n\n- 必须传入 `id` 和 `name`。此操作替换可编辑字段:省略 `note` 会清空备注,省略 `team_id` 会重置为 0。\n- 请使用移动接口更改父目录。\n- 每次调用均记录账户审计日志。请勿在请求字段中传入密钥。", + "href": "/zh/api-reference/monitors/rule-folders/monit-folder-write-update", + "metadata": { + "sidebarTitle": "更新监控目录" + } + }, + "responses": { + "200": { + "description": "成功", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/FolderItem" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "id": 12, + "account_id": 10023, + "parent_id": 10, + "parent_path": "10", + "team_id": 0, + "name": "Checkout production", + "note": "Production services", + "creator_id": 10023, + "creator_name": "Ada Lovelace", + "updater_id": 10088, + "updater_name": "Grace Hopper", + "created_at": 1791000000, + "updated_at": 1791100800 + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderUpdateRequest" + }, + "example": { + "id": 12, + "name": "Checkout production", + "note": "Production services", + "team_id": 0 + } + } + } + } + } + }, + "/monit/folder/move": { + "post": { + "operationId": "monit-folder-write-move", + "summary": "移动监控目录", + "description": "将监控目录及其后代移至其他目录下或顶层。", + "tags": [ + "Monitors/规则分组" + ], + "x-mint": { + "content": "## 限制\n\n| 项目 | 值 |\n| ------ | ----- |\n| 频率限制 | 每账户 **300 次/分钟**;**20 次/秒** |\n| 权限 | **告警规则管理**或**节点权限管理**(`monit`) |\n\n## 使用说明\n\n- 您必须有权访问源目录和目标目录。\n- 必须传入 `dest_parent_id`;移至顶层时显式传 0。不能移至自身或后代目录下。\n- 后代目录路径在同一事务中更新。\n- 每次调用均记录账户审计日志。请勿在请求字段中传入密钥。", + "href": "/zh/api-reference/monitors/rule-folders/monit-folder-write-move", + "metadata": { + "sidebarTitle": "移动监控目录" + } + }, + "responses": { + "200": { + "description": "成功", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "空对象。" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": {} + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderMoveRequest" + }, + "example": { + "id": 12, + "dest_parent_id": 0 + } + } + } + } + } + }, + "/monit/folder/delete": { + "post": { + "operationId": "monit-folder-write-delete", + "summary": "删除监控目录", + "description": "移除子目录、规则和仪表盘后,删除有权访问的监控目录。", + "tags": [ + "Monitors/规则分组" + ], + "x-mint": { + "content": "## 限制\n\n| 项目 | 值 |\n| ------ | ----- |\n| 频率限制 | 每账户 **300 次/分钟**;**20 次/秒** |\n| 权限 | **告警规则管理**或**节点权限管理**(`monit`) |\n\n## 使用说明\n\n- 只能删除空目录。存在子目录、告警规则或仪表盘时,返回 HTTP 409,错误码为 `FolderNotEmpty`。\n- 每次调用均记录账户审计日志。请勿在请求字段中传入密钥。", + "href": "/zh/api-reference/monitors/rule-folders/monit-folder-write-delete", + "metadata": { + "sidebarTitle": "删除监控目录" + } + }, + "responses": { + "200": { + "description": "成功", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "空对象。" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": {} + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + }, + "409": { + "description": "目录非空。", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "request_id": { + "type": "string", + "description": "请求 ID。" + }, + "error": { + "type": "object", + "description": "错误详情。", + "properties": { + "code": { + "type": "string", + "description": "FolderNotEmpty:请移除目录内容后重试。", + "enum": [ + "FolderNotEmpty" + ] + }, + "message": { + "type": "string", + "description": "错误消息。" + } + }, + "required": [ + "code", + "message" + ] + } + }, + "required": [ + "request_id", + "error" + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "FolderNotEmpty", + "message": "Folder contains dashboards" + } + } + } + } + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderDeleteRequest" + }, + "example": { + "id": 12 + } + } + } + } + } } }, "components": { @@ -12205,6 +12634,140 @@ "created_at", "updated_at" ] + }, + "FolderCreateRequest": { + "type": "object", + "description": "监控目录请求。", + "properties": { + "name": { + "type": "string", + "description": "目录名称;不能包含句点、斜杠或逗号。", + "minLength": 1 + }, + "note": { + "type": "string", + "description": "备注;更新时省略或传空字符串会清空备注。" + }, + "parent_id": { + "type": "integer", + "description": "父目录 ID;0 或省略时创建顶层目录。", + "format": "uint64", + "minimum": 0 + }, + "team_id": { + "type": "integer", + "description": "团队 ID;0 表示账户范围。更新时省略会重置为 0。", + "format": "uint64", + "minimum": 0 + } + }, + "required": [ + "name" + ] + }, + "FolderUpdateRequest": { + "type": "object", + "description": "监控目录请求。", + "properties": { + "id": { + "type": "integer", + "description": "目录 ID。", + "format": "uint64", + "minimum": 1 + }, + "name": { + "type": "string", + "description": "目录名称;不能包含句点、斜杠或逗号。", + "minLength": 1 + }, + "note": { + "type": "string", + "description": "备注;更新时省略或传空字符串会清空备注。" + }, + "team_id": { + "type": "integer", + "description": "团队 ID;0 表示账户范围。更新时省略会重置为 0。", + "format": "uint64", + "minimum": 0 + } + }, + "required": [ + "id", + "name" + ] + }, + "FolderMoveRequest": { + "type": "object", + "description": "监控目录请求。", + "properties": { + "id": { + "type": "integer", + "description": "目录 ID。", + "format": "uint64", + "minimum": 1 + }, + "dest_parent_id": { + "type": "integer", + "description": "目标父目录 ID;移至顶层时必须显式传 0。", + "format": "uint64", + "minimum": 0 + } + }, + "required": [ + "id", + "dest_parent_id" + ] + }, + "FolderDeleteRequest": { + "type": "object", + "description": "监控目录请求。", + "properties": { + "id": { + "type": "integer", + "description": "目录 ID。", + "format": "uint64", + "minimum": 1 + } + }, + "required": [ + "id" + ] + }, + "FolderHealthItem": { + "type": "object", + "description": "目录健康摘要。", + "properties": { + "folder_id": { + "type": "integer", + "description": "目录 ID。", + "format": "uint64", + "minimum": 0 + }, + "has_issue": { + "type": "boolean", + "description": "本目录或可见后代目录是否存在活跃告警或规则运行问题。" + } + }, + "required": [ + "folder_id", + "has_issue" + ] + }, + "FolderHealthOutput": { + "type": "object", + "description": "可见目录的健康摘要。", + "properties": { + "items": { + "type": "array", + "description": "目录摘要列表。", + "items": { + "$ref": "#/components/schemas/FolderHealthItem" + } + } + }, + "required": [ + "items" + ] } } } diff --git a/api-reference/openapi.en.json b/api-reference/openapi.en.json index 83b94f763..95a3c2e1a 100644 --- a/api-reference/openapi.en.json +++ b/api-reference/openapi.en.json @@ -33311,6 +33311,140 @@ "example": "0123456789abcdef0123456789abcdef100" } } + }, + "FolderCreateRequest": { + "type": "object", + "description": "Monitor folder request.", + "properties": { + "name": { + "type": "string", + "description": "Folder name; cannot contain a period, slash, or comma.", + "minLength": 1 + }, + "note": { + "type": "string", + "description": "Free-form note; omitted or empty clears the note on update." + }, + "parent_id": { + "type": "integer", + "description": "Parent folder ID; 0 or omitted creates a root folder.", + "format": "uint64", + "minimum": 0 + }, + "team_id": { + "type": "integer", + "description": "Team ID; 0 means account-wide. Omitted on update resets to 0.", + "format": "uint64", + "minimum": 0 + } + }, + "required": [ + "name" + ] + }, + "FolderUpdateRequest": { + "type": "object", + "description": "Monitor folder request.", + "properties": { + "id": { + "type": "integer", + "description": "Folder ID.", + "format": "uint64", + "minimum": 1 + }, + "name": { + "type": "string", + "description": "Folder name; cannot contain a period, slash, or comma.", + "minLength": 1 + }, + "note": { + "type": "string", + "description": "Free-form note; omitted or empty clears the note on update." + }, + "team_id": { + "type": "integer", + "description": "Team ID; 0 means account-wide. Omitted on update resets to 0.", + "format": "uint64", + "minimum": 0 + } + }, + "required": [ + "id", + "name" + ] + }, + "FolderMoveRequest": { + "type": "object", + "description": "Monitor folder request.", + "properties": { + "id": { + "type": "integer", + "description": "Folder ID.", + "format": "uint64", + "minimum": 1 + }, + "dest_parent_id": { + "type": "integer", + "description": "Destination parent folder ID; required even when 0 to move to the root.", + "format": "uint64", + "minimum": 0 + } + }, + "required": [ + "id", + "dest_parent_id" + ] + }, + "FolderDeleteRequest": { + "type": "object", + "description": "Monitor folder request.", + "properties": { + "id": { + "type": "integer", + "description": "Folder ID.", + "format": "uint64", + "minimum": 1 + } + }, + "required": [ + "id" + ] + }, + "FolderHealthItem": { + "type": "object", + "description": "Health summary for a visible folder.", + "properties": { + "folder_id": { + "type": "integer", + "description": "Folder ID.", + "format": "uint64", + "minimum": 0 + }, + "has_issue": { + "type": "boolean", + "description": "Whether this folder or a visible descendant has active alerts or rule runtime issues." + } + }, + "required": [ + "folder_id", + "has_issue" + ] + }, + "FolderHealthOutput": { + "type": "object", + "description": "Health summaries for visible folders.", + "properties": { + "items": { + "type": "array", + "description": "Folder health summaries.", + "items": { + "$ref": "#/components/schemas/FolderHealthItem" + } + } + }, + "required": [ + "items" + ] } }, "securitySchemes": { @@ -67177,7 +67311,7 @@ "Monitors/Rule folders" ], "x-mint": { - "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **1,000 requests/minute**; **50 requests/second** per account |\n| Permissions | **Alerting Rules Read** or **Node Permissions Read** (`monit`) |\n\n## Usage\n\n- The response is a flat list of every readable folder, not a tree: rebuild the hierarchy from `parent_id` and `parent_path`, and search client-side.\n- Dashboards and alert rules both live in this folder tree, so the same IDs appear as `folder_id` on dashboard payloads.\n- Folder writes (`create`, `update`, `move`, `delete`) are still JWT-only and are not part of the public API.", + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **1,000 requests/minute**; **50 requests/second** per account |\n| Permissions | **Alerting Rules Read** or **Node Permissions Read** (`monit`) |\n\n## Usage\n\n- The response is a flat list of every readable folder, not a tree: rebuild the hierarchy from `parent_id` and `parent_path`, and search client-side.\n- Dashboards and alert rules both live in this folder tree, so the same IDs appear as `folder_id` on dashboard payloads.", "href": "/en/api-reference/monitors/rule-folders/monit-folder-read-list", "metadata": { "sidebarTitle": "List monitor folders" @@ -68006,6 +68140,435 @@ } } } + }, + "/monit/folder/health": { + "post": { + "operationId": "monit-folder-read-health", + "summary": "Get monitor folder health", + "description": "Get health summaries for visible folders, including issues in their descendants.", + "tags": [ + "Monitors/Rule folders" + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **30 requests/minute**; **2 requests/second** per account |\n| Permissions | **Alerting Rules Read**, **Alerting Rules Manage**, **Node Permissions Read**, or **Node Permissions Manage** (`monit`) |\n\n## Usage\n\n- Issues include active alerts and alert-rule runtime problems; issues propagate to visible ancestors.\n- This operation scans folder rules and runtime state. Avoid frequent polling.", + "href": "/en/api-reference/monitors/rule-folders/monit-folder-read-health", + "metadata": { + "sidebarTitle": "Get monitor folder health" + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/FolderHealthOutput" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "items": [ + { + "folder_id": 12, + "has_issue": false + } + ] + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/monit/folder/create": { + "post": { + "operationId": "monit-folder-write-create", + "summary": "Create monitor folder", + "description": "Create a monitor folder at the root or under an accessible parent folder.", + "tags": [ + "Monitors/Rule folders" + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Alerting Rules Manage** or **Node Permissions Manage** (`monit`) |\n\n## Usage\n\n- Use `parent_id: 0` or omit it to create a root folder. The parent must be accessible.\n- Every call is recorded in the account audit log. Don't put secrets in request fields.", + "href": "/en/api-reference/monitors/rule-folders/monit-folder-write-create", + "metadata": { + "sidebarTitle": "Create monitor folder" + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/FolderItem" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "id": 12, + "account_id": 10023, + "parent_id": 10, + "parent_path": "10", + "team_id": 0, + "name": "Checkout", + "note": "Checkout service folders", + "creator_id": 10023, + "creator_name": "Ada Lovelace", + "updater_id": 10088, + "updater_name": "Grace Hopper", + "created_at": 1791000000, + "updated_at": 1791100800 + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderCreateRequest" + }, + "example": { + "name": "Checkout", + "note": "Checkout service folders", + "parent_id": 10, + "team_id": 0 + } + } + } + } + } + }, + "/monit/folder/update": { + "post": { + "operationId": "monit-folder-write-update", + "summary": "Update monitor folder", + "description": "Replace the name, note, and team assignment of an accessible monitor folder.", + "tags": [ + "Monitors/Rule folders" + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Alerting Rules Manage** or **Node Permissions Manage** (`monit`) |\n\n## Usage\n\n- Send both `id` and `name`. This is a replacement of editable fields: omitting `note` clears it, and omitting `team_id` resets it to 0.\n- Use the move operation to change the parent folder.\n- Every call is recorded in the account audit log. Don't put secrets in request fields.", + "href": "/en/api-reference/monitors/rule-folders/monit-folder-write-update", + "metadata": { + "sidebarTitle": "Update monitor folder" + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/FolderItem" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "id": 12, + "account_id": 10023, + "parent_id": 10, + "parent_path": "10", + "team_id": 0, + "name": "Checkout production", + "note": "Production services", + "creator_id": 10023, + "creator_name": "Ada Lovelace", + "updater_id": 10088, + "updater_name": "Grace Hopper", + "created_at": 1791000000, + "updated_at": 1791100800 + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderUpdateRequest" + }, + "example": { + "id": 12, + "name": "Checkout production", + "note": "Production services", + "team_id": 0 + } + } + } + } + } + }, + "/monit/folder/move": { + "post": { + "operationId": "monit-folder-write-move", + "summary": "Move monitor folder", + "description": "Move a monitor folder and its descendants under another folder or to the root.", + "tags": [ + "Monitors/Rule folders" + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Alerting Rules Manage** or **Node Permissions Manage** (`monit`) |\n\n## Usage\n\n- You must have access to both the source and destination folders.\n- `dest_parent_id` is required; send 0 explicitly to move to the root. Moving under the folder itself or a descendant is rejected.\n- Descendant paths are updated in the same transaction.\n- Every call is recorded in the account audit log. Don't put secrets in request fields.", + "href": "/en/api-reference/monitors/rule-folders/monit-folder-write-move", + "metadata": { + "sidebarTitle": "Move monitor folder" + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "Empty object." + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": {} + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderMoveRequest" + }, + "example": { + "id": 12, + "dest_parent_id": 0 + } + } + } + } + } + }, + "/monit/folder/delete": { + "post": { + "operationId": "monit-folder-write-delete", + "summary": "Delete monitor folder", + "description": "Delete an accessible monitor folder after removing its children, rules, and dashboards.", + "tags": [ + "Monitors/Rule folders" + ], + "x-mint": { + "content": "## Restrictions\n\n| Aspect | Value |\n| ------ | ----- |\n| Rate limits | **300 requests/minute**; **20 requests/second** per account |\n| Permissions | **Alerting Rules Manage** or **Node Permissions Manage** (`monit`) |\n\n## Usage\n\n- Only empty folders can be deleted. Child folders, alert rules, and dashboards block deletion with HTTP 409 and error code `FolderNotEmpty`.\n- Every call is recorded in the account audit log. Don't put secrets in request fields.", + "href": "/en/api-reference/monitors/rule-folders/monit-folder-write-delete", + "metadata": { + "sidebarTitle": "Delete monitor folder" + } + }, + "responses": { + "200": { + "description": "Success", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "Empty object." + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": {} + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + }, + "409": { + "description": "Folder is not empty.", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "request_id": { + "type": "string", + "description": "Request ID." + }, + "error": { + "type": "object", + "description": "Error details.", + "properties": { + "code": { + "type": "string", + "description": "FolderNotEmpty: remove the folder contents before retrying.", + "enum": [ + "FolderNotEmpty" + ] + }, + "message": { + "type": "string", + "description": "Error message." + } + }, + "required": [ + "code", + "message" + ] + } + }, + "required": [ + "request_id", + "error" + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "FolderNotEmpty", + "message": "Folder contains dashboards" + } + } + } + } + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderDeleteRequest" + }, + "example": { + "id": 12 + } + } + } + } + } } }, "security": [ diff --git a/api-reference/openapi.zh.json b/api-reference/openapi.zh.json index 06f44622b..99adf484f 100644 --- a/api-reference/openapi.zh.json +++ b/api-reference/openapi.zh.json @@ -33311,6 +33311,140 @@ "example": "0123456789abcdef0123456789abcdef100" } } + }, + "FolderCreateRequest": { + "type": "object", + "description": "监控目录请求。", + "properties": { + "name": { + "type": "string", + "description": "目录名称;不能包含句点、斜杠或逗号。", + "minLength": 1 + }, + "note": { + "type": "string", + "description": "备注;更新时省略或传空字符串会清空备注。" + }, + "parent_id": { + "type": "integer", + "description": "父目录 ID;0 或省略时创建顶层目录。", + "format": "uint64", + "minimum": 0 + }, + "team_id": { + "type": "integer", + "description": "团队 ID;0 表示账户范围。更新时省略会重置为 0。", + "format": "uint64", + "minimum": 0 + } + }, + "required": [ + "name" + ] + }, + "FolderUpdateRequest": { + "type": "object", + "description": "监控目录请求。", + "properties": { + "id": { + "type": "integer", + "description": "目录 ID。", + "format": "uint64", + "minimum": 1 + }, + "name": { + "type": "string", + "description": "目录名称;不能包含句点、斜杠或逗号。", + "minLength": 1 + }, + "note": { + "type": "string", + "description": "备注;更新时省略或传空字符串会清空备注。" + }, + "team_id": { + "type": "integer", + "description": "团队 ID;0 表示账户范围。更新时省略会重置为 0。", + "format": "uint64", + "minimum": 0 + } + }, + "required": [ + "id", + "name" + ] + }, + "FolderMoveRequest": { + "type": "object", + "description": "监控目录请求。", + "properties": { + "id": { + "type": "integer", + "description": "目录 ID。", + "format": "uint64", + "minimum": 1 + }, + "dest_parent_id": { + "type": "integer", + "description": "目标父目录 ID;移至顶层时必须显式传 0。", + "format": "uint64", + "minimum": 0 + } + }, + "required": [ + "id", + "dest_parent_id" + ] + }, + "FolderDeleteRequest": { + "type": "object", + "description": "监控目录请求。", + "properties": { + "id": { + "type": "integer", + "description": "目录 ID。", + "format": "uint64", + "minimum": 1 + } + }, + "required": [ + "id" + ] + }, + "FolderHealthItem": { + "type": "object", + "description": "目录健康摘要。", + "properties": { + "folder_id": { + "type": "integer", + "description": "目录 ID。", + "format": "uint64", + "minimum": 0 + }, + "has_issue": { + "type": "boolean", + "description": "本目录或可见后代目录是否存在活跃告警或规则运行问题。" + } + }, + "required": [ + "folder_id", + "has_issue" + ] + }, + "FolderHealthOutput": { + "type": "object", + "description": "可见目录的健康摘要。", + "properties": { + "items": { + "type": "array", + "description": "目录摘要列表。", + "items": { + "$ref": "#/components/schemas/FolderHealthItem" + } + } + }, + "required": [ + "items" + ] } }, "securitySchemes": { @@ -67177,7 +67311,7 @@ "Monitors/规则分组" ], "x-mint": { - "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | **1,000 requests/minute**; **50 requests/second** per account |\n| 权限要求 | **告警规则查看** 或 **节点权限查看**(`monit`) |\n\n## 使用说明\n\n- 响应是可读目录的扁平列表而非树:请用 `parent_id` 与 `parent_path` 重建层级,并在客户端自行搜索。\n- 仪表盘与告警规则共用这棵目录树,因此同一批 ID 会作为仪表盘载荷中的 `folder_id` 出现。\n- 目录写操作(`create`、`update`、`move`、`delete`)仍仅支持 JWT,不在公开 API 范围内。", + "content": "## 限制说明\n\n| 项目 | 说明 |\n| ---- | ---- |\n| 速率限制 | **1,000 requests/minute**; **50 requests/second** per account |\n| 权限要求 | **告警规则查看** 或 **节点权限查看**(`monit`) |\n\n## 使用说明\n\n- 响应是可读目录的扁平列表而非树:请用 `parent_id` 与 `parent_path` 重建层级,并在客户端自行搜索。\n- 仪表盘与告警规则共用这棵目录树,因此同一批 ID 会作为仪表盘载荷中的 `folder_id` 出现。", "href": "/zh/api-reference/monitors/rule-folders/monit-folder-read-list", "metadata": { "sidebarTitle": "查询监控节点列表" @@ -68006,6 +68140,435 @@ } } } + }, + "/monit/folder/health": { + "post": { + "operationId": "monit-folder-read-health", + "summary": "获取监控目录健康摘要", + "description": "获取可见目录的健康摘要,包含后代目录中的问题。", + "tags": [ + "Monitors/规则分组" + ], + "x-mint": { + "content": "## 限制\n\n| 项目 | 值 |\n| ------ | ----- |\n| 频率限制 | 每账户 **30 次/分钟**;**2 次/秒** |\n| 权限 | **告警规则查看**、**告警规则管理**、**节点权限查看**或**节点权限管理**(`monit`) |\n\n## 使用说明\n\n- 问题包括活跃告警和告警规则运行问题,并向可见祖先目录汇总。\n- 此操作扫描目录规则和运行状态,请避免频繁轮询。", + "href": "/zh/api-reference/monitors/rule-folders/monit-folder-read-health", + "metadata": { + "sidebarTitle": "获取监控目录健康摘要" + } + }, + "responses": { + "200": { + "description": "成功", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/FolderHealthOutput" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "items": [ + { + "folder_id": 12, + "has_issue": false + } + ] + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + } + } + }, + "/monit/folder/create": { + "post": { + "operationId": "monit-folder-write-create", + "summary": "创建监控目录", + "description": "在顶层或有权访问的父目录下创建监控目录。", + "tags": [ + "Monitors/规则分组" + ], + "x-mint": { + "content": "## 限制\n\n| 项目 | 值 |\n| ------ | ----- |\n| 频率限制 | 每账户 **300 次/分钟**;**20 次/秒** |\n| 权限 | **告警规则管理**或**节点权限管理**(`monit`) |\n\n## 使用说明\n\n- 传入 `parent_id: 0` 或省略该字段以创建顶层目录。您必须有权访问父目录。\n- 每次调用均记录账户审计日志。请勿在请求字段中传入密钥。", + "href": "/zh/api-reference/monitors/rule-folders/monit-folder-write-create", + "metadata": { + "sidebarTitle": "创建监控目录" + } + }, + "responses": { + "200": { + "description": "成功", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/FolderItem" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "id": 12, + "account_id": 10023, + "parent_id": 10, + "parent_path": "10", + "team_id": 0, + "name": "Checkout", + "note": "Checkout service folders", + "creator_id": 10023, + "creator_name": "Ada Lovelace", + "updater_id": 10088, + "updater_name": "Grace Hopper", + "created_at": 1791000000, + "updated_at": 1791100800 + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderCreateRequest" + }, + "example": { + "name": "Checkout", + "note": "Checkout service folders", + "parent_id": 10, + "team_id": 0 + } + } + } + } + } + }, + "/monit/folder/update": { + "post": { + "operationId": "monit-folder-write-update", + "summary": "更新监控目录", + "description": "替换有权访问的监控目录的名称、备注和团队归属。", + "tags": [ + "Monitors/规则分组" + ], + "x-mint": { + "content": "## 限制\n\n| 项目 | 值 |\n| ------ | ----- |\n| 频率限制 | 每账户 **300 次/分钟**;**20 次/秒** |\n| 权限 | **告警规则管理**或**节点权限管理**(`monit`) |\n\n## 使用说明\n\n- 必须传入 `id` 和 `name`。此操作替换可编辑字段:省略 `note` 会清空备注,省略 `team_id` 会重置为 0。\n- 请使用移动接口更改父目录。\n- 每次调用均记录账户审计日志。请勿在请求字段中传入密钥。", + "href": "/zh/api-reference/monitors/rule-folders/monit-folder-write-update", + "metadata": { + "sidebarTitle": "更新监控目录" + } + }, + "responses": { + "200": { + "description": "成功", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "$ref": "#/components/schemas/FolderItem" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": { + "id": 12, + "account_id": 10023, + "parent_id": 10, + "parent_path": "10", + "team_id": 0, + "name": "Checkout production", + "note": "Production services", + "creator_id": 10023, + "creator_name": "Ada Lovelace", + "updater_id": 10088, + "updater_name": "Grace Hopper", + "created_at": 1791000000, + "updated_at": 1791100800 + } + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderUpdateRequest" + }, + "example": { + "id": 12, + "name": "Checkout production", + "note": "Production services", + "team_id": 0 + } + } + } + } + } + }, + "/monit/folder/move": { + "post": { + "operationId": "monit-folder-write-move", + "summary": "移动监控目录", + "description": "将监控目录及其后代移至其他目录下或顶层。", + "tags": [ + "Monitors/规则分组" + ], + "x-mint": { + "content": "## 限制\n\n| 项目 | 值 |\n| ------ | ----- |\n| 频率限制 | 每账户 **300 次/分钟**;**20 次/秒** |\n| 权限 | **告警规则管理**或**节点权限管理**(`monit`) |\n\n## 使用说明\n\n- 您必须有权访问源目录和目标目录。\n- 必须传入 `dest_parent_id`;移至顶层时显式传 0。不能移至自身或后代目录下。\n- 后代目录路径在同一事务中更新。\n- 每次调用均记录账户审计日志。请勿在请求字段中传入密钥。", + "href": "/zh/api-reference/monitors/rule-folders/monit-folder-write-move", + "metadata": { + "sidebarTitle": "移动监控目录" + } + }, + "responses": { + "200": { + "description": "成功", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "空对象。" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": {} + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderMoveRequest" + }, + "example": { + "id": 12, + "dest_parent_id": 0 + } + } + } + } + } + }, + "/monit/folder/delete": { + "post": { + "operationId": "monit-folder-write-delete", + "summary": "删除监控目录", + "description": "移除子目录、规则和仪表盘后,删除有权访问的监控目录。", + "tags": [ + "Monitors/规则分组" + ], + "x-mint": { + "content": "## 限制\n\n| 项目 | 值 |\n| ------ | ----- |\n| 频率限制 | 每账户 **300 次/分钟**;**20 次/秒** |\n| 权限 | **告警规则管理**或**节点权限管理**(`monit`) |\n\n## 使用说明\n\n- 只能删除空目录。存在子目录、告警规则或仪表盘时,返回 HTTP 409,错误码为 `FolderNotEmpty`。\n- 每次调用均记录账户审计日志。请勿在请求字段中传入密钥。", + "href": "/zh/api-reference/monitors/rule-folders/monit-folder-write-delete", + "metadata": { + "sidebarTitle": "删除监控目录" + } + }, + "responses": { + "200": { + "description": "成功", + "content": { + "application/json": { + "schema": { + "allOf": [ + { + "$ref": "#/components/schemas/SuccessEnvelope" + }, + { + "type": "object", + "properties": { + "data": { + "type": "object", + "description": "空对象。" + } + } + } + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "data": {} + } + } + } + }, + "400": { + "$ref": "#/components/responses/BadRequest" + }, + "401": { + "$ref": "#/components/responses/Unauthorized" + }, + "429": { + "$ref": "#/components/responses/TooManyRequests" + }, + "500": { + "$ref": "#/components/responses/ServerError" + }, + "409": { + "description": "目录非空。", + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "request_id": { + "type": "string", + "description": "请求 ID。" + }, + "error": { + "type": "object", + "description": "错误详情。", + "properties": { + "code": { + "type": "string", + "description": "FolderNotEmpty:请移除目录内容后重试。", + "enum": [ + "FolderNotEmpty" + ] + }, + "message": { + "type": "string", + "description": "错误消息。" + } + }, + "required": [ + "code", + "message" + ] + } + }, + "required": [ + "request_id", + "error" + ] + }, + "example": { + "request_id": "01HK8XQE3Z7JM2NTFQ5YJ8P9R4", + "error": { + "code": "FolderNotEmpty", + "message": "Folder contains dashboards" + } + } + } + } + } + }, + "requestBody": { + "required": true, + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/FolderDeleteRequest" + }, + "example": { + "id": 12 + } + } + } + } + } } }, "security": [ diff --git a/docs.json b/docs.json index c500a15b1..957a16f1e 100644 --- a/docs.json +++ b/docs.json @@ -2251,10 +2251,30 @@ "source": "/monit/datasource/update", "destination": "/zh/api-reference/monitors/data-sources/monit-datasource-write-update" }, + { + "source": "/monit/folder/create", + "destination": "/zh/api-reference/monitors/rule-folders/monit-folder-write-create" + }, + { + "source": "/monit/folder/delete", + "destination": "/zh/api-reference/monitors/rule-folders/monit-folder-write-delete" + }, + { + "source": "/monit/folder/health", + "destination": "/zh/api-reference/monitors/rule-folders/monit-folder-read-health" + }, { "source": "/monit/folder/list", "destination": "/zh/api-reference/monitors/rule-folders/monit-folder-read-list" }, + { + "source": "/monit/folder/move", + "destination": "/zh/api-reference/monitors/rule-folders/monit-folder-write-move" + }, + { + "source": "/monit/folder/update", + "destination": "/zh/api-reference/monitors/rule-folders/monit-folder-write-update" + }, { "source": "/monit/prometheus/api/v1/label/{label_name}/values", "destination": "/zh/api-reference/monitors/data-sources/monit-prometheus-read-label-values" @@ -4365,7 +4385,12 @@ "group": "规则分组", "icon": "folder-tree", "pages": [ - "POST /monit/folder/list" + "POST /monit/folder/list", + "POST /monit/folder/health", + "POST /monit/folder/create", + "POST /monit/folder/update", + "POST /monit/folder/move", + "POST /monit/folder/delete" ] } ] @@ -6116,7 +6141,12 @@ "group": "Rule folders", "icon": "folder-tree", "pages": [ - "POST /monit/folder/list" + "POST /monit/folder/list", + "POST /monit/folder/health", + "POST /monit/folder/create", + "POST /monit/folder/update", + "POST /monit/folder/move", + "POST /monit/folder/delete" ] } ] From 30ef237f9e84f3b8c65447f1bb4c0eff836146fb Mon Sep 17 00:00:00 2001 From: Fiona Date: Thu, 15 Oct 2026 06:52:05 -0700 Subject: [PATCH 3/4] docs(rum): document Electron SDK 0.4.0 sampling and remote configuration Electron SDK 0.4.0 adds sessionSampleRate, sessionOnError and remoteConfigurationEnabled with getRemoteConfig(). - Electron advanced config: add the three options to the init table and new sections for session sampling, on-error session capture (including the Session Replay limitation) and remote configuration. - Electron compatibility: list the new capabilities and the replay limitation; note in the integration guide that sampling is decided by the main process. - Application management: list Electron among the platforms with the remote configuration tab and the "Allow remote configuration" switch, with the knobs it reads and the 0.4.0 minimum version. --- en/rum/quickstart/app-management.mdx | 11 +-- en/rum/sdk/electron/advanced-config.mdx | 90 ++++++++++++++++++++++++- en/rum/sdk/electron/compatible.mdx | 3 + en/rum/sdk/electron/sdk-integration.mdx | 2 + zh/rum/quickstart/app-management.mdx | 10 +-- zh/rum/sdk/electron/advanced-config.mdx | 90 ++++++++++++++++++++++++- zh/rum/sdk/electron/compatible.mdx | 3 + zh/rum/sdk/electron/sdk-integration.mdx | 2 + 8 files changed, 197 insertions(+), 14 deletions(-) diff --git a/en/rum/quickstart/app-management.mdx b/en/rum/quickstart/app-management.mdx index 35ff973b0..8a79a3c45 100644 --- a/en/rum/quickstart/app-management.mdx +++ b/en/rum/quickstart/app-management.mdx @@ -86,7 +86,7 @@ The console provides detailed integration guides for each platform: Each platform's SDK configuration page automatically fills in the current application's `applicationId` and `clientToken`, so you can copy the code directly into your project. -The Web, Android, iOS, Flutter, HarmonyOS, and WeChat Mini Program SDK configuration pages all provide an **Allow remote configuration** switch, which is on by default. When it's on, the generated code includes `remoteConfigurationEnabled: true` (`.setRemoteConfigurationEnabled(true)` on Android and HarmonyOS). When it's off, that line is omitted and the client never requests the remote configuration. The switch only affects the generated code. You still need to enable and publish a configuration on the [Remote Configuration](#remote-configuration) tab. Until you publish, the client uses the configuration in your code. +The Web, Android, iOS, Flutter, HarmonyOS, WeChat Mini Program, and Electron SDK configuration pages all provide an **Allow remote configuration** switch, which is on by default. When it's on, the generated code includes `remoteConfigurationEnabled: true` (`.setRemoteConfigurationEnabled(true)` on Android and HarmonyOS; in the main-process `init()` on Electron). When it's off, that line is omitted and the client never requests the remote configuration. The switch only affects the generated code. You still need to enable and publish a configuration on the [Remote Configuration](#remote-configuration) tab. Until you publish, the client uses the configuration in your code. Modifying SDK configuration in application management will not take effect in real-time on integrated clients. All configuration changes need to be updated in your frontend code and redeployed to take effect. @@ -353,7 +353,7 @@ The repository names shown in the list link to the corresponding repository on G The **Remote Configuration** tab lets you adjust collection and privacy parameters online, without code changes or a new release. When enabled, the configuration on this page overrides SDK initialization settings. When disabled, clients fall back to their SDK initialization settings and data collection continues uninterrupted. -- Remote Configuration is currently available for **Browser**, **iOS**, **Android**, **HarmonyOS**, **Flutter** and **WeChat Mini Program** applications. Other platform types will be enabled as their SDKs ship support. +- Remote Configuration is currently available for **Browser**, **iOS**, **Android**, **HarmonyOS**, **Flutter**, **WeChat Mini Program**, and **Electron** applications. Other platform types will be enabled as their SDKs ship support. - The **Remote Configuration** tab is open to every account, on SaaS and private deployments alike — there is nothing for support to enable. Whether the tab appears depends only on whether the application type is one of the supported platforms listed above. @@ -363,20 +363,21 @@ The **Remote Configuration** tab lets you adjust collection and privacy paramete |------|------|------| | **Session sample rate** | Integer 0–100 (%) | Percentage of sessions collected. Leave it empty to skip delivering this field; clients keep the value set at SDK initialization | | **Session replay sample rate** | Integer 0–100 (%) | Percentage of sessions recorded by Session Replay. If empty, the SDK setting applies | -| **On-error session capture** (`sessionOnError`) | On / Off / Use SDK setting | For sessions missed by session sampling, uploads up to the last minute of events on error and continues collection; Web applications require Web SDK 0.3.0 or later, HarmonyOS applications require HarmonyOS SDK 0.7.0 or later | +| **On-error session capture** (`sessionOnError`) | On / Off / Use SDK setting | For sessions missed by session sampling, uploads up to the last minute of events on error and continues collection; Web applications require Web SDK 0.3.0 or later, HarmonyOS applications require HarmonyOS SDK 0.7.0 or later, Electron applications require Electron SDK 0.4.0 or later | | **On-error replay capture** (`sessionReplayOnError`) | On / Off / Use SDK setting | For collected sessions missed by replay sampling, uploads buffered replay on error and continues recording; requires Web SDK 0.3.0 or later | | **Trace sample rate** | Integer 0–100 (%) | A second session-level sampling pass within already-collected sessions, deciding which sessions inject trace headers into eligible requests; the outcome is consistent within one session. Overall trace coverage is roughly *session sample rate × trace sample rate*. If empty, the SDK setting applies | | **Replay privacy level** | `mask` / `mask-user-input` / `allow` | Default masking of page content in Session Replay: `mask` obscures text and hides input values; `mask-user-input` keeps page text and hides only what users typed; `allow` records the page as it is. Loosening the level starts collecting content that was not collected before, and replays already uploaded cannot be retroactively masked. The console asks you to confirm again before publishing | | **Custom configuration keys** | Key–value pairs typed as string / number / boolean / JSON | Delivered to clients together with the remote configuration and read by your application code; the platform does not interpret them. Up to 5 keys; each key name is at most 64 bytes (counted in UTF-8, not characters), each value at most 4 KB and nesting at most 3 levels (objects and arrays each count one level), and all custom entries together at most 16 KB | -The two on-error options are independent: **On** delivers `true`, **Off** delivers `false`, and **Use SDK setting** omits the field so the corresponding `init()` option applies (off if unset). Browser applications require Web SDK 0.3.0 or later with `remoteConfigurationEnabled: true` at initialization; HarmonyOS applications use the same `sessionOnError` option (HarmonyOS SDK 0.7.0 or later, with `.setRemoteConfigurationEnabled(true)` at initialization) and have no on-error replay option. Turning off on-error capture does not affect sessions selected by ordinary sampling. See [Web SDK on-error session capture](/en/rum/sdk/web/advanced-config#on-error-session-capture) for scope, billing, and limitations. +The two on-error options are independent: **On** delivers `true`, **Off** delivers `false`, and **Use SDK setting** omits the field so the corresponding `init()` option applies (off if unset). Browser applications require Web SDK 0.3.0 or later with `remoteConfigurationEnabled: true` at initialization; HarmonyOS applications use the same `sessionOnError` option (HarmonyOS SDK 0.7.0 or later, with `.setRemoteConfigurationEnabled(true)` at initialization) and have no on-error replay option; Electron applications use the same `sessionOnError` option too (Electron SDK 0.4.0 or later, with `remoteConfigurationEnabled: true` in the main-process `init()`) and have no on-error replay option either. Turning off on-error capture does not affect sessions selected by ordinary sampling. See [Web SDK on-error session capture](/en/rum/sdk/web/advanced-config#on-error-session-capture) for scope, billing, and limitations. -iOS, Android, Flutter, and WeChat Mini Program applications also show the **Remote Configuration** tab, but their SDKs read only the **session sample rate** and **custom configuration keys**; the HarmonyOS SDK reads those two plus **on-error session capture**. None of them has Session Replay, and the other values have no effect. Every SDK has to opt in with `remoteConfigurationEnabled: true` at initialization (off by default; an application that has not opted in never requests the configuration). Minimum versions: +iOS, Android, Flutter, and WeChat Mini Program applications also show the **Remote Configuration** tab, but their SDKs read only the **session sample rate** and **custom configuration keys**; the HarmonyOS and Electron SDKs read those two plus **on-error session capture**. None of them has Session Replay to sample through this channel (Electron's replay is sampled by the renderer's own Browser SDK, which remote configuration does not reach), and the other values have no effect. Every SDK has to opt in with `remoteConfigurationEnabled: true` at initialization (off by default; an application that has not opted in never requests the configuration). Minimum versions: - iOS: SDK 0.6.0. See [iOS SDK Advanced Configuration](/en/rum/sdk/ios/advanced-config#remote-configuration). - Android: SDK 0.7.0. See [Android SDK Advanced Configuration](/en/rum/sdk/android/advanced-config#remote-configuration-adjust-the-sample-rate-from-the-console). - HarmonyOS: SDK 0.6.0, or 0.7.0 for on-error session capture. See [HarmonyOS SDK Advanced Configuration](/en/rum/sdk/harmony/advanced-config#remote-configuration). - Flutter: `flashcat_flutter_plugin` 0.2.0. See [Flutter SDK Advanced Configuration](/en/rum/sdk/flutter/advanced-config#remote-configuration). +- Electron: `@flashcatcloud/electron-sdk` 0.4.0, enabled in the main process. See [Electron SDK Advanced Configuration](/en/rum/sdk/electron/advanced-config#remote-configuration). Custom entries are delivered to clients and may be readable by end users. Never put secrets, access tokens, or personally sensitive information in them. diff --git a/en/rum/sdk/electron/advanced-config.mdx b/en/rum/sdk/electron/advanced-config.mdx index 0b5fd051e..3597de311 100644 --- a/en/rum/sdk/electron/advanced-config.mdx +++ b/en/rum/sdk/electron/advanced-config.mdx @@ -1,7 +1,7 @@ --- title: "Electron SDK advanced configuration" -description: "Configure Electron RUM upload endpoints, batching, user identity, manual error reporting, and other advanced options" -keywords: ["RUM", "Electron SDK", "advanced configuration", "proxy", "user identity", "manual reporting"] +description: "Configure Electron RUM session sampling, remote configuration, upload endpoints, batching, user identity, manual error reporting, and other advanced options" +keywords: ["RUM", "Electron SDK", "advanced configuration", "session sampling", "on-error session capture", "remote configuration", "proxy", "user identity", "manual reporting"] --- This page covers optional settings and public APIs for the main-process `@flashcatcloud/electron-sdk`. The renderer uses the Browser SDK; see [Web SDK advanced configuration](/en/rum/sdk/web/advanced-config) for its general options. @@ -34,6 +34,9 @@ if (!initialized) { | `proxy` | `string` | No | — | Custom forwarding endpoint for regular RUM events | | `env` | `string` | No | — | Environment. Main-process RUM events do not currently contain this field; configure it separately in the renderer | | `version` | `string` | No | — | Application version; use the same value when uploading source maps | +| `sessionSampleRate` | `number` | No | `100` | Session sample rate from 0–100; an out-of-range value fails `init()`. See [Session sampling](#session-sampling). SDK 0.4.0 or later | +| `sessionOnError` | `boolean` | No | `false` | On-error session capture: sessions the sample rate did not select are held in memory and uploaded only if they report an error. See [On-error session capture](#on-error-session-capture). SDK 0.4.0 or later | +| `remoteConfigurationEnabled` | `boolean` | No | `false` | Lets the console adjust sampling and deliver custom configuration keys. See [Remote configuration](#remote-configuration). SDK 0.4.0 or later | | `telemetrySampleRate` | `number` | No | `20` | SDK telemetry sample rate from 0–100; set it to `0` to disable telemetry | | `batchSize` | `'SMALL' \| 'MEDIUM' \| 'LARGE'` | No | `MEDIUM` | Batch size for regular RUM events | | `uploadFrequency` | `'RARE' \| 'NORMAL' \| 'FREQUENT'` | No | `NORMAL` | Upload interval for regular RUM events | @@ -45,6 +48,89 @@ if (!initialized) { `init()` returns `false` when validation fails. The SDK prints the reason in the main-process console and does not start collection, but it does not prevent the application from starting. +## Session sampling + +Starting with Electron SDK 0.4.0, you can sample sessions. `sessionSampleRate` sets the share of sessions collected and defaults to `100` (every session): + +```ts main.ts +await init({ + // other options + sessionSampleRate: 20, // collect 20% of sessions +}); +``` + +- Each session is drawn once, in the main process, and the result decides for the renderers too: renderer events reach the intake through the main process over the bridge, and the main process drops the events of sessions it did not select. +- The `sessionSampleRate` in the renderer's `flashcatRum.init()` does not apply to events sent over the bridge. Set it in the main process only. +- A session resumed after the application restarts keeps the decision it was created with. +- Every RUM event carries the sample rate its session was drawn at (`_dd.configuration.session_sample_rate`), which the platform uses to extrapolate totals. + +## On-error session capture + +With `sessionOnError` on, sessions the sample rate did not select are not dropped right away. Their events are held in memory and uploaded only if the session reports an error: + +```ts main.ts +await init({ + // other options + sessionSampleRate: 0, // no session is collected unconditionally... + sessionOnError: true, // ...but every session that reports an error is +}); +``` + +- Events from the main process and the renderers alike are held, up to the 60 seconds before the error (also capped at 64 KiB of detail, 200 events, and 50 views). The buffer stays in memory and is never written to disk. +- At the session's first error, what is held is uploaded together with the error 0–3 seconds later (spread per session, so that one outage does not make every client upload at once). The session then reports like any sampled session. +- When the application may be about to exit (an uncaught main-process exception, `before-quit`, `will-quit`, and the process `exit` event), content already released by an error is written to disk first and uploaded on the next launch. For an uncaught main-process exception to count, register your own `uncaughtException` listener after `await init(...)` completes. +- A session that ends without an error is discarded whole, and nothing is uploaded at quit either. +- Only errors your application reports count: an error dropped by a renderer's `beforeSend`, or one the SDK reports about itself, does not. A native crash counts too, and is reported on the next launch together with the view it happened in. + +A captured session reports a session sample rate of `0` and carries `session.sampled_for_error: true` on its views, so it is counted as itself rather than extrapolated by the sample rate. To filter these sessions, see `session_sampled_for_error` in the [data query syntax guide](/en/rum/explorer/data-query). + + +**Session Replay is not withheld by on-error session capture.** The renderer uploads replay directly, bypassing the main process. With `sessionReplayDirectUpload` on, the replay of a session held by `sessionOnError` is uploaded whether or not the session ever reports an error. Do not combine `sessionOnError` with Session Replay. + + +## Remote configuration + +Starting with Electron SDK 0.4.0, the session sample rate, on-error session capture, and custom configuration keys can be adjusted online on the **Application management → Remote Configuration** tab in the console, without code changes or a new release. The feature is off by default; enable it when you initialize the main process: + +```ts main.ts +await init({ + // other options + sessionSampleRate: 100, // used until the console delivers a value + remoteConfigurationEnabled: true, // let the console adjust sampling and on-error capture +}); +``` + +When the **Allow remote configuration** switch in the console's Electron setup guide is on, the generated main-process initialization code includes this line. + +Once enabled: + +- The main process requests the configuration at `init()` and whenever a new session starts; `init()` does not wait for the request. A received configuration applies from the **next new session**, and running sessions are unaffected. +- A value set in the console takes precedence over the initialization value. Values the console does not set, or every value while remote configuration is turned off in the console, fall back to the initialization options. +- The last accepted configuration is kept in the application's `userData` directory, so new sessions after a cold start are drawn with it before the network answers, even offline. The cache applies only to the same intake, application, `env`, and `version`. +- Network failures, timeouts, and unrecognized responses keep the configuration in force; the SDK retries after about 5 seconds and again after about 60 seconds (each with ±20% jitter). +- The Electron SDK reads only the **session sample rate**, **on-error session capture**, and **custom configuration keys**. Session Replay is recorded and sampled by the renderer's own Browser SDK, which remote configuration does not reach, so replay and trace options do not appear for Electron applications. +- The renderers need no remote configuration of their own; the main process owns the sessions and their sampling. +- A session drawn with a delivered configuration reports its version as `_dd.configuration.rc_version`. + + +The configuration endpoint sits next to the intake at `/api/v2/rum/config`: `https:///api/v2/rum/config` when you set `site`. When you set `proxy`, the request also goes to `proxy`, with the target path `/api/v2/rum/config` carried in the `ddforward` parameter. For private deployments, make sure your forwarding service allows this path. + + +Read custom configuration keys with `getRemoteConfig()`, which returns a copy, or `undefined` when remote configuration is off, no configuration has been received yet, or the console set none. Until the server answers, it reads from the cache on disk, so you can call it as soon as `init()` resolves: + +```ts main.ts +import { getRemoteConfig } from '@flashcatcloud/electron-sdk'; + +const custom = getRemoteConfig(); +const debugUsers = custom?.['debugUsers'] as string[] | undefined; +``` + + +Custom configuration keys are delivered to clients, and anyone holding the Client Token can read them. Never put secrets, access tokens, or personally sensitive information in them. + + +For the console-side options, publishing, and activation, see [Application management · Remote configuration](/en/rum/quickstart/app-management#remote-configuration). + ## Custom upload endpoints By default, the main process uploads regular RUM events to Flashduty SaaS. You do not need to set `site` or `proxy`. diff --git a/en/rum/sdk/electron/compatible.mdx b/en/rum/sdk/electron/compatible.mdx index 38b282ce6..6c7aaccf6 100644 --- a/en/rum/sdk/electron/compatible.mdx +++ b/en/rum/sdk/electron/compatible.mdx @@ -52,6 +52,8 @@ The page loaded by the current window does not need a host allowlist entry. | Main-process network requests | Supported | Captures `http`, `https`, `fetch`, and `net.fetch` | | Renderer page experience | Supported | Matches Web SDK view, action, resource, error, and Web Vitals collection | | Session Replay | Supported | Requires direct renderer upload and a compatible CSP | +| Session sampling and on-error session capture | Supported | Requires SDK 0.4.0 or later; sessions are drawn in the main process | +| Remote configuration | Supported | Requires SDK 0.4.0 or later; reads the session sample rate, on-error session capture, and custom configuration keys | | JavaScript source maps | Supported | Applies to main-process and renderer errors | | Native crash symbolication | Supported | Requires matching Breakpad symbol files | @@ -60,6 +62,7 @@ The page loaded by the current window does not need a host allowlist entry. | Limitation | Impact | |------------|--------| | Session Replay bypasses the main process | Configure `sessionReplayDirectUpload`, CSP, and any self-hosted endpoint in the renderer | +| Session Replay is not withheld by on-error session capture | With `sessionReplayDirectUpload` on, the replay of a session held by `sessionOnError` is uploaded whether or not the session reports an error; do not combine the two | | No full APM pipeline | Only main-process HTTP spans become RUM resources; IPC and child-process spans are not uploaded | | Logs are not forwarded | Log events sent through the renderer bridge are not uploaded as RUM data | | No main-process Web Vitals | LCP, INP, CLS, long tasks, and user actions come from renderers | diff --git a/en/rum/sdk/electron/sdk-integration.mdx b/en/rum/sdk/electron/sdk-integration.mdx index 017990b4a..f97cf6b10 100644 --- a/en/rum/sdk/electron/sdk-integration.mdx +++ b/en/rum/sdk/electron/sdk-integration.mdx @@ -149,6 +149,8 @@ flashcatRum.init({ Use the same `applicationId`, `clientToken`, `service`, `env`, and `version` in both processes so one application is not split across different dimensions. +Session sampling is decided by the main process (set with the main process's `sessionSampleRate`, Electron SDK 0.4.0 or later); the renderer's `sessionSampleRate` does not apply to events sent over the bridge. See [Session sampling](/en/rum/sdk/electron/advanced-config#session-sampling). + After the main process is configured, the SDK injects its preload and establishes the bridge automatically. You do not need to modify your application's preload or write `ipcRenderer` / `ipcMain` forwarding code. Use `allowedWebViewHosts` only for third-party pages loaded in a `` or `BrowserView`. diff --git a/zh/rum/quickstart/app-management.mdx b/zh/rum/quickstart/app-management.mdx index f1e9d2172..a0f354922 100644 --- a/zh/rum/quickstart/app-management.mdx +++ b/zh/rum/quickstart/app-management.mdx @@ -83,7 +83,7 @@ RUM 应用是承载前端性能监控数据的容器,用于采集、存储和 每个平台的 SDK 配置页面都会自动填入当前应用的 `applicationId` 和 `clientToken`,您可以直接复制代码到项目中使用。 -Web、Android、iOS、Flutter、HarmonyOS 与微信小程序的 SDK 配置页都提供 **允许远程配置** 开关,默认为开启。开启时生成的初始化代码会带上 `remoteConfigurationEnabled: true`(Android 与 HarmonyOS 端为对应的 `.setRemoteConfigurationEnabled(true)` 初始化参数);关闭后该行不会出现在代码中,客户端也就不再请求远程配置。开关只影响生成的代码——配置本身还需在 [远程配置](#远程配置) 页签中启用并发布,发布前客户端仍使用代码中的配置。 +Web、Android、iOS、Flutter、HarmonyOS、微信小程序与 Electron 的 SDK 配置页都提供 **允许远程配置** 开关,默认为开启。开启时生成的初始化代码会带上 `remoteConfigurationEnabled: true`(Android 与 HarmonyOS 端为对应的 `.setRemoteConfigurationEnabled(true)` 初始化参数,Electron 端加在主进程的 `init()` 中);关闭后该行不会出现在代码中,客户端也就不再请求远程配置。开关只影响生成的代码——配置本身还需在 [远程配置](#远程配置) 页签中启用并发布,发布前客户端仍使用代码中的配置。 在应用管理中修改 SDK 配置并不会实时生效到已集成的客户端。所有配置更改需要在您的前端代码中更新并重新部署才能生效。 @@ -350,7 +350,7 @@ Link 集成会从当前事件上下文中提取变量并替换到 URL 模板中 「远程配置」页签允许您在线调整采集与隐私参数,而无需改代码、重新发版。启用后,本页配置覆盖 SDK 初始化设置;停用后,各端恢复使用 SDK 初始化设置,数据采集不会停止。 -- 远程配置目前支持 **Browser**、**iOS**、**Android**、**HarmonyOS**、**Flutter** 与**微信小程序**类型应用,其他平台类型将随各端 SDK 发布逐步开放。 +- 远程配置目前支持 **Browser**、**iOS**、**Android**、**HarmonyOS**、**Flutter**、**微信小程序**与 **Electron** 类型应用,其他平台类型将随各端 SDK 发布逐步开放。 - 「远程配置」页签对所有账户开放(SaaS 与私有化部署一致),无需联系支持团队开通;页签是否显示只取决于应用类型是否属于上一条列出的受支持平台。 @@ -360,15 +360,15 @@ Link 集成会从当前事件上下文中提取变量并替换到 URL 模板中 |------|------|------| | **会话采样率** | 0–100 整数(%) | 采集会话的比例。留空表示不下发该项,各端沿用 SDK 初始化时设置的值 | | **会话回放采样率** | 0–100 整数(%) | 录制会话回放的比例。留空时沿用 SDK 设置 | -| **异常会话补采**(`sessionOnError`) | 开 / 关 / 沿用 SDK 设置 | 对会话采样未抽中的会话,报错后补报最多最近一分钟的事件,并继续采集;Web 应用需 Web SDK 0.3.0 及以上,HarmonyOS 应用需 HarmonyOS SDK 0.7.0 及以上 | +| **异常会话补采**(`sessionOnError`) | 开 / 关 / 沿用 SDK 设置 | 对会话采样未抽中的会话,报错后补报最多最近一分钟的事件,并继续采集;Web 应用需 Web SDK 0.3.0 及以上,HarmonyOS 应用需 HarmonyOS SDK 0.7.0 及以上,Electron 应用需 Electron SDK 0.4.0 及以上 | | **异常回放补采**(`sessionReplayOnError`) | 开 / 关 / 沿用 SDK 设置 | 对已采集但回放采样未抽中的会话,报错后补报缓存回放并继续录制;仅支持 Web SDK 0.3.0 及以上 | | **Trace 采样率** | 0–100 整数(%) | 在已采集的会话中再按此比例做一次会话级采样,决定哪些会话为符合条件请求注入 trace 头;同一会话内的结果一致。整体 Trace 覆盖率约为「会话采样率 × Trace 采样率」。留空时沿用 SDK 设置 | | **回放脱敏级别** | `mask` / `mask-user-input` / `allow` | 会话回放对页面内容的默认遮蔽程度:`mask` 遮蔽文本并隐藏输入值;`mask-user-input` 保留页面文本、仅隐藏用户输入;`allow` 按原样录制。放宽级别会开始录制此前未采集的内容,且已上传的回放无法事后补充脱敏,发布前系统会再次确认 | | **自定义配置项** | 键值对,类型支持字符串 / 数字 / 布尔值 / JSON | 随远程配置下发到客户端、由应用代码读取,平台不解析其内容。最多 5 个;键名最长 64 字节(按 UTF-8 计,非字符数),单个值最大 4 KB,JSON 值最多嵌套 3 层(对象、数组各计一层),全部自定义项合计最大 16 KB | -两个异常补采开关独立设置:**开**下发 `true`,**关**下发 `false`,**沿用 SDK 设置**不下发该项,由 `init()` 中对应参数决定(未设置时默认关闭)。Browser 应用需使用 Web SDK 0.3.0 及以上,并在初始化时开启 `remoteConfigurationEnabled: true`;HarmonyOS 应用使用同一个 `sessionOnError` 开关(需 HarmonyOS SDK 0.7.0 及以上,初始化时调用 `.setRemoteConfigurationEnabled(true)`),没有回放补采开关。关闭补采不影响普通采样已抽中的会话。补采范围、计费与限制见 [Web SDK 异常会话补采](/zh/rum/sdk/web/advanced-config#异常会话补采)。 +两个异常补采开关独立设置:**开**下发 `true`,**关**下发 `false`,**沿用 SDK 设置**不下发该项,由 `init()` 中对应参数决定(未设置时默认关闭)。Browser 应用需使用 Web SDK 0.3.0 及以上,并在初始化时开启 `remoteConfigurationEnabled: true`;HarmonyOS 应用使用同一个 `sessionOnError` 开关(需 HarmonyOS SDK 0.7.0 及以上,初始化时调用 `.setRemoteConfigurationEnabled(true)`),没有回放补采开关;Electron 应用同样使用 `sessionOnError` 开关(需 Electron SDK 0.4.0 及以上,在主进程 `init()` 中开启 `remoteConfigurationEnabled: true`),也没有回放补采开关。关闭补采不影响普通采样已抽中的会话。补采范围、计费与限制见 [Web SDK 异常会话补采](/zh/rum/sdk/web/advanced-config#异常会话补采)。 -iOS、Android、Flutter 与微信小程序应用同样展示「远程配置」页签,但这几端的 SDK 仅读取**会话采样率**与**自定义配置项**两项(均无 Session Replay,其余配置不下发也不生效);HarmonyOS 应用除这两项外还会读取**异常会话补采**(同样没有 Session Replay,回放与 Trace 相关配置不下发也不生效)。各端 SDK 都需要在初始化时开启 `remoteConfigurationEnabled: true`(默认关闭,未开启的应用不会请求配置);iOS 端需要 SDK 0.6.0 及以上,接入方式见 [iOS SDK 高级配置](/zh/rum/sdk/ios/advanced-config);Android 端需要 SDK 0.7.0 及以上,接入方式见 [Android SDK 高级配置](/zh/rum/sdk/android/advanced-config);HarmonyOS 端需要 SDK 0.6.0 及以上(读取异常会话补采需要 0.7.0 及以上),接入方式见 [HarmonyOS SDK 高级配置](/zh/rum/sdk/harmony/advanced-config#远程配置);Flutter 端需要 `flashcat_flutter_plugin` 0.2.0 及以上,接入方式见 [Flutter SDK 高级配置](/zh/rum/sdk/flutter/advanced-config)。 +iOS、Android、Flutter 与微信小程序应用同样展示「远程配置」页签,但这几端的 SDK 仅读取**会话采样率**与**自定义配置项**两项(均无 Session Replay,其余配置不下发也不生效);HarmonyOS 与 Electron 应用除这两项外还会读取**异常会话补采**(HarmonyOS 没有 Session Replay;Electron 的会话回放由渲染进程的 Browser SDK 自行采样,远程配置不会下发到渲染进程。两端的回放与 Trace 相关配置都不下发也不生效)。各端 SDK 都需要在初始化时开启 `remoteConfigurationEnabled: true`(默认关闭,未开启的应用不会请求配置);iOS 端需要 SDK 0.6.0 及以上,接入方式见 [iOS SDK 高级配置](/zh/rum/sdk/ios/advanced-config);Android 端需要 SDK 0.7.0 及以上,接入方式见 [Android SDK 高级配置](/zh/rum/sdk/android/advanced-config);HarmonyOS 端需要 SDK 0.6.0 及以上(读取异常会话补采需要 0.7.0 及以上),接入方式见 [HarmonyOS SDK 高级配置](/zh/rum/sdk/harmony/advanced-config#远程配置);Flutter 端需要 `flashcat_flutter_plugin` 0.2.0 及以上,接入方式见 [Flutter SDK 高级配置](/zh/rum/sdk/flutter/advanced-config);Electron 端需要 `@flashcatcloud/electron-sdk` 0.4.0 及以上,在主进程开启,接入方式见 [Electron SDK 高级配置](/zh/rum/sdk/electron/advanced-config#远程配置)。 自定义配置项会下发到客户端,可能被终端用户读取。请勿填写密钥、访问令牌或个人敏感信息。 diff --git a/zh/rum/sdk/electron/advanced-config.mdx b/zh/rum/sdk/electron/advanced-config.mdx index 3456dbfc9..952a6364d 100644 --- a/zh/rum/sdk/electron/advanced-config.mdx +++ b/zh/rum/sdk/electron/advanced-config.mdx @@ -1,7 +1,7 @@ --- title: "Electron SDK 高级配置" -description: "配置 Electron RUM SDK 的上报地址、批次、用户身份、错误上报和其他进阶选项" -keywords: ["RUM", "Electron SDK", "高级配置", "代理", "用户身份", "手动上报"] +description: "配置 Electron RUM SDK 的会话采样、远程配置、上报地址、批次、用户身份、错误上报和其他进阶选项" +keywords: ["RUM", "Electron SDK", "高级配置", "会话采样", "异常会话补采", "远程配置", "代理", "用户身份", "手动上报"] --- 本文介绍主进程 `@flashcatcloud/electron-sdk` 的可选配置和公开 API。渲染进程使用 Browser SDK,其通用配置见 [Web SDK 高级配置](/zh/rum/sdk/web/advanced-config)。 @@ -34,6 +34,9 @@ if (!initialized) { | `proxy` | `string` | 否 | — | 普通 RUM 事件的自定义转发地址 | | `env` | `string` | 否 | — | 环境标识。当前主进程 RUM 事件不带该字段;渲染进程需要单独配置 | | `version` | `string` | 否 | — | 应用版本;上传 sourcemap 时需要使用相同的值 | +| `sessionSampleRate` | `number` | 否 | `100` | 会话采样率,范围为 0–100,超出范围时 `init()` 失败。见[会话采样](#会话采样),SDK 0.4.0 起 | +| `sessionOnError` | `boolean` | 否 | `false` | 异常会话补采:未被采样抽中的会话先暂存在内存中,只有报错时才上报。见[异常会话补采](#异常会话补采),SDK 0.4.0 起 | +| `remoteConfigurationEnabled` | `boolean` | 否 | `false` | 允许控制台在线调整采样并下发自定义配置项。见[远程配置](#远程配置),SDK 0.4.0 起 | | `telemetrySampleRate` | `number` | 否 | `20` | SDK 自身遥测采样率,范围为 0–100;设为 `0` 可关闭 | | `batchSize` | `'SMALL' \| 'MEDIUM' \| 'LARGE'` | 否 | `MEDIUM` | 普通 RUM 事件的单批大小 | | `uploadFrequency` | `'RARE' \| 'NORMAL' \| 'FREQUENT'` | 否 | `NORMAL` | 普通 RUM 事件的上报间隔 | @@ -45,6 +48,89 @@ if (!initialized) { `init()` 返回 `false` 表示配置校验失败。SDK 会在主进程控制台输出具体原因,并且不会开始采集,但不会阻止应用继续启动。 +## 会话采样 + +Electron SDK 0.4.0 起支持会话采样。`sessionSampleRate` 决定采集多少比例的会话,默认 `100`(全部采集): + +```ts main.ts +await init({ + // 其余配置 + sessionSampleRate: 20, // 采集 20% 的会话 +}); +``` + +- 每个会话只在主进程抽样一次,结果同时决定渲染进程:渲染进程事件经桥接由主进程上报,主进程会丢弃未抽中会话的事件。 +- 渲染进程 `flashcatRum.init()` 中的 `sessionSampleRate` 对经桥接上报的事件不生效,请只在主进程设置。 +- 应用重启后恢复的会话沿用创建时的抽样结果。 +- 每条 RUM 事件都会带上会话抽样时使用的采样率(`_dd.configuration.session_sample_rate`),平台据此换算总量。 + +## 异常会话补采 + +开启 `sessionOnError` 后,采样未抽中的会话不会直接丢弃,而是先在内存中暂存,只有该会话报错时才上报: + +```ts main.ts +await init({ + // 其余配置 + sessionSampleRate: 0, // 不无条件采集任何会话…… + sessionOnError: true, // ……但所有报错的会话都会被采集 +}); +``` + +- 暂存范围覆盖主进程和渲染进程的事件,最多保留报错前 60 秒的内容(同时受 64 KiB 明细、200 条事件和 50 个 view 的上限约束)。暂存内容只在内存中,不写入磁盘。 +- 会话首次报错后,暂存内容与错误一起在 0–3 秒内上报(按会话打散,避免一次故障让所有客户端同时上传),之后该会话像普通采集的会话一样持续上报。 +- 应用可能即将退出时(主进程未捕获异常、`before-quit`、`will-quit` 与进程 `exit` 事件),已经触发补采的内容会先写入磁盘,下次启动再上传。主进程未捕获异常要被计入,应用自己的 `uncaughtException` 监听器需在 `await init(...)` 完成之后注册。 +- 没有报错就结束的会话整体丢弃,退出应用时也不会上传。 +- 只有应用自身的错误会触发补采:被渲染进程 `beforeSend` 丢弃的错误、SDK 自身上报的错误不计入。原生崩溃同样计入,会在下次启动时随崩溃所在的 view 一起上报。 + +被补采的会话上报的会话采样率为 `0`,并在 view 上带 `session.sampled_for_error: true` 标记,按自身计数、不会按采样率放大。筛选方式见[数据查询语法指南](/zh/rum/explorer/data-query)中的 `session_sampled_for_error`。 + + +**会话回放不受异常补采控制。** 回放由渲染进程直接上传,不经过主进程。开启 `sessionReplayDirectUpload` 时,被异常补采暂存的会话无论最终是否报错,其回放都会上传。请不要同时使用 `sessionOnError` 与会话回放。 + + +## 远程配置 + +Electron SDK 0.4.0 起,会话采样率、异常会话补采与自定义配置项可以在控制台 **应用管理 → 远程配置** 页签在线调整,无需改代码、重新发版。该功能默认关闭,需要在主进程初始化时开启: + +```ts main.ts +await init({ + // 其余配置 + sessionSampleRate: 100, // 控制台未下发时使用的采样率 + remoteConfigurationEnabled: true, // 允许控制台调整采样率与异常补采 +}); +``` + +控制台 Electron 接入向导中的 **允许远程配置** 开关打开时,生成的主进程初始化代码会自动带上这一行。 + +开启后的行为: + +- 配置由主进程在 `init()` 时与每次新建会话时请求,`init()` 不会等待请求完成。拿到的配置对**下一个新会话**生效,进行中的会话不受影响。 +- 控制台下发的值优先于初始化值;控制台未设置的项、或控制台停用远程配置时,沿用初始化值。 +- 最近一次接收的配置保存在应用的 `userData` 目录,冷启动后的新会话在网络返回前、甚至离线时也会按它抽样。缓存只对相同的上报地址、应用、`env` 与 `version` 生效。 +- 请求失败、超时或响应无法识别时,沿用已生效的配置;SDK 约 5 秒后重试一次、再约 60 秒后重试一次(两次都带 ±20% 抖动)。 +- Electron SDK 只读取**会话采样率**、**异常会话补采**与**自定义配置项**三项。会话回放由渲染进程的 Browser SDK 自行录制与采样,远程配置不会下发到渲染进程,回放与 Trace 相关的配置项不会在 Electron 应用里出现。 +- 渲染进程无需开启远程配置,主进程会统一处理会话与采样。 +- 抽样时读到了下发配置的会话,会在事件中带上配置版本(`_dd.configuration.rc_version`)。 + + +配置接口位于上报地址旁的 `/api/v2/rum/config` 路径:配置 `site` 时为 `https:///api/v2/rum/config`;配置 `proxy` 时,请求同样发往 `proxy`,目标路径 `/api/v2/rum/config` 放在 `ddforward` 参数中。私有化部署请确认转发服务放行了该路径。 + + +自定义配置项通过 `getRemoteConfig()` 读取,返回一份副本;未开启远程配置、尚未拿到配置或控制台未设置该项时返回 `undefined`。在服务端返回前,它从本地缓存读取,因此 `init()` 完成后即可调用: + +```ts main.ts +import { getRemoteConfig } from '@flashcatcloud/electron-sdk'; + +const custom = getRemoteConfig(); +const debugUsers = custom?.['debugUsers'] as string[] | undefined; +``` + + +自定义配置项会下发到客户端,任何持有 Client Token 的人都能读到。请勿在其中存放密钥、访问令牌或个人敏感信息。 + + +控制台侧的配置项、发布与生效方式见 [应用管理 · 远程配置](/zh/rum/quickstart/app-management#远程配置)。 + ## 自定义上报地址 默认情况下,普通 RUM 事件由主进程上报到 Flashduty SaaS,无需配置 `site` 或 `proxy`。 diff --git a/zh/rum/sdk/electron/compatible.mdx b/zh/rum/sdk/electron/compatible.mdx index 5e7033eaa..5d6bc99fe 100644 --- a/zh/rum/sdk/electron/compatible.mdx +++ b/zh/rum/sdk/electron/compatible.mdx @@ -52,6 +52,8 @@ keywords: ["RUM", "Electron SDK", "兼容性", "打包工具", "已知限制"] | 主进程网络请求 | 支持 | 采集 `http`、`https`、`fetch` 和 `net.fetch` | | 渲染进程页面体验 | 支持 | 与 Web SDK 一致,包含 view、action、resource、error 和 Web Vitals | | 会话回放 | 支持 | 需要渲染进程直传并允许相应 CSP | +| 会话采样与异常会话补采 | 支持 | 需要 SDK 0.4.0 或更高版本,由主进程统一抽样 | +| 远程配置 | 支持 | 需要 SDK 0.4.0 或更高版本;读取会话采样率、异常会话补采与自定义配置项 | | JavaScript sourcemap | 支持 | 主进程和渲染进程均支持 | | 原生崩溃符号还原 | 支持 | 需要上传匹配的 Breakpad 符号文件 | @@ -60,6 +62,7 @@ keywords: ["RUM", "Electron SDK", "兼容性", "打包工具", "已知限制"] | 限制 | 影响 | |------|------| | 会话回放不经过主进程 | 回放需要在渲染进程配置 `sessionReplayDirectUpload`、CSP 和私有化转发地址 | +| 会话回放不受异常会话补采控制 | 开启 `sessionReplayDirectUpload` 时,被 `sessionOnError` 暂存的会话无论是否报错都会上传回放,请不要同时使用 | | 不提供完整 APM | 当前只将主进程 HTTP span 转换为 RUM resource;IPC 和子进程 span 不会上报 | | 不转发 Logs | 渲染进程通过桥接发送的 log 事件不会作为 RUM 数据上报 | | 主进程没有 Web Vitals | LCP、INP、CLS、long task 和用户操作来自渲染进程 | diff --git a/zh/rum/sdk/electron/sdk-integration.mdx b/zh/rum/sdk/electron/sdk-integration.mdx index 3224225df..3c7bc5c7d 100644 --- a/zh/rum/sdk/electron/sdk-integration.mdx +++ b/zh/rum/sdk/electron/sdk-integration.mdx @@ -149,6 +149,8 @@ flashcatRum.init({ 建议让两个进程使用相同的 `applicationId`、`clientToken`、`service`、`env` 和 `version`,避免同一个应用的数据被拆到不同维度。 +会话采样由主进程统一决定(Electron SDK 0.4.0 起通过主进程的 `sessionSampleRate` 设置),渲染进程 `sessionSampleRate` 对经桥接上报的事件不生效。详见 [会话采样](/zh/rum/sdk/electron/advanced-config#会话采样)。 + 主进程配置正确后,SDK 会自动注入 preload 并建立桥接。您不需要修改应用自己的 preload,也不需要编写 `ipcRenderer` / `ipcMain` 转发代码。`allowedWebViewHosts` 仅用于采集 `` 或 `BrowserView` 中加载的第三方页面。 From 2a0d0611fd706b920b638c984894866a75ae3d55 Mon Sep 17 00:00:00 2001 From: ysyneu Date: Thu, 15 Oct 2026 20:23:40 -0700 Subject: [PATCH 4/4] docs(cli): install.sh replaces the flashduty already on PATH The shell installer now defaults to the directory of the flashduty found on PATH and only falls back to /usr/local/bin for fresh installs. --- en/developer/cli.mdx | 4 ++-- zh/developer/cli.mdx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/en/developer/cli.mdx b/en/developer/cli.mdx index 1c767853d..871e9bcfa 100644 --- a/en/developer/cli.mdx +++ b/en/developer/cli.mdx @@ -18,7 +18,7 @@ The tool is open source at [flashcatcloud/flashduty-cli](https://github.com/flas curl -sSL https://static.flashcat.cloud/flashduty-cli/install.sh | sh ``` -Installs to `/usr/local/bin` by default. Override with the `FLASHDUTY_INSTALL_DIR` environment variable. +Installs into the directory of the `flashduty` already on your `PATH` (an upgrade replaces it in place, so no stale copy shadows the new one), or `/usr/local/bin` if there is none. Override with the `FLASHDUTY_INSTALL_DIR` environment variable. @@ -39,7 +39,7 @@ Read the latest version from the [CDN latest version pointer](https://static.fla | Variable | Description | Default | |----------|-------------|---------| | `FLASHDUTY_VERSION` | Install a specific version, for example, `v1.5.12` | latest | -| `FLASHDUTY_INSTALL_DIR` | Custom install directory | `/usr/local/bin` (shell), `~\.flashduty\bin` (PowerShell) | +| `FLASHDUTY_INSTALL_DIR` | Custom install directory | shell: the directory of the `flashduty` already on `PATH`, else `/usr/local/bin`; PowerShell: `~\.flashduty\bin` | | `MIRROR_URL` | Override the release asset download mirror prefix used by the installer script; must use `https://` | `https://static.flashcat.cloud/flashduty-cli` | | `FLASHDUTY_UPDATE_BASE_URL` | Override the base URL used by `flashduty update` and the automatic update check | `https://static.flashcat.cloud/flashduty-cli` | | `INSTALLED_NAME` | Command name to install as, for example, `fduty` | `flashduty` | diff --git a/zh/developer/cli.mdx b/zh/developer/cli.mdx index 1999dcb5e..38c794528 100644 --- a/zh/developer/cli.mdx +++ b/zh/developer/cli.mdx @@ -18,7 +18,7 @@ Flashduty CLI(`flashduty`)是一款命令行工具,可在终端中完成 curl -sSL https://static.flashcat.cloud/flashduty-cli/install.sh | sh ``` -默认安装到 `/usr/local/bin`,可通过环境变量 `FLASHDUTY_INSTALL_DIR` 自定义。 +默认安装到 `PATH` 上已有 `flashduty` 所在的目录(升级时原地替换,不会留下旧版本挡在前面),没有则安装到 `/usr/local/bin`。可通过环境变量 `FLASHDUTY_INSTALL_DIR` 自定义。 @@ -39,7 +39,7 @@ irm https://static.flashcat.cloud/flashduty-cli/install.ps1 | iex | 环境变量 | 说明 | 默认值 | |----------|------|--------| | `FLASHDUTY_VERSION` | 安装指定版本,如 `v1.5.12` | 最新版本 | -| `FLASHDUTY_INSTALL_DIR` | 安装目录 | `/usr/local/bin`(Shell)、`~\.flashduty\bin`(PowerShell) | +| `FLASHDUTY_INSTALL_DIR` | 安装目录 | Shell:`PATH` 上已有 `flashduty` 所在目录,没有则 `/usr/local/bin`;PowerShell:`~\.flashduty\bin` | | `MIRROR_URL` | 覆盖安装脚本的 Release 资产下载镜像前缀,需以 `https://` 开头 | `https://static.flashcat.cloud/flashduty-cli` | | `FLASHDUTY_UPDATE_BASE_URL` | 覆盖 `flashduty update` 及自动更新检查的下载基础 URL | `https://static.flashcat.cloud/flashduty-cli` | | `INSTALLED_NAME` | 安装后的命令名,例如 `fduty` | `flashduty` |