From 49bc076fd8285e7d0e4bbab753fe304b4c2b9ec2 Mon Sep 17 00:00:00 2001 From: "flashduty[bot]" Date: Mon, 19 Oct 2026 08:23:03 +0000 Subject: [PATCH 1/2] =?UTF-8?q?docs:=20doc-review=202026-10-19=20=E2=80=94?= =?UTF-8?q?=20WeCom=20hosted=20domain,=20Lark=20managed=20app,=20template?= =?UTF-8?q?=20send-test,=20RUM=20custom=20dashboards,=20monit=20edge=20sel?= =?UTF-8?q?f-monitoring?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Window: 2026-10-16T08:12:21Z -> 2026-10-19T08:00Z, 30 findings (4 high / 20 medium / 6 low). 29 applied across 41 files (zh + en), 2 new pages registered in docs.json, 1 handed off to api-review. mint broken-links: success, no broken links. --- docs.json | 12 +- en/ai-sre/im.mdx | 2 + en/ai-sre/sessions.mdx | 14 ++ en/ai-sre/skills.mdx | 2 +- en/changelog/changelog.mdx | 36 ++- en/developer/cli.mdx | 36 ++- en/monitors/alert-rules/mysql.mdx | 10 +- en/monitors/engine/self-monitoring.mdx | 134 +++++++++++ en/monitors/quickstart/quickstart.mdx | 1 + en/on-call/configuration/custom-forms.mdx | 6 + en/on-call/configuration/templates.mdx | 129 +++++++++++ .../integration/instant-messaging/lark.mdx | 159 +++++++++---- .../instant-messaging/microsoft-teams.mdx | 30 ++- .../integration/instant-messaging/wecom.mdx | 208 ++++++++++++++---- .../integration/webhooks/custom-actions.mdx | 13 ++ en/on-call/statuspage/create-manage-page.mdx | 8 +- en/platform/permission-design.mdx | 3 +- en/rum/analytics/custom-dashboards.mdx | 183 +++++++++++++++ en/rum/analytics/web.mdx | 2 + en/rum/error-tracking/error-viewing.mdx | 4 +- en/rum/error-tracking/issue-status.mdx | 22 +- en/rum/explorer/analytics.mdx | 18 ++ en/rum/explorer/data-query.mdx | 16 ++ zh/ai-sre/im.mdx | 2 + zh/ai-sre/sessions.mdx | 14 ++ zh/ai-sre/skills.mdx | 2 +- zh/changelog/changelog.mdx | 36 ++- zh/developer/cli.mdx | 36 ++- zh/monitors/alert-rules/mysql.mdx | 10 +- zh/monitors/engine/self-monitoring.mdx | 134 +++++++++++ zh/monitors/quickstart/quickstart.mdx | 1 + zh/on-call/configuration/custom-forms.mdx | 6 + zh/on-call/configuration/templates.mdx | 129 +++++++++++ .../integration/instant-messaging/lark.mdx | 159 +++++++++---- .../instant-messaging/microsoft-teams.mdx | 30 ++- .../integration/instant-messaging/wecom.mdx | 208 ++++++++++++++---- .../integration/webhooks/custom-actions.mdx | 13 ++ zh/on-call/statuspage/create-manage-page.mdx | 8 +- zh/platform/permission-design.mdx | 3 +- zh/rum/analytics/custom-dashboards.mdx | 183 +++++++++++++++ zh/rum/analytics/web.mdx | 2 + zh/rum/error-tracking/error-viewing.mdx | 4 +- zh/rum/error-tracking/issue-status.mdx | 24 +- zh/rum/explorer/analytics.mdx | 18 ++ zh/rum/explorer/data-query.mdx | 16 ++ 45 files changed, 1861 insertions(+), 225 deletions(-) create mode 100644 en/monitors/engine/self-monitoring.mdx create mode 100644 en/rum/analytics/custom-dashboards.mdx create mode 100644 zh/monitors/engine/self-monitoring.mdx create mode 100644 zh/rum/analytics/custom-dashboards.mdx diff --git a/docs.json b/docs.json index 957a16f1..3577cdee 100644 --- a/docs.json +++ b/docs.json @@ -3653,7 +3653,8 @@ "pages": [ "zh/rum/analytics/web", "zh/rum/analytics/native", - "zh/rum/analytics/miniprogram" + "zh/rum/analytics/miniprogram", + "zh/rum/analytics/custom-dashboards" ] }, { @@ -3857,7 +3858,8 @@ "icon": "gears", "pages": [ "zh/monitors/engine/engine", - "zh/monitors/engine/engine-lost-alert" + "zh/monitors/engine/engine-lost-alert", + "zh/monitors/engine/self-monitoring" ] }, { @@ -5409,7 +5411,8 @@ "pages": [ "en/rum/analytics/web", "en/rum/analytics/native", - "en/rum/analytics/miniprogram" + "en/rum/analytics/miniprogram", + "en/rum/analytics/custom-dashboards" ] }, { @@ -5613,7 +5616,8 @@ "icon": "gears", "pages": [ "en/monitors/engine/engine", - "en/monitors/engine/engine-lost-alert" + "en/monitors/engine/engine-lost-alert", + "en/monitors/engine/self-monitoring" ] }, { diff --git a/en/ai-sre/im.mdx b/en/ai-sre/im.mdx index 008810b3..96a6bf3b 100644 --- a/en/ai-sre/im.mdx +++ b/en/ai-sre/im.mdx @@ -113,6 +113,8 @@ Once you hand AI SRE an investigation, every message that appears in the group i - **Result**: an answer to your concrete question, or an actionable interim finding. - **Progress**: a one-or-two-sentence update while work is still going on: confirmed progress, what it is waiting on, a real blocker. A progress message is **not the answer**: it does not finish the turn, and the result is posted separately as usual. +When AI SRE hands the work to a background sub-agent (a Subagent or an A2A delegation) and nothing has been sent to the chat yet in that turn, it first posts a short **acknowledgement** saying what it dispatched and that the conclusion will follow when the sub-agent reports back, so the group is not left silent until the child finishes. This is a progress message too: it does not end the turn. + This reply style applies only to **human-initiated IM requests**: console sessions still answer with streaming text, and war-room auto-diagnosis is an unattended background turn that also keeps native streaming output. diff --git a/en/ai-sre/sessions.mdx b/en/ai-sre/sessions.mdx index 5ae7613f..99238994 100644 --- a/en/ai-sre/sessions.mdx +++ b/en/ai-sre/sessions.mdx @@ -271,6 +271,20 @@ As a conversation grows, AI SRE automatically summarizes older history while kee Progress is shown while compacting; afterwards the **Context** percentage in the chat header drops (hover for token usage). +A summary paraphrases the conversation, but the text before it is not lost: the messages and tool results it replaced are still stored exactly as they were returned. When an investigation depends on an exact value (an ID, a host, a metric value, a threshold, a log line, the text of an error, a command), the agent calls the built-in `session_transcript` tool to write that text into the `.history//` directory of its execution environment, then searches it with `grep` / `read` instead of repeating the summary's wording. If the value really is not there, it says so plainly rather than guessing. + +| File | Content | +| --- | --- | +| `seg_NNN.md` | The text replaced by the Nth summary | +| `tail.md` | Everything after the latest summary (always present, may be empty) | +| `index.md` | How many rows each file covers, its time span, and a checksum hash | + +What gets recorded is **what the agent was given at the time**: images and attachments leave only an `[inline …]` / `[file …]` placeholder, a truncated result keeps only the `file_path` of its full output, and a sub-agent's internal steps stay in its own session rather than in these files. + + +This recall only works for agents that **carry the tool**: the main session and a `general` sub-agent with full tools get the recall note after a summary, while a restricted sub-agent such as the read-only `explore` does not have the tool and can only go by the summary after compaction. + + ## Choosing a runtime environment When creating a session, choose the **runtime environment** in the input area to decide where the agent's tools, Skills and MCP run: Auto (default), the cloud sandbox, or an online self-hosted BYOC Runner. For the selection order, offline handling, and why the environment is fixed once the session is created, see [Environments · Selecting an environment in a session](/en/ai-sre/environments#selecting-an-environment-in-a-session). diff --git a/en/ai-sre/skills.mdx b/en/ai-sre/skills.mdx index da7324e7..4a32bfe3 100644 --- a/en/ai-sre/skills.mdx +++ b/en/ai-sre/skills.mdx @@ -216,7 +216,7 @@ Use `venues` for skills that depend on one kind of environment. For example, a s Tools can be specified in two ways: -- **Built-in tools**: write the tool name directly. Available built-in tools include `read`, `write`, `edit`, `bash`, `grep`, `glob`, `skill`, `mcp`, `todo`, `time`, `webfetch`, `web_search`, and others. +- **Built-in tools**: write the tool name directly. Available built-in tools include `read`, `write`, `edit`, `bash`, `grep`, `glob`, `skill`, `mcp`, `todo`, `time`, `webfetch`, `web_search`, `session_transcript`, and others. - **MCP tools**: write them as `mcp:server/tool` (for example, `mcp:my-server/query`). At upload time, only the existence of the MCP server is validated; the specific tool name is verified when the MCP is loaded during a session. ## Related pages diff --git a/en/changelog/changelog.mdx b/en/changelog/changelog.mdx index b7a260ff..d636b4c7 100644 --- a/en/changelog/changelog.mdx +++ b/en/changelog/changelog.mdx @@ -4,6 +4,32 @@ description: "This page documents important updates and feature releases for Fla keywords: ["Changelog", "Product Release", "Feature Updates", "Flashduty", "Version History"] --- + + +### RUM custom dashboards: build your own under Insights + +The Insights tab bar no longer holds only the built-in dashboards: the **+** menu at its right end offers **New dashboard** (requires the Dashboard management permission) and **Manage dashboards**, and a custom dashboard opens as an extra tab (up to 100 tabs are listed); **Save to dashboard** on an analytics chart also turns the current question into one full-width panel. A panel asks the same aggregation question as the explorer — application (or the built-in `$app` variable, which follows the page's current application), event type, aggregation and field, group by and top N, plus a DQL filter — and draws as a time series, table, stat or text panel. Dashboard variables come in field-value and custom kinds, referenced in a panel's DQL as `${name}`, and a new dashboard starts with `env` / `service` / `version` field variables. Every save leaves a revision (the last 20 are kept), and a deleted dashboard goes to the trash for 30 days and can be restored from the manage page. See [RUM custom dashboards](/en/rum/analytics/custom-dashboards). + +### A redesigned template detail page, with send test + +The preview on the right of the template detail page renders by content block: native and Markdown tables are counted separately, and the bottom line gives the channel's format and its used / limit bytes; when the channel does not render Markdown tables or the content exceeds its limit, a yellow notice above the preview says so (the send fails, is truncated, or falls back to a compact summary). A built-in template, and any member without edit permission, opens read-only — **Built-in · Read-only** next to its name, with **Copy as new template** as the primary action; when you can edit, the old **Edit** button becomes **Template settings** and delete moves into **More actions**. + +The preview header gains **Send test**: it renders the editor's current content (including unsaved edits) with the incident you pick and really sends it — to your own direct chat for an IM app, or to a group the bot has joined (a WeCom app has no group option), to a robot address you type in for this one send, or to your account's email; SMS and voice are not offered. A test message's title carries a `[Test]` prefix and the last line names the template and the sender, and its card buttons only answer "this is a test message, its buttons do not change any incident"; each member can send 5 per minute. A template syntax error makes a real notification fall back to the built-in template silently, while a test send reports it. See [Notification templates](/en/on-call/configuration/templates). + +### Feishu apps: native tables instead of Markdown tables + +Templates gain a **Native tables** switch (`feishu_app_card_v2_table_enabled`, off by default): when it is on, a GFM Markdown table in the template renders as a Feishu Card 2.0 native table component, so the recipient sees a table rather than the Markdown source. One card carries at most 5 native tables, each with at most 8 columns, 100 rows, 10 rows per page and 2000 bytes per cell; exceeding any of them, or a card over 30 KB, makes every table in the content go out as a Markdown table instead, and the preview says why. Apart from Feishu apps and the WeCom bot's markdown_v2, no channel renders Markdown tables, and the preview warns that the recipient will see the table source. See [Notification templates](/en/on-call/configuration/templates). + +### WeCom self-built apps: Flashduty hosts the access domain + +Integrating a self-built WeCom app needs one required field, **Access domain** (the bare domain, and it must sit under a subject filed by your enterprise). After you save, step 1 of the detail page hands you the DNS records: one certificate-hosting dns-01 delegation that proves the domain is yours, from which Flashduty issues and renews the HTTPS certificate, and one access CNAME — the mainland entry points at `cn.wecom.flashduty.com`, the international one at `wecom.flashduty.com`, and either one alone serves the domain — after which Flashduty proxies the domain directly, with no Nginx of your own and no certificate to apply for. Step 2 downloads `WW_verify_xxx.txt` from WeCom's **Set trusted domain → Apply for domain verification** and uploads it to the detail page, where Flashduty serves it from the domain's root path (WeCom does not follow redirects, so both http and https must answer). A domain a status page or another Flashduty account already holds cannot be hosted, and self-hosted deployments have no step 1. See [WeCom](/en/on-call/integration/instant-messaging/wecom). + +### Feishu integrations: create a Flashduty-managed app by QR code + +Creating a Feishu integration now offers three choices: **Create a new app (recommended)** / **Use an existing app** / **Enter credentials manually**. With either of the first two, saving shows a Feishu QR code; once you scan it, Flashduty creates (or takes over) the app in your Feishu tenant and finishes all of its configuration — the event and callback encryption strategy and request URLs, the bot and web app abilities, the scopes the features you enabled need — then publishes a new version; the credentials stay with Flashduty, so the detail page shows only the App ID, the current version and the state. Feishu asks its admin to approve twice (the app first, then the configuration version), and the detail page and integration list show that as "Pending admin review (1/2)", "Configuration pending review (2/2)", "New version in review" and so on, re-checking every 10 seconds, with **Copy message for admin** at hand. An integration set up by hand can click **Switch to Flashduty-managed** to scan and upgrade, with the App Secret and push URL unchanged and no gap in notifications; a self-hosted deployment cannot reach Feishu's QR endpoints and keeps manual credentials only. See [Feishu/Lark](/en/on-call/integration/instant-messaging/lark). + + + ### AI SRE automations gain an execution place: rules can be bound to the IM chat that created them @@ -12,12 +38,12 @@ Each automation rule now has an **execution place**, fixed at creation: **Web** Every run of an IM rule opens a new session in the original chat and posts the conclusion back to that chat. If the chat's scope or session has changed by the time the rule fires (for example after `/scope` or `/new`), the run is **skipped** (reason `chat_scope_mismatch`) and the rule stays enabled. The rule list gains an **Execution place** column. See [AI SRE automations](/en/ai-sre/automations#execution-place-where-a-rule-runs). -### RUM Issue resolve mode: Resolved / Resolve in next release +### RUM Issue resolve mode: Reopen on recurrence / Reopen in a new version -The **Resolved** and **Resolve in next release** options in the issue status dropdown are two ways of handling recurrence for the same resolved state: +The **Reopen on recurrence** and **Reopen in a new version** options in the issue status dropdown are two ways of handling recurrence for the same resolved state: -- **Resolved**: if the error occurs again, even in the same version, the issue is reopened as pending and marked **regression**. -- **Resolve in next release** (default): the issue reopens only when the error appears in a version the issue has not seen before; repeats within the same version do not reopen it. +- **Reopen on recurrence**: if the error occurs again, even in the same version, the issue is reopened as pending and marked **regression**. +- **Reopen in a new version** (default): the issue reopens only when the error appears in a version the issue has not seen before; repeats within the same version do not reopen it. You can switch between the two at any time. See [Issue status management](/en/rum/error-tracking/issue-status). @@ -98,7 +124,7 @@ Labels whose value is an http(s) link and not an image no longer stay in the lis ### Dashboards: global search, panel query mode and template checks -The dashboard list page gains a **Global search** at the top right: it searches dashboards across every folder the account can read (permission is filtered server-side), matches title or description, takes several keywords separated by spaces, labels each result with its folder path so duplicate titles can be told apart, shows 20 per page and offers a retry on failure. Its search state lives only inside the dialog — it neither affects the list page's "search the current folder" nor is written into the page address. +The dashboard list page gains a **Global search** at the top right: it searches dashboards across every folder the account can read (permission is filtered server-side), matches title or description, takes several keywords separated by spaces, labels each result with its folder path so duplicate titles can be told apart, shows 20 per page and offers a retry on failure. Its search state lives only inside the search box that expands in place, and closing it discards the search — it neither affects the list page's "search the current folder" nor is written into the page address. In the panel editor's query **Options**, a Loki **table** or **stat** panel now lets you choose the **Query mode** yourself: **Range** computes point by point along the step to draw a trend, **Instant** computes a single value over the whole time range (`[$__auto]` takes the whole range) — table defaults to instant and stat to range. Loki time series stay fixed at range, and Loki instant does not accept a **min step**. Query-type template variables now get a form shaped by the data source: MySQL writes SQL, Loki fills a stream selector plus a label name, and other data sources use the Prometheus label form. The query is now pre-checked in the editor for the three patterns the backend refuses on save — a multi-select or "all" variable inside an exact matcher, a reference to an undefined variable, and a built-in variable unavailable for the current data source and query mode — and the exact-matcher one comes with a one-click **Switch to regex match**. **Preview candidates** now runs with the current variable selections and the panel's time range, so the preview matches what the dashboard actually renders. See [Dashboards](/en/monitors/dashboards/dashboards). diff --git a/en/developer/cli.mdx b/en/developer/cli.mdx index acc117e1..6145468a 100644 --- a/en/developer/cli.mdx +++ b/en/developer/cli.mdx @@ -810,7 +810,7 @@ Beyond the curated commands above, the CLI covers **all** Flashduty Open APIs th - **On-call & scheduling**: calendar, schedule - **Platform administration**: account, member, person, team, role (roles-permissions), audit (audit-logs) - **Monitoring & RUM**: monit, rum, sourcemap -- **Integrations & webhooks**: datasource (IM integrations), integration (create, read, update, delete and key-rotation for alert- and change-source integrations), webhook (integrations) +- **Integrations & webhooks**: datasource (IM integrations), integration (create, read, update, delete and key-rotation for alert- and change-source integrations; secret masking and update rules in "integration — integrations and secrets" below), webhook (integrations) These generated leaf commands use a `resource-action` naming form (for example, `flashduty safari a2a-agent-get`, `flashduty safari session-list`); Every public API operation is reachable as `flashduty `, with the name derived from its API path: request fields become typed flags, and `--data` accepts the full JSON request body (typed flags override fields with the same name). This page does not list them all; explore them level by level with `flashduty --help`: @@ -883,6 +883,40 @@ Public-sharing and signing semantics: - The `artifact-sign` token is bound to the calling account and person and is valid for 5 minutes (response `expires_in`); `download_url` / `preview_url` are relative paths (`/safari/artifact/stream?...`) — prepend the API base (`https://api.flashcat.cloud`). - An artifact's initial scope inherits from its source session (personal-session artifacts belong to the creator; team-bound session artifacts belong to the team); `can_edit` tells whether the caller may rename/transfer/delete/share it (the creator, any member of the owning team, or a manager of the source session). +#### integration — integrations and secrets + +The `integration` command family manages account-level public integrations — alert sources, change sources, IM bots and custom webhooks — with 9 subcommands: `list`, `info `, `type-list`, `create`, `update `, `delete `, `enable `, `disable `, and `key-rotate `. Channel-owned integrations (a `ref_id` starting with `c_`) are not included; configure those in the console. + +```bash +flashduty integration type-list --category event.alert # List the integration types the account can configure (only types with supports_api_create: true can be created) +flashduty integration list --status enabled # List integrations (filter with --category, --plugin-type, --status, --team-ids, --name, --ref-ids) +flashduty integration info # Get one integration, including settings and integration_key +flashduty integration create --plugin-type standard.alert --name "Production alerts" --is-test --data '{"settings":{}}' +flashduty integration update --name "Production alerts (primary)" +flashduty integration key-rotate # Issue a new key; the previous key stops working immediately +flashduty integration disable # Pause event intake, keep the configuration +flashduty integration enable # Re-enable +flashduty integration delete # Delete; rejected with ReferenceExist while other objects still reference it +``` + +Fields available on create and update: + +| Flag | Description | +|------|-------------| +| `--plugin-type` | Integration type (required on `create`), taken from `integration type-list` entries with `supports_api_create: true`, for example `standard.alert` or `zabbix.alert` | +| `--name` | Integration name, 2–49 characters; on create, omitting it or sending an empty value uses the integration type name | +| `--description` | Free-form description, at most 499 characters | +| `--team-id` | Owning team ID; omitted or `0` means account-level, and a nonzero value must belong to the current account. On update, passing `0` clears the team assignment | +| `--integration-key` | Only on `create`: an optional pre-generated key, 35 characters, ending with the account ID modulo 1000 padded to three digits, and it must not already be in use; omit it to generate a key | +| `--is-test` | Runs the plugin connectivity test. Omitted on create means no test; on update it only runs when `settings` is actually submitted, so a name/description-only update performs no test. Read `test.success` and `test.message` from the response: an HTTP success is not a passing test, and an absent `test` means no test was run | +| `--data` | Full JSON request body; the type-specific `settings` block can only be passed here, and the accepted keys depend on `plugin_type` | + +How `settings` reads back, and the secret rules (the same handling as `monit` datasources above): + +- In the `settings` returned by `list` and `info`, every **saved secret** — passwords, app secrets, tokens, encryption keys, webhook endpoints, HTTP pull URL and body, headers, custom bodies — is shown as `******`; unset values stay empty. **`integration_key` is the exception and comes back as is** (it authenticates inbound pushes and is not the account-level `app_key`). A script that reads credentials from `integration info --json` therefore gets the mask; for a new key, rotate it with `key-rotate`, which invalidates the previous key immediately. +- The `settings` submitted by `update` replaces the type-specific configuration as a whole: **a known sensitive field that is omitted or sent as `******` keeps its stored value**, and an explicit empty `headers: {}` clears headers. But **a stored secret cannot be kept while its destination changes** — changing `endpoint`, `url`, or a database host or port, for example, requires sending the real secret in the same call, otherwise the request is rejected. +- `editable` only reflects object permission (`no_editable: true` marks a read-only integration); write calls additionally require **Integrations Manage**. + ### Utility commands ```bash diff --git a/en/monitors/alert-rules/mysql.mdx b/en/monitors/alert-rules/mysql.mdx index a20b4b1d..e78e3c28 100644 --- a/en/monitors/alert-rules/mysql.mdx +++ b/en/monitors/alert-rules/mysql.mdx @@ -13,7 +13,13 @@ Monitors supports using standard SQL syntax to query MySQL and trigger alerts ba | **Field Processing** | All field names are automatically converted to lowercase; please use lowercase letters when configuring | | **Time Processing** | Recommended to use `now()`, `unix_timestamp()` and other functions for time filtering | -Related queries and recovery queries support the `${label_name}` variable and the Go template `{{ $labels. }}`, which require alert engine `v0.76.0` or later. After substituting variables, the alert engine checks that the SQL is a read-only query; if it isn't, that query run fails. The main query doesn't support variables. +Related queries and recovery queries support the `${label_name}` variable and the Go template `{{ $labels. }}`. The main query doesn't support variables; a main query containing `${...}` is rejected at save time with `variables are only supported in relate and recovery queries`. + + +Variables require alert engine `v0.76.0` or later; if any instance in the cluster serving the rule's datasources is older, saving or enabling the rule is rejected with an error that starts with `MySQL relate or recovery queries with variables require` and names the cluster and the required version. Rules that use no variables are not subject to this version requirement. + + +The SQL after variable substitution is only known at runtime, so it can't be pre-checked at save time. The alert engine renders the variables per alert instance and then checks that the rendered text is a read-only query; if it isn't a plain read-only SELECT, that evaluation run fails. ## Threshold evaluation mode @@ -45,7 +51,7 @@ Monitors distinguishes alert objects by their label fields and evaluates thresho ### Recovery logic -See [Alert recovery](/en/monitors/quickstart/quickstart#alert-recovery) for the recovery options. A recovery query is a separate SQL statement that supports the `${label_name}` variable and the Go template `{{ $labels. }}` (requires alert engine `v0.76.0` or later). +See [Alert recovery](/en/monitors/quickstart/quickstart#alert-recovery) for the recovery options. A recovery query is a separate SQL statement that supports the `${label_name}` variable and the Go template `{{ $labels. }}`. These rules are subject to the same version requirement as above: if any instance in the cluster is older than `v0.76.0`, saving or enabling the rule is rejected. ## Data exists mode diff --git a/en/monitors/engine/self-monitoring.mdx b/en/monitors/engine/self-monitoring.mdx new file mode 100644 index 00000000..0a52fb2c --- /dev/null +++ b/en/monitors/engine/self-monitoring.mdx @@ -0,0 +1,134 @@ +--- +title: "Engine self-monitoring metrics" +description: "Scrape the Prometheus metrics the alert engine exposes to monitor its connection to the center, rule sync, and rule evaluation health" +keywords: ["alert engine", "monitedge", "self-monitoring", "Prometheus metrics", "engine health check"] +--- + +The alert engine (`monitedge`) exposes a set of Prometheus metrics locally, covering three failures that silently stop monitoring: the connection to the center is down, rule sync has stalled, and rules keep failing to evaluate. Scrape them into your own time series database and you can tell, outside the console, whether this engine is still doing its job. + +These metrics are not shown in the console and have no menu entry. You can only read them from the engine's `/metrics`. + +## How to scrape + +`/metrics` shares the engine's `http.listen` port, `0.0.0.0:6868` by default, and needs no authentication. Point your own Prometheus or VictoriaMetrics at it: + +```yaml +scrape_configs: + - job_name: monit-edge + static_configs: + - targets: ["10.0.0.1:6868"] +``` + +Flashduty does not scrape these metrics for you. The **Alert Engine Status** page in the console shows the instance list, heartbeat, and version, but none of these metrics; they exist only in your own time series database. + + +**Don't let the engine you are monitoring evaluate alerts on its own metrics.** When the engine process exits, its lease is not `ACTIVE`, or it has lost the connection to the center, rules running on that engine neither evaluate nor deliver events. Use the center-side [engine disconnection alert](/en/monitors/engine/engine-lost-alert) to catch an engine going down; the metric alerts below belong on your own Prometheus/Alertmanager, or on another engine cluster. + + +## Connection to the center + +| Metric | Type | Labels | Description | +|------|------|------|------| +| `monit_edge_websocket_connected` | Gauge | — | Whether the engine currently holds a WebSocket connection to the center: 1 connected, 0 not. Console query previews and entity runtime status go over this connection | +| `monit_edge_websocket_connects_total` | Counter | — | Successful dials, including the first connection. A steady increase means the connection keeps being rebuilt | +| `monit_edge_websocket_dial_failures_total` | Counter | — | Failed dials. If this keeps climbing while `connects_total` doesn't, the engine can't reach the center (network, proxy, or the center refusing it) | +| `monit_edge_lease_info` | Gauge | `session_mode`, `phase` | Always 1; read the lease phase from `phase`: `BOOT`, `INITIALIZING`, `ACTIVE`, `FENCED`. Only `ACTIVE` evaluates rules and delivers events. `session_mode` is currently always `lease_v1` | +| `monit_edge_lease_renewal_age_seconds` | Gauge | — | Seconds since the last successful heartbeat renewal. Heartbeats default to every 10 seconds, adjustable with `alerter.heartbeatInterval`; the lease lifetime comes from the center | +| `monit_edge_webapi_access_blocked` | Gauge | — | 1 means the center permanently refused this process on authorization or license grounds, so it has stopped talking to the center. Restart the engine after the authorization is restored | + +```promql +# Connection down for more than 5 minutes +monit_edge_websocket_connected == 0 + +# Lease not ACTIVE for more than 2 minutes: the engine neither evaluates nor delivers +(sum(monit_edge_lease_info{phase="ACTIVE"}) or vector(0)) == 0 + +# Any dial failure in the last 10 minutes +increase(monit_edge_websocket_dial_failures_total[10m]) > 0 +``` + + +An engine started with `-alerter.webSocketDisable` never opens a WebSocket connection, so `monit_edge_websocket_connected` stays 0 and `connects_total` never grows. Don't use `monit_edge_websocket_connected` to judge connection health on such an instance; the lease metrics are not affected by this flag. + + +## Rule sync + +The engine syncs rules from the center every 10 seconds by default. + +| Metric | Type | Labels | Description | +|------|------|------|------| +| `monit_edge_alert_rule_sync_age_seconds` | Gauge | — | Seconds since the last successful sync | +| `monit_edge_alert_rule_sync_status` | Gauge | `outcome` | Result of the latest sync, always 1, present only on the current `outcome`: `success`, `error`, or `pending` (never synced yet) | +| `monit_edge_alert_rule_sync_rules` | Gauge | — | Number of rules the center assigned to this engine at the last successful sync. If it drops to 0 or far below what you expect, first check whether rules were disabled and whether datasource bindings or instance sharding changed | + +```promql +# Sync stalled for more than 3 minutes: rules created or changed in the center won't take effect +monit_edge_alert_rule_sync_age_seconds > 300 + +# The latest sync failed +monit_edge_alert_rule_sync_status{outcome="error"} == 1 +``` + +## Rule evaluation + +| Metric | Type | Labels | Description | +|------|------|------|------| +| `monit_edge_alert_rule_evaluations_total` | Counter | `outcome`, `error_type` | Cumulative evaluation rounds. `outcome` is `success` or `error`; for successful rounds `error_type` is an empty string, and failed rounds use the values in the table below | +| `monit_edge_alert_rule_evaluation_duration_seconds_total` | Counter | — | Cumulative evaluation time in seconds. Divide by the sum of `evaluations_total` for `success` and `error` to get the average time per round | +| `monit_edge_alert_rule_scopes` | Gauge | `datasource_id`, `state` | Latest state of each rule-datasource scope, counted per datasource, always three series per datasource: `ok`, `failing`, and `pending` (first round not finished yet) | +| `monit_edge_alert_rule_failing_duration_seconds` | Gauge | `datasource_id` | How long the longest currently failing scope on that datasource has been failing, in seconds; 0 when nothing is failing | + +The `error_type` of a failed round comes from a fixed set, so you can group alerts on it directly: + +| `error_type` | Meaning | +|------|------| +| `datasource_unavailable` | No connection to the datasource, so this round didn't run | +| `query_failed` | The query itself failed: syntax, permission, or timeout | +| `result_invalid` | The query result can't be mapped to the fields the rule needs | +| `threshold_failed` | The threshold check failed | +| `anydata_failed` | The data-exists check failed | +| `nodata_failed` | The data-missing check failed | +| `recovery_failed` | The recovery query or recovery check failed, so this round produces no recovery event | + +```promql +# Rules on a datasource have been failing for more than 5 minutes +max by (datasource_id) (monit_edge_alert_rule_failing_duration_seconds) > 300 + +# Failed rounds in the last 15 minutes, broken down by error type +sum by (error_type) (increase(monit_edge_alert_rule_evaluations_total{outcome="error"}[15m])) > 0 +``` + + +When `monit_edge_alert_rule_failing_duration_seconds` climbs, check the scope with `monit_edge_alert_rule_scopes{state="failing"}`. Many scopes failing on one datasource usually means that datasource is unavailable; only one or two failing means the query or threshold of that rule is the problem. Per-rule execution state and error summary are in the console under **Runtime Details**; see [Rule runtime details](/en/monitors/quickstart/quickstart). + + +## Query concurrency and build + +| Metric | Type | Labels | Description | +|------|------|------|------| +| `monit_edge_alert_query_admission` | Gauge | `state` | Query concurrency admission shared by ordinary alerts and entity alerts: `waiting` is the number of queries queued for a permit, `in_flight` the number executing | +| `monit_edge_alert_query_admission_rejected_total` | Counter | `reason` | Cumulative rejections at the admission boundary: `timeout` means the wait expired and that evaluation round was skipped, `canceled` means the caller cancelled | +| `monit_edge_build_info` | Gauge | `version` | Always 1; `version` is the running engine version. Linux auto-upgrade replaces the process in place, so neither the PID nor `process_start_time_seconds` changes and this metric is the only place a version change shows up | + +```promql +# A query was skipped in the last 10 minutes because its admission wait timed out +sum(increase(monit_edge_alert_query_admission_rejected_total{reason="timeout"}[10m])) > 0 +``` + +## Recommended alerts + +The table below gives rules you can copy as-is, as a metric plus a sustained condition. With more than one instance, keep `instance` or your own instance label so the same series name on different instances doesn't overwrite itself. + +| Alert | PromQL | For | What to do | +|------|--------|------|------| +| WebSocket disconnected | `monit_edge_websocket_connected == 0` | 5 minutes | The console can't query through this engine; check the network and proxy to the center, and use the growth of `dial_failures_total` to confirm if it keeps reconnecting | +| Lease not ACTIVE | `(sum(monit_edge_lease_info{phase="ACTIVE"}) or vector(0)) == 0` | 2 minutes | The engine neither evaluates nor delivers; check `monit_edge_lease_renewal_age_seconds` and heartbeats | +| Center permanently refused | `monit_edge_webapi_access_blocked == 1` | 1 occurrence | Authorization or license problem; restart the engine once it is restored | +| Rule sync stalled | `monit_edge_alert_rule_sync_age_seconds > 300` | 3 minutes | Rules created or changed in the center don't take effect; check the network to the center | +| Rule sync failing | `monit_edge_alert_rule_sync_status{outcome="error"} == 1` | 3 minutes | Same, plus the engine log for the sync error | +| Rules keep failing to evaluate | `max by (datasource_id) (monit_edge_alert_rule_failing_duration_seconds) > 300` | 1 occurrence | Use `monit_edge_alert_rule_scopes{state="failing"}` to tell an unavailable datasource from a broken rule query | +| Query admission timed out | `sum(increase(monit_edge_alert_query_admission_rejected_total{reason="timeout"}[10m])) > 0` | 3 minutes | A slow datasource has taken all the query concurrency and an evaluation round was skipped | + + +Entity tree alerts use a different metric family (`monit_edge_entity_alert_*`) for entity discovery, condition queries, and entity event delivery. This page covers ordinary alert rules only. + diff --git a/en/monitors/quickstart/quickstart.mdx b/en/monitors/quickstart/quickstart.mdx index de89c612..f5e84488 100644 --- a/en/monitors/quickstart/quickstart.mdx +++ b/en/monitors/quickstart/quickstart.mdx @@ -100,6 +100,7 @@ Go templates can use these variables: - A single value can't mix `${...}` and `{{...}}`. - A label that fails to render isn't sent, and the alert event is still delivered. The error goes to the `__alert_rule_label_template_error` label, which you can see in the alert details. - Leading and trailing whitespace in label names is trimmed on save, and the trimmed names must be unique. Label names can't start with `__alert_rule_`. All additional labels together are limited to 1,024 characters as JSON, and `job` and `instance` values to 255 characters. +- A rendered template value has two more limits, and exceeding either counts as a render failure (the label isn't sent and the reason goes to `__alert_rule_label_template_error`): one label may render to at most 8 KiB, and a rendered `job` or `instance` may be at most 255 characters. The rendered value only exists at runtime, so neither case is rejected at save time. - When the alert engine renders descriptions, custom fields, and titles, template labels in `$appendLabels` are still the raw template text. To use query results there, reference `$annotations` directly. ### Data sources and queries diff --git a/en/on-call/configuration/custom-forms.mdx b/en/on-call/configuration/custom-forms.mdx index 5d6ffc1b..9a7c4642 100644 --- a/en/on-call/configuration/custom-forms.mdx +++ b/en/on-call/configuration/custom-forms.mdx @@ -47,6 +47,12 @@ These forms also work in the Flashduty App: creating, acknowledging, and closing Flashduty skips the form when the action has no form elements, or when no element is visible for the current incident and user. It then runs the normal create, acknowledge, or close flow. +## Closing an incident from IM + +When a **Close incident** form is configured, users can't complete the closure directly from an IM incident card: selecting close has no effect, and the bot replies with a guide message stating that the incident needs a resolve form, along with an **Open the resolve form** link. The link opens that incident's detail page and starts the close form automatically; the incident closes once the form is filled in and submitted. + +Feishu/Lark, Dingtalk, WeCom, Slack, and Microsoft Teams all support this guide. Feishu/Lark adds two touches: the guide message is delivered to the person who clicked as an ephemeral message that others in the chat can't see, and the card shows a toast reminding the clicker to complete the form through the link in the guide message. + ## Batch acknowledge or close When you **acknowledge** or **close** several incidents at once from the incident list: diff --git a/en/on-call/configuration/templates.mdx b/en/on-call/configuration/templates.mdx index 5d464b49..c4d294f3 100644 --- a/en/on-call/configuration/templates.mdx +++ b/en/on-call/configuration/templates.mdx @@ -43,6 +43,12 @@ Click **Save** to complete creation + +**System preset templates and read-only**: System preset templates (Rendering Example, Default Template) and templates the current member has no permission to manage are always read-only on the detail page, marked **Built-in · Read-only** next to the title (a template you lack permission for shows no badge, but is equally uneditable). To make one your own, click **Copy as new template** in the top right and edit the copy, whose name is pre-filled as "Copy of *original template name*"; copying requires the **Templates Manage** permission and On-call Pro. + +For a template you can edit, the top right shows **Template settings** (gear icon), which changes the template name, team and description only — not the body. Delete sits in the **More actions** ⋯ menu; system preset templates have no such menu and cannot be deleted. + + ### Apply template @@ -157,6 +163,98 @@ Complete variable list for incident objects: +## Preview and test send + +The right side of the template detail page is the preview pane: it renders the current channel's content with the selected incident, followed by a line of diagnostics and any warnings the send engine reports. The preview's title bar also holds **Send test**, which sends the editor's current content for real so you can see what the recipient gets. + +### What the preview tells you + +The preview renders the current channel's content with the incident selected under **Render with this incident's data**; with nothing selected it uses the system default data. When the editor is empty, the preview renders the channel's system default template and notes "No template content set, system default template will be used." + +One line under the preview explains how this content was rendered: + +| Field | Description | +| :--- | :--- | +| Channel format | The Feishu/Lark app shows **Feishu Card 2.0**; the WeCom bot shows **Markdown** or **Markdown v2**; other channels show nothing | +| Native tables: N / Markdown tables: N | How many tables in this render became native tables and how many stayed Markdown tables; a zero count is hidden | +| Used / limit | The content's byte count and the channel's size limit, e.g. `12.3 KB / 30.0 KB`; when the channel has no known limit, only the used bytes are shown | + +The yellow notices above that line come from the send engine. Any of them means the template will be reshaped or rejected on a real send — trim it before saving: + +| Notice | Meaning | +| :--- | :--- | +| `table_not_rendered` | This channel doesn't render Markdown tables, so recipients see the raw `\|` rows; consider a list instead | +| `native_table_fallback` | **Native tables** is on for the Feishu/Lark app, but a table here misses the native-table conditions and will be sent as a Markdown table | +| `content_too_large` | The content exceeds the channel's size limit | + +`native_table_fallback` has six reasons: `too_many_tables`, `too_many_columns`, `too_many_rows`, `cell_too_long`, `unsupported_cell`, `card_too_large` — see [Native tables](#native-tables). + +`content_too_large` has three outcomes: + +| Outcome | Meaning | +| :--- | :--- | +| `rejected` | The channel refuses the message, so this notification is not delivered — real alerts included. You must trim the template | +| `truncated` | The excess is truncated and the notification still goes out; per-channel limits are listed below | +| `compact_fallback` | A WeCom bot Markdown v2 message over 4096 bytes is replaced by a compact summary, so nothing is lost | + +### Send test + +**Send test** sits in the preview's title bar. It renders the editor's current content (**unsaved changes included**) with the incident selected in the preview and sends it to the channel for real, so you can confirm what the recipient sees. It needs the **Templates Manage** permission; system preset templates can be test-sent too. The button is disabled while the template fails validation. + +Supported channels: the five IM apps (Feishu/Lark, Dingtalk, WeCom, Slack, Teams), six group chat bots (Feishu/Lark, Dingtalk, WeCom, Slack, Telegram, Zoom) and email. SMS and voice are not offered — they are billed per message and the preview already equals what would be sent. + +The content is the same one the preview shows: the editor's current content plus the selected incident's data (system default data when none is selected), with the same channel switches (card field visibility, the Feishu Card 2.0 table and blank-line switches, WeCom bot Markdown v2). When the editor is empty, the channel's system default template is sent. + +Where each channel can send: + +| Channel | Target | Notes | +| :--- | :--- | :--- | +| IM app | Myself / Group chat | The WeCom app only offers **Myself**: WeCom has no API to list the chats a bot has joined, and group messages go through the WeCom bot template | +| Group chat bot | Enter the robot address | The same address the notification rule takes, either a bare token or a full URL; Telegram also needs a Chat ID, Zoom a Verify token. These values are used for this send only and are never saved | +| Email | Your own account email | Cannot send when your account has no email address | + +- **Myself**: requires your account to be linked to that IM integration; when it is not, the send button is disabled and members with permission can click the link to finish linking in the onboarding flow. The Slack app is the exception — no per-person linking is needed (it DMs workspace members directly), but the send still resolves you to a member of that workspace and returns `im_not_linked` if it cannot. +- **Group chat**: lists only chats this integration's bot has **already joined** (Slack by channel, Teams by team channel or group chat). Everyone in the chat receives the test message. +- When the channel has several integrations, pick one under **Integration** first; only enabled integrations are listed. + +Test message markings: + +- The incident title is prefixed with `[Test] ` (Chinese accounts get `[测试] `). +- A footer is appended: `Test message · Template "" · Sent by `. +- Buttons on the card only answer "This is a test message. Its buttons do not change any incident." and neither acknowledge nor close anything; the **Create War Room** button does not appear in a test message. + +The result is one of two values: + +| Result | Channel | Prompt | +| :--- | :--- | :--- | +| `sent` | IM app | `Sent to your Feishu DM` / `Sent to ""` | +| `queued` | Group chat bots and email | "Submitted. It usually arrives within seconds; if not, check the address" | + +IM apps call the platform's API synchronously, so a success is `sent`; bots and email go to the notification queue and are delivered asynchronously, so `queued` only means submitted — a wrong address does not fail immediately. + +Limits and common failures: + +- Each member can send at most 5 test messages per minute; beyond that the call returns `rate_limited`. +- Failures are reported by error code: + +| Error code | Meaning | +| :--- | :--- | +| `template_invalid` | The template failed to render (parse or execution error). Fix the template first | +| `channel_not_supported` | This channel does not support test sends | +| `invalid_target` | The target is incomplete or malformed: no robot address, a Telegram test without a Chat ID, a Zoom test without a Verify token, or a chat that does not belong to the integration | +| `integration_not_found` | The integration was not found — it may be deleted or disabled, or not an integration of this channel | +| `im_not_linked` | Your account is not linked to this IM, so it cannot DM you | +| `rate_limited` | Sending too often | +| `webhook_url_blocked` | Robot addresses must be public `http`/`https`; a full URL pointing into a private network is refused. A bare token is joined to the vendor's URL by the platform and is not checked | +| `content_too_large` | The content exceeds the channel's size limit, such as the 30 KB Feishu card | +| `send_failed` | The channel returned a failure; the message carries the channel's own error | + + +How test sends differ from real notifications: when the template has a syntax error, a real notification silently falls back to the system default template and is still delivered, while a test send returns `template_invalid` — that is the point, to surface template problems. Conversely, a test send writes no notification record, takes no send lock and adds nothing to the notification feed, so it never acknowledges or closes an incident and never creates a war room. + +The console calls `POST /template/preview/send` with `template_id`, `type`, the editor's current `content`, `incident_id` and the channel render switches, and receives either `status` (`sent` / `queued`) or `error_code` + `message`. + + ## Channel templates Different notification channels support different template formats and limitations. @@ -213,6 +311,33 @@ Requires [Feishu/Lark integration](/en/on-call/integration/instant-messaging/lar ``` On Feishu/Lark app cards, **More Actions** at the bottom is a dropdown menu with custom actions, snooze durations, and unacknowledge; the **👥 Create War Room** button sits outside that menu. + +### Native tables + +**Feishu Card 2.0** adds a **Native tables** switch (off by default, saved with the template). When it is on, GFM Markdown tables in the template are recognised and rendered as the card's native table component, so recipients see a pageable table instead of rows of text with `|`. + +Native tables have limits, and when any condition fails **every table on that card** falls back to Markdown text: + +| Limit | Value | +| :--- | :--- | +| Native tables per card | At most 5 | +| Columns per table | At most 8 | +| Rows per table | At most 100 | +| Cell content | At most 2000 bytes per cell | +| Rows per page | 10 rows per page; the rest is paged inside the card | + +Why a table falls back (shown as a `native_table_fallback` warning in [What the preview tells you](#what-the-preview-tells-you)): + +| Reason | Trigger | +| :--- | :--- | +| `too_many_tables` | The card would carry more than 5 native tables | +| `too_many_columns` | A table has more than 8 columns | +| `too_many_rows` | A table has more than 100 rows | +| `cell_too_long` | A cell is longer than 2000 bytes | +| `unsupported_cell` | A cell holds Markdown beyond plain text and `
` line breaks, a header cell is empty, or a row's cell count differs from the header's | +| `card_too_large` | Native tables would push the card past Feishu's size limit | + +The whole Feishu card is capped at **30 KB**. Beyond that Feishu refuses the message outright and the preview reports `content_too_large` + `rejected`; trim the template and send again. @@ -319,6 +444,10 @@ For the switches on the **Additional info** tab, see [Card Fields and Bottom Act Template formats supported by group chat bots. + +Only the Feishu/Lark app (card 2.0, via **Native tables**) and the WeCom bot's **Markdown v2** render Markdown tables. Every other channel sends tables as raw text with `|`: the Dingtalk, Slack and Telegram bots, the WeCom bot on Markdown v1, and the Dingtalk, Slack, WeCom and Teams apps. The preview flags this. For those channels, rewrite tables as lists. + + Supports message card, rich text, and plain text formats. Max message 4000 bytes, truncated if exceeded. diff --git a/en/on-call/integration/instant-messaging/lark.mdx b/en/on-call/integration/instant-messaging/lark.mdx index 80416d8c..76151852 100644 --- a/en/on-call/integration/instant-messaging/lark.mdx +++ b/en/on-call/integration/instant-messaging/lark.mdx @@ -13,8 +13,60 @@ description: "By integrating a Feishu/Lark custom app, you can receive and respo */} +## Choose how to connect + +You have three ways to create a Feishu/Lark integration. They differ in who creates the Feishu app and who does the configuration afterwards: + +| Access method | When to use | What you do | +| :--- | :--- | :--- | +| **Create a new app (recommended)** | Most cases. Flashduty creates the app in your Feishu tenant and completes all of its configuration; you never open the Feishu developer console | Scan with Feishu to confirm; a Feishu admin approves twice | +| **Use an existing app** | Your tenant already has this app (for example a colleague created it), or you want to keep the current one | Scan with the Feishu account of the app's owner or a collaborator to authorize it; a Feishu admin approves | +| **Enter credentials manually** | Private Feishu deployment, Lark international, or you don't want to hand the app over to Flashduty | Enter the credentials in Flashduty, then configure the app in the Feishu developer console | + +A private Flashduty deployment cannot reach the Feishu QR-code endpoints, so it can only use **Enter credentials manually**. + +### Scan to create a Flashduty-managed app (recommended) + +1. When you create a Feishu/Lark integration, **Feishu/Lark app authorization** defaults to **Create a new app (recommended)**. Fill in the name and click **Save**; a QR code appears. + +2. Scan it with Feishu to confirm: the Feishu confirmation page lists the permissions requested, click **Create**. Flashduty stores nothing before you confirm. + +3. After you confirm, Flashduty creates the app in your Feishu tenant (named Flashduty by default) and submits the first review. The app sends and receives messages and reads group messages, and Feishu requires a tenant admin to approve it before it can do so. + +4. Once the first review passes, Flashduty configures everything: it generates the `Encrypt Key` and `Verification Token` for the event and callback encryption strategy; adds the web and mobile login redirect URLs; sets the availability scope to all members (on the first configuration only — later versions keep the scope an admin chose); adds the permissions the enabled features need, plus `application:application:self_manage` and `application:application:patch` so Flashduty can update the configuration and publish versions later without another scan; points both events and callbacks at the Flashduty request URL (event `im.message.receive_v1`, callback `card.action.trigger`); enables the **Web App** and **Bot** abilities, with the Flashduty web app as the workplace default. It then publishes a new version for the admin to review. + +5. After both reviews pass, the integration is ready to use. If Feishu did not accept the default ability in that version, Flashduty publishes one more small version to add it, without affecting usage. + +While a review is pending the details page checks the result with Feishu every 10 seconds. You can also click **Copy message for admin** to send the admin what needs approval, or **Review in Feishu Admin** to open the app review page in the Feishu admin console. + +Flashduty generates and keeps the credentials of a managed app, so the page shows only **App ID**, **Current version**, and **Status**, and the edit page says "Feishu app xxx is managed by Flashduty. Flashduty keeps its credentials, so there is nothing to fill in." The backend owns these credential fields, so changing them in Flashduty has no effect. + +### Use an existing app + +Choose **Use an existing app** under **Feishu/Lark app authorization**, enter the app's `App ID`, and click **Save**. Then scan with the Feishu account of the app's owner or a collaborator to authorize Flashduty to manage it. The two reviews and the automatic configuration are the same as for a new app. + +### Managed app states + +Both the integration detail page and the integration list show the state of a managed app. + +| State | Meaning | What to do | +| :--- | :--- | :--- | +| Pending admin review (1/2) | The app is created and waits for the Feishu tenant admin to review it | Ask the admin to approve it in Feishu Admin → App review; Flashduty then configures the app and submits the second review | +| Configuration pending review (2/2) | The configuration is applied and a new version published, waiting for the admin to review it | Ask the admin to approve it; the integration is then ready to use | +| New version in review | A working integration submitted a new version (permission additions, default ability adjustment, and so on) | Nothing to do; existing features keep working while it is reviewed | +| New version rejected | A Feishu admin rejected a new version of a working integration | Existing features keep working; the changes in that version did not take effect. After you handle it, click **Resubmit for Feishu admin review** | +| Rejected | An admin rejected the app or its first configuration version, so the integration cannot be used yet | Confirm the reason with the admin, then click **Scan again to resubmit** to submit it for review again | +| Configuration failed | Feishu did not accept Flashduty's automatic configuration; the detail page shows why | Click **Retry configuration** to apply the configuration again and publish a version | +| Credentials invalid | Someone reset the App Secret in the Feishu developer console, or a Feishu admin deactivated the app, so alerts cannot be sent | After a Secret reset, click **Scan again to authorize**; if the app was deactivated, a Feishu admin has to enable it in the admin console first | +| App deleted | The app was deleted in Feishu | Click **Recreate Feishu app** to scan and create it again; the integration keeps its name, team, and enhanced feature settings, so escalation rules that reference it need no change | +| Permissions available | The integration works, but a newly enabled feature still lacks Feishu permissions | Click **Submit for Feishu admin review**; or click **Not now** to skip, and open it later from **Add permissions** next to the status | + +For a working integration Flashduty checks the credential every 10 minutes; when the Secret is reset, the app is deactivated, or it is deleted, the state changes to one of the above. After you enable a feature that needs more permissions (for example importing war room chat history into a post-mortem, or using AI SRE in Feishu), the detail page shows "Enabled features need N more Feishu permissions"; click **Submit for Feishu admin review** and Flashduty adds the permissions and publishes a new version, with no scan needed. + ## Create Feishu/Lark app +The steps below apply only to **Enter credentials manually** (including Lark international and private deployments). Flashduty does this configuration for a scanned or already upgraded integration, so you don't need to perform them. + ### 1. Create custom app Visit [Feishu/Lark Developer Console](https://open.feishu.cn/app) to create an enterprise custom app. You can use the [Flashduty official icon](https://flashduty-public.oss-cn-beijing.aliyuncs.com/icons/flashduty-20251216.png) as the app icon. @@ -32,81 +84,96 @@ Go to **Credentials & Basic Info** page, copy `App ID` and `App Secret` for late ### 3. Copy event callback token information -Go to Development Configuration → Events & Callbacks → **Encryption Strategy** page, generate and copy `Encrypt Key` (recommended for better security) and `Verification Token` for later use. +Go to Development Configuration → Events & Callbacks → **Encryption Strategy** page, generate and copy `Encrypt Key` (recommended for better security; leave it empty if you did not set one in Feishu) and `Verification Token` for later use. ![Feishu/Lark: Copy Event Callback Token Information screenshot](https://docs-cdn.flashcat.cloud/images/png/ac558d48464310fe27ef97912b298df1.png) ## Add Feishu/Lark integration -In the Flashduty On-call **Integration Center**, select Instant Messaging → **Feishu/Lark**. Fill in `Name`, plus the `App ID`, `App Secret`, `Verification Token`, and `Encrypt Key` from the previous step, then click **Save**. +In the Flashduty On-call **Integration Center**, select Instant Messaging → **Feishu/Lark**, choose **Enter credentials manually** under **Feishu/Lark app authorization** (a new integration defaults to scanning; a private deployment and an existing integration go straight to the credential form), then fill in `Name`, plus the `App ID`, `App Secret`, `Verification Token`, and `Encrypt Key` from the previous step, and click **Save**. On save, the system exchanges the `App ID` and `App Secret` for an access token once; a wrong value is reported right away. If you use Lark (the international version), also turn on the **Is Lark Version** toggle. When enabled, the system communicates with Lark international endpoints. The Lark Developer Console is at [open.larksuite.com](https://open.larksuite.com) — complete the app creation and permission configuration steps described above on that site. -After successful creation, you'll see the added Feishu/Lark integration in the list. Click its name to enter the details page to view **Web Configuration** URL, **Redirect URL**, and **Message Card Request URL**, which will be used in subsequent steps. +After successful creation, you'll see the added Feishu/Lark integration in the list. Click its name to open the details page: it gives you a numbered 5-step setup guide, each step offering the addresses or permission list to copy, and the numbers match "Finish setup in Feishu" below. ![Feishu/Lark: Add Feishu/Lark Integration screenshot](https://docs-cdn.flashcat.cloud/images/png/1e8ffb6c39f99ef12bd85ae49992ebad.png) -## Configure Feishu/Lark app +## Finish setup in Feishu + +**Finish setup in Feishu** on the details page splits the remaining work into 5 numbered steps, one per Feishu developer console page, numbered as in this section, with "N / 5 done" at the top right. Granting permissions and verifying the request URL happen on the Feishu side, so the page re-reads the state when you come back to it (or when you click **Check again**): Feishu verifies the request URL, so step 3 ticks itself off; Feishu never reports the app abilities, the redirect URLs, or the version release, so confirm those three by clicking the button once you have done them. Confirming needs edit permission on the integration; members without it only see the state. + + + + -### 1. Enable and configure app capabilities +### 1. Grant permissions + +Go to the **Permission Management** page, choose "Batch actions › Batch import/export permissions", paste the permission list copied from **Copy permission list (N)** in step 1 of the details page, click "Next, confirm new permissions", then confirm. The list is the JSON Feishu's batch import accepts, shaped like `{"scopes":{"tenant":[...],"user":[]}}`, and contains every permission in the table below. + +The table covers basic notifications, War Room, AI SRE conversations, and AI-generated post-mortems. Pay special attention to `im:message.p2p_msg:readonly` and `im:message.group_at_msg:readonly`; without them, Feishu/Lark will not push direct-chat messages or group @bot messages to Flashduty. After changing permissions or event subscriptions, publish a new app version for the changes to take effect in production. + +| Group | Official permission name | Purpose | +| :--- | :--- | :--- | +| `base` (required) | `im:chat` | Create and manage War Room chats, get chat information, and add chat members | +| `base` (required) | `im:message` | Read and send direct and group messages | +| `base` (required) | `im:message:send_as_bot` | Send AI SRE replies and War Room messages as the bot | +| `base` (required) | `im:message:update` | Update messages sent by the app for AI SRE streaming replies | +| `base` (required) | `im:resource` | Upload and download images, files, and other message resources | +| `base` (required) | `contact:user.id:readonly` | Get user IDs by phone number or email for automatic linking and member invitation | +| `war_room_history`, `ai_sre` | `im:message.group_msg` | Read group chat history; required for AI SRE context and AI-generated post-mortem reports | +| `ai_sre` | `im:message.p2p_msg:readonly` | Receive direct messages sent by users to the bot; without this permission, AI SRE cannot receive direct-chat questions | +| `ai_sre` | `im:message.group_at_msg:readonly` | Receive group messages that mention the bot; required for group-chat AI SRE conversations | +| `ai_sre` | `im:message.reactions:read` | Query message reactions for AI SRE processing status confirmation | +| `ai_sre` | `im:message.reactions:write_only` | Add or delete message reactions for AI SRE processing status confirmation | +| `ai_sre` | `cardkit:card:write` | Create and update CardKit cards for AI SRE streaming replies | +| `ai_sre` | `contact:contact.base:readonly` | Get basic contact information for reading user and department metadata | +| `ai_sre` | `contact:user.base:readonly` | Get basic user information for displaying sender names | + +What the three groups are for: `base` is required for alert notifications, card buttons, member linking, and War Room; `war_room_history` is only needed when a post-mortem imports the war room chat history; `ai_sre` is only needed to use AI SRE in Feishu. + +The platform checks this step for you: it is done once the whole **base** group is granted, shown as "All N basic permissions granted", and as "N basic permission(s) missing" while any is missing; grant the other two groups as needed, they do not affect the check mark. + +![Feishu/Lark: Request App Permissions screenshot](https://docs-cdn.flashcat.cloud/images/png/d919be62107f6b9d0c662f440d620e61.png) + +### 2. Add the web app and bot 1. Return to Feishu/Lark Developer Console, enter the Feishu/Lark app you just created, go to Add App Capabilities → **Add by Capability** page, and enable both **Web App** and **Bot** capabilities. ![Feishu/Lark: Enable and Configure App Capabilities screenshot](https://docs-cdn.flashcat.cloud/images/png/5ab84aec1593c7118782765676a51c6a.png) -2. Go to **Web App** page, configure `Desktop Homepage` and `Mobile Homepage`, both using the **Web Configuration** URL from integration details. See Feishu/Lark development documentation [Configure App Homepage URL](https://open.feishu.cn/document/uYjL24iN/uMTMuMTMuMTM/development-guide/step1#8366b844). +2. Go to **Web App** page, configure `Desktop Homepage` and `Mobile Homepage`, both using the address given in step 2 of the details page. See Feishu/Lark development documentation [Configure App Homepage URL](https://open.feishu.cn/document/uYjL24iN/uMTMuMTMuMTM/development-guide/step1#8366b844). ![Feishu/Lark: Enable and Configure App Capabilities screenshot](https://docs-cdn.flashcat.cloud/images/png/d91efc598bda17e1bfcb367aec47c779.png) -3. Go to Event Callbacks → **Event Configuration** page, configure `Subscription Method` (content is the **Message Card Request URL** from integration details). Then add the event `im.message.receive_v1`. +You can use the [Flashduty official icon](https://flashduty-public.oss-cn-beijing.aliyuncs.com/icons/flashduty-20251216.png) as the app icon and upload it on the **Credentials & Basic Info** page. Feishu does not report the app abilities back, so click **I've added them** in step 2 of the details page when you are done. - ![Feishu/Lark: Enable and Configure App Capabilities screenshot](https://docs-cdn.flashcat.cloud/images/png/71910d8af8d60b5f30baf009081646df.png) +### 3. Set up events and callbacks -4. Go to Event Callbacks → **Callback Configuration** page, configure `Subscription Method` (content is the **Message Card Request URL** from integration details). Then subscribe to the following two callbacks: +Events and callbacks share one request URL, the address given in step 3 of the details page. When you save the request URL, Feishu immediately calls it to verify it. + +1. Go to Event Callbacks → **Event Configuration** page, choose "Send events to the developer server" as the subscription method, paste the request URL, and save. Then add the event `im.message.receive_v1`. + + ![Feishu/Lark: Enable and Configure App Capabilities screenshot](https://docs-cdn.flashcat.cloud/images/png/71910d8af8d60b5f30baf009081646df.png) - - `card.action.trigger` - - `card.action.trigger_v1` +2. Switch to the **Callback Configuration** page, paste the same request URL and save, then add the callback `card.action.trigger`. ![Feishu/Lark: Enable and Configure App Capabilities screenshot](https://docs-cdn.flashcat.cloud/images/png/f58b20e52fc53f428bc493e18f0a567f.png) -### 2. Add redirect URL to Feishu/Lark app +This step ticks itself off once Feishu verifies the request URL (shown as **Verified**); until then it shows **Waiting for Feishu to verify**. If Feishu reports a failed request URL verification, the `Verification Token` or `Encrypt Key` entered when you created the integration most likely differs from the one on Feishu's **Encryption Strategy** page; correct it on the integration edit page and save again. -Go to **Security Settings** page, configure `Redirect URL`, content is the **Redirect URL** from integration details. +### 4. Set redirect URLs + +Go to the **Security Settings** page and add both addresses given in step 4 of the details page under `Redirect URL`: the web one (`…/login`) and the mobile one (`…/login`). Feishu does not report this configuration back, so click **I've added them** in step 4 of the details page when you are done. See Feishu/Lark development documentation [Configure Redirect URL](https://open.feishu.cn/document/uYjL24iN/uYjN3QjL2YzN04iN2cDN?lang=zh-CN#c863e533). ![Feishu/Lark: Add Redirect URL to Feishu/Lark App screenshot](https://docs-cdn.flashcat.cloud/images/png/00a7cdd10c09c90c2d7b2f0a99ee4d8d.png) - - - - -### 3. Request app permissions - -Go to the **Permission Management** page and request all permissions in the table below (they cover basic notifications, War Room, AI SRE conversations, and AI-generated post-mortems). Pay special attention to `im:message.p2p_msg:readonly` and `im:message.group_at_msg:readonly`; without them, Feishu/Lark will not push direct-chat messages or group @bot messages to Flashduty. After changing permissions or event subscriptions, publish a new app version for the changes to take effect in production. - -| Official permission name | Purpose | -| :--- | :--- | -| `im:chat` | Create and manage War Room chats, get chat information, and add chat members | -| `im:message` | Read and send direct and group messages | -| `im:message.p2p_msg:readonly` | Receive direct messages sent by users to the bot; without this permission, AI SRE cannot receive direct-chat questions | -| `im:message.group_at_msg:readonly` | Receive group messages that mention the bot; required for group-chat AI SRE conversations | -| `im:message:send_as_bot` | Send AI SRE replies and War Room messages as the bot | -| `im:message:update` | Update messages sent by the app for AI SRE streaming replies | -| `im:message.group_msg` | Read group chat history; required for AI SRE context and AI-generated post-mortem reports | -| `im:message.reactions:read` | Query message reactions for AI SRE processing status confirmation | -| `im:message.reactions:write_only` | Add or delete message reactions for AI SRE processing status confirmation | -| `im:resource` | Upload and download images, files, and other message resources | -| `cardkit:card:write` | Create and update CardKit cards for AI SRE streaming replies | -| `contact:user.id:readonly` | Get user IDs by phone number or email for automatic linking and member invitation | -| `contact:contact.base:readonly` | Get basic contact information for reading user and department metadata | -| `contact:user.base:readonly` | Get basic user information for displaying sender names | +### 5. Set visibility and publish -![Feishu/Lark: Request App Permissions screenshot](https://docs-cdn.flashcat.cloud/images/png/d919be62107f6b9d0c662f440d620e61.png) +Open the **Version Management & Release** page, create a version, set **Availability** to "all employees", and submit it for release (it takes effect after a tenant admin approves). The changes from steps 1–4 only take effect after you publish; every later change to permissions, events, or callbacks needs another published version. Click **I've published it** in step 5 of the details page when you are done. -## Publish and use app - -After completing all the above configurations, publish the app. It can be used after admin approval. See Feishu/Lark development documentation [App Publishing and Usage](https://open.feishu.cn/document/uYjL24iN/uMTMuMTMuMTM/development-guide/step-4). +See Feishu/Lark development documentation [App Publishing and Usage](https://open.feishu.cn/document/uYjL24iN/uMTMuMTMuMTM/development-guide/step-4). To ensure everyone can use the app, adjust the app **Visibility** to all employees before publishing. @@ -114,6 +181,14 @@ To ensure everyone can use the app, adjust the app **Visibility** to all employe ![Feishu/Lark: Publish and Use App screenshot](https://docs-cdn.flashcat.cloud/images/png/6bbc285986808af14c29d0eb633a2bf7.png) +## Switch to Flashduty-managed + +For an integration that entered credentials manually, the details page shows "Switch to Flashduty-managed so you no longer edit settings in the Feishu console" above the setup steps. Once you scan with the Feishu account of the app's owner or a collaborator, Flashduty takes the app over: the 5 steps above are no longer needed, and permissions that new features require are submitted to the Feishu admin automatically. The `App Secret` and the push address stay the same, so alert notifications are not interrupted. + +Lark international and private deployment integrations do not show this entry; keep maintaining them manually. + +## Access the app + After publishing, you can access the app on **Mobile** or **Desktop**. First access requires login to link Feishu/Lark and Flashduty accounts; subsequent access will be passwordless. On both mobile and desktop: Feishu/Lark → Workspace → Search app name → **Open App**. @@ -188,7 +263,7 @@ After exceeding API call limits, Feishu/Lark app cannot deliver messages properl -- Check again that the app has been granted the [required permissions](#request-permissions) listed in the Request app permissions step +- Check again that the app has been granted the [permissions](#request-permissions) listed in the Grant permissions step - See the **FAQ** section in [War Room documentation](/en/on-call/advanced/war-room) diff --git a/en/on-call/integration/instant-messaging/microsoft-teams.mdx b/en/on-call/integration/instant-messaging/microsoft-teams.mdx index 98f64e7e..78bc2e07 100644 --- a/en/on-call/integration/instant-messaging/microsoft-teams.mdx +++ b/en/on-call/integration/instant-messaging/microsoft-teams.mdx @@ -25,6 +25,16 @@ Select **Add**. Choose the destination that matches your use case: a team/channe In the commands below, replace `{ID}` with the Flashduty Microsoft Teams integration ID. Replace `{ChatName}` with the group chat name. +## Notification targets + +The top of the **Configuration** tab on the integration detail page is the **Notification targets** card: Flashduty sends incident notifications only to the teams and group chats listed here. The card has two tabs, **Teams** and **Group chats**, and the number after each tab label is how many targets are linked: + +- **Teams** lists each linked team with the channels the Flashduty app was read from in that team (shown as `Channels: General`). When no channel can be read, the row warns that the Flashduty app may have been removed from the team; select **Re-read** to bypass the cache and read that team again. +- Both tabs embed a three-step guide for adding a target: install the app in Teams and pick the destination (a team must use the General channel), send the link command in the target conversation, then select **Link Now** in the bot reply. +- **Unlink** on a row opens a confirmation dialog that states the team or group chat stops receiving incident notifications once unlinked; select **Confirm** in the dialog to actually unlink. The dialog also folds in "How do I remove the Flashduty bot after unlinking?". + +Below that card is the **Advanced features** card, which configures this integration's AI SRE capabilities. + ## Link team @@ -43,13 +53,15 @@ This step must select the target Team's General Channel, otherwise incidents can -In the Team's General channel, @Flashduty and send `linkTeam {ID}`, then select **Link Now**. +In the Team's General channel, type @Flashduty and send `linkTeam {ID}`, then select **Link Now** in the bot reply. You must type @Flashduty yourself and pick the bot from the popup list; a pasted @ is not a mention. + +Selecting **Link Now** brings you back to the integration detail page and completes the link automatically, so the team appears under **Notification targets** > **Teams**. ![Microsoft Teams: Send Link Command screenshot](https://docs-cdn.flashcat.cloud/images/png/3192b5481b0595fcb58e5cc43abad125.png) -To remove this Team's link, in the same channel send `@Flashduty unlinkTeam {ID}`. After you select **Unlink in FlashDuty** in the response card, Flashduty removes the link automatically and shows a success message. +To remove this Team's link, go to **Integration Center → Instant Messaging → Microsoft Teams → Configuration → Notification targets → Teams**, select **Unlink** on the team, and then select **Confirm** in the dialog. You can also send `@Flashduty unlinkTeam {ID}` in the same channel and follow the prompts on the response card. @@ -67,13 +79,15 @@ Add the app to the target Chat. -In the group chat, @Flashduty and send `linkChat {ID} {ChatName}`, then select **Link Now**. +First fill in **Group chat name** in the input below the step (it is only used to display the chat in Flashduty, and the command on the page can't be copied until it's filled in). Then, in the group chat, type @Flashduty and send `linkChat {ID} {ChatName}`, and select **Link Now** in the bot reply. You must type @Flashduty yourself and pick the bot from the popup list; a pasted @ is not a mention. + +Selecting **Link Now** brings you back to the integration detail page and completes the link automatically, so the group chat appears under **Notification targets** > **Group chats**. ![Microsoft Teams: Send Link Command screenshot](https://docs-cdn.flashcat.cloud/images/png/d0beee141db63714ccecb095affee79b.png) -To remove this group's link, send `@Flashduty unlinkChat {ID}` in the same group chat. After you select **Unlink in FlashDuty** in the response card, Flashduty removes the link automatically and shows a success message. +To remove this chat's link, go to **Integration Center → Instant Messaging → Microsoft Teams → Configuration → Notification targets → Group chats**, select **Unlink** on the group chat, and then select **Confirm** in the dialog. You can also send `@Flashduty unlinkChat {ID}` in the same group chat and follow the prompts on the response card.
@@ -104,7 +118,9 @@ Click **Open App**. -Send `linkUser {ID}` in your personal chat with Flashduty, then select **Link Now**. +Send `linkUser {ID}` in your personal chat with Flashduty, then select **Link Now** in the bot reply. + +Selecting **Link Now** brings you back to the integration detail page, landing on the **Linked Users** tab where the link completes. ![Microsoft Teams: Send Link Command screenshot](https://docs-cdn.flashcat.cloud/images/png/671ae7883bbba839419e539762db99de.png) @@ -126,10 +142,10 @@ Go to Integration Center → Instant Messaging → **Microsoft Teams**, check if -Go to Integration Center → Instant Messaging → **Microsoft Teams**, view in **Linked Teams** and **Linked Users**. +Go to Integration Center → Instant Messaging → **Microsoft Teams** → the **Configuration** tab, and view linked teams and group chats under **Notification targets** > **Teams** and **Notification targets** > **Group chats**. Users are listed on the **Linked Users** tab. -Use the matching command in its original Teams context: `unlinkTeam {ID}` in a Team channel, `unlinkChat {ID}` in a group chat, or `unlinkUser {ID}` in your personal chat. After you select **Unlink in FlashDuty** in the response card, Flashduty removes the matching link automatically. If your account is already linked, you can also select **Unlink me** under **Linked Users**, then select **Confirm** in the dialog. +Unlink teams and group chats on the **Configuration** tab under **Notification targets**: open the **Teams** or **Group chats** tab, select **Unlink** on the entry, and then select **Confirm** in the dialog. You can also use the matching command in its original Teams context: `unlinkTeam {ID}` in a Team channel, `unlinkChat {ID}` in a group chat, or `unlinkUser {ID}` in your personal chat, and follow the prompts on the response card. If your account is already linked, you can also select **Unlink me** under **Linked Users**, then select **Confirm** in the dialog. diff --git a/en/on-call/integration/instant-messaging/wecom.mdx b/en/on-call/integration/instant-messaging/wecom.mdx index 34dca3ba..d7352945 100644 --- a/en/on-call/integration/instant-messaging/wecom.mdx +++ b/en/on-call/integration/instant-messaging/wecom.mdx @@ -1,11 +1,11 @@ --- title: "WeCom" -description: "By integrating a WeCom third-party app, you can receive and respond to alerts within WeCom" +description: "By integrating a WeCom third-party app or a custom app, you can receive and respond to alerts within WeCom" --- **Plan requirement**: IM integration requires an On-call Pro or higher subscription. [Learn more](https://flashcat.cloud/flashduty/price/) -Choose one of two methods: [Third-Party App Integration](#third-party) or [Custom App Integration](#self). War Room and @ AI SRE in group chats are available only with a custom app. +Choose one of two methods: [Third-Party App Integration](#third-party) or [Custom App Integration](#self). When you create a WeCom integration, **App Version** defaults to **Custom app (recommended)**; the other option is **Third-party app (QR code)**. War Room and @ AI SRE in group chats are available only with a custom app.
@@ -25,6 +25,12 @@ Choose one of two methods: [Third-Party App Integration](#third-party) or [Custo As a WeCom service provider, Flashduty provides a long-term free version of the Flashduty app. This app requires WeCom API call authorization to use (passwordless login + message sending). This authorization currently supports **up to 60 days** free; beyond this usage period, Flashduty needs to purchase WeCom licenses for you before you can continue using it. +The third-party app mode is not available for private deployments. In this mode the enterprise ID is neither typed nor typeable: the enterprise an integration belongs to is set only by a QR bind session that WeCom vouches for. + +### Install the Flashduty app in WeCom + +If your enterprise has never installed the Flashduty app, install it in the admin console first; skip this section if it is already installed. + 1. Visit [WeCom Admin Console](https://work.weixin.qq.com/wework_admin/frame#apps), go to App Management → **Apps** page, click **Add Third-Party App**. ![WeCom: Third-Party App Integration screenshot](https://docs-cdn.flashcat.cloud/images/png/ae4d35a354aaf22dd41b7493200517bc.png) @@ -37,57 +43,179 @@ As a WeCom service provider, Flashduty provides a long-term free version of the ![WeCom: Third-Party App Integration screenshot](https://docs-cdn.flashcat.cloud/images/png/0821d1afdeb5db34c4c9b5548d5c8ca1.png) -4. Visit [WeCom Admin Console](https://work.weixin.qq.com/wework_admin/frame#apps), go to **My Enterprise** page, get `Corp ID`. + +You can skip installing beforehand: choose **Flashduty app not installed yet** when saving the integration, and Flashduty opens the WeCom authorization page where a WeCom admin installs it. + - ![WeCom: Third-Party App Integration screenshot](https://docs-cdn.flashcat.cloud/images/png/c032dc755a72550d57658dd5962dafe4.png) +### Bind the enterprise by QR code in Flashduty -5. Return to Flashduty On-call integration configuration page, fill in the `Corp ID` obtained in the previous step, click **Save** to complete integration. + + + Go to **Integration Center** → **New Integration**, choose **WeCom**, select **Third-party app (QR code)** as the **App Version**, and fill in **Name**. + + + Pick one of the two options under **Enterprise binding method**, depending on whether the enterprise has the app installed: + + | Option | When to choose it | What happens after saving | + | :--- | :--- | :--- | + | **Flashduty app not installed yet** (default) | The enterprise has never installed the Flashduty app | The WeCom authorization page opens; a WeCom admin scans to install the app, and the enterprise is bound once installation finishes | + | **Enterprise already installed the Flashduty app** | The app is already installed | Any member of that enterprise scans with the WeCom app to confirm; the enterprise is bound once the confirmation passes | + + + Click **Save**; a WeCom authorization dialog opens: + + - For "not installed yet", click **Open the WeCom authorization page** and complete the authorized install as a WeCom admin. If you are not the admin, send the link in the dialog to your admin; the dialog continues automatically once the admin finishes. + - For "already installed", scan the QR code with the WeCom app, or send the link to any member of that enterprise to open in WeCom. + + Once WeCom identifies the enterprise, the dialog shows "Enterprise xxx identified, waiting for you to click Confirm binding on the WeCom page…". On the **Confirm WeCom binding** page opened in WeCom, check the Flashduty account, the requester and the enterprise, then click **Confirm**. The console saves the integration automatically; you do not need to click Save again. + + + +After binding: + +- **Enterprise ID** becomes read-only and shows "Confirmed through WeCom, it cannot be changed". Switching enterprises requires scanning again; there is no field to edit the ID directly. +- A bind session is valid for 30 minutes and can be used once. After it expires the dialog says "The link has expired, please regenerate" — click **Regenerate** and scan again. +- When binding fails the dialog explains why: `This enterprise has not installed the Flashduty app yet. Close this and choose "Not installed yet" to complete the authorized install.` (the enterprise has no app but "already installed" was chosen), `Binding was cancelled on the WeCom page.` (Cancel was clicked on the confirmation page), `WeCom did not complete the confirmation, please regenerate and try again.` (anything else). +- Submitting an enterprise ID in the form alone no longer works: without a confirmed bind session, saving returns `scan with WeCom to bind your enterprise first`. ## Custom app integration -1. Visit [WeCom Admin Console](https://work.weixin.qq.com/wework_admin/frame#apps), go to App Management → **Apps** page, click **Create App**. +War Room and @ AI SRE in group chats work only in this mode, which is also the default **App Version** when you create a WeCom integration. + +### Create the integration and fill in the credentials + + + + Visit [WeCom Admin Console](https://work.weixin.qq.com/wework_admin/frame#apps), go to App Management → **Apps** page, click **Create App**. - ![WeCom: Custom App Integration screenshot](https://docs-cdn.flashcat.cloud/images/png/ed274f6a897b808678a5a29b23adcb66.png) + ![WeCom: Custom App Integration screenshot](https://docs-cdn.flashcat.cloud/images/png/ed274f6a897b808678a5a29b23adcb66.png) -2. Configure **App Logo**, **App Name**, and **App Visibility**. + Configure **App Logo**, **App Name**, and **App Visibility**. - ![WeCom: Custom App Integration screenshot](https://docs-cdn.flashcat.cloud/images/png/26ec124891e580a6d1e1035ba52636ba.png) + ![WeCom: Custom App Integration screenshot](https://docs-cdn.flashcat.cloud/images/png/26ec124891e580a6d1e1035ba52636ba.png) + + + Get `Enterprise ID` from the **My Enterprise** page and `AgentId` and `Secret` from the **App Details** page of the app you just created, then fill them in the Flashduty On-call integration configuration page. -3. Return to Flashduty On-call integration configuration page, select whether your WeCom is `Non-private deployment version` based on your actual situation. + If your WeCom is a private deployment, also fill in `Endpoint`. The Flashduty service must be able to reach that `Endpoint`; you can set up **whitelist authorization** for it. + + + **Access Domain** takes one domain only: no `https://`, no path, for example `flashduty.example.com`. - If your WeCom is a private deployment version, you need to fill in `Endpoint` in the configuration page. This address needs to be accessible by Flashduty services—you may consider setting up **whitelist authorization** for it. + Flashduty issues and renews the HTTPS certificate for this domain and proxies the traffic to it, so you do not need your own Nginx or your own certificate. WeCom requires the trusted domain and the callback URL to use this domain. -4. Visit [WeCom Admin Console](https://work.weixin.qq.com/wework_admin/frame#apps), go to **My Enterprise** page, get `Corp ID`, and fill it in the Flashduty On-call integration configuration page. + + A private deployment has no hosting step: the access domain is your own Flashduty domain, and WeCom must be able to reach it over the public internet. + + + + Click **Save**. The `Token` and `EncodingAESKey` for receive messages and for the smart robot are all generated by Flashduty; you do not fill them in when creating the integration, you generate them in the matching step on the detail page. -5. Return to [WeCom Admin Console](https://work.weixin.qq.com/wework_admin/frame#apps), go to **App Management** page, click the app you created to enter details page. Get `AgentId` from the page and fill it in the Flashduty On-call integration configuration page. + After saving, the **Complete the configuration in WeCom** section on the detail page walks you through the rest. + + -6. On the app details page, get `Secret` and fill it in the Flashduty On-call integration configuration page. +## Complete the configuration in WeCom -7. On the app details page, go to **Web Authorization & JS-SDK** and click **Set Trusted Domain**. The domain must point to Flashduty On-call's backend address `{api_host}` (with a CNAME or a proxy); see WeCom's [Enterprise Internal Development Domain Configuration Guide](https://open.work.weixin.qq.com/wwopen/common/readDocument/40754) for requirements. +The detail page lists six steps in this order, numbered the same as this section. Steps whose outcome WeCom reports are ticked automatically; for the steps nothing reports back, click the confirm button inside the step once you have done it, which requires permission to edit the integration. - ![WeCom: Custom App Integration screenshot](https://docs-cdn.flashcat.cloud/images/png/09a91d682198d1c8f830b5ed523965ef.png) + +A private deployment has no step 1 (the access domain is your own deployment address), so the later steps shift down by one, and the verification file goes to the document root of your own server. + - Return to Flashduty On-call integration configuration page, fill in this domain and complete verification. +### 1. Add the DNS records -8. On the app details page, go to **Receive Messages** page, and **Set API Receive**. Click **Random Generate** for `Token` and `EncodingAESKey` respectively, then copy and save the generated values. +Add the CNAME records shown in step 1 of the detail page at the DNS provider that hosts the access domain: - ![WeCom: Custom App Integration screenshot](https://docs-cdn.flashcat.cloud/images/png/919bd2722c75513ce1d301779b39c3bf.png) +| Purpose | Type | Host record | Value | +| :--- | :--- | :--- | :--- | +| Certificate hosting | CNAME | `_acme-challenge.` | The dns-01 delegation target shown on the page | +| Mainland China entry | CNAME | `` | `cn.wecom.flashduty.com` | +| International entry | CNAME | `` | `wecom.flashduty.com` | - Return to Flashduty On-call integration configuration page, fill in the saved `Token` and `EncodingAESKey`, click **Save** to complete integration. +Use the values shown on the page. The **certificate hosting** record proves the domain belongs to you, and Flashduty uses it to issue the HTTPS certificate through DNS-01 validation; the two **access entry** records route traffic to Flashduty, and one of them is enough. -9. Copy the `Callback URL` from Flashduty On-call integration details page, return to WeCom's **Receive Messages** page. In **API Receive** settings, fill in this `Callback URL` and the `Token` and `EncodingAESKey` saved in the previous step, then click **Save**. +Which entry to use: the international entry `wecom.flashduty.com` (Hong Kong) is the default and works for any domain; if the domain has completed ICP access filing on Alibaba Cloud, you can use the mainland entry `cn.wecom.flashduty.com` instead for faster access inside China; if your DNS provider supports region-based resolution, configure both. A domain without Alibaba Cloud ICP access filing that points at the mainland entry is blocked by Alibaba Cloud. - ![WeCom: Custom App Integration screenshot](https://docs-cdn.flashcat.cloud/images/png/c990c27f7ad90af172e159fc4acfead7.png) +Once the records resolve and the certificate is issued, step 1 is ticked automatically and shows "In effect · certificate issued". While records are missing it shows "N record(s) not in effect"; when every record resolves but the certificate is still being issued it shows "Issuing certificate". Click **Check again** to read the state again. -10. Configure a **Frontend Trusted Domain** pointing to Flashduty On-call's frontend address `console.flashcat.cloud` (with a CNAME or a proxy). After it is verified, set the generated **Homepage URL** as the WeCom app's **Workspace App Homepage**. + +If a status page or another Flashduty account already holds this domain, Flashduty cannot host it for this integration and step 1 shows "Domain already in use". To host it, edit the integration and use a different access domain; if the domain really is yours, contact Flashduty support. + + + +An older integration that proxies to Flashduty through a gateway you run yourself does not point at the entries above. When Flashduty finds that `https:///ping` answers `pong`, step 1 shows "Own proxy · reachable" and skips DNS records and certificate hosting; the remaining steps still apply, except that in step 2 the verification file goes to the document root of your own server. + - ![WeCom: Custom App Integration screenshot](https://docs-cdn.flashcat.cloud/images/png/595a71dd5624a37312676e83c45d79c4.png) +### 2. Set the trusted domain -11. Configure the **Trusted IP Address**: `47.93.12.134` (SaaS version; for a private deployment, enter the egress IP your own environment uses to reach WeCom). +On the app details page, go to **Developer Interface** → **Web Authorization & JS-SDK**, click **Set Trusted Domain**, and enter the access domain. See WeCom's [Enterprise Internal Development Domain Configuration Guide](https://open.work.weixin.qq.com/wwopen/common/readDocument/40754) for the requirements. - ![WeCom: Custom App Integration screenshot](https://docs-cdn.flashcat.cloud/images/png/fe3b2b788dda5d331148ba0946631b91.png) +![WeCom: Custom App Integration screenshot](https://docs-cdn.flashcat.cloud/images/png/09a91d682198d1c8f830b5ed523965ef.png) + +Then on that page click **Request domain verification**, download `WW_verify_xxx.txt`, and back in step 2 of the detail page click **Upload verification file** to upload it. WeCom fetches this file from the document root of the domain, and Flashduty serves it there (WeCom follows no redirect, so both http and https must serve it directly). + +Once WeCom has fetched it, step 2 is ticked automatically and shows "Verification file fetched by WeCom"; while waiting for WeCom to fetch an uploaded file it shows "Waiting for WeCom to fetch", and before uploading it shows "Waiting for the verification file". + +Back in WeCom, click **OK** to finish the verification. Fill the same domain into the OAuth2.0 web authorization callback domain on the same page. + + +WeCom requires the domain's ICP filing entity to be the same enterprise or a related one (parent/subsidiary, head office/branch, and so on). If it is not, WeCom refuses to save and you can only use another domain or ask WeCom support to accept proof of the relationship. + + + +For a private deployment, and for the older self-proxied integrations described above, put the verification file in the document root yourself: both http and https must serve it directly, with no redirect. Then click **I have verified** to record the step. + + +### 3. Set the API receive messages + +On the app details page, go to **Receive Messages** → **Set API Receive**, fill in the three items shown in step 3 of the detail page, and click **Save**: + +| Item | Value | +| :--- | :--- | +| `URL` | `https:///event/push/wecom/data?integration_key=` | +| `Token` | Generated in step 3 of the detail page by clicking **Generate and show** | +| `EncodingAESKey` | Same, 43 alphanumeric characters | + +WeCom calls the URL immediately when you save. Once the verification passes, step 3 is ticked automatically and shows "Verified"; until then it shows "Waiting for WeCom to verify". + + +Do not click **Random generate** in WeCom: use the `Token` and `EncodingAESKey` Flashduty generates, so both sides match. + + +`Token` and `EncodingAESKey` are shown only once and only a masked value remains after you leave the page, so copy them into WeCom right away. To replace them, click **Regenerate**: the pair already saved stops working immediately, so any old value still in WeCom must be replaced and saved, otherwise callbacks stop arriving; because this step records a fingerprint of the credentials, it goes back to "Waiting for WeCom to verify" and is ticked again only after WeCom verifies the new pair. + +![WeCom: Custom App Integration screenshot](https://docs-cdn.flashcat.cloud/images/png/919bd2722c75513ce1d301779b39c3bf.png) + +![WeCom: Custom App Integration screenshot](https://docs-cdn.flashcat.cloud/images/png/c990c27f7ad90af172e159fc4acfead7.png) + +### 4. Configure the trusted enterprise IP + +At the bottom of the app details page, click **Trusted Enterprise IP** to configure it and enter the IP shown in step 4 of the detail page. Every enterprise using Flashduty enters this same IP. + +WeCom only allows configuring the trusted IP after step 2 or step 3 is done. + +Step 4 is determined by Flashduty calling one WeCom API with the app's `access_token`: a successful call shows "In effect"; WeCom answering `60020` shows "Not configured yet", meaning the trusted IP is still missing; any other error code is shown as WeCom returned it; an unreachable WeCom shows "Check failed". Click **Check again** to read the state again. + +![WeCom: Custom App Integration screenshot](https://docs-cdn.flashcat.cloud/images/png/fe3b2b788dda5d331148ba0946631b91.png) + + +The IP shown on the detail page is the correct egress IP only while no private `Endpoint` is set (that is, while Flashduty proxies the WeCom calls). If WeCom reports "the following IP belongs to a third-party service provider", send a screenshot to Flashduty support. For a private deployment, enter the egress IP of your own Flashduty server. + + +### 5. Set the workspace app homepage + +On the app details page, click set on **Workspace App Homepage**, choose **Web page**, and enter the address shown in step 5 of the detail page. That address is an OAuth2 authorization link built from the access domain, with `redirect_uri` pointing at `https://?app_type=wecom_app...`: Flashduty forwards requests carrying `app_type=wecom_app` on the access domain to the console, which signs the member in with the OAuth code, so there is no separate "frontend trusted domain" to configure anymore. + +WeCom does not report this setting back, so click **I have set it** after configuring it. + +![WeCom: Custom App Integration screenshot](https://docs-cdn.flashcat.cloud/images/png/595a71dd5624a37312676e83c45d79c4.png) + +### 6. Create the smart robot (optional) + +An optional step for @mentioning the smart robot in single chats and group chats to use AI SRE; see [Configure the AI SRE smart robot](#bot) for the details. Step 6 gives `https:///event/push/wecom/bot?integration_key=`, and its `Token` and `EncodingAESKey` are likewise generated by Flashduty, shown once, and not interchangeable with the receive-message pair. The step is ticked automatically once WeCom verifies the URL. ## Linked users @@ -114,13 +242,15 @@ Configure the AI SRE switch under **Enhanced Features**. It is enabled by defaul | **Custom App Integration** | Supported | Supported | | **Third-party App** | Not supported; switch to **Custom App Integration** mode | Supported | + + ## Configure the AI SRE smart robot @mention the Smart Robot in a WeCom single chat or group chat to start an AI SRE investigation session without leaving WeCom. The Smart Robot can only be configured in **Custom App Integration** mode. - AI SRE must be enabled for your account, and **Allow AI SRE conversations in IM** must be on in the Flashduty WeCom integration (War Room is not required). Otherwise the robot does not respond to messages. -- Enter and verify the **Trusted Domain** on the integration configuration page first; the Smart Robot callback URL is generated only after that. +- The callback URL is built from the **Access Domain** saved in the integration, so save the integration and its access domain first as described in [Custom App Integration](#self); only then does step 6 of the detail page show a usable URL. @@ -134,16 +264,20 @@ The Smart Robot uses its **own set of credentials** (Token and Encoding-AESKey), Have an internal company member create it: even with visibility set to **all internal members**, others only see the robot after the creator shares it. - On the Smart Robot's **API configuration** page, set the connection method to **Use URL callback**. Enter the `URL` from the **Smart Robot Configuration** area of the Flashduty WeCom integration page. Its format is: + On the Smart Robot's **API configuration** page, set the connection method to **Use URL callback**. Enter the `URL` shown in step 6 of the Flashduty WeCom integration detail page. Its format is: ``` - https:///event/push/wecom/bot?integration_key= + https:///event/push/wecom/bot?integration_key= ``` - WeCom sends a GET request to this URL for verification; Flashduty answers it automatically. + WeCom sends a GET request to this URL for verification; Flashduty answers it automatically. Once the verification passes, step 6 of the detail page is ticked automatically. - - Generate the `Token` and `Encoding-AESKey` on the Smart Robot's API configuration page, then enter the same values in the **Smart Robot Configuration** area of the Flashduty integration page. Make sure both sides match exactly, then save both configurations. + + In step 6 of the detail page, click **Generate and show** to generate the `Token` and `EncodingAESKey`, and enter both values together with the URL above on the Smart Robot's **API configuration** page, then save. + + **Do not click "Random generate" in WeCom**: both sides must use the same pair Flashduty generated. + + The plaintext is shown only at generation time; afterwards only a masked value remains. Clicking **Regenerate** makes the previous pair stop working immediately: replace the old values in WeCom, save, and verify the callback URL again (the step is ticked again only after that verification passes). @@ -170,23 +304,23 @@ Once configured, **@mention the Smart Robot** in a WeCom single chat or group ch Check in order: 1. Confirm that AI SRE is enabled for your account and **Allow AI SRE conversations in IM** is on in the integration. 2. Confirm the `integration_key` in the callback URL exactly matches the value shown on the Flashduty integration details page. -3. Confirm the `Token` and `Encoding-AESKey` saved in Flashduty come from the Smart Robot's **API configuration** page, not from the custom app's **Receive Messages** settings. +3. Confirm the `Token` and `Encoding-AESKey` entered in WeCom are the pair generated in step 6 of the detail page (Smart Robot), not the pair generated in step 3 (Receive Messages). 4. Check the robot's callback records in WeCom (if available) to see whether requests arrive and whether any error codes are returned. -- Confirm that the Flashduty server is publicly reachable by WeCom servers and the Trusted Domain has been verified. -- Confirm that the `Token` and `Encoding-AESKey` exactly match what is saved in the Flashduty integration configuration. +- Confirm that the Flashduty server is publicly reachable by WeCom servers and that the access domain is reachable: with hosting, the DNS records from step 1 must resolve and the certificate must be issued. +- Confirm that the `Token` and `Encoding-AESKey` entered in WeCom exactly match the pair generated in step 6 of the detail page. - Confirm that the `integration_key` parameter in the callback URL is correct and that the corresponding integration is enabled. -This usually means the `Token` or `Encoding-AESKey` is wrong. The Smart Robot's credentials and the custom app's **Receive Messages** credentials are **two independent sets**. Regenerate them on the Smart Robot's **API configuration** page and update both values in the Flashduty integration configuration. +This usually means the `Token` or `Encoding-AESKey` is wrong. The Smart Robot's credentials and the custom app's **Receive Messages** credentials are **two independent sets**. Regenerate the matching set in step 6 (Smart Robot) or step 3 (Receive Messages) of the detail page, enter the new values in WeCom and save, then verify the callback URL again. - Check if you've completed the app installation steps. For example, can you see the Flashduty On-call app in WeCom Workspace -- Check if you've correctly configured `Corp ID` +- A third-party app's enterprise is set by a QR binding confirmation; scan again as described in [Third-Party App Integration](#third-party) diff --git a/en/on-call/integration/webhooks/custom-actions.mdx b/en/on-call/integration/webhooks/custom-actions.mdx index f2ed4488..b9b31f38 100644 --- a/en/on-call/integration/webhooks/custom-actions.mdx +++ b/en/on-call/integration/webhooks/custom-actions.mdx @@ -28,6 +28,19 @@ Disabling TLS certificate verification may expose you to man-in-the-middle attac Each channel can have a maximum of 5 custom actions. You can't select a channel that has reached this limit when adding a custom action. +## Edit a saved action + +Once saved, the Endpoint, the custom Headers values, and the custom request body are treated as secrets and are never shown back: the integration detail page shows a **Saved; hidden for security reasons** hint in place of the Endpoint, and the Headers and custom Payload values read as `******`. + +The edit form pre-fills those fields with `******`, and saving follows these rules: + +| What you submit | What gets saved | +| --- | --- | +| Leave the `******` mask untouched | The stored value is kept; it is never overwritten with the mask | +| Type a new value | The new value replaces the stored one | + +To change the Endpoint, a Header value, or the request body, you therefore have to retype it — the saved content can't be read back from the console. If you change the Endpoint while another secret field still holds `******`, the save is rejected and asks you to enter the saved secrets again, because those secrets would otherwise be sent to the new address. + ## Push description ### Request method diff --git a/en/on-call/statuspage/create-manage-page.mdx b/en/on-call/statuspage/create-manage-page.mdx index 3d8a578f..b00ad8bb 100644 --- a/en/on-call/statuspage/create-manage-page.mdx +++ b/en/on-call/statuspage/create-manage-page.mdx @@ -69,9 +69,15 @@ By default, status pages are accessed through a Flashduty-provided subdomain. To -Custom domains must be globally unique across the platform. If the domain is already in use by another status page, the system will reject the configuration. +Custom domains must be globally unique across the platform. A domain cannot be used by another status page, and it cannot be the access domain of a [self-built WeCom app](/en/on-call/integration/instant-messaging/wecom#self) claimed by any account. If the domain is already held that way, the system rejects the configuration, even when your own account is the holder. + +**The first claimant keeps the domain; a released domain can be claimed again** + +A domain belongs to its first claimant until the domain is released. After you delete the custom domain from a status page, or remove it from the WeCom self-built app integration, Flashduty reclaims the domain's certificate asynchronously. Until that cleanup finishes, the domain still counts as taken, so binding it to another status page or another account fails. Once cleanup finishes the domain is released, and any status page or account can claim it. + + **Switch from the legacy entry with no HTTPS interruption** diff --git a/en/platform/permission-design.mdx b/en/platform/permission-design.mdx index 795d9250..90fc8dfe 100644 --- a/en/platform/permission-design.mdx +++ b/en/platform/permission-design.mdx @@ -145,6 +145,7 @@ For creating and managing custom menus themselves, use **Platform Management → | Permission | Type | Description | | --- | --- | --- | | **Applications Manage** | Manage | Create, configure and manage frontend monitoring apps, set sampling rules and alert policies, and so on | + | **Dashboards Manage** | Manage | Create, edit and delete custom dashboards in Insights, and restore deleted ones from the recycle bin (kept for 30 days). See [Custom Dashboards](/en/rum/analytics/custom-dashboards) | | **Performance Read** | Read | View frontend performance monitoring issues, and so on | | **Error Tracking Read** | Read | View frontend issues, and so on | | **Session Explorer Read** | Read | View event details, and so on | @@ -204,7 +205,7 @@ In addition to preset roles, you can create custom roles for more granular permi | **Configuration** (Custom Fields, Schedules, Calendars, Templates, Mappings) | ✔️ | ✔️ | Read | | **Status Pages** | ✔️ | ✔️ | Read | | **Monitors** (Overview, Alerting Rules, Node Permissions, Datasources, Alerting Engines, Entity Tree) | ✔️ | ✔️ | Read | -| **RUM** (Applications, Performance, Error Tracking, Session Explorer, Session Replay) | ✔️ | ✔️ | Read | +| **RUM** (Applications, Dashboards, Performance, Error Tracking, Session Explorer, Session Replay) | ✔️ | ✔️ | Read | | **Onboarding** | ✔️ | ✔️ | | | **Custom Menu** (custom roles are granted per menu) | Automatic | Automatic | Automatic | diff --git a/en/rum/analytics/custom-dashboards.mdx b/en/rum/analytics/custom-dashboards.mdx new file mode 100644 index 00000000..a6e5f3a7 --- /dev/null +++ b/en/rum/analytics/custom-dashboards.mdx @@ -0,0 +1,183 @@ +--- +title: "RUM custom dashboards" +sidebarTitle: "Custom dashboards" +description: "Turn an Explorer analytics question into a Flashduty RUM custom dashboard: panel types, panel queries, dashboard variables, saving from the Explorer, table drill-down, version history and the trash." +keywords: ["RUM", "custom dashboards", "insights", "panels", "charts", "dashboard variables", "version history"] +--- + +## Overview + +A custom dashboard is an extra tab in the **Insights** tab bar, next to the built-in Overview, Performance, Errors and Resources tabs. Each dashboard holds panels (charts), and a panel's query is the Explorer's analytics question: event type + aggregation + group-by field + DQL filter. That makes a one-off analysis such as "which browsers do the errors come from" or "which page loads slowest" a dashboard you can keep coming back to, without rebuilding the query. + +## Entry points and routes + +| Where | Address | Notes | +|-------|---------|-------| +| Dashboard tab | `/rum/dashboard/custom/:id` | Opens one dashboard; it shows as a tab of Insights | +| Edit mode | `/rum/dashboard/custom/:id?edit=1` | Opens straight into editing; changes go into a browser draft first | +| New dashboard | `/rum/dashboard/custom/new?edit=1` | From the **+** menu at the right of the tab bar | +| Dashboard management | `/rum/dashboard/manage` | The dashboard list: search, delete, trash | +| Legacy addresses | `/rum/dashboards`, `/rum/dashboards/:id` | Redirect to the addresses above | + +The **+** menu at the right of the tab bar has two items: + +- **New dashboard**: opens an empty dashboard in edit mode. This needs the **Dashboards Manage** permission; without it, the item is not shown. +- **Manage dashboards**: opens the management page; viewing it needs no edit permission. + +The tab bar lists every dashboard by title, up to 100; beyond that, reach the rest from the management page. + +## Application and time range + +- A dashboard follows the **application** and **time range** in the top-right corner of the page. When you open a dashboard whose link carries no time range, the dashboard's own default time range applies; changing the time range on a dashboard also changes the page's time picker. +- Switching tabs carries the page's application and time range over. A dashboard's `env`, `service` and `version` variables and the matching filters of the built-in tabs are translated into each other; other variables are not carried across tabs. + +## Panel types + +The panel editor's **Visualization** offers four kinds: + +| Type | Description | +|------|-------------| +| **Time series** (`time_series`) | The measure over time, drawn as lines or bars | +| **Table** (`table`) | One row per group, with drill-down (see [Table drill-down](#table-drill-down)) | +| **Stat** (`stat`) | One key number, with optional threshold colors; a stat does not use a group-by | +| **Text** (`text`) | Markdown text, bound to no query | + +The dashboard definition also accepts **bar** (`bar`) and **top list** (`top_list`) panels, which a dashboard written through the API can contain, but the RUM console does not render those two charts yet: the type picker shows them as "Coming in a later release" and they cannot be selected. + +## Adding a panel + + + + Open the dashboard and click **Edit** in the top-right corner, or open a link with `?edit=1`. Edit mode is one browser draft submitted as a whole; a page refresh loses the draft, and leaving warns you first. + + + Click **Add panel** to open the panel editor. A dashboard organizes panels into tabs and sections; a new dashboard starts with one tab. + + + Pick the application (or **Page application**), the event type, the aggregation and measured field, the group-by field and the number of groups to keep, then write a DQL filter if you need one. The preview runs once the query is complete: after a picker change it reruns once the change settles, and a DQL filter runs on Enter or blur. Saving does not require a successful preview. + + + A title you leave empty is generated from the query, such as "P75 Time to First Byte · by Browser Name"; an empty description becomes the application name as the panel subtitle, which tells panels querying different applications apart. + + + Click **Apply** to put the panel back on the canvas, then **Save** to submit. You can fill in a **version message** (optional); each save produces a new version. + + + +## Panel queries + +A panel query has the same parts as the Explorer's analytics view: + +| Part | Description | +|------|-------------| +| **Application** | One application, or **Page application** (`$app`, so the panel follows the page's application) | +| **Event type** | Sessions, Views, Resources, Actions, Errors, Long Tasks, Vitals | +| **Aggregation / measured field** | Count, unique count, average, sum, min, max, P50 / P75 / P90 / P95 / P99; every aggregation except Count needs a field | +| **Group-by field / top** | Split by a field and keep the top `5`, `10`, `20` or `50` groups | +| **DQL filter** | The Explorer's search syntax, see [Data query syntax](../explorer/data-query) | + +Group-by fields are limited by field cardinality, exactly as in the Explorer (see [Analytics · Group-by field](../explorer/analytics#group-by-field)): a field that is an event's own ID cannot be grouped by, and any other high-cardinality field takes Count or Sum only, with a time range of at most 7 days. + +A panel's query is compiled in the browser and sent to `/rum/data/query`; when the result is estimated from a sample, the panel shows a **Sampled** badge and its explanation. + +## Dashboard variables + +The variable bar sits at the top of the dashboard. A new dashboard starts with three field variables, `env`, `service` and `version`, all defaulting to **All** (All means no condition). + +| Variable kind | Description | +|---------------|-------------| +| Built-in `$app` | The page's current application. Choosing **Page application** in a panel query is the same as using `$app`; only an application's `application_id` can reference it | +| Field variable (`rum_field`) | Candidates are the values of one RUM field; multi-select, All allowed | +| Custom variable (`custom`) | Candidates you type in (one per line, `label=value` or a bare value); single or multi-select | + +- Reference a variable in a panel's DQL as `${name}` or `$name`, such as `env:${env}`. A variable with several values expands to `(a OR b)`; with **All** selected, or with no value, the whole condition holding the reference (`env:${env}`, `-env:${env}`, `NOT env:${env}`, and a value list containing it) is dropped rather than queried as an empty value. +- References are replaced in the browser before the query runs, so a panel's DQL has to fill in into a complete statement to return data. +- A new panel's filter starts with the dashboard's field variables that its event type has. +- Renaming a variable rewrites the queries that reference it; a variable still referenced cannot be deleted until those references are cleaned up. +- Variable selections are kept in the link (parameters such as `var-env=...`), so copying the link shares the same selection. + + +A RUM dashboard has just these two kinds of custom variables: field variables that list the values of a RUM field, and custom variables with candidates you type in. Datasource variables and Prometheus / SQL / logs variable queries do not apply and never appear on a dashboard. + + +## Saving from the Explorer + +In the Explorer's analytics chart toolbar, **Save to dashboard** turns the current question into a dashboard panel: + +- The chart type can be **Time series / Table / Stat**; pies and distributions are not supported yet and the button says "Pies and distributions cannot be saved to a dashboard yet". +- You can **append to an existing dashboard** (its edit mode opens with this panel appended, and nothing is written before you save), or **create a new dashboard** (a dashboard holding just this panel is created first, then its edit mode opens). +- The panel keeps the current time range and filter and picks up the dashboard's `env` / `service` / `version` variables; its title is generated from the query. + +For the procedure and limits, see [Analytics · Saving to a dashboard](../explorer/analytics#saving-to-a-dashboard). + +## Table drill-down + +Every row of a **Table** panel carries an "Open in Explorer: `field = value`" link. Clicking it opens the Explorer's analytics view with the panel's query and aggregation, its filter narrowed to that row's group value, over the dashboard's current time range. + +- A table without a group-by has no drill-down link. +- When a group value contains characters that a DQL condition cannot express exactly, such as quotes or brackets, the link matches by wildcard instead, and its title is marked "(approximate wildcard match)". + +## Version history and restore + +**Version history** in the dashboard's **⋯** menu opens the version list: + +- Each save (deleting and restoring included) produces a new version; the most recent **20** versions are kept and older ones are pruned. +- The table lists the **version** (`v1`, `v2`, …), its **time**, the **actor** and the **message**, and marks the current version as **current**. While the dialog is open, tick two versions and click **Compare** to diff what each one saved. +- Restoring an earlier version saves that version's content as a new version, so the version you restored from stays in the history and you can restore back to it. + +## Deleting and the trash + +- **Delete** in the dashboard's **⋯** menu moves the dashboard to the trash; the confirmation names the dashboard and says it "will be moved to trash and purged after 30 days." +- **Trash** on the management page lists deleted dashboards with the note "Deleted dashboards are kept for 30 days and can be restored here."; the **restore** icon at the end of a row puts the dashboard back in the list and in the tab bar. +- A dashboard left in the trash for more than 30 days is purged and cannot be restored. + + +RUM dashboards have no folders: they are not grouped, and the management page has no folder tree or cross-folder move. The list can be searched by title. + + +## Permissions + +Creating, editing, deleting and restoring a dashboard needs the **Dashboards Manage** permission (`rumDashboard:manage`). Without it: + +- the **+** menu has no **New dashboard**, and the management page has no **New dashboard** button and no delete or restore entry; +- a dashboard has no **Edit**, and its **⋯** menu has no **Clone** or **Delete**; +- you can still view every dashboard, change its variables and time range, and inspect its content with **Export JSON** and **Version history**. + +## Limits + +| Item | Limit | +|------|-------| +| Tabs in a dashboard | 10 | +| Sections per tab | 10 | +| Panels per tab | 30 | +| Panels per dashboard | 100 | +| Queries per tab | 100 | +| Queries per dashboard | 300 | +| Variables | 20 | +| Candidates per variable | 1000 | +| Data links per panel | 5 | +| Queries in a time series / top list panel | 1–26 | +| Queries in a table / stat / bar panel | exactly 1 | +| Values in a top list panel | 1–100 | +| Title length | 189 characters | +| Description length | 1024 characters | +| Dashboard definition size | 1 MB (request body 2 MB) | +| Default time ranges | `now-15m`, `now-30m`, `now-1h`, `now-3h`, `now-6h`, `now-12h`, `now-24h`, `now-7d` | +| Auto refresh options | off, `30s`, `1m`, `5m` | + +## Next steps + + + + The four analysis dimensions of the built-in Insights dashboard + + + Aggregations, groups, chart types and drill-down + + + The search syntax behind a panel's filter + + + The list and analytics view modes + + diff --git a/en/rum/analytics/web.mdx b/en/rum/analytics/web.mdx index 0fdb4f53..8701a9c9 100644 --- a/en/rum/analytics/web.mdx +++ b/en/rum/analytics/web.mdx @@ -28,6 +28,8 @@ The Insights dashboard includes four main analysis dimensions: +Besides these four built-in dimensions, the Insights tab bar can also hold **custom dashboards**: your own boards built from the Explorer's analytics questions, see [Custom dashboards](./custom-dashboards). + ## Overview diff --git a/en/rum/error-tracking/error-viewing.mdx b/en/rum/error-tracking/error-viewing.mdx index aa93aaa0..d2b1268a 100644 --- a/en/rum/error-tracking/error-viewing.mdx +++ b/en/rum/error-tracking/error-viewing.mdx @@ -39,7 +39,7 @@ The Issue list can be **sorted by priority**, and the Priority filter is include ## Issue status -An Issue is For Review, Reviewed, Resolved, or Ignored. Resolved has two modes, "Resolved" and "Resolved in next version", which you switch in the status dropdown. For the transition rules, see [Issue status](./issue-status). +An Issue is For Review, Reviewed, Resolved, or Ignored. Resolved has two modes, "Reopen on recurrence" and "Reopen in a new version", which are submenu items under Resolved in the status dropdown. For the transition rules, see [Issue status](./issue-status). ## Filtering and sorting @@ -128,7 +128,7 @@ The system first classifies by error type and message. For example, a request wi ## Regression -A resolved Issue that occurs again reopens and is marked as a regression. With "Resolved", any recurrence reopens it, including in the same version; with "Resolved in next version", only a newer version does. See [Issue status](./issue-status#resolve-mode). +A resolved Issue that occurs again reopens and is marked as a regression. With "Reopen on recurrence", any recurrence reopens it, including in the same version; with "Reopen in a new version", only a newer version does. See [Issue status](./issue-status#resolve-mode). ## Issue details diff --git a/en/rum/error-tracking/issue-status.mdx b/en/rum/error-tracking/issue-status.mdx index 2bc2d6e5..90a5226e 100644 --- a/en/rum/error-tracking/issue-status.mdx +++ b/en/rum/error-tracking/issue-status.mdx @@ -12,7 +12,7 @@ In Error Tracking, all Issues have a status that helps you categorize and priori | **For Review** | New or regressed Issues that need attention | | **Reviewed** | Categorized Issues that need fixing, now or later | | **Ignored** | Issues that don't need further investigation or handling | -| **Resolved** | Issues that have been fixed and no longer occur. Resolved comes in two modes, **Resolved** and **Resolved in next version**, which differ in what happens when the error comes back (see [Resolve mode](#resolve-mode)) | +| **Resolved** | Issues that have been fixed and no longer occur. Resolved comes in two modes, **Reopen on recurrence** and **Reopen in a new version**, which are submenu items under Resolved and differ in what happens when the error comes back (see [Resolve mode](#resolve-mode)) | All newly discovered Issues have an initial status of **For Review**. Flashduty automatically updates status based on specific conditions, and you can also manually adjust it. @@ -48,8 +48,8 @@ Regression refers to a previously fixed problem appearing again, most often an u When an error is grouped into a **Resolved** Issue, whether it counts as a regression depends on the Issue's **resolve mode** (see [Resolve mode](#resolve-mode)): -- **Resolved in next version** (default): only a version the Issue has not seen before triggers detection -- **Resolved**: any recurrence triggers detection, including repeats in the same version +- **Reopen in a new version** (default): only a version the Issue has not seen before triggers detection +- **Reopen on recurrence**: any recurrence triggers detection, including repeats in the same version - An error without a `version` tag triggers detection in both modes - An error that occurred before the Issue's resolve time does not trigger detection (duplicate delivery, or an old error an SDK buffered offline and sent late) @@ -67,7 +67,7 @@ Once regression is detected, Flashduty will: ### Associated version configuration -Regression detection considers the service version information when the error occurred. With **Resolved in next version** (default), detection is triggered only in a version the Issue has not seen before, after it was marked **Resolved**. With **Resolved**, versions are not compared, so a recurrence in the same version also triggers detection. +Regression detection considers the service version information when the error occurred. With **Reopen in a new version** (default), detection is triggered only in a version the Issue has not seen before, after it was marked **Resolved**. With **Reopen on recurrence**, versions are not compared, so a recurrence in the same version also triggers detection. ```javascript window.FC_RUM.init({ @@ -85,17 +85,17 @@ Without a version tag, a resolved Issue that gets new errors is still marked "Re ## Resolve mode -In the status dropdown, **Resolved** is split into two options by what should happen when the error comes back. Both set the Issue to resolved: +Resolved is one status, but it comes in two resolve modes that differ in what should happen when the error comes back. They are not options next to Resolved but submenu items under it: the status dropdown has 4 items in all, and hovering **Resolved** expands two children, both of which set the Issue to resolved: -| Dropdown option | Behavior on recurrence | +| Submenu item | Behavior on recurrence | |------|------| -| **Resolved** | Reopens whenever it occurs again, including in the same version | -| **Resolved in next version** | Reopens only when it occurs in a version the Issue has not seen before | +| **Reopen on recurrence** | Applies to every version: a recurrence in the same version reopens it too | +| **Reopen in a new version** | Reopens only when it occurs in a version the Issue has not seen before; it also reopens when no version is reported | - You can switch between the two at any time in the status dropdown. Switching only the mode does not change the Issue's statistics or alert state. -- New Issues default to **Resolved in next version**, and so do Issues the system resolves automatically. +- New Issues default to **Reopen in a new version**, and so do Issues the system resolves automatically. - An error without a `version` tag reopens the Issue in both modes, because without version information old and fixed code cannot be told apart. -- On reopening, the Issue goes to **For Review**, is marked as a regression, and records when and in which version it regressed. The status cell in the list is narrow, so both modes show as **Resolved**; the details panel adds a "Resolved in next version" label next to it. +- On reopening, the Issue goes to **For Review**, is marked as a regression, and records when and in which version it regressed. The status label in the list is narrow, so both modes show as **Resolved**; the details panel adds a "Reopen in a new version" note next to it. ## Manually update status @@ -105,7 +105,7 @@ You can manually update Issue status anywhere Issues are displayed, including th Manually Update Status -Click the current status and select the new status from the dropdown menu. The dropdown has 5 options: **For Review**, **Reviewed**, **Resolved**, **Resolved in next version**, and **Ignored**. The two resolved options each show their reopen behavior beneath them; see [Resolve mode](#resolve-mode). +Click the current status and select the new status from the dropdown menu. The dropdown has 4 items: **For Review**, **Reviewed**, **Resolved** (a submenu with **Reopen on recurrence** and **Reopen in a new version**), and **Ignored**. Each submenu item shows its reopen behavior; see [Resolve mode](#resolve-mode). ## Best practices diff --git a/en/rum/explorer/analytics.mdx b/en/rum/explorer/analytics.mdx index 40f90a6c..1539e56a 100644 --- a/en/rum/explorer/analytics.mdx +++ b/en/rum/explorer/analytics.mdx @@ -39,6 +39,16 @@ In the legend, table and CSV, Count and Distribution measures read "Events"; oth The **Group-by field** splits the measure so each value is shown separately. Use **top** to keep the top `5`, `10` (default), `20` or `50` groups; without a group-by, the result is a single number. Array fields cannot be grouped by, and a **Distribution** does not use a group-by. +### Cardinality limits on the group-by field + +A group-by is also limited by field cardinality, because a high-cardinality field makes for an expensive grouping: + +- **A field that is an event's own ID cannot be grouped by**: `session_id`, `view_id`, `resource_id`, `action_id`, `error_id`, `long_task_id` and `vital_id` hold one value per event, so grouping by one puts every event in its own group; they do not appear in the group-by picker. **Unique count** still accepts them, to count sessions or views. +- **A high-cardinality field takes Count or Sum only**: URLs, users, error messages, stacks, IPs and `trace_id` are flagged as high-cardinality in the field list. Grouping by one of them leaves only **Count** and **Sum** in the **Aggregation** picker (picking such a field while another aggregation is selected switches it to Count), and the grouping is annotated "High-cardinality field: count and sum only, time range up to 7 days". +- Such a grouping covers a time range of at most **7 days**: with a longer range the chart returns no data and says "Grouping by a high-cardinality field covers at most 7 days. Shorten the time range". + +The same rules apply to a [custom dashboard](../analytics/custom-dashboards) panel: the server validates them again on save, with the errors `cannot group by an event ID` and `grouping by a high-cardinality field supports only count and sum`. + ## Chart types The analytics view offers five chart types: @@ -80,6 +90,14 @@ From the top-right corner of the chart: - **Download image**: exports a PNG at 2× resolution. Not available for tables or a single ungrouped number. - **CSV**: exports the chart data. A timeseries has one row per time, group and measure; other charts have one row per group. +### Saving to a dashboard + +**Save to dashboard** in the same row turns the current question into a [custom dashboard](../analytics/custom-dashboards) panel: + +- The chart type can be **Time series / Table / Stat**; **Pie** and **Distribution** cannot be saved yet and the button says "Pie and distribution charts can't be saved to a dashboard yet". An incomplete question (no measured field, for instance) says "Select a field to measure". +- You can **append to an existing dashboard** (its edit mode opens with this panel appended, and nothing is written before you save), or **create a new dashboard** (a dashboard holding just this panel is created first, then its edit mode opens). +- The panel keeps the current application, time range and filter, and picks up the dashboard's `env` / `service` / `version` variables; its title is generated from the query. + ## Distribution A **Distribution** buckets one numeric field by value range and shows the number of events in each range, without an aggregation or group-by. diff --git a/en/rum/explorer/data-query.mdx b/en/rum/explorer/data-query.mdx index 817068f9..234dd1e7 100644 --- a/en/rum/explorer/data-query.mdx +++ b/en/rum/explorer/data-query.mdx @@ -140,6 +140,22 @@ For numeric type attributes, comparison operators can be used: +## Path fields and URL normalization + +`view_url_path` and `view_url_path_group` (and the same-named path fields on session, error, resource, action and long task events) are derived by normalizing the page URL, and both can be filtered, grouped and charted on: + +| Raw URL | `view_url_path` | `view_url_path_group` | +|---------|-----------------|------------------------| +| `https://a.com/orders/123?tab=1` | `/orders/123` | `/orders/?` | +| `https://a.com/app/#/list?page=2` | `/app/#/list` | `/app#/list` | +| `https://a.com/docs/page#section-2` | `/docs/page` | `/docs/page` | + +- Query parameters (`?tab=1`, `?page=2`) do not enter either field. +- **A hash route stays in the path**: a fragment starting with `/` (how hash-mode SPA routers write routes) is appended to the path and the path group, so every page of such an application has its own value. Filter or group by page with the `#/...` part included, for example `view_url_path:/app/#/list`. +- A query inside the route is dropped (`#/list?page=2` is recorded as `#/list`), and a bare `#/` or a plain anchor such as `#section-2` is ignored. +- ID segments — numbers, UUIDs, object IDs, trace IDs, long hashes, dates — become `?` in `view_url_path_group`, so a detail page with one address per object falls into one group; `view_url_path` keeps the original value. +- Resource URLs do not follow these rules: `resource_url_path` takes the path only, and a `#...` on a resource address has always been dropped. + ## WeChat Mini Program-Specific attributes For View events collected from the WeChat Mini Program platform, the following queryable attributes are available in addition to the shared `view_*` fields. They help diagnose cold-start latency, `setData` cost, and lifecycle-stage durations: diff --git a/zh/ai-sre/im.mdx b/zh/ai-sre/im.mdx index c50f58e2..3fdea67d 100644 --- a/zh/ai-sre/im.mdx +++ b/zh/ai-sre/im.mdx @@ -113,6 +113,8 @@ Slack 除此之外还会显示一条原生的“正在处理”状态,与表 - **结论**:回答您的具体问题,或给出一个可行动的阶段性结论。 - **进展**:工作仍在进行时的一两句话更新,说明确认的进展、当前在等什么、遇到的真实阻塞。进展消息**不是结论**,不结束这轮任务,结论会照常单独发出。 +当 AI SRE 把工作交给后台子代理(Subagent 或 A2A 委派)、而这一轮还没有向聊天发过任何消息时,它会先发一条简短的**受理说明**——说明派了什么、结论会在子代理回报时补上——不会让群里一直静默到子代理跑完。这条同样属于进展消息,不结束本轮。 + 这套回复方式只作用于**由人发起的 IM 请求**:控制台会话仍按流式文本作答;作战室自动诊断是无人值守的后台回合,也保持流式输出。 diff --git a/zh/ai-sre/sessions.mdx b/zh/ai-sre/sessions.mdx index 333e8071..873f25dd 100644 --- a/zh/ai-sre/sessions.mdx +++ b/zh/ai-sre/sessions.mdx @@ -271,6 +271,20 @@ IM 和 API 会话不显示草稿卡,Agent 会在回复开头给出“审阅并 压缩时对话中会显示进度,完成后聊天页头部的 **Context** 占用百分比会下降(悬停可查看 token 用量)。 +摘要会改写原话,但压缩前的原文没有丢:被摘要替换掉的消息与工具结果仍按当时返回的样子完整保存。当排查依赖某个**精确值**(ID、主机名、指标值、阈值、日志行、报错原文、命令)时,Agent 会调用内置工具 `session_transcript` 把这部分原文写进执行环境的 `.history//` 目录,再用 `grep` / `read` 检索,而不是照着摘要的措辞复述;原文里确实没有时,它会明说,不会猜。 + +| 文件 | 内容 | +| --- | --- | +| `seg_NNN.md` | 被第 N 次摘要替换掉的那段原文 | +| `tail.md` | 最近一次摘要之后的全部内容(始终存在,可能为空) | +| `index.md` | 每个文件覆盖的条数、起止时间与校验哈希 | + +记录的是**当时交给 Agent 的内容**:图片与附件只留下 `[inline …]` / `[file …]` 占位,没有原文;被截断的大输出只保留完整结果的 `file_path`;子代理的内部步骤在它自己的会话里,不写进这些文件。 + + +原文召回只对**携带该工具**的 Agent 生效:主会话与拥有完整工具的 `general` 子代理会在摘要后拿到召回提示;像只读的 `explore` 这类受限子代理没有该工具,压缩后只能依靠摘要。 + + ## 选择运行环境 新建会话时,可以在输入区选择 **运行环境**,决定 Agent 的工具、Skill 和 MCP 在哪里执行:自动(默认)、云端沙箱或一台在线的自托管 BYOC Runner。选择顺序、离线处理,以及“会话创建后环境固定”的规则,见 [运行环境 · 在会话中选择环境](/zh/ai-sre/environments#在会话中选择环境)。 diff --git a/zh/ai-sre/skills.mdx b/zh/ai-sre/skills.mdx index a25a9a98..cf3bf11e 100644 --- a/zh/ai-sre/skills.mdx +++ b/zh/ai-sre/skills.mdx @@ -216,7 +216,7 @@ frontmatter 字段如下: 工具的写法有两种: -- **内置工具**:直接写工具名。可用的内置工具包括 `read`、`write`、`edit`、`bash`、`grep`、`glob`、`skill`、`mcp`、`todo`、`time`、`webfetch`、`web_search` 等。 +- **内置工具**:直接写工具名。可用的内置工具包括 `read`、`write`、`edit`、`bash`、`grep`、`glob`、`skill`、`mcp`、`todo`、`time`、`webfetch`、`web_search`、`session_transcript` 等。 - **MCP 工具**:写成 `mcp:服务名/工具名`(如 `mcp:my-server/query`)。上传时只校验该 MCP 服务是否存在,具体工具名在会话中加载 MCP 时才会被验证。 ## 相关页面 diff --git a/zh/changelog/changelog.mdx b/zh/changelog/changelog.mdx index b85314d3..9c1cd5d1 100644 --- a/zh/changelog/changelog.mdx +++ b/zh/changelog/changelog.mdx @@ -4,6 +4,32 @@ description: "本页面记录 Flashduty 产品的重要更新和功能发布" keywords: ["更新日志", "产品发布", "功能更新", "Flashduty", "版本记录"] --- + + +### RUM 自定义看板:在分析看板里自己搭看板 + +分析看板(Insights)的页签栏不再只有内置看板:页签栏右上角的 **+** 菜单提供 **新建看板**(需「看板管理」权限)与 **管理看板**,自建看板作为额外页签打开,最多列出 100 个页签;聚合分析图表工具栏的 **保存到看板** 也能把当前问题直接加成整宽面板。面板问的就是查看器里的聚合分析问题——应用(或内置变量 `$app`,跟随页面当前应用)、事件类型、统计方式与统计字段、分组与取前 N,外加 DQL 过滤,可画时序图、表格、单值和文本面板。看板变量支持字段取值与自定义两类,在面板 DQL 里用 `${name}` 引用,新看板默认带 `env` / `service` / `version` 三个字段变量。每次保存留下一个版本(保留最近 20 个),删除的看板进回收站保留 30 天,可在管理页恢复。详见 [RUM 自定义看板](/zh/rum/analytics/custom-dashboards)。 + +### 通知模板详情页改版与「发送测试」 + +模板详情页右侧的预览按内容块渲染:原生表格与 Markdown 表格分开计数,底部给出该渠道的格式与「已用 / 上限」字节数;渠道不渲染 Markdown 表格或内容超过渠道上限时,预览上方直接给出黄色提示(发送会失败、会被截断,或改发精简摘要)。系统预置模板与没有编辑权限的成员进入 **系统预置 · 只读** 状态,主按钮换成 **复制为新模板**;可编辑时原来的 **编辑** 改为 **模板设置**,删除收进 **更多操作**。 + +预览标题栏新增 **发送测试**:把编辑器当前内容(含未保存的修改)按所选故障渲染后真实发一次——IM 应用可发到自己的私聊,或机器人已加入的群(企业微信应用没有群聊选项),机器人临时填地址,邮件发到当前账号邮箱,短信与语音不提供。测试消息标题带 `[测试]` 前缀、末尾注明模板与发送人,卡片按钮点了只回「这是测试消息,按钮不会改动故障」;每人每分钟最多 5 条。模板语法错误时真实通知会静默回退默认模板,测试发送则直接报错。详见[通知模板](/zh/on-call/configuration/templates)。 + +### 飞书应用:「表格转原生表格」 + +通知模板新增开关 **表格转原生表格**(`feishu_app_card_v2_table_enabled`,默认关闭):开启后模板里的 GFM Markdown 表格渲染成飞书卡片 JSON 2.0 的原生表格组件,接收方看到的是表格组件而不是 Markdown 原文。一张卡片最多 5 张原生表格,每张最多 8 列、100 行、每页 10 行、单元格 2000 字节;超出任一条,或卡片本身超过 30 KB 时,整份内容的表格回退成 Markdown 表格发送,预览会说明原因。除飞书应用与企业微信机器人的 markdown_v2 之外,其它通道不渲染 Markdown 表格,预览会提示接收方看到的只是表格原文。详见[通知模板](/zh/on-call/configuration/templates)。 + +### 企业微信自建应用:访问域名由 Flashduty 托管 + +集成企业自建应用只需要填一个必填的 **访问域名**(只填域名本身,且须是本企业备案主体的域名)。保存后详情页第 1 步给出 DNS 记录:一条「证书托管」的 dns-01 委派,证明域名归你们所有,由 Flashduty 据此签发并自动续期 HTTPS 证书;一条访问入口 CNAME,大陆入口指向 `cn.wecom.flashduty.com`、国际入口指向 `wecom.flashduty.com`,配其中一条即可,之后由 Flashduty 直接反代,不用自己搭 Nginx 或申请证书。第 2 步在企业微信「设置可信域名 → 申请校验域名」下载 `WW_verify_xxx.txt` 后上传到详情页,由 Flashduty 在该域名根路径返回(企业微信不跟随重定向,http 与 https 都要能取)。域名已被状态页或其他 Flashduty 账户占用时不能托管;私有化部署没有第 1 步。详见[企业微信](/zh/on-call/integration/instant-messaging/wecom)。 + +### 飞书集成:扫码创建由 Flashduty 托管的应用 + +新建飞书集成时表单给出三选一:**创建新应用(推荐)** / **使用已有应用** / **改为手动填写凭证**。选前两者保存后弹出飞书二维码,扫码后 Flashduty 在你的飞书企业里创建(或接管)应用并完成全部配置——事件与回调的加密策略和请求地址、机器人与网页应用能力、已启用功能需要的权限,然后发布一个新版本;凭证由 Flashduty 保管,详情页只显示 App ID、当前版本与状态。飞书要求管理员审批两次(先审应用,再审配置版本),详情页与集成列表据此显示「待管理员审核(1/2)」「待管理员审核配置(2/2)」「新版本审核中」等状态,每 10 秒自动核对,也可以 **复制消息给管理员**。手动填凭证接入的老集成可在详情页点 **升级为自动管理** 扫码升级,App Secret 与推送地址不变、通知不中断;私有化部署连不到飞书扫码接口,只能手动填写凭证。详见[飞书/Lark](/zh/on-call/integration/instant-messaging/lark)。 + + + ### AI SRE 自动化新增「执行位置」:规则可绑定到发起它的 IM 会话 @@ -12,12 +38,12 @@ keywords: ["更新日志", "产品发布", "功能更新", "Flashduty", "版本 IM 规则的每一次运行都在原来的群聊里新开一个会话,结论也回帖到该群。如果执行时这个群聊的范围或会话已经变了(例如用过 `/scope`、`/new`),本次运行会被 **跳过**(原因 `chat_scope_mismatch`),规则保持启用。规则列表新增 **执行位置** 一列。详见 [AI SRE 自动化](/zh/ai-sre/automations#执行位置规则跑在哪里)。 -### RUM Issue 新增「解决方式」:已解决 / 在下一版本中解决 +### RUM Issue 新增「解决方式」:再次出现时重新打开 / 新版本出现时重新打开 -Issue 状态下拉里的 **已解决** 与 **在下一版本中解决** 是同一个「已解决」状态按复现处理方式拆出的两个选项: +Issue 状态下拉里的 **再次出现时重新打开** 与 **新版本出现时重新打开** 是同一个「已解决」状态按复现处理方式拆出的两个选项: -- **已解决**:该错误之后再出现,即使还是同一个版本,也会把 Issue 重开为待处理并标记 **问题复现**; -- **在下一版本中解决**(默认):只有 Issue 之前没见过的新版本里再次出现才重开,同一版本里的重复出现不会重开。 +- **再次出现时重新打开**:该错误之后再出现,即使还是同一个版本,也会把 Issue 重开为待处理并标记 **问题复现**; +- **新版本出现时重新打开**(默认):只有 Issue 之前没见过的新版本里再次出现才重开,同一版本里的重复出现不会重开。 两个选项可随时互相切换。详见[Issue 状态管理](/zh/rum/error-tracking/issue-status)。 @@ -98,7 +124,7 @@ CLI 新增全局参数 `--fields`:`json` / `toon` 输出时只保留逗号分 ### 仪表盘:全局搜索、面板查询方式与模板校验 -仪表盘列表页右上角新增 **全局搜索**:跨当前账号所有可读目录搜仪表盘(权限由后端过滤),按标题或描述匹配、多个关键词用空格分隔,结果带目录路径以便区分同名仪表盘,每页 20 条、失败可重试;它的搜索状态只活在弹窗里,与列表页「搜索当前目录」互不影响,也不写进页面地址。 +仪表盘列表页右上角新增 **全局搜索**:跨当前账号所有可读目录搜仪表盘(权限由后端过滤),按标题或描述匹配、多个关键词用空格分隔,结果带目录路径以便区分同名仪表盘,每页 20 条、失败可重试;它的搜索状态只活在这个就地展开的搜索框内,关闭即丢弃,与列表页「搜索当前目录」互不影响,也不写进页面地址。 面板编辑器的查询 **选项** 里,Loki 的 **表格** 与 **单值** 面板现在可以自己选 **查询方式**:**区间** 按步长逐点计算画趋势、**即时** 整个时间范围算一个值(`[$__auto]` 取整段范围),表格默认即时、单值默认区间;Loki 时序仍固定区间,Loki 即时不接受 **最小步长**。查询类模板变量按数据源给出对应表单:MySQL 写 SQL、Loki 填 stream selector 与 label 名、其它数据源用 Prometheus 标签。查询表达式还会在编辑器里提前指出三类保存时会被拒绝的写法——多选或「可选全部」的变量写在精确匹配里、引用了未定义的变量、内置变量在当前数据源与查询方式下不可用;精确匹配那条可以一键 **改为正则匹配**。**预览候选** 也改为带上当前已选的变量值与面板时间范围去取数,预览结果与看板实际渲染一致。详见[仪表盘](/zh/monitors/dashboards/dashboards)。 diff --git a/zh/developer/cli.mdx b/zh/developer/cli.mdx index 42471294..fef395e1 100644 --- a/zh/developer/cli.mdx +++ b/zh/developer/cli.mdx @@ -805,7 +805,7 @@ flashduty insight incident-export [flags] # 导出筛选后的故障列表为 - **On-call 与日程**:calendar、schedule - **平台管理**:account、member、person、team、role(roles-permissions)、audit(audit-logs) - **监控与 RUM**:monit、rum、sourcemap -- **集成与 Webhook**:datasource(IM 集成)、integration(告警源 / 变更源集成的增删改查与密钥轮换)、webhook(integrations) +- **集成与 Webhook**:datasource(IM 集成)、integration(告警源 / 变更源集成的增删改查与密钥轮换,密钥掩码与更新规则见下文「integration — 集成与密钥」)、webhook(integrations) 这些生成命令的叶子名称采用「资源-动作」形式(如 `flashduty safari a2a-agent-get`、`flashduty safari session-list`),每个公开 API 操作都对应一条 `flashduty <分组> <动作>` 命令,命令名由 API 路径推导,请求字段生成带类型的命令行参数,`--data` 可传完整 JSON 请求体(具名参数覆盖其中的同名字段)。完整清单不在本页展开,用 `flashduty <资源> --help` 逐层探索: @@ -872,6 +872,40 @@ flashduty safari artifact-gallery-share-sync - `artifact-sign` 的签名 token 绑定调用账户与成员,有效期 5 分钟(响应 `expires_in`);`download_url` / `preview_url` 是相对路径(`/safari/artifact/stream?...`),使用时需拼接 API base(`https://api.flashcat.cloud`)。 - 产物初始作用域继承来源会话(个人会话的产物归创建者个人,绑定团队的会话的产物归团队);`can_edit` 决定调用者能否重命名/转移/删除/分享(创建者、所属团队成员或源会话管理者)。 +#### integration — 集成与密钥 + +`integration` 命令族管理账户级的公开集成——告警源、变更源、IM 机器人与自定义 Webhook,共 9 个子命令:`list`、`info `、`type-list`、`create`、`update `、`delete `、`enable `、`disable `、`key-rotate `。渠道专属的集成(`ref_id` 以 `c_` 开头)不在此列,只在控制台配置。 + +```bash +flashduty integration type-list --category event.alert # 列出账户可配置的集成类型(只有 supports_api_create: true 的类型能用 create 创建) +flashduty integration list --status enabled # 列出集成(可按 --category、--plugin-type、--status、--team-ids、--name、--ref-ids 过滤) +flashduty integration info # 查看单个集成,含 settings 与 integration_key +flashduty integration create --plugin-type standard.alert --name "生产告警" --is-test --data '{"settings":{}}' +flashduty integration update --name "生产告警(主)" +flashduty integration key-rotate # 签发新密钥,旧密钥立即失效 +flashduty integration disable # 暂停接收事件,保留配置 +flashduty integration enable # 重新启用 +flashduty integration delete # 删除;仍被其他对象引用时以 ReferenceExist 拒绝 +``` + +创建与更新的可用字段: + +| 参数 | 说明 | +|------|------| +| `--plugin-type` | 集成类型(`create` 必填),取值来自 `integration type-list` 中 `supports_api_create: true` 的条目,如 `standard.alert`、`zabbix.alert` | +| `--name` | 集成名称,2–49 字符;创建时省略或传空则使用集成类型名 | +| `--description` | 备注,最多 499 字符 | +| `--team-id` | 归属团队 ID;省略或 `0` 表示账户级,非 0 时必须属于当前账户。更新时显式传 `0` 会清除团队归属 | +| `--integration-key` | 仅 `create` 可用:可选的自定义密钥,长度 35 字符,必须以「账户 ID 对 1000 取模后补零到三位」的数字结尾,且不得与其他集成重复;省略则自动生成 | +| `--is-test` | 执行插件的连通性测试。创建时省略即不测试;更新时只在确实提交了 `settings` 的调用中测试,只改名称/备注不会发测试。结果看响应的 `test.success` 与 `test.message`:HTTP 调用成功不等于测试通过,响应中没有 `test` 表示本次没有执行测试 | +| `--data` | 完整 JSON 请求体;类型专属配置 `settings` 只能通过它传入,可接受的键由 `plugin_type` 决定 | + +`settings` 的读回与密钥语义(与上文 `monit` 数据源的处理一致): + +- `list` 与 `info` 返回的 `settings` 中,每个**已保存的密钥类值**——密码、App Secret、Token、加密密钥、Webhook 地址、HTTP 拉取的 URL 与 Body、Headers、自定义 Body——一律显示为 `******`;未设置的值保持为空。**`integration_key` 是例外,按原值返回**(它用于接入推送,与账户级 `app_key` 不是一回事)。依赖 `integration info --json` 读取凭据的脚本拿到的是掩码;需要新密钥时用 `key-rotate` 轮换,旧密钥会立即失效。 +- `update` 提交的 `settings` 是整体替换:**已知敏感字段被省略或传 `******` 时保留已存密钥**,显式传空 `headers: {}` 会清空 Headers。但**改密钥的发送目标时不能沿用已存密钥**——例如改 `endpoint`、`url`,或改数据库的主机/端口——必须同时传入真实密钥,否则请求被拒。 +- `editable` 只表示对象权限(`no_editable: true` 表示该集成只读);写操作除此之外还需要**集成管理**权限。 + ### 工具命令 ```bash diff --git a/zh/monitors/alert-rules/mysql.mdx b/zh/monitors/alert-rules/mysql.mdx index 1dc09bb9..04f5dd39 100644 --- a/zh/monitors/alert-rules/mysql.mdx +++ b/zh/monitors/alert-rules/mysql.mdx @@ -14,7 +14,13 @@ Monitors 支持使用标准 SQL 语法对 MySQL 进行查询,并根据查询 | **字段处理** | 所有字段名自动转换为小写,配置时请使用小写字母 | | **时间处理** | 建议使用 `now()`、`unix_timestamp()` 等函数进行时间过滤 | -关联查询和恢复查询支持 `${label_name}` 变量和 Go 模板 `{{ $labels.<标签名> }}`,需告警引擎 `v0.76.0` 及以上。告警引擎会在变量替换后检查 SQL 是否为只读查询,不满足时本次查询失败。主查询不支持变量。 +关联查询和恢复查询支持 `${label_name}` 变量和 Go 模板 `{{ $labels.<标签名> }}`。主查询不支持变量,包含 `${...}` 的主查询在保存时会被拒绝,提示 `variables are only supported in relate and recovery queries`。 + + +变量需要告警引擎 `v0.76.0` 或以上版本;服务该规则数据源的集群内有实例低于此版本时,保存或启用会被拒绝,错误提示以 `MySQL relate or recovery queries with variables require` 开头并指明集群与所需版本。不使用变量的规则不受该版本限制。 + + +变量替换后的 SQL 到运行期才确定,保存时无法预检。告警引擎按告警实例渲染变量,再检查渲染结果是否为只读查询,不是纯只读 SELECT 时该次评估失败。 ## 阈值判定模式 @@ -46,7 +52,7 @@ Monitors 按标签字段区分告警对象,并使用值字段进行阈值判 ### 恢复逻辑 -恢复方式见[告警恢复](/zh/monitors/quickstart/quickstart#告警恢复)。恢复查询是独立 SQL,支持 `${label_name}` 变量,也支持 Go 模板 `{{ $labels.<标签名> }}`(需告警引擎 `v0.76.0` 及以上)。 +恢复方式见[告警恢复](/zh/monitors/quickstart/quickstart#告警恢复)。恢复查询是独立 SQL,支持 `${label_name}` 变量,也支持 Go 模板 `{{ $labels.<标签名> }}`。这类规则同样受上文版本限制:集群内有实例低于 `v0.76.0` 时,保存或启用会被拒绝。 ## 数据存在模式 diff --git a/zh/monitors/engine/self-monitoring.mdx b/zh/monitors/engine/self-monitoring.mdx new file mode 100644 index 00000000..88f09f0a --- /dev/null +++ b/zh/monitors/engine/self-monitoring.mdx @@ -0,0 +1,134 @@ +--- +title: "引擎自监控指标" +description: "采集告警引擎暴露的 Prometheus 指标,监控引擎与中心的连接、规则同步和规则评估健康度" +keywords: ["告警引擎", "monitedge", "自监控", "Prometheus 指标", "引擎健康检查"] +--- + +告警引擎(`monitedge`)在本地暴露一组 Prometheus 指标,覆盖三项会静默停掉监控的故障:与中心的连接断了、规则同步卡住了、规则一直在评估失败。把它们采集到您自己的时序库,就可以在控制台之外持续判断这台引擎是否还在正常工作。 + +这些指标不在控制台展示,也没有对应的菜单入口,只能从引擎的 `/metrics` 读取。 + +## 采集方式 + +`/metrics` 与引擎的 `http.listen` 是同一个端口,默认 `0.0.0.0:6868`,不需要认证。用您自己的 Prometheus 或 VictoriaMetrics 直接抓取该地址即可: + +```yaml +scrape_configs: + - job_name: monit-edge + static_configs: + - targets: ["10.0.0.1:6868"] +``` + +Flashduty 不会代为抓取这些指标。控制台的**告警引擎状态**页展示的是实例清单、心跳与版本,不含这些指标;指标只存在于您自己的时序库中。 + + +**不要让被监控的这台引擎评估它自己的指标告警。** 引擎进程退出、租约不处于 `ACTIVE` 或与中心断连时,跑在这台引擎上的规则既不进行评估也发不出事件。引擎是否存活请用中心侧的[引擎失联告警](/zh/monitors/engine/engine-lost-alert)兜底;下文这些指标告警适合放在您自己的 Prometheus/Alertmanager 上,或另一个引擎集群上评估。 + + +## 与中心的连接 + +| 指标 | 类型 | 标签 | 说明 | +|------|------|------|------| +| `monit_edge_websocket_connected` | Gauge | — | 当前是否持有到中心的 WebSocket 连接,1 为在线、0 为不在线。控制台的查询预览、实体运行状态都走这条连接 | +| `monit_edge_websocket_connects_total` | Counter | — | 拨号成功的累计次数,含首次连接。持续增长说明连接在反复重建 | +| `monit_edge_websocket_dial_failures_total` | Counter | — | 拨号失败的累计次数。它一直涨而 `connects_total` 不涨,就是连不上中心(网络、代理或中心拒绝) | +| `monit_edge_lease_info` | Gauge | `session_mode`、`phase` | 恒为 1,用 `phase` 读租约阶段:`BOOT`、`INITIALIZING`、`ACTIVE`、`FENCED`。只有 `ACTIVE` 阶段才评估规则并投递事件;`session_mode` 目前恒为 `lease_v1` | +| `monit_edge_lease_renewal_age_seconds` | Gauge | — | 距上一次心跳续租成功过去的秒数。心跳默认每 10 秒一次,可用 `alerter.heartbeatInterval` 调整;租约有效期由中心下发 | +| `monit_edge_webapi_access_blocked` | Gauge | — | 1 表示中心因授权或 license 原因永久拒绝,该进程已停止与中心通信,恢复授权后需要重启引擎 | + +```promql +# 连接断了 5 分钟以上 +monit_edge_websocket_connected == 0 + +# 租约不在 ACTIVE 超过 2 分钟:引擎既不评估也不投递 +(sum(monit_edge_lease_info{phase="ACTIVE"}) or vector(0)) == 0 + +# 最近 10 分钟内出现过拨号失败 +increase(monit_edge_websocket_dial_failures_total[10m]) > 0 +``` + + +以 `-alerter.webSocketDisable` 启动的引擎不会建立 WebSocket 连接,`monit_edge_websocket_connected` 恒为 0,`connects_total` 也不增长。这类实例上不要用 `monit_edge_websocket_connected` 判断连接健康度;租约相关指标不受该参数影响。 + + +## 规则同步 + +引擎默认每 10 秒从中心同步一次规则。 + +| 指标 | 类型 | 标签 | 说明 | +|------|------|------|------| +| `monit_edge_alert_rule_sync_age_seconds` | Gauge | — | 距上一次同步成功过去的秒数 | +| `monit_edge_alert_rule_sync_status` | Gauge | `outcome` | 最近一次同步的结果,值恒为 1,只在当前结果的 `outcome` 上出现一条:`success`、`error`,或 `pending`(还没同步过) | +| `monit_edge_alert_rule_sync_rules` | Gauge | — | 最近一次同步成功时中心分配给这台引擎的规则数。它掉到 0 或远小于预期时,先确认规则是否被停用、数据源绑定或实例分片是否变化 | + +```promql +# 同步停滞 3 分钟以上:中心新建或修改的规则不会生效 +monit_edge_alert_rule_sync_age_seconds > 300 + +# 最近一次同步失败 +monit_edge_alert_rule_sync_status{outcome="error"} == 1 +``` + +## 规则评估 + +| 指标 | 类型 | 标签 | 说明 | +|------|------|------|------| +| `monit_edge_alert_rule_evaluations_total` | Counter | `outcome`、`error_type` | 评估轮次累计。`outcome` 为 `success` 或 `error`;成功轮次的 `error_type` 是空字符串,失败轮次的取值见下表 | +| `monit_edge_alert_rule_evaluation_duration_seconds_total` | Counter | — | 评估耗时累计(秒),除以 `evaluations_total` 的 `success` 与 `error` 之和,得到平均每轮评估耗时 | +| `monit_edge_alert_rule_scopes` | Gauge | `datasource_id`、`state` | 按数据源统计每条“规则 × 数据源”作用域的最新状态,每个数据源固定三条:`ok`、`failing`、`pending`(尚未完成首轮评估) | +| `monit_edge_alert_rule_failing_duration_seconds` | Gauge | `datasource_id` | 该数据源上持续失败最久的那条作用域已失败的秒数;没有失败作用域时为 0 | + +失败轮次的 `error_type` 取值是固定集合,可以直接按它分组做告警: + +| `error_type` | 含义 | +|------|------| +| `datasource_unavailable` | 拿不到该数据源的连接,这一轮评估没有执行 | +| `query_failed` | 查询语句本身报错,如语法、权限或超时 | +| `result_invalid` | 查询返回的数据无法映射成规则需要的字段 | +| `threshold_failed` | 阈值判定这一步出错 | +| `anydata_failed` | 数据存在判定这一步出错 | +| `nodata_failed` | 数据缺失判定这一步出错 | +| `recovery_failed` | 恢复查询或恢复判定出错,这一轮不产生恢复事件 | + +```promql +# 某个数据源上的规则持续失败 5 分钟以上 +max by (datasource_id) (monit_edge_alert_rule_failing_duration_seconds) > 300 + +# 最近 15 分钟内按错误类型分布的失败轮次 +sum by (error_type) (increase(monit_edge_alert_rule_evaluations_total{outcome="error"}[15m])) > 0 +``` + + +`monit_edge_alert_rule_failing_duration_seconds` 涨起来时,对照 `monit_edge_alert_rule_scopes{state="failing"}` 看范围:同一数据源下大量作用域同时失败,通常是这个数据源不可用;只有个别作用域失败,问题在那条规则的查询或阈值配置上。单条规则的执行状态和错误摘要见控制台的**运行详情**,说明见[规则运行详情](/zh/monitors/quickstart/quickstart)。 + + +## 查询并发与版本 + +| 指标 | 类型 | 标签 | 说明 | +|------|------|------|------| +| `monit_edge_alert_query_admission` | Gauge | `state` | 普通告警与实体告警共用的查询并发准入:`waiting` 是排队等许可的查询数,`in_flight` 是正在执行的查询数 | +| `monit_edge_alert_query_admission_rejected_total` | Counter | `reason` | 被准入边界拒绝的累计数:`timeout` 是排队超时、这一轮评估被跳过,`canceled` 是调用方已取消 | +| `monit_edge_build_info` | Gauge | `version` | 恒为 1,`version` 是当前运行的引擎版本。Linux 自动升级是就地替换进程,PID 与 `process_start_time_seconds` 都不变,版本变化只能从这条指标看出来 | + +```promql +# 最近 10 分钟内有查询因排队超时被跳过 +sum(increase(monit_edge_alert_query_admission_rejected_total{reason="timeout"}[10m])) > 0 +``` + +## 推荐告警 + +下表按“指标 + 持续条件”给出可以直接复制的规则。多实例部署时请保留 `instance` 或您自己的实例标签,避免不同实例的同名序列互相覆盖。 + +| 告警 | PromQL | 持续 | 处置 | +|------|--------|------|------| +| WebSocket 断连 | `monit_edge_websocket_connected == 0` | 5 分钟 | 控制台无法通过该引擎查询;检查到中心的网络与代理,用 `dial_failures_total` 的增长确认是否在反复重连 | +| 租约不在 ACTIVE | `(sum(monit_edge_lease_info{phase="ACTIVE"}) or vector(0)) == 0` | 2 分钟 | 引擎既不评估也不投递;看 `monit_edge_lease_renewal_age_seconds` 与心跳 | +| 中心永久拒绝 | `monit_edge_webapi_access_blocked == 1` | 1 次 | 授权或 license 问题,恢复后重启引擎 | +| 规则同步停滞 | `monit_edge_alert_rule_sync_age_seconds > 300` | 3 分钟 | 中心新建或修改的规则不生效;检查到中心的网络 | +| 规则同步失败 | `monit_edge_alert_rule_sync_status{outcome="error"} == 1` | 3 分钟 | 同上,并结合引擎日志看同步报错 | +| 规则持续评估失败 | `max by (datasource_id) (monit_edge_alert_rule_failing_duration_seconds) > 300` | 1 次 | 对照 `monit_edge_alert_rule_scopes{state="failing"}` 判断是数据源不可用还是单条规则查询有问题 | +| 查询准入排队超时 | `sum(increase(monit_edge_alert_query_admission_rejected_total{reason="timeout"}[10m])) > 0` | 3 分钟 | 某个数据源变慢占满了查询并发,这一轮评估被跳过 | + + +实体树告警走另一组指标(`monit_edge_entity_alert_*`),覆盖实体发现、条件查询和实体事件投递;本文只覆盖普通告警规则。 + diff --git a/zh/monitors/quickstart/quickstart.mdx b/zh/monitors/quickstart/quickstart.mdx index bbe57fb3..34b2b1d8 100644 --- a/zh/monitors/quickstart/quickstart.mdx +++ b/zh/monitors/quickstart/quickstart.mdx @@ -101,6 +101,7 @@ Go 模板可以使用以下变量: - 同一个值不能同时使用 `${...}` 和 `{{...}}`。 - 渲染失败的标签不会推送,告警事件照常推送,错误原因写入 `__alert_rule_label_template_error` 标签,可在告警详情中查看。 - 标签名首尾的空白会在保存时去掉,去掉后不能重复;标签名不能以 `__alert_rule_` 开头。全部附加标签按 JSON 计合计不超过 1024 个字符,`job` 和 `instance` 的值不超过 255 个字符。 +- 模板标签的渲染结果另受两条限制,超出都按渲染失败处理(标签不推送,原因写入 `__alert_rule_label_template_error`):单个标签渲染后不超过 8 KiB;`job`、`instance` 的渲染结果不超过 255 个字符。渲染值到运行期才产生,因此这两种情况不会在保存时被拒绝。 - 告警引擎渲染备注描述、自定义字段和标题时,`$appendLabels` 中的模板标签仍是模板原文。需要在这些位置使用查询结果时,请直接引用 `$annotations`。 ### 数据源与查询 diff --git a/zh/on-call/configuration/custom-forms.mdx b/zh/on-call/configuration/custom-forms.mdx index 0b2593fd..53dc1a0d 100644 --- a/zh/on-call/configuration/custom-forms.mdx +++ b/zh/on-call/configuration/custom-forms.mdx @@ -47,6 +47,12 @@ keywords: ["自定义表单", "故障创建", "故障认领", "故障关闭", " 如果某个操作没有配置表单元素,或当前故障和用户不满足表单元素的显示条件,系统会直接执行原有的创建、认领或关闭流程,不会额外要求填写内容。 +## 在 IM 里关闭故障 + +配置了 **关闭故障** 表单后,用户无法在 IM 故障卡片上直接完成关闭:点关闭不会生效,机器人会回复一条引导消息,说明该故障需要填写关闭表单,并附一个 **前往填写关闭表单** 链接。该链接直达这条故障的详情页并自动打开关闭表单,填写提交后才完成关闭。 + +飞书、钉钉、企业微信、Slack 和 Microsoft Teams 都支持这项引导。飞书另外做了两处处理:引导消息以临时消息的形式发给点击者,同群其他人看不到;同时在卡片上弹一条 toast,提示通过引导消息中的链接填写表单。 + ## 批量认领或关闭 在故障列表中批量 **认领** 或 **关闭** 多条故障时: diff --git a/zh/on-call/configuration/templates.mdx b/zh/on-call/configuration/templates.mdx index f71ba0ac..304c55c1 100644 --- a/zh/on-call/configuration/templates.mdx +++ b/zh/on-call/configuration/templates.mdx @@ -44,6 +44,12 @@ keywords: ["通知模板", "模板配置", "消息格式", "模板变量", "通 + +**系统预置模板与只读**:系统预置模板(渲染示例、默认模板)和当前成员没有模板管理权限的模板,详情页一律是只读状态,标题旁标有 **系统预置 · 只读**(权限不足的模板不显示这个标识,但同样不能改)。要改成自己的内容,点右上角 **复制为新模板** 复制一份副本再改,副本名称预填「复制 原模板名」;复制需要「通知模板管理」权限和 On-call 专业版。 + +可编辑的模板,右上角是 **模板设置**(齿轮图标),用来改模板名称、管理团队和模板描述,不涉及正文;删除收在 **更多操作** ⋯ 菜单里,系统预置模板没有这个菜单,也不能删除。 + + ### 应用模板 @@ -158,6 +164,98 @@ keywords: ["通知模板", "模板配置", "消息格式", "模板变量", "通 +## 预览与发送测试 + +模板详情页右侧是预览区:按所选故障渲染当前渠道的内容,下方带一行诊断信息和发送引擎给出的警告。预览区标题栏还有 **发送测试**,可以把编辑器里的当前内容真实发到渠道里看效果。 + +### 预览会提示什么 + +预览按 **用这个故障的数据渲染** 里选中的故障渲染当前渠道的内容;没选故障时用系统默认数据。编辑器内容为空时,预览按该渠道的系统默认模板渲染,并提示「未设置模板内容,将使用系统默认模板」。 + +预览下方一行元信息说明这条内容是怎么渲染出来的: + +| 栏位 | 说明 | +| :--- | :--- | +| 渠道格式 | 飞书应用显示 **飞书卡片 2.0**;企业微信机器人显示 **Markdown** 或 **Markdown v2**;其他渠道不显示 | +| 原生表格 N / Markdown 表格 N | 这次渲染里有几张表画成原生表格、几张画成 Markdown 表格;为 0 时不显示 | +| 已用 / 上限 | 内容字节数和该渠道的大小上限,如 `12.3 KB / 30.0 KB`;渠道没有已知上限时只显示已用字节数 | + +元信息上方的黄色提示来自发送引擎,出现任意一条就说明这条模板在真实发送时会变形或超限,保存前按提示精简: + +| 提示 | 含义 | +| :--- | :--- | +| `table_not_rendered` | 该渠道不渲染 Markdown 表格,接收方看到的是一行行带 `\|` 的原文,建议改写成列表 | +| `native_table_fallback` | 飞书应用开了 **表格转原生表格**,但这次有表格达不到原生表格的条件,会按 Markdown 表格发送 | +| `content_too_large` | 内容超过渠道大小上限 | + +`native_table_fallback` 的原因有 6 种:`too_many_tables`、`too_many_columns`、`too_many_rows`、`cell_too_long`、`unsupported_cell`、`card_too_large`,含义见[表格转原生表格](#表格转原生表格)。 + +`content_too_large` 的处理分 3 种: + +| 处理 | 含义 | +| :--- | :--- | +| `rejected` | 渠道直接拒收,这条通知发不出去,真实告警也一样,必须精简模板 | +| `truncated` | 超出上限的部分被截断,通知仍会发出,各渠道的具体上限见下文 | +| `compact_fallback` | 企业微信机器人 Markdown v2 超过 4096 字节时改发精简摘要,不丢通知 | + +### 发送测试 + +**发送测试** 在预览区标题栏。它把编辑器当前内容(**含未保存的修改**)按预览里选中的故障渲染后,真实发到渠道里,用来确认接收方看到的样子。需要「通知模板管理」权限;系统预置模板也可以发送测试。模板校验没通过时按钮不可点。 + +支持的渠道:5 个 IM 应用(飞书、钉钉、企业微信、Slack、Teams)、6 个群聊机器人(飞书、钉钉、企业微信、Slack、Telegram、Zoom)和邮件。短信和语音不提供发送测试——它们按条计费,而预览内容就等于实际发出的内容。 + +发送内容与预览是同一份:编辑器当前内容 + 选中的故障数据(未选故障时用系统默认数据),渠道渲染开关(卡片字段显隐、飞书卡片 2.0 的表格与空白行开关、企业微信机器人的 Markdown v2)也一致。编辑器内容为空时,发的是该渠道的系统默认模板。 + +各渠道的发送目标: + +| 渠道 | 发送目标 | 说明 | +| :--- | :--- | :--- | +| IM 应用 | 我自己 / 群聊 | 企业微信应用只有 **我自己**:企业微信没有「列出机器人所在的群」的接口,群消息走企业微信机器人模板 | +| 群聊机器人 | 临时填机器人地址 | 与通知规则里填的地址相同,裸 token 或完整地址都行;Telegram 另填 Chat ID,Zoom 另填 Verify Token。这些值只用于这一次发送,不会保存 | +| 邮件 | 自己的账号邮箱 | 账号没有邮箱时不能发送 | + +- **我自己**:要求当前账号已关联该 IM 集成,未关联时发送按钮不可点,有权限的成员可以点 **去关联** 到接入向导完成关联。Slack 应用例外,不需要逐人关联(按工作区成员直接私信),但发送时仍要把你解析成该工作区的成员,解析不到会返回 `im_not_linked`。 +- **群聊**:只列出该机器人在这个集成里**已加入**的群(Slack 按频道,Teams 按团队下的频道或群聊)。群里所有成员都会收到这条测试消息。 +- 同一渠道配了多个集成时,先在 **集成** 里选一个;只列已启用的集成。 + +测试消息的标记: + +- 故障标题前加 `[测试] `(英文账户为 `[Test] `)。 +- 正文末尾加脚注「测试消息 · 模板「模板名」· 由 发送人 发送」。 +- 卡片上的按钮点击后只回一句「这是测试消息,按钮不会改动故障。」,不会认领或关闭任何故障;**创建作战室** 按钮在测试消息里不出现。 + +发送结果有两种: + +| 结果 | 渠道 | 提示 | +| :--- | :--- | :--- | +| `sent` | IM 应用 | 「已发送到你的飞书私聊」/「已发送到「群名」」 | +| `queued` | 群聊机器人与邮件 | 「已提交发送,通常几秒内送达;没收到请检查地址」 | + +IM 应用是同步调用平台接口,成功即 `sent`;机器人和邮件进通知队列异步投递,`queued` 只表示已提交,地址填错时不会有即时报错。 + +限制与常见失败原因: + +- 每个成员每分钟最多发 5 条测试消息,超过返回 `rate_limited`。 +- 失败时控制台按错误码给出提示: + +| 错误码 | 含义 | +| :--- | :--- | +| `template_invalid` | 模板渲染失败(语法或执行出错),先修正模板 | +| `channel_not_supported` | 该渠道不支持发送测试 | +| `invalid_target` | 发送目标不完整或格式不对:机器人没填地址、Telegram 缺 Chat ID、Zoom 缺 Verify Token、群聊不属于该集成 | +| `integration_not_found` | 找不到这个集成,可能已被删除或停用,或不是该渠道的集成 | +| `im_not_linked` | 你的账号没有关联这个 IM,无法私信自己 | +| `rate_limited` | 发送太频繁 | +| `webhook_url_blocked` | 机器人地址只支持公网 `http`/`https`;填完整地址时内网地址会被拒绝。裸 token 由平台拼成厂商地址,不做这项检查 | +| `content_too_large` | 内容超过渠道大小上限,例如飞书卡片 30 KB | +| `send_failed` | 渠道返回失败,提示里带渠道的原始报错 | + + +测试发送与真实通知的差别:模板语法有错时,真实通知会静默回退成系统默认模板继续发,而发送测试直接报 `template_invalid`,把模板问题暴露出来。反过来,测试发送不写故障通知记录、不占发送锁、不进通知流水,所以它不会认领、关闭故障或创建作战室。 + +控制台调用的是 `POST /template/preview/send`,请求带 `template_id`、`type`、编辑器当前 `content`、`incident_id` 和渠道渲染开关,返回 `status`(`sent` / `queued`)或 `error_code` + `message`。 + + ## 渠道模板 不同通知渠道支持的模板格式和限制不同。 @@ -214,6 +312,33 @@ keywords: ["通知模板", "模板配置", "消息格式", "模板变量", "通 ``` 飞书应用卡片底部的 **更多操作** 是一个下拉菜单,包含自定义操作、暂缓时长与取消认领等选项;**👥 创建作战室** 按钮单独显示,不在该菜单中。 + +### 表格转原生表格 + +**飞书卡片 2.0** 下另有一个开关 **表格转原生表格**(默认关闭,随模板保存)。开启后,模板里的 GFM Markdown 表格会被识别出来,渲染成飞书卡片的原生表格组件,接收方看到的是可翻页的表格,而不是一行行带 `|` 的文本。 + +原生表格是有限额的,任一条件不满足时,**这张卡片上的所有表格**一起退回 Markdown 文本发送: + +| 限制 | 上限 | +| :--- | :--- | +| 原生表格数量 | 单张卡片最多 5 个 | +| 每表列数 | 最多 8 列 | +| 每表行数 | 最多 100 行 | +| 单元格内容 | 每格最多 2000 字节 | +| 每页行数 | 每页显示 10 行,超出部分在卡片里翻页 | + +触发退回的原因(本页[预览会提示什么](#预览会提示什么)里以 `native_table_fallback` 警告显示): + +| 原因 | 触发条件 | +| :--- | :--- | +| `too_many_tables` | 这张卡片上的原生表格超过 5 个 | +| `too_many_columns` | 某张表超过 8 列 | +| `too_many_rows` | 某张表超过 100 行 | +| `cell_too_long` | 某个单元格超过 2000 字节 | +| `unsupported_cell` | 单元格里除纯文本和 `
` 换行外还有其它 Markdown 标记,表头有空单元格,或某行的列数与表头不一致 | +| `card_too_large` | 用原生表格后卡片会超过飞书的大小上限 | + +飞书卡片整体上限是 **30 KB**。超过时飞书直接拒收这条消息,预览会给 `content_too_large` + `rejected`,需要精简模板后再发。 @@ -320,6 +445,10 @@ keywords: ["通知模板", "模板配置", "消息格式", "模板变量", "通 群聊机器人支持的模板格式。 + +除飞书应用(卡片 2.0,开启 **表格转原生表格** 后走原生表格)和企业微信机器人的 **Markdown v2** 之外,各渠道都不渲染 Markdown 表格:钉钉、Slack、Telegram 机器人、企业微信机器人(Markdown v1),以及钉钉、Slack、企业微信、Teams 应用,都会把表格按带 `|` 的原文发出去,预览里也会给出提示。发给这些渠道的模板建议把表格改写成列表。 + + 支持消息卡片、富文本和普通文本三种格式。消息最大 4000 字节,超出后截断。 diff --git a/zh/on-call/integration/instant-messaging/lark.mdx b/zh/on-call/integration/instant-messaging/lark.mdx index 8ecb4d7d..33ef19f7 100644 --- a/zh/on-call/integration/instant-messaging/lark.mdx +++ b/zh/on-call/integration/instant-messaging/lark.mdx @@ -14,8 +14,60 @@ keywords: ["飞书", "Lark", "即时消息", "告警通知", "IM集成"] */}
+## 选择接入方式 + +新建飞书集成时有三种接入方式,区别在于飞书应用由谁来建、后续配置由谁做: + +| 接入方式 | 适用场景 | 需要做什么 | +| :--- | :--- | :--- | +| **创建新应用(推荐)** | 大多数情况。Flashduty 在你的飞书企业里创建应用并完成全部配置,不需要打开飞书开发者后台 | 用飞书扫码确认,飞书管理员审批两次 | +| **使用已有应用** | 企业里已经有这个应用(例如同事先建过),或想继续用原来的应用 | 用应用所有者或协作者的飞书账号扫码授权,飞书管理员审批 | +| **手动填写凭证** | 飞书私有化部署、Lark 国际版,或不愿把应用交给 Flashduty 托管 | 在 Flashduty 填凭证,再在飞书开发者后台逐项配置 | + +私有化部署的 Flashduty 连不到飞书扫码接口,只能用 **手动填写凭证**。 + +### 扫码创建并自动托管(推荐) + +1. 新建飞书集成时,**飞书应用授权** 默认选中 **创建新应用(推荐)**。填好名称后点击 **保存**,页面弹出二维码。 + +2. 用飞书扫一扫确认:飞书确认页会列出要申请的权限,点 **创建** 即可。确认之前 Flashduty 不保存任何记录。 + +3. 扫码确认后,Flashduty 在你的飞书企业里创建应用(默认名称 Flashduty),并提交第一次审批。应用要收发消息、读群消息,飞书规定由企业管理员审核通过后才能使用。 + +4. 第一次审批通过后,Flashduty 自动完成全部配置:生成事件与回调加密策略用的 `Encrypt Key` 和 `Verification Token`;添加网页端和移动端的登录重定向 URL;把可用范围设为全部成员(仅首次配置时设置,后续版本保留管理员改过的范围);补齐已开启功能需要的权限,以及 `application:application:self_manage`、`application:application:patch` 两项自管理权限(让 Flashduty 之后能自己更新配置、发布版本,不用再扫码);把事件和回调都指向 Flashduty 的请求地址(事件 `im.message.receive_v1`,回调 `card.action.trigger`);开启 **网页应用** 与 **机器人** 能力,工作台默认打开 Flashduty 网页应用。配置完发布一个新版本,等管理员审批配置版本。 + +5. 两次审批都通过后,集成即可使用。若飞书没有接受版本里的默认打开方式,Flashduty 会再提交一个小版本补上,审核期间不影响使用。 + +审核中详情页每 10 秒自动向飞书核对一次结果;也可以点 **复制消息给管理员**,把要审批的内容发给飞书管理员,或点 **去飞书管理后台审核** 直接打开飞书管理后台的应用审核页。 + +托管应用的凭证由 Flashduty 生成和保管,页面只显示 **App ID**、**当前版本** 和 **状态**,编辑页会提示「飞书应用 xxx 由 Flashduty 自动管理,凭证由 Flashduty 保管,不需要填写」。这些凭证字段由后端维护,在 Flashduty 里修改不会生效。 + +### 使用已有应用 + +在 **飞书应用授权** 里选 **使用已有应用**,填入应用的 `App ID`,点击 **保存** 后用应用所有者或协作者的飞书账号扫码,授权 Flashduty 管理这个应用。之后的两次审批和自动配置与创建新应用相同。 + +### 托管应用的状态与处理 + +集成详情页和集成列表都会显示托管应用的状态。 + +| 状态 | 含义 | 该做什么 | +| :--- | :--- | :--- | +| 待管理员审核(1/2) | 应用已创建,等飞书企业管理员审核应用 | 提醒管理员在飞书管理后台 → 应用审核里通过;通过后 Flashduty 自动配置并提交第 2 次审批 | +| 待管理员审核配置(2/2) | 配置已下发并发布新版本,等管理员审核配置版本 | 提醒管理员审批;通过后集成即可使用 | +| 新版本审核中 | 已生效的集成又提交了新版本(追加权限、调整默认打开方式等) | 无需操作,审核期间现有功能照常工作 | +| 新版本被拒 | 已生效的集成,新版本被飞书管理员拒绝 | 现有功能照常工作,新版本里的改动没有生效;处理后点 **重新提交给飞书管理员审核** | +| 审核被拒 | 应用或首个配置版本被管理员拒绝,集成还不能用 | 和管理员确认原因后点 **重新扫码提交**,会重新提交审批 | +| 配置失败 | 飞书没有接受 Flashduty 的自动配置,详情页显示原因 | 点 **重试配置**,重新下发配置并发布版本 | +| 凭证失效 | 有人在飞书开发者后台重置了 App Secret,或飞书管理员停用了应用,告警消息发不出去 | 重置 Secret 的,点 **重新扫码授权** 即可恢复;应用被停用的,需要飞书管理员先在管理后台启用 | +| 应用已删除 | 飞书里的应用已被删除 | 点 **重新创建飞书应用** 重新扫码创建;会沿用这条集成的名称、团队和增强功能设置,通知策略里的引用不用改 | +| 可追加权限 | 集成已生效,但新开启的功能还缺飞书权限 | 点 **提交给飞书管理员审核**;也可以点 **暂不追加** 先跳过,之后在状态行的 **追加权限** 里再打开 | + +已生效的集成,Flashduty 每 10 分钟核对一次凭证;Secret 被重置、应用被停用或删除时会自动变成上面的状态。开启需要额外权限的功能(例如作战室复盘导入群聊记录、在飞书里使用 AI SRE)后,详情页会出现「已开启的功能需要追加 N 项飞书权限」的提示,点 **提交给飞书管理员审核** 后 Flashduty 自动给应用加上权限并发布新版本,不需要重新扫码。 + ## 创建飞书应用 +以下步骤只适用于 **手动填写凭证** 接入(含 Lark 国际版与私有化部署)。扫码创建或已升级为自动管理的集成由 Flashduty 完成这些配置,不需要手工执行。 + ### 1. 创建自建应用 访问 [飞书开发者后台](https://open.feishu.cn/app),创建企业内自建应用。应用图标可使用 [Flashduty 官方 icon](https://flashduty-public.oss-cn-beijing.aliyuncs.com/icons/flashduty-20251216.png)。 @@ -32,81 +84,96 @@ keywords: ["飞书", "Lark", "即时消息", "告警通知", "IM集成"] ### 3. 复制事件回调的 Token 信息 -前往 开发配置 → 事件与回调 → **加密策略** 页面,生成并复制 `Encrypt Key`(推荐启用,更安全)和 `Verification Token` 备用。 +前往 开发配置 → 事件与回调 → **加密策略** 页面,生成并复制 `Encrypt Key`(推荐启用,更安全;飞书里没设就留空)和 `Verification Token` 备用。 ![飞书/Lark:复制事件回调的 Token 信息 界面截图](https://docs-cdn.flashcat.cloud/images/png/ac558d48464310fe27ef97912b298df1.png) ## 添加飞书集成 -回到 Flashduty On-call **集成中心** 页面,选择 即时消息 → **飞书**,在表单中填入 `名称` 以及上一步复制的 `App ID`、`App Secret`、`Verification Token` 和 `Encrypt Key` 后,点击 **保存** 完成创建。 +回到 Flashduty On-call **集成中心** 页面,选择 即时消息 → **飞书**,在 **飞书应用授权** 里点 **改为手动填写凭证**(新建时默认是扫码授权;私有化部署和编辑已有集成时直接就是凭证表单),然后在表单中填入 `名称` 以及上一步复制的 `App ID`、`App Secret`、`Verification Token` 和 `Encrypt Key` 后,点击 **保存** 完成创建。保存时系统会用 `App ID` 和 `App Secret` 向飞书换一次访问凭证,填错会直接提示。 如果您使用的是 Lark(国际版),请同时开启 **是否为 Lark 版本** 开关,开启后系统将使用 Lark 国际版端点进行通信。Lark 开放平台的开发者后台入口为 [open.larksuite.com](https://open.larksuite.com),上文创建应用、配置权限等步骤均在该站点完成。 -创建成功后,您将在列表中看到已添加的飞书集成。点击其名称进入详情页面,即可查看 **网页配置** 地址、**重定向 URL** 和 **消息卡片请求网址**,这些信息将在后续步骤中使用。 +创建成功后,您将在列表中看到已添加的飞书集成。点击其名称进入详情页,详情页会给出编号 5 步的接入向导,每一步都提供可直接复制的地址或权限清单,编号与下文「在飞书完成配置」一致。 ![飞书/Lark:添加飞书集成 界面截图](https://docs-cdn.flashcat.cloud/images/png/1e8ffb6c39f99ef12bd85ae49992ebad.png) -## 配置飞书应用 +## 在飞书完成配置 + +详情页的 **在飞书完成配置** 把剩下的工作拆成编号 5 步,每一步对应飞书开发者后台的一个页面,编号与本节一致,标题右侧显示「N / 5 已完成」。权限开通和请求地址校验发生在飞书那边,切回详情页时会自动重新读一次(也可以点 **重新检测**):飞书会校验请求地址,所以第 3 步能自动打勾;飞书不回传应用能力、重定向 URL 和版本发布,这三步做完后请点按钮手动确认。手动确认需要集成的编辑权限,没有权限的成员只能看状态。 + + + + -### 1. 开通并配置应用能力 +### 1. 开通权限 + +进入 **权限管理** 页面,点「批量处理 › 批量导入/导出权限」,粘贴详情页第 1 步 **复制权限清单(N 项)** 复制的权限清单,点「下一步,确认新增权限」后确认开通。清单是飞书批量导入接受的 JSON,形如 `{"scopes":{"tenant":[...],"user":[]}}`,内容是下表全部权限。 + +下表权限覆盖基础通知、作战室、AI SRE 对话和 AI 生成复盘。请特别确认已开通 `im:message.p2p_msg:readonly` 和 `im:message.group_at_msg:readonly`,否则飞书不会向 Flashduty 推送单聊消息或群聊 @ 机器人消息。权限或事件配置变更后,需要重新发布应用后才会在线上生效。 + +| 分组 | 官方权限名称 | 用途 | +| :--- | :--- | :--- | +| `base`(必需) | `im:chat` | 创建和管理作战室群、获取群信息、添加群成员 | +| `base`(必需) | `im:message` | 读取和发送单聊、群组消息 | +| `base`(必需) | `im:message:send_as_bot` | 以机器人身份发送 AI SRE 回复和作战室消息 | +| `base`(必需) | `im:message:update` | 更新应用已发送的消息,用于 AI SRE 流式回复更新 | +| `base`(必需) | `im:resource` | 上传和下载消息中的图片、文件等资源 | +| `base`(必需) | `contact:user.id:readonly` | 通过手机号或邮箱获取用户 ID,用于自动关联和邀请成员 | +| `war_room_history`、`ai_sre` | `im:message.group_msg` | 读取群聊历史消息;AI SRE 上下文和 AI 生成复盘报告需要该权限 | +| `ai_sre` | `im:message.p2p_msg:readonly` | 接收用户发给机器人的单聊消息;缺少该权限时,AI SRE 无法收到单聊提问 | +| `ai_sre` | `im:message.group_at_msg:readonly` | 接收群聊中 @ 机器人的消息;群聊 AI SRE 对话需要该权限 | +| `ai_sre` | `im:message.reactions:read` | 查询消息表情反应,用于 AI SRE 处理状态确认 | +| `ai_sre` | `im:message.reactions:write_only` | 添加或删除消息表情反应,用于 AI SRE 处理状态确认 | +| `ai_sre` | `cardkit:card:write` | 创建和更新 CardKit 卡片,用于 AI SRE 流式回复 | +| `ai_sre` | `contact:contact.base:readonly` | 获取通讯录基本信息,用于读取用户和部门基础资料 | +| `ai_sre` | `contact:user.base:readonly` | 获取用户基础信息,用于显示发送者名称 | + +三组权限的用途:`base` 是告警通知、卡片按钮、关联成员和作战室所必需;`war_room_history` 只在作战室复盘要导入群聊记录时需要;`ai_sre` 只在飞书里使用 AI SRE 时需要。 + +这一步由平台自动检测:**基础** 一组全部开通就算完成,显示「基础 N 项已开通」,缺权限时显示「缺 N 项基础权限」;后两组按需开通,不影响打勾。 + +![飞书/Lark:申请应用权限 界面截图](https://docs-cdn.flashcat.cloud/images/png/d919be62107f6b9d0c662f440d620e61.png) + +### 2. 添加网页应用和机器人 1. 回到飞书开发者后台,进入刚才创建的飞书应用,进入 添加应用能力 → **按能力添加** 页面,同时开通 **网页应用** 和 **机器人** 能力。 ![飞书/Lark:开通并配置应用能力 界面截图](https://docs-cdn.flashcat.cloud/images/png/5ab84aec1593c7118782765676a51c6a.png) -2. 前往 **网页应用** 页面,配置 `桌面端主页` 和 `移动端主页`,内容均为集成详情中的 **网页配置** 地址。详见飞书开发文档 [配置应用主页地址](https://open.feishu.cn/document/uYjL24iN/uMTMuMTMuMTM/development-guide/step1#8366b844)。 +2. 前往 **网页应用** 页面,配置 `桌面端主页` 和 `移动端主页`,内容均为详情页第 2 步给出的地址。详见飞书开发文档 [配置应用主页地址](https://open.feishu.cn/document/uYjL24iN/uMTMuMTMuMTM/development-guide/step1#8366b844)。 ![飞书/Lark:开通并配置应用能力 界面截图](https://docs-cdn.flashcat.cloud/images/png/d91efc598bda17e1bfcb367aec47c779.png) -3. 前往 事件回调 → **事件配置** 页面,配置 `订阅方式`(内容为集成详情中的 **消息卡片请求网址**)。然后,添加事件 `im.message.receive_v1`。 +应用图标可使用 [Flashduty 官方 icon](https://flashduty-public.oss-cn-beijing.aliyuncs.com/icons/flashduty-20251216.png),在 **凭证与基础信息** 页面上传。飞书不回传应用能力配置,完成后在详情页第 2 步点 **我已添加** 确认。 - ![飞书/Lark:开通并配置应用能力 界面截图](https://docs-cdn.flashcat.cloud/images/png/71910d8af8d60b5f30baf009081646df.png) +### 3. 配置事件与回调 -4. 前往 事件回调 → **回调配置** 页面,配置 `订阅方式`(内容为集成详情中的 **消息卡片请求网址**)。然后,订阅以下两项回调: +事件和回调用同一个请求地址,内容为详情页第 3 步给出的地址。飞书保存请求地址时会立刻访问这个地址做校验。 + +1. 前往 事件回调 → **事件配置** 页面,订阅方式选「将事件发送至开发者服务器」,粘贴请求地址并保存。然后添加事件 `im.message.receive_v1`。 + + ![飞书/Lark:开通并配置应用能力 界面截图](https://docs-cdn.flashcat.cloud/images/png/71910d8af8d60b5f30baf009081646df.png) - - `card.action.trigger` - - `card.action.trigger_v1` +2. 切到 **回调配置** 页面,同样粘贴请求地址并保存,再添加回调 `card.action.trigger`。 ![飞书/Lark:开通并配置应用能力 界面截图](https://docs-cdn.flashcat.cloud/images/png/f58b20e52fc53f428bc493e18f0a567f.png) -### 2. 添加重定向 URL 到飞书应用 +飞书校验通过后这一步自动打勾(显示 **已通过校验**),还没收到校验时显示 **等待飞书校验**。如果飞书提示「请求地址校验失败」,多半是新建集成时填的 `Verification Token` 或 `Encrypt Key` 和飞书「加密策略」页不一致,到集成编辑页改成一致后重新保存。 -进入 **安全设置** 页面,配置 `重定向URL`,内容为集成详情中的 **重定向 URL**。 +### 4. 配置重定向 URL + +进入 **安全设置** 页面,在 `重定向URL` 下添加详情页第 4 步给出的两条地址:网页端 `…/login` 和移动端 `…/login`。飞书不回传这项配置,完成后在详情页第 4 步点 **我已添加** 确认。 详见飞书开发文档 [配置重定向 URL](https://open.feishu.cn/document/uYjL24iN/uYjN3QjL2YzN04iN2cDN?lang=zh-CN#c863e533)。 ![飞书/Lark:添加重定向 URL 到飞书应用 界面截图](https://docs-cdn.flashcat.cloud/images/png/00a7cdd10c09c90c2d7b2f0a99ee4d8d.png) - - - - -### 3. 申请应用权限 - -进入 **权限管理** 页面,申请下表全部权限(覆盖基础通知、作战室、AI SRE 对话和 AI 生成复盘)。请特别确认已开通 `im:message.p2p_msg:readonly` 和 `im:message.group_at_msg:readonly`,否则飞书不会向 Flashduty 推送单聊消息或群聊 @ 机器人消息。权限或事件配置变更后,需要重新发布应用后才会在线上生效。 - -| 官方权限名称 | 用途 | -| :--- | :--- | -| `im:chat` | 创建和管理作战室群、获取群信息、添加群成员 | -| `im:message` | 读取和发送单聊、群组消息 | -| `im:message.p2p_msg:readonly` | 接收用户发给机器人的单聊消息;缺少该权限时,AI SRE 无法收到单聊提问 | -| `im:message.group_at_msg:readonly` | 接收群聊中 @ 机器人的消息;群聊 AI SRE 对话需要该权限 | -| `im:message:send_as_bot` | 以机器人身份发送 AI SRE 回复和作战室消息 | -| `im:message:update` | 更新应用已发送的消息,用于 AI SRE 流式回复更新 | -| `im:message.group_msg` | 读取群聊历史消息;AI SRE 上下文和 AI 生成复盘报告需要该权限 | -| `im:message.reactions:read` | 查询消息表情反应,用于 AI SRE 处理状态确认 | -| `im:message.reactions:write_only` | 添加或删除消息表情反应,用于 AI SRE 处理状态确认 | -| `im:resource` | 上传和下载消息中的图片、文件等资源 | -| `cardkit:card:write` | 创建和更新 CardKit 卡片,用于 AI SRE 流式回复 | -| `contact:user.id:readonly` | 通过手机号或邮箱获取用户 ID,用于自动关联和邀请成员 | -| `contact:contact.base:readonly` | 获取通讯录基本信息,用于读取用户和部门基础资料 | -| `contact:user.base:readonly` | 获取用户基础信息,用于显示发送者名称 | +### 5. 调整可见范围并发布 -![飞书/Lark:申请应用权限 界面截图](https://docs-cdn.flashcat.cloud/images/png/d919be62107f6b9d0c662f440d620e61.png) +打开 **版本管理与发布** 页面,创建版本,将 **可用范围** 选为「全部员工」,提交发布(企业管理员审核通过后生效)。第 1–4 步的改动都要发布后才生效;之后再改权限、事件或回调,要再发一个版本。完成后在详情页第 5 步点 **我已发布** 确认。 -## 应用发布与使用 - -完成上述所有配置后,请发布应用。待管理员审核通过后即可使用。详见飞书开发文档 [应用发布与使用](https://open.feishu.cn/document/uYjL24iN/uMTMuMTMuMTM/development-guide/step-4)。 +详见飞书开发文档 [应用发布与使用](https://open.feishu.cn/document/uYjL24iN/uMTMuMTMuMTM/development-guide/step-4)。 为了确保所有人可以使用应用,需将应用 **可见范围** 调整为全部员工,再进行应用发布。 @@ -114,6 +181,14 @@ keywords: ["飞书", "Lark", "即时消息", "告警通知", "IM集成"] ![飞书/Lark:应用发布与使用 界面截图](https://docs-cdn.flashcat.cloud/images/png/6bbc285986808af14c29d0eb633a2bf7.png) +## 升级为自动管理 + +手动填写凭证接入的集成,详情页「在飞书完成配置」上方会出现「升级为自动管理,以后不用再去飞书后台改配置」的提示。用应用所有者或协作者的飞书账号扫码授权后,Flashduty 接管这个应用:上文 5 步不用再做,新功能需要的权限也会自动提交给飞书管理员审核。`App Secret` 和推送地址都不变,告警通知不中断。 + +Lark 国际版和私有化部署的集成不显示这个入口,继续按手动方式维护。 + +## 访问应用 + 应用发布后,即可通过 **手机端** 或 **PC 端** 访问应用。首次访问需要登录并关联飞书与 Flashduty 账号,后续可以免登录使用。 手机端和 PC 端的入口相同:飞书 → 工作台 → 搜索应用名称 → **打开应用**。 @@ -188,7 +263,7 @@ keywords: ["飞书", "Lark", "即时消息", "告警通知", "IM集成"] -- 请再次检查是否为应用配置了[申请应用权限](#request-permissions)中列出的权限 +- 请再次检查是否为应用配置了[开通权限](#request-permissions)中列出的权限 - 请参考 [作战室介绍文档](/zh/on-call/advanced/war-room) 的 **常见问题** 部分 diff --git a/zh/on-call/integration/instant-messaging/microsoft-teams.mdx b/zh/on-call/integration/instant-messaging/microsoft-teams.mdx index 1912fdde..006fc29c 100644 --- a/zh/on-call/integration/instant-messaging/microsoft-teams.mdx +++ b/zh/on-call/integration/instant-messaging/microsoft-teams.mdx @@ -26,6 +26,16 @@ keywords: ["Microsoft Teams", "Teams", "即时消息", "告警通知", "IM集成 以下指令中的 `{ID}` 请替换为 Flashduty Microsoft Teams 集成 ID;`{ChatName}` 请替换为群聊名称。 +## 通知位置 + +集成详情页 **配置** 页签的顶部是 **通知位置** 卡片,Flashduty 只往其中列出的团队和群聊发故障通知。卡片里是 **团队** 和 **群聊** 两个页签,页签名后的数字是已关联的数量: + +- **团队** 按团队列出,每个团队下方显示 Flashduty 应用在该团队里读到的频道(形如 `频道:General`)。读不到频道时会提示 Flashduty 应用可能已被移出这个团队,点 **重新读取** 可跳过缓存重新读取该团队。 +- 两个页签都内嵌了新增目标的三步引导:在 Teams 里安装应用并选中目标(团队必须选 General 频道),在目标会话里 @Flashduty 发送关联指令,再点机器人回复里的 **Link Now**。 +- 列表每一项右侧的 **取消关联** 会先弹出确认框,写明取消关联该团队或群聊后它将收不到故障通知;在弹窗里选择 **确认** 才真正解除。弹窗里还折叠了「取消关联后如何移除 Flashduty Bot ?」的说明。 + +卡片下方是 **增强功能** 卡片,用于配置该集成的 AI SRE 能力。 + ## 关联团队 (Team) @@ -44,13 +54,15 @@ keywords: ["Microsoft Teams", "Teams", "即时消息", "告警通知", "IM集成 -在 Team 的 General Channel 中 @Flashduty 并发送 `linkTeam {ID}`,然后选择 **立即关联**。 +在 Team 的 General Channel 中输入 @Flashduty 并发送 `linkTeam {ID}`,然后点机器人回复里的 **Link Now**。@Flashduty 必须在 Teams 里手动输入并从弹出列表里选中机器人,直接粘贴的 @ 不算提及。 + +点 **Link Now** 后会跳回集成详情页并自动完成关联,该团队随即出现在 **通知位置** 的 **团队** 页签里。 ![Microsoft Teams:发送关联指令 界面截图](https://docs-cdn.flashcat.cloud/images/png/3192b5481b0595fcb58e5cc43abad125.png) -如需解除该 Team 的关联,请在同一频道中发送 `@Flashduty unlinkTeam {ID}`。在响应卡片中选择 **Unlink in FlashDuty** 后,Flashduty 会自动解除关联并显示成功提示。 +如需解除该 Team 的关联,请前往 **集成中心 → 即时消息 → Microsoft Teams → 配置 → 通知位置 → 团队**,点击该团队的 **取消关联**,再在弹窗中选择 **确认**。也可以在同一频道中发送 `@Flashduty unlinkTeam {ID}`,并按响应卡片的提示操作。 @@ -68,13 +80,15 @@ keywords: ["Microsoft Teams", "Teams", "即时消息", "告警通知", "IM集成 -在群聊中 @Flashduty 并发送 `linkChat {ID} {ChatName}`,然后选择 **立即关联**。 +先在下方的输入框里填写 **群名称**(只用于在 Flashduty 里显示,未填写时页面上的指令不能复制),再在群聊中输入 @Flashduty 并发送 `linkChat {ID} {ChatName}`,然后点机器人回复里的 **Link Now**。@Flashduty 必须在 Teams 里手动输入并从弹出列表里选中机器人,直接粘贴的 @ 不算提及。 + +点 **Link Now** 后会跳回集成详情页并自动完成关联,该群聊随即出现在 **通知位置** 的 **群聊** 页签里。 ![Microsoft Teams:发送关联指令 界面截图](https://docs-cdn.flashcat.cloud/images/png/d0beee141db63714ccecb095affee79b.png) -如需解除该群聊的关联,请在同一群聊中发送 `@Flashduty unlinkChat {ID}`。在响应卡片中选择 **Unlink in FlashDuty** 后,Flashduty 会自动解除关联并显示成功提示。 +如需解除该群聊的关联,请前往 **集成中心 → 即时消息 → Microsoft Teams → 配置 → 通知位置 → 群聊**,点击该群聊的 **取消关联**,再在弹窗中选择 **确认**。也可以在同一群聊中发送 `@Flashduty unlinkChat {ID}`,并按响应卡片的提示操作。 @@ -105,7 +119,9 @@ keywords: ["Microsoft Teams", "Teams", "即时消息", "告警通知", "IM集成 -在与 Flashduty 的个人聊天中发送 `linkUser {ID}`,然后选择 **立即关联**。 +在与 Flashduty 的个人聊天中发送 `linkUser {ID}`,然后点机器人回复里的 **Link Now**。 + +点 **Link Now** 后会跳回集成详情页,并直接落在 **关联用户** 页签完成关联。 ![Microsoft Teams:发送关联指令 界面截图](https://docs-cdn.flashcat.cloud/images/png/671ae7883bbba839419e539762db99de.png) @@ -127,10 +143,10 @@ Flashduty Teams 应用只处理发送告警通知、完成关联配置和处理 -请前往 集成中心 → 即时消息 → **Microsoft Teams**,在 **关联 Teams** 和 **关联用户** 中查看。 +请前往 集成中心 → 即时消息 → **Microsoft Teams** → **配置** 页签,在 **通知位置** 的 **团队** 和 **群聊** 页签中查看已关联的团队与群聊;用户在 **关联用户** 页签中查看。 -请在原始 Teams 上下文中使用对应指令:在 Team 频道中使用 `unlinkTeam {ID}`,在群聊中使用 `unlinkChat {ID}`,或在个人聊天中使用 `unlinkUser {ID}`。在响应卡片中选择 **Unlink in FlashDuty** 后,Flashduty 会自动解除对应关联。已关联的当前用户也可以在 **关联用户** 中点击 **解除我的关联**,再在确认框中选择 **确认**。 +团队和群聊请在 **配置** 页签的 **通知位置** 中取消:在 **团队** 或 **群聊** 页签里点击对应条目的 **取消关联**,再在弹窗中选择 **确认**。也可以在原始 Teams 上下文中使用对应指令:在 Team 频道中使用 `unlinkTeam {ID}`,在群聊中使用 `unlinkChat {ID}`,或在个人聊天中使用 `unlinkUser {ID}`,并按响应卡片的提示操作。已关联的当前用户也可以在 **关联用户** 中点击 **解除我的关联**,再在确认框中选择 **确认**。 diff --git a/zh/on-call/integration/instant-messaging/wecom.mdx b/zh/on-call/integration/instant-messaging/wecom.mdx index 7b2c2611..6e5cf012 100644 --- a/zh/on-call/integration/instant-messaging/wecom.mdx +++ b/zh/on-call/integration/instant-messaging/wecom.mdx @@ -1,12 +1,12 @@ --- title: "企业微信" -description: "通过集成企业微信第三方应用,实现在企业微信端接收和响应告警的能力" +description: "通过集成企业微信第三方应用或企业自建应用,实现在企业微信端接收和响应告警的能力" keywords: ["企业微信", "WeCom", "即时消息", "告警通知", "IM集成"] --- **版本要求**:IM 集成需要 On-call 专业版及以上订阅。[了解更多](https://flashcat.cloud/flashduty/price/) -支持 [集成第三方应用](#third-party) 和 [集成企业自建应用](#self) 两种方式,按需选择其一即可。作战室和群聊 @ AI SRE 仅支持企业自建应用。 +支持 [集成第三方应用](#third-party) 和 [集成企业自建应用](#self) 两种方式,按需选择其一即可。新建企业微信集成时,**应用版本** 默认选中 **企业自建应用(推荐)**,另一个选项是 **第三方应用(扫码)**。作战室和群聊 @ AI SRE 仅支持企业自建应用。
@@ -26,6 +26,12 @@ keywords: ["企业微信", "WeCom", "即时消息", "告警通知", "IM集成"] Flashduty 作为企业微信服务商,为您提供 Flashduty 应用的长期免费版本。该应用需要获得企业微信接口调用许可才能使用(免密登录 + 消息发送)。该许可目前支持 **最多 60 天** 免费,超出该使用时长后,Flashduty 需要为您购买企业微信许可,您方可继续使用。 +第三方应用模式不支持私有化部署。这个模式下的企业 ID 不用填、也不能填:集成的企业归属只能由一次经过企业微信证明的扫码绑定会话确定。 + +### 在企业微信中安装 Flashduty 应用 + +企业还没有装过 Flashduty 应用时,先在管理后台装好;已经装过的可以跳过本节。 + 1. 访问 [企业微信管理后台](https://work.weixin.qq.com/wework_admin/frame#apps),进入 应用管理 → **应用** 页面,点击 **添加第三方应用**。 ![企业微信:集成第三方应用 界面截图](https://docs-cdn.flashcat.cloud/images/png/ae4d35a354aaf22dd41b7493200517bc.png) @@ -38,57 +44,179 @@ Flashduty 作为企业微信服务商,为您提供 Flashduty 应用的长期 ![企业微信:集成第三方应用 界面截图](https://docs-cdn.flashcat.cloud/images/png/0821d1afdeb5db34c4c9b5548d5c8ca1.png) -4. 访问 [企业微信管理后台](https://work.weixin.qq.com/wework_admin/frame#apps),进入 **我的企业** 页面,获取 `企业 ID`。 + +没有事先安装也可以:保存集成时选 **还没有安装 Flashduty 应用**,Flashduty 会打开企业微信授权页,由企业管理员在那里完成安装。 + - ![企业微信:集成第三方应用 界面截图](https://docs-cdn.flashcat.cloud/images/png/c032dc755a72550d57658dd5962dafe4.png) +### 在 Flashduty 扫码绑定企业 -5. 返回 Flashduty On-call 集成配置页面,填写上一步获取的 `企业 ID`,点击 **保存** 完成集成。 + + + 进入 **集成中心** → **新建集成**,选择 **企业微信**,**应用版本** 选 **第三方应用(扫码)**,填好 **名称**。 + + + 按企业的实际状态在 **企业绑定方式** 里二选一: + + | 选项 | 什么时候选 | 保存后的流程 | + | :--- | :--- | :--- | + | **还没有安装 Flashduty 应用**(默认) | 企业还没装过 Flashduty 应用 | 打开企业微信授权页,由企业管理员扫码安装应用,完成后自动绑定该企业 | + | **企业已安装 Flashduty 应用** | 企业已经装好 Flashduty 应用 | 由该企业的任一成员用企业微信扫码确认,确认通过后绑定该企业 | + + + 点击 **保存**,弹出企业微信授权窗口: + + - 选「还没有安装」时点 **打开企业微信授权页**,由企业管理员用企业微信完成授权安装。不是企业管理员的话,把窗口里的链接发给管理员,管理员装完后这里会自动继续。 + - 选「已安装」时用企业微信 App 扫描窗口里的二维码,或把链接发给该企业的任一成员在企业微信里打开。 + + 企业微信识别出企业后,窗口显示「已识别企业 xxx,等待在企业微信页面点击「确认绑定」…」。在企业微信打开的 **确认绑定企业微信** 页面核对 Flashduty 账户、发起人和企业,点击 **确认绑定**。确认后 console 自动完成保存,不需要再点保存。 + + + +绑定完成后: + +- **企业 ID** 变为只读,并提示「已通过企业微信确认,不能修改」。换企业只能重新扫码,没有直接改 ID 的入口。 +- 绑定会话 30 分钟内有效,且只能用一次。过期后窗口提示「链接已过期,请重新生成」,点 **重新生成** 后重新扫码。 +- 绑定失败时窗口给出原因:`该企业还没有安装 Flashduty 应用,请关闭后改选「还没有安装」,先完成授权安装。`(企业其实没装应用,却选了「已安装」)、`已在企业微信页面上取消绑定。`(在确认页点了取消)、`企业微信没有完成确认,请重新生成后再试。`(其他情况)。 +- 只在表单里提交企业 ID 不再生效:没有经过确认的绑定会话,保存会提示 `scan with WeCom to bind your enterprise first`。 ## 集成企业自建应用 -1. 访问 [企业微信管理后台](https://work.weixin.qq.com/wework_admin/frame#apps),进入 应用管理 → **应用** 页面,点击 **创建应用**。 +作战室和群聊 @ AI SRE 只能在这个模式下使用;新建企业微信集成时,**应用版本** 默认也是它。 + +### 新建集成并填写凭证 + + + + 访问 [企业微信管理后台](https://work.weixin.qq.com/wework_admin/frame#apps),进入 应用管理 → **应用** 页面,点击 **创建应用**。 - ![企业微信:集成企业自建应用 界面截图](https://docs-cdn.flashcat.cloud/images/png/ed274f6a897b808678a5a29b23adcb66.png) + ![企业微信:集成企业自建应用 界面截图](https://docs-cdn.flashcat.cloud/images/png/ed274f6a897b808678a5a29b23adcb66.png) -2. 配置 **应用 Logo**、**应用名称** 和 **应用可见范围**。 + 配置 **应用 Logo**、**应用名称** 和 **应用可见范围**。 - ![企业微信:集成企业自建应用 界面截图](https://docs-cdn.flashcat.cloud/images/png/26ec124891e580a6d1e1035ba52636ba.png) + ![企业微信:集成企业自建应用 界面截图](https://docs-cdn.flashcat.cloud/images/png/26ec124891e580a6d1e1035ba52636ba.png) + + + 在 **我的企业** 页面取 `企业 ID`,在刚创建的应用的 **应用详情** 页取 `AgentId` 和 `Secret`,填入 Flashduty On-call 集成配置页面。 -3. 返回 Flashduty On-call 集成配置页面,根据您的实际情况选择企业微信是否为 `非私有化部署版本`。 + 企业微信为私有化部署版本时,还要填写 `Endpoint`。Flashduty 服务必须能访问该 `Endpoint`,您可以为它设置 **白名单授权**。 + + + **访问域名** 只填一个域名:不带 `https://`、不带路径,例如 `flashduty.example.com`。 - 若您的企业微信为私有化部署版本,需要在配置页面中填写 `Endpoint`。Flashduty 服务必须能访问该 `Endpoint`,您可以为它设置 **白名单授权**。 + 这个域名的 HTTPS 证书由 Flashduty 签发并自动续期,访问也由 Flashduty 转发,不用自己搭 Nginx、不用自己申请证书。企业微信要求可信域名和回调地址都使用这个域名。 -4. 访问 [企业微信管理后台](https://work.weixin.qq.com/wework_admin/frame#apps),进入 **我的企业** 页面,获取 `企业 ID`,并将其填写至 Flashduty On-call 集成配置页面。 + + 私有化部署没有托管这一步:访问域名就是贵司 Flashduty 的访问域名,企业微信要能从公网访问它。 + + + + 点击 **保存**。接收消息和智能机器人的 `Token`、`EncodingAESKey` 都由 Flashduty 生成,创建集成时不需要填,保存后在详情页对应步骤生成。 -5. 返回 [企业微信管理后台](https://work.weixin.qq.com/wework_admin/frame#apps),进入 **应用管理** 页面,点击您所创建的应用进入详情页。获取页面中的 `AgentId`,并将其填写至 Flashduty On-call 集成配置页面。 + 保存后,详情页的 **在企业微信完成配置** 会带您完成剩下的配置。 + + -6. 在应用详情页,获取 `Secret`,并将其填写至 Flashduty On-call 集成配置页面。 +## 在企业微信完成配置 -7. 在应用详情页,进入 **网页授权及 JS-SDK** 页面,点击 **设置可信域名**。该域名需指向 Flashduty On-call 的后端地址 `{api_host}`(可通过 CNAME 或代理转发实现),要求详见企业微信官方文档 [《企业内部开发配置域名指引》](https://open.work.weixin.qq.com/wwopen/common/readDocument/40754)。 +详情页按下面的顺序列出 6 步,编号与本节一致。能从企业微信侧看到结果的步骤会自动打勾;看不到结果的步骤在完成后点步骤里的确认按钮记录,需要集成编辑权限。 - ![企业微信:集成企业自建应用 界面截图](https://docs-cdn.flashcat.cloud/images/png/09a91d682198d1c8f830b5ed523965ef.png) + +私有化部署没有第 1 步(访问域名就是自己的部署地址),后面各步编号依次提前一位,校验文件也要放到自己的服务器根目录。 + - 返回 Flashduty On-call 集成配置页面,填写该域名,并完成验证。 +### 1. 添加 DNS 记录 -8. 在应用详情页,进入 **接收消息** 页面,并 **设置 API 接收**。分别对 `Token` 和 `EncodingAESKey` 点击 **随机获取**,然后复制并保存所生成的值。 +在访问域名所在的 DNS 服务商添加详情页第 1 步列出的 CNAME 记录: - ![企业微信:集成企业自建应用 界面截图](https://docs-cdn.flashcat.cloud/images/png/919bd2722c75513ce1d301779b39c3bf.png) +| 用途 | 类型 | 主机记录 | 记录值 | +| :--- | :--- | :--- | :--- | +| 证书托管 | CNAME | `_acme-challenge.<访问域名>` | 页面给出的 dns-01 委派目标 | +| 大陆访问入口 | CNAME | `<访问域名>` | `cn.wecom.flashduty.com` | +| 国际访问入口 | CNAME | `<访问域名>` | `wecom.flashduty.com` | - 返回 Flashduty On-call 集成配置页面,填写已保存的 `Token` 和 `EncodingAESKey`,点击 **保存** 完成集成。 +记录值以页面显示的为准。**证书托管** 这条用来证明域名归你们所有,Flashduty 据此通过 DNS-01 方式签发 HTTPS 证书;**访问入口** 这两条把访问转到 Flashduty,配其中一条即可。 -9. 复制 Flashduty On-call 集成详情页中的 `回调地址`,返回企业微信刚才的 **接收消息** 页面。在 **API 接收** 设置中,填入该 `回调地址` 以及上一步保存的 `Token` 和 `EncodingAESKey`,然后点击 **保存**。 +入口选哪个:默认用国际访问入口 `wecom.flashduty.com`(香港),任何域名都能用;域名已在阿里云完成接入备案的,可以改用大陆访问入口 `cn.wecom.flashduty.com`,国内访问更快;DNS 服务商支持按地区解析的,两个都配。没在阿里云接入备案的域名指向大陆入口会被阿里云拦截。 - ![企业微信:集成企业自建应用 界面截图](https://docs-cdn.flashcat.cloud/images/png/c990c27f7ad90af172e159fc4acfead7.png) +记录生效且证书签发后,第 1 步自动打勾,显示「已生效 · 证书已签发」。有记录没生效时显示「N 条记录未生效」,记录都已生效但证书还在签发时显示「证书签发中」;可以点 **重新检测** 重新读一次。 -10. 配置 **前端可信域名**,指向 Flashduty On-call 的前端地址 `console.flashcat.cloud`(可通过 CNAME 或代理转发实现)。校验通过后,将生成的 **主页地址** 配置为企业微信应用的 **工作台应用主页**。 + +这个域名如果已被某个状态页或另一个 Flashduty 账户占用,Flashduty 不能为这个集成托管它,第 1 步会显示「域名已被占用」。要托管就编辑集成换一个访问域名;域名确实属于你们的,联系 Flashduty 支持。 + + + +老集成如果是自己搭了代理转发到 Flashduty,域名不会指向上面的入口。Flashduty 探测到 `https://<访问域名>/ping` 返回 `pong` 时,第 1 步显示「自行转发 · 已连通」,跳过 DNS 记录和证书托管,按后面各步继续,只是第 2 步的校验文件要自己放到服务器根目录。 + - ![企业微信:集成企业自建应用 界面截图](https://docs-cdn.flashcat.cloud/images/png/595a71dd5624a37312676e83c45d79c4.png) +### 2. 设置可信域名 -11. 配置 **可信 IP 地址**:`47.93.12.134`(SaaS 版本;私有化部署请填写您自有环境访问企业微信的出口 IP)。 +在应用详情页进入 **开发者接口** → **网页授权及JS-SDK**,点击 **设置可信域名**,填入访问域名。要求详见企业微信官方文档 [《企业内部开发配置域名指引》](https://open.work.weixin.qq.com/wwopen/common/readDocument/40754)。 - ![企业微信:集成企业自建应用 界面截图](https://docs-cdn.flashcat.cloud/images/png/fe3b2b788dda5d331148ba0946631b91.png) +![企业微信:集成企业自建应用 界面截图](https://docs-cdn.flashcat.cloud/images/png/09a91d682198d1c8f830b5ed523965ef.png) + +然后在这一页点 **申请校验域名**,下载 `WW_verify_xxx.txt`,回到详情页第 2 步点 **上传校验文件** 上传它。企业微信会从域名根路径取这个文件,由 Flashduty 返回(企业微信不跟随重定向,http 和 https 都要能直接取到)。 + +企业微信来取过之后,第 2 步自动打勾,显示「校验文件已被企业微信读取」;上传后等待企业微信来取时显示「待企业微信读取」,还没上传时显示「待上传校验文件」。 + +回到企业微信点 **确定** 完成校验。同一页的 OAuth2.0 网页授权回调域名也填这个域名。 + + +企业微信要求域名的备案主体与本企业相同或有关联关系(母子公司、总分公司等),不符时企业微信不让保存,只能换域名或联系企业微信客服提交关联证明。 + + + +私有化部署,以及上面说的自行转发的老集成,校验文件要自己放到域名根目录:http 和 https 都要能直接访问,不能跳转。放好后点 **我已完成校验** 记录这一步。 + + +### 3. 设置 API 接收消息 + +在应用详情页进入 **接收消息** → **设置 API 接收**,把详情页第 3 步显示的三项填进去,点击 **保存**: + +| 项 | 值 | +| :--- | :--- | +| `URL` | `https://<访问域名>/event/push/wecom/data?integration_key=` | +| `Token` | 详情页第 3 步点 **生成并显示** 生成 | +| `EncodingAESKey` | 同上,43 位字母数字 | + +企业微信保存时会立刻访问这个 URL 做校验。校验通过后第 3 步自动打勾,显示「已通过校验」;还没通过时显示「等待企业微信校验」。 + + +不要在企业微信里点 **随机获取**:`Token` 和 `EncodingAESKey` 用 Flashduty 生成的这一套,两边才一致。 + + +`Token` 和 `EncodingAESKey` 只显示一次,离开页面后只看到掩码,请当次复制到企业微信。需要换一套时点 **重新生成**:重新生成后已保存的那一套立即失效,企业微信里填了旧值的要换成新值并保存,否则收不到回调;因为这一步记的是凭据指纹,换过凭据后该步骤会退回「等待企业微信校验」,等企业微信重新校验通过后再打勾。 + +![企业微信:集成企业自建应用 界面截图](https://docs-cdn.flashcat.cloud/images/png/919bd2722c75513ce1d301779b39c3bf.png) + +![企业微信:集成企业自建应用 界面截图](https://docs-cdn.flashcat.cloud/images/png/c990c27f7ad90af172e159fc4acfead7.png) + +### 4. 配置企业可信 IP + +在应用详情页底部 **企业可信IP** 点配置,填入详情页第 4 步显示的 IP。所有使用 Flashduty 的企业都填这一个。 + +企业微信要求先完成第 2 或第 3 步,才允许配置可信 IP。 + +第 4 步由 Flashduty 用应用的 `access_token` 调一次企业微信接口来判断:调用成功显示「已生效」;企业微信返回 `60020` 显示「待配置」,表示可信 IP 还没配好;其他错误码原样显示企业微信返回的码;调不通企业微信显示「检测失败」。可以点 **重新检测** 重读。 + +![企业微信:集成企业自建应用 界面截图](https://docs-cdn.flashcat.cloud/images/png/fe3b2b788dda5d331148ba0946631b91.png) + + +只有在没有填写私有 `Endpoint`(即由 Flashduty 代理转发调用企业微信)时,详情页给出的 IP 才是正确的出口 IP。企业微信如果报「以下IP属于第三方服务商」,把报错截图发给 Flashduty 支持。私有化部署请填写贵司 Flashduty 服务器的出口 IP。 + + +### 5. 设置工作台应用主页 + +在应用详情页的 **工作台应用主页** 点设置,选 **网页**,填入详情页第 5 步给出的地址。该地址是以访问域名拼出的 OAuth2 授权链接,`redirect_uri` 指到 `https://<访问域名>?app_type=wecom_app...`:Flashduty 在访问域名上把带 `app_type=wecom_app` 的请求转到控制台,用 OAuth 拿到的 code 完成免密登录。不需要再单独配置「前端可信域名」。 + +企业微信不回传这项配置,设置完成后点 **我已设置** 记录这一步。 + +![企业微信:集成企业自建应用 界面截图](https://docs-cdn.flashcat.cloud/images/png/595a71dd5624a37312676e83c45d79c4.png) + +### 6. 创建智能机器人(可选) + +可选的一步,用于在单聊和群聊里 @ 智能机器人使用 AI SRE,详细步骤见 [配置 AI SRE 智能机器人](#bot)。第 6 步给出 `https://<访问域名>/event/push/wecom/bot?integration_key=`,`Token` 和 `EncodingAESKey` 同样由 Flashduty 生成、只显示一次,与接收消息那一套互不通用。企业微信校验通过后这一步自动打勾。 ## 关联用户 @@ -115,13 +243,15 @@ Flashduty 作为企业微信服务商,为您提供 Flashduty 应用的长期 | **企业自建应用** | 支持 | 支持 | | **第三方应用** | 不支持,需切换为 **企业自建应用** 模式 | 支持 | + + ## 配置 AI SRE 智能机器人 在企业微信单聊或群聊中 @ 智能机器人,即可发起 AI SRE 排障会话,无需离开企业微信。智能机器人只能在 **企业自建应用** 模式下配置。 - 账户需已开通 AI SRE,且 Flashduty 企业微信集成中的 **允许在 IM 中使用 AI SRE** 已开启(无需开启作战室)。未开通时,机器人收到消息后不会响应。 -- 集成配置页的 **可信域名** 需先填写并校验通过,页面才会生成智能机器人的回调 URL。 +- 回调 URL 由集成里保存的 **访问域名** 拼出,所以要先按 [集成企业自建应用](#self) 把集成和访问域名保存好,详情页第 6 步才会给出可用的 URL。 @@ -135,16 +265,20 @@ Flashduty 作为企业微信服务商,为您提供 Flashduty 应用的长期 建议由企业内部成员创建:即使可见范围设为 **企业内部全体成员**,其他成员也要等创建者分享后才能看到该机器人。 - 在智能机器人的 **API 配置** 页面,连接方式选择 **使用 URL 回调**,填入 Flashduty 企业微信集成配置页 **智能机器人配置** 区域提供的 `URL`,格式为: + 在智能机器人的 **API 配置** 页面,连接方式选择 **使用 URL 回调**,填入 Flashduty 企业微信集成详情页第 6 步给出的 `URL`,格式为: ``` - https://<可信域名>/event/push/wecom/bot?integration_key= + https://<访问域名>/event/push/wecom/bot?integration_key= ``` - 企业微信会向该地址发送一次 GET 请求做 URL 验证,Flashduty 会自动应答。 + 企业微信会向该地址发送一次 GET 请求做 URL 验证,Flashduty 会自动应答。校验通过后详情页第 6 步自动打勾。 - - 在企业微信智能机器人的 API 配置页中生成 `Token` 和 `Encoding-AESKey`,将相同的值填写到 Flashduty 集成配置页的 **智能机器人配置** 区域。确保两边的值完全一致,然后分别保存两边配置。 + + 在详情页第 6 步点 **生成并显示** 生成 `Token` 和 `EncodingAESKey`,把这两个值连同上面的 URL 一起填到企业微信智能机器人的 **API 配置** 页并保存。 + + **不要在企业微信里点「随机获取」**:两边必须用 Flashduty 生成的同一套值。 + + 明文只在生成的那一次显示,离开页面后只看到掩码。点 **重新生成** 后旧的那一套立即失效:企业微信里填了旧值的要换成新值重新保存,并重新校验回调 URL(校验通过后该步骤才会重新打勾)。 @@ -171,23 +305,23 @@ Flashduty 作为企业微信服务商,为您提供 Flashduty 应用的长期 请按以下顺序排查: 1. 确认账户已开通 AI SRE,且集成中的 **允许在 IM 中使用 AI SRE** 已开启 2. 确认回调 URL 中的 `integration_key` 与 Flashduty 集成详情页显示的值完全一致 -3. 确认 Flashduty 中填写的 `Token` 和 `Encoding-AESKey` 来自智能机器人的 **API 配置** 页,而非企业自建应用的 **接收消息** +3. 确认企业微信里填写的 `Token` 和 `Encoding-AESKey` 是详情页第 6 步(智能机器人)生成的那一套,而不是第 3 步(接收消息)生成的那一套 4. 在企业微信中查看该机器人的回调记录(如有),确认请求是否到达以及是否有错误码 -- 确认 Flashduty 服务能够被企业微信服务器正常访问(公网可达),且可信域名已校验通过 -- 确认填写的 `Token` 和 `Encoding-AESKey` 与 Flashduty 集成配置页中保存的值完全一致 +- 确认 Flashduty 服务能够被企业微信服务器正常访问(公网可达),且访问域名可访问:托管时第 1 步的 DNS 记录要已生效、证书已签发 +- 确认企业微信里填写的 `Token` 和 `Encoding-AESKey` 与详情页第 6 步生成的那一套完全一致 - 确认回调 URL 中的 `integration_key` 参数正确,且对应的集成处于启用状态 -通常是 `Token` 或 `Encoding-AESKey` 填写有误。智能机器人的凭据与企业自建应用 **接收消息** 的凭据是**两套独立凭据**,请在智能机器人的 **API 配置** 页重新生成,并同步更新到 Flashduty 集成配置。 +通常是 `Token` 或 `Encoding-AESKey` 填写有误。智能机器人的凭据与企业自建应用 **接收消息** 的凭据是**两套独立凭据**:在详情页第 6 步(智能机器人)或第 3 步(接收消息)重新生成对应的那一套,把新值填到企业微信保存,再重新校验回调 URL。 - 请检查您是否已完成应用的安装步骤。例如,您是否可以在企业微信工作台中看到 Flashduty On-call 应用 -- 请检查您是否正确配置了 `Corp ID` +- 第三方应用的企业是通过扫码绑定确认的,请按 [集成第三方应用](#third-party) 重新扫码绑定 diff --git a/zh/on-call/integration/webhooks/custom-actions.mdx b/zh/on-call/integration/webhooks/custom-actions.mdx index b5dc418f..610f5726 100644 --- a/zh/on-call/integration/webhooks/custom-actions.mdx +++ b/zh/on-call/integration/webhooks/custom-actions.mdx @@ -16,6 +16,19 @@ keywords: ["自定义操作", "HTTP回调", "事件通知", "系统集成", "自 6. 配置 Endpoint、自定义 Headers 7. 保存,完成 +## 编辑已保存的操作 + +保存之后,Endpoint、自定义 Headers 的取值和自定义请求体都按密钥处理,Flashduty 不会再把它们显示回来:集成详情页的 Endpoint 只显示「已保存,出于安全考虑不再显示」,Headers 和自定义 Payload 中的值显示为 `******`。 + +编辑时这些字段同样以 `******` 预填,保存规则如下: + +| 提交的内容 | 保存结果 | +| --- | --- | +| 保持 `******` 不动 | 沿用已保存的值,不会被覆盖成掩码 | +| 填入新值 | 新值覆盖已保存的值 | + +所以想更换 Endpoint、某个 Header 的取值或请求体,必须重新输入一遍——已保存的内容无法从控制台读回。如果改了 Endpoint 而其他密钥字段仍是 `******`,保存会被拒绝并提示重新填写已保存的密钥:否则这些密钥会被发送到新的地址。 + ## 推送描述 ### 请求方式 diff --git a/zh/on-call/statuspage/create-manage-page.mdx b/zh/on-call/statuspage/create-manage-page.mdx index 3b54a804..48f1dc75 100644 --- a/zh/on-call/statuspage/create-manage-page.mdx +++ b/zh/on-call/statuspage/create-manage-page.mdx @@ -69,9 +69,15 @@ URL 标识是状态页访问地址中的唯一路径段。创建后可以修改 -自定义域名在全平台范围内必须唯一。如果该域名已被其他状态页使用,系统会拒绝设置。 +自定义域名在全平台范围内必须唯一。该域名既不能被其他状态页使用,也不能被任何账户用作[企业微信自建应用](/zh/on-call/integration/instant-messaging/wecom#self)的访问域名——即使占用方就是您自己的账户,系统同样会拒绝设置。 + +**域名归首个占用者所有,释放后可被重新占用** + +域名由首个占用者持有,直到该域名被释放。从状态页删除自定义域名、或把该域名从企业微信自建应用中移除后,系统会异步回收该域名的证书;回收完成前该域名仍算被占用,此时把它绑定到另一个状态页或另一个账户会失败。回收完成后域名释放,任何状态页或账户都可以重新占用。 + + **从旧入口切换,无 HTTPS 中断** diff --git a/zh/platform/permission-design.mdx b/zh/platform/permission-design.mdx index 4d334348..b2f97999 100644 --- a/zh/platform/permission-design.mdx +++ b/zh/platform/permission-design.mdx @@ -145,6 +145,7 @@ Flashduty 的权限点分为两类: | 权限点 | 类型 | 说明 | | --- | --- | --- | | **应用管理** | 管理 | 创建、配置和管理前端监控应用,设置采样规则和告警策略等 | + | **看板管理** | 管理 | 在分析看板中创建、编辑和删除自定义看板,并可从回收站恢复已删除的看板(回收站保留 30 天)。详见 [自定义看板](/zh/rum/analytics/custom-dashboards) | | **性能监控查看** | 只读 | 查看前端性能监控问题等 | | **错误追踪查看** | 只读 | 查看前端错误等 | | **会话浏览器查看** | 只读 | 查看事件详情等 | @@ -204,7 +205,7 @@ Flashduty 的权限点分为两类: | **配置管理**(自定义字段、值班、日历、模板、映射) | ✔️ | ✔️ | 只读 | | **状态页** | ✔️ | ✔️ | 只读 | | **监控告警**(概览、告警规则、节点权限、数据源、告警引擎、实体树) | ✔️ | ✔️ | 只读 | -| **前端监控**(应用管理、性能监控、错误追踪、会话浏览器、会话回放) | ✔️ | ✔️ | 只读 | +| **前端监控**(应用管理、看板管理、性能监控、错误追踪、会话浏览器、会话回放) | ✔️ | ✔️ | 只读 | | **快速开始** | ✔️ | ✔️ | | | **自定义菜单**(自定义角色需在菜单中逐个授权) | 自动拥有 | 自动拥有 | 自动拥有 | diff --git a/zh/rum/analytics/custom-dashboards.mdx b/zh/rum/analytics/custom-dashboards.mdx new file mode 100644 index 00000000..39ff08c5 --- /dev/null +++ b/zh/rum/analytics/custom-dashboards.mdx @@ -0,0 +1,183 @@ +--- +title: "RUM 自定义看板" +sidebarTitle: "自定义看板" +description: "在 Flashduty RUM 中把查看器的聚合分析问题固化成自定义看板:面板类型、面板查询、看板变量、从查看器保存、表格下钻、版本历史与回收站。" +keywords: ["RUM", "自定义看板", "分析看板", "面板", "图表", "看板变量", "版本历史"] +--- + +## 概述 + +自定义看板是**分析看板(Insights)**页签栏里的额外页签,与内置的概览、性能、异常、资源页签并列。每个看板由若干面板(图表)组成,而面板的查询就是查看器的聚合分析问题:事件类型 + 统计方式 + 分组字段 + DQL 过滤。于是「错误集中在哪些浏览器」「哪个页面加载最慢」这类一次性分析可以固化成长期可看的看板,不必每次重新拼查询。 + +## 入口与路由 + +| 位置 | 地址 | 说明 | +|------|------|------| +| 看板页签 | `/rum/dashboard/custom/:id` | 打开单个看板;看板以页签形式出现在分析看板里 | +| 编辑态 | `/rum/dashboard/custom/:id?edit=1` | 打开即进入编辑,改动先落在浏览器里的草稿 | +| 新建看板 | `/rum/dashboard/custom/new?edit=1` | 由页签栏右侧的 **+** 菜单进入 | +| 看板管理 | `/rum/dashboard/manage` | 看板列表:搜索、删除、回收站 | +| 旧地址 | `/rum/dashboards`、`/rum/dashboards/:id` | 自动重定向到上面的地址 | + +页签栏右侧的 **+** 菜单有两项: + +- **新建看板**:打开一个空白看板的编辑态。需要**看板管理**权限,没有权限时该项不显示。 +- **管理看板**:进入看板管理页;只是查看,不需要编辑权限。 + +页签栏按标题顺序列出全部看板,最多 100 个;超过 100 个的看板从看板管理页进入。 + +## 应用与时间范围 + +- 看板跟随页面右上角的**应用**与**时间范围**。打开看板时如果链接里没有时间范围,使用看板自己的默认时间范围;在看板里调整时间范围会同步改变页面的时间选择器。 +- 切换页签时页面的应用与时间范围一并带过去。看板的 `env`、`service`、`version` 三个变量与内置页签的对应筛选条件互相转换,其他变量不跨页签保留。 + +## 面板类型 + +面板编辑器的**图表类型**提供四种: + +| 类型 | 说明 | +|------|------| +| **时序**(`time_series`) | 统计值随时间的走势,可选折线或柱状画法 | +| **表格**(`table`) | 按分组的明细,一行一个分组,可点击下钻(见[表格下钻](#表格下钻)) | +| **单值**(`stat`) | 一个关键数字,可设置阈值着色;单值面板不使用分组 | +| **文本**(`text`) | Markdown 说明文字,不绑定查询 | + +看板定义同时接受**柱状**(`bar`)与**排行**(`top_list`)两种面板类型(通过 API 写入的看板可以有),但 RUM 控制台暂未渲染这两种图表:类型选择器里它们显示为「后续版本支持」,不可选择。 + +## 添加面板 + + + + 打开看板后点击右上角的 **编辑**,或用带 `?edit=1` 的链接进入。编辑态是浏览器里的一份草稿,整盘一次提交;刷新页面草稿即丢失,离开前会提示未保存。 + + + 点击 **添加图表** 打开面板编辑弹窗。看板按页签与分组组织面板,一个新看板自带一个页签。 + + + 选择应用(或**跟随页面应用**)、事件类型、统计方式与统计字段、分组字段与**取前**组数,再按需写 DQL 过滤条件。查询就绪后预览自动运行:选择类控件改动后延迟重跑,DQL 输入框按回车或失焦时运行。保存不要求预览成功。 + + + 标题留空时按查询自动生成,例如「P75 首字节时间 · 按浏览器名称」;描述留空时以应用名作面板副标题,用来区分查询不同应用的面板。 + + + 在弹窗里点 **应用** 把面板放回画布,再点 **保存** 提交。保存时可以写**版本说明**(选填),一次保存产生一个新版本。 + + + +## 面板查询 + +一条面板查询由以下部分组成,与查看器的聚合分析一致: + +| 组成 | 说明 | +|------|------| +| **应用** | 具体某个应用,或**跟随页面应用**(`$app`,看板跟着页面的应用切换) | +| **事件类型** | `Sessions`、`Views`、`Resources`、`Actions`、`Errors`、`Long Tasks`、`Vitals` | +| **统计方式 / 统计字段** | 计数、去重计数、平均值、求和、最小值、最大值、P50 / P75 / P90 / P95 / P99;除计数外需要选统计字段 | +| **分组字段 / 取前** | 按某个字段拆开,保留前 `5`、`10`、`20` 或 `50` 组 | +| **DQL 过滤** | 与查看器的搜索语法一致,见[数据查询语法](../explorer/data-query) | + +分组字段受字段基数限制,规则与查看器相同,见[聚合分析 · 分组字段](../explorer/analytics#分组字段):事件的 ID 字段不能用于分组,其他高基数字段只能配计数或求和且时间范围最长 7 天。 + +面板的查询在浏览器里编译后发往 `/rum/data/query`,结果按抽样估算时面板会显示**采样估算**标记与说明。 + +## 看板变量 + +看板顶部是变量栏。新看板默认带 `env`、`service`、`version` 三个字段变量,默认值都是**全部**(全部表示不加该条件)。 + +| 变量类型 | 说明 | +|----------|------| +| 内置 `$app` | 页面的当前应用。面板查询里选**跟随页面应用**就等于用 `$app`;只有应用的 `application_id` 能引用它 | +| 字段变量(`rum_field`) | 候选值取自某个 RUM 字段的取值,多选,「全部」可用 | +| 自定义变量(`custom`) | 手动填写候选值(每行一个,`显示名=值` 或纯值),单选或多选 | + +- 在面板的 DQL 里用 `${name}` 或 `$name` 引用变量,例如 `env:${env}`。一个变量选了多个值时展开为 `(a OR b)`;选中**全部**或没有取值时,包含该引用的整条条件(`env:${env}`、`-env:${env}`、`NOT env:${env}`,以及含有它的值列表)会被整条去掉,而不是当成空值查询。 +- 引用在浏览器里替换后才查询,因此面板的 DQL 必须能替换成完整语句才有结果。 +- 新建面板时,过滤条件会自动带上看板的字段变量(该事件类型有这些字段时)。 +- 变量改名会同步改写引用它的查询;仍被引用的变量不能删除,需要先清理引用。 +- 变量的选择保存在链接里(形如 `var-env=...` 的参数),复制链接即可分享同一组筛选。 + + +RUM 看板只有两种自定义变量:取某个 RUM 字段的取值的字段变量,与手填候选的自定义变量。数据源变量与 Prometheus / SQL / 日志类变量查询不适用,看板里不会出现。 + + +## 从查看器保存到看板 + +在查看器的聚合分析图表工具栏里点 **保存到看板**,可以把当前这个分析问题直接固化成一个看板面板: + +- 图表类型可选**时序 / 表格 / 单值**;饼图与分布图暂不支持,按钮会提示「饼图和分布图还不能保存到看板」。 +- 可以**追加到现有看板**(打开该看板的编辑态并追加这个面板,保存前不写入任何数据),也可以**新建看板**(先创建只含这个面板的看板,再打开它的编辑态)。 +- 面板沿用当前的时间范围与过滤条件,并自动带上看板的 `env` / `service` / `version` 变量,标题按查询自动生成。 + +具体操作与限制见[聚合分析 · 保存到看板](../explorer/analytics#保存到看板)。 + +## 表格下钻 + +**表格**面板的每一行都带一个「在查看器中打开 `字段 = 值`」的链接,点击后跳到查看器的聚合分析:沿用该面板的查询与统计方式,把过滤条件缩到这一行的分组值,时间范围用看板当前的。 + +- 没有分组的表格没有下钻链接。 +- 分组值里有引号、括号等无法精确表达为 DQL 条件的字符时,链接改用通配匹配(近似匹配),标题后面会标出「(通配符近似匹配)」。 + +## 版本历史与恢复 + +看板右上角 **⋯** 菜单里的 **版本历史** 打开版本列表: + +- 每次保存(含删除、恢复)产生一个新版本,最多保留最近 **20** 个版本,更早的版本自动清理。 +- 表格列出**版本**、**时间**、**操作人**与**说明**,当前版本标为**当前**。同一次打开时勾选两个版本可以**对比**两版保存内容的差异。 +- 选择早期版本可以**恢复**:恢复是用该版本的内容做一次新的保存,因此会产生一个新版本,恢复前的版本仍留在历史里,可以再恢复回来。 + +## 删除与回收站 + +- 看板右上角 **⋯** 菜单里的 **删除** 把看板移入回收站,确认提示为「『看板标题』将进入回收站,30 天后自动清除。」 +- 看板管理页的 **回收站** 列出已删除的看板,提示「删除的看板保留 30 天,可在这里恢复。」,点行尾的**恢复**按钮(图标)把看板放回列表,并重新出现在页签栏里。 +- 回收站里的看板超过 30 天后被彻底清除,无法恢复。 + + +RUM 看板没有目录(文件夹):看板不做分组,管理页也没有目录树与跨目录移动。看板列表支持按标题关键词搜索。 + + +## 权限 + +新建、编辑、删除与恢复看板需要**看板管理**权限(`rumDashboard:manage`)。没有该权限时: + +- 页签栏 **+** 菜单里没有**新建看板**,看板的管理页没有**新建看板**按钮与删除、恢复入口; +- 看板右上角没有**编辑**,**⋯** 菜单里没有**克隆**与**删除**; +- 仍然可以查看全部看板、切换变量与时间范围,也可以用**导出 JSON** 与**版本历史**查看内容。 + +## 数值限制 + +| 项 | 上限 | +|----|------| +| 看板内的页签数 | 10 | +| 每个页签的分组数 | 10 | +| 每个页签的面板数 | 30 | +| 整个看板的面板数 | 100 | +| 每个页签的查询数 | 100 | +| 整个看板的查询数 | 300 | +| 变量数 | 20 | +| 每个变量的候选值 | 1000 | +| 单个面板的数据链接 | 5 | +| 时序 / 排行面板的查询数 | 1–26 | +| 表格 / 单值 / 柱状面板的查询数 | 恰好 1 | +| 排行面板的取值个数 | 1–100 | +| 标题长度 | 189 字符 | +| 描述长度 | 1024 字符 | +| 看板定义大小 | 1 MB(请求体 2 MB) | +| 默认时间范围可选值 | `now-15m`、`now-30m`、`now-1h`、`now-3h`、`now-6h`、`now-12h`、`now-24h`、`now-7d` | +| 自动刷新可选值 | 不刷新、`30s`、`1m`、`5m` | + +## 下一步 + + + + 内置分析看板的四个分析维度 + + + 统计方式、分组、图表类型与下钻 + + + 面板过滤条件用的搜索语法 + + + 列表与分析两种查看方式 + + diff --git a/zh/rum/analytics/web.mdx b/zh/rum/analytics/web.mdx index 64ed6070..91f60bde 100644 --- a/zh/rum/analytics/web.mdx +++ b/zh/rum/analytics/web.mdx @@ -29,6 +29,8 @@ Web RUM 分析看板按用户会话、性能、资源和错误汇总应用的真 +除这四个内置分析维度外,分析看板页签栏里还可以放**自定义看板**:把查看器的聚合分析问题固化成自己的看板,见[自定义看板](./custom-dashboards)。 + ## 概览 diff --git a/zh/rum/error-tracking/error-viewing.mdx b/zh/rum/error-tracking/error-viewing.mdx index 0a1f32f7..4ae5b31f 100644 --- a/zh/rum/error-tracking/error-viewing.mdx +++ b/zh/rum/error-tracking/error-viewing.mdx @@ -40,7 +40,7 @@ Issue 列表支持按**优先级**排序,筛选条件默认包含「优先级 ## Issue 状态 -Issue 有待处理、处理中、已解决和已忽略 4 种状态;其中已解决按复现后的处理方式分为「已解决」与「在下一版本中解决」两种,可在状态下拉中切换。流转规则见 [Issue 状态](./issue-status)。 +Issue 有待处理、处理中、已解决和已忽略 4 种状态;其中已解决按复现后的处理方式分为「再次出现时重新打开」与「新版本出现时重新打开」两种,作为已解决的子菜单项在状态下拉中切换。流转规则见 [Issue 状态](./issue-status)。 ## 筛选与排序 @@ -129,7 +129,7 @@ Flashduty 在创建 Issue 时为其标注可能的错误原因分类,用于缩 ## 问题复现 -已解决的 Issue 再次出现时会重新打开,并标记为问题复现:解决方式为「已解决」时任何再次出现都会重开(同一版本也算),为「在下一版本中解决」时只有新版本再次出现才重开。详见 [Issue 状态](./issue-status#解决方式)。 +已解决的 Issue 再次出现时会重新打开,并标记为问题复现:解决方式为「再次出现时重新打开」时任何再次出现都会重开(同一版本也算),为「新版本出现时重新打开」时只有新版本再次出现才重开。详见 [Issue 状态](./issue-status#解决方式)。 ## Issue 详情 diff --git a/zh/rum/error-tracking/issue-status.mdx b/zh/rum/error-tracking/issue-status.mdx index 1039d670..8d45526b 100644 --- a/zh/rum/error-tracking/issue-status.mdx +++ b/zh/rum/error-tracking/issue-status.mdx @@ -13,7 +13,7 @@ keywords: ["RUM", "异常追踪", "用户监控", "前端监控"] | **待处理** | 需要关注的新问题或复现的问题 | | **处理中** | 已分类且需要修复的问题,可立即或稍后处理 | | **已忽略** | 不需要进一步调查或处理的问题 | -| **已解决** | 已修复且不再发生的问题;按复现后的处理方式分为**已解决**与**在下一版本中解决**两种(见[解决方式](#解决方式)) | +| **已解决** | 已修复且不再发生的问题;按复现后的处理方式分为**再次出现时重新打开**与**新版本出现时重新打开**两种,两者是「已解决」下的子菜单项(见[解决方式](#解决方式)) | 所有新发现的 Issue 初始状态为**待处理**。Flashduty 会根据特定条件自动更新状态,您也可以手动调整。 @@ -49,8 +49,8 @@ Flashduty 具备 Issue 检测功能,当已解决的 Issue 再次出现、且 错误被聚合进**已解决**的 Issue 时,是否判定为复现取决于该 Issue 的**解决方式**(见下文[解决方式](#解决方式)): -- 解决方式为**在下一版本中解决**(默认):只有该 Issue 之前未出现过的新版本中再次出现才触发检测 -- 解决方式为**已解决**:任何再次出现都触发检测,同一版本内的重复出现同样算复现 +- 解决方式为**新版本出现时重新打开**(默认):只有该 Issue 之前未出现过的新版本中再次出现才触发检测 +- 解决方式为**再次出现时重新打开**:任何再次出现都触发检测,同一版本内的重复出现同样算复现 - 错误未携带 `version` 标签时,两种解决方式都会触发检测 - 错误的发生时间早于该 Issue 的解决时间时不会触发(重复上报,或 SDK 离线缓存后补传的旧错误) @@ -68,7 +68,7 @@ Flashduty 具备 Issue 检测功能,当已解决的 Issue 再次出现、且 ### 关联版本配置 -复现检测会考虑错误发生的服务版本信息:解决方式为**在下一版本中解决**(默认)时,只有在 Issue 标记为**已解决**之后的、之前未出现过的新版本中才会触发检测;解决方式为**已解决**时不比较版本,同一版本中再次出现也会触发。 +复现检测会考虑错误发生的服务版本信息:解决方式为**新版本出现时重新打开**(默认)时,只有在 Issue 标记为**已解决**之后的、之前未出现过的新版本中才会触发检测;解决方式为**再次出现时重新打开**时不比较版本,同一版本中再次出现也会触发。 ```javascript window.FC_RUM.init({ @@ -86,17 +86,17 @@ window.FC_RUM.init({ ## 解决方式 -在状态下拉里,**已解决**按「该错误再次出现时如何处理」拆成两个选项,两者的状态都是已解决: +「已解决」是一个状态,但按「该错误再次出现时如何处理」分成两种解决方式。它们不是与「已解决」平级的选项,而是**已解决**下的子菜单项:状态下拉中共 4 项,鼠标移到**已解决**上会展开两个子项,两者的状态都是已解决: -| 下拉选项 | 复现时的行为 | +| 子菜单项 | 复现时的行为 | |------|------| -| **已解决** | 之后再出现就重新打开:同一版本内的再次出现同样会重开 | -| **在下一版本中解决** | 只有新版本再出现才重新打开:仅该 Issue 之前未出现过的新版本中再次出现才重开 | +| **再次出现时重新打开** | 适用于所有版本:同一版本内的再次出现同样会重开 | +| **新版本出现时重新打开** | 只有新版本再出现才重新打开:仅该 Issue 之前未出现过的新版本中再次出现才重开;未上报版本时也会重新打开 | -- 两个选项可随时在状态下拉里互相切换;只切换解决方式不改动 Issue 的统计数据,也不改变告警状态。 -- 新建 Issue 默认是**在下一版本中解决**,系统自动解决 Issue 时也使用这一方式。 +- 两个子项可随时在状态下拉里互相切换;只切换解决方式不改动 Issue 的统计数据,也不改变告警状态。 +- 新建 Issue 默认是**新版本出现时重新打开**,系统自动解决 Issue 时也使用这一方式。 - 错误未携带 `version` 标签时,两种解决方式都会重开:没有版本信息就无从区分修复前后的代码。 -- 重开时 Issue 会被置为**待处理**并标记「问题复现」,同时记录复现发生的时间与版本。列表中状态单元格受宽度限制,两种解决方式都显示为**已解决**;详情面板会在**已解决**旁额外标出「在下一版本中解决」。 +- 重开时 Issue 会被置为**待处理**并标记「问题复现」,同时记录复现发生的时间与版本。列表里的状态标签受宽度限制,两种解决方式都显示为**已解决**;详情面板会在**已解决**旁额外标出「新版本出现时重新打开」。 ## 手动更新状态 @@ -106,7 +106,7 @@ window.FC_RUM.init({ 手动更新状态 -只需点击当前状态,然后从下拉菜单中选择新状态即可。下拉中共 5 个选项:**待处理**、**处理中**、**已解决**、**在下一版本中解决**、**已忽略**;两个「已解决」选项下方会显示各自的重开说明,区别见[解决方式](#解决方式)。 +只需点击当前状态,然后从下拉菜单中选择新状态即可。下拉中共 4 项:**待处理**、**处理中**、**已解决**(子菜单,含**再次出现时重新打开**与**新版本出现时重新打开**)、**已忽略**;子项上会显示各自的重开说明,区别见[解决方式](#解决方式)。 ## 最佳实践 diff --git a/zh/rum/explorer/analytics.mdx b/zh/rum/explorer/analytics.mdx index 4a570165..1c5b52e6 100644 --- a/zh/rum/explorer/analytics.mdx +++ b/zh/rum/explorer/analytics.mdx @@ -39,6 +39,16 @@ RUM 查看器提供**列表**与**分析**两种查看方式。**分析**视图 **分组字段**决定按什么拆开,每个取值单独展示。可以用**取前**选择保留前 `5`、`10`(默认)、`20` 或 `50` 组;不选分组字段时,结果汇总为单个数值。数组字段不能用于分组,**分布图**不使用分组。 +### 分组字段的基数限制 + +分组还受字段基数约束,基数越高的字段分组代价越大: + +- **事件的 ID 字段不能用于分组**:`session_id`、`view_id`、`resource_id`、`action_id`、`error_id`、`long_task_id`、`vital_id` 每条事件各有一个取值,按它分组等于一条事件一组,因此不出现在分组字段的可选项里。**去重计数**仍然可以选这些字段,用来数「有多少个会话 / 视图」。 +- **高基数字段只能配计数或求和**:URL、用户、错误消息、堆栈、IP、`trace_id` 这类字段在字段列表中被标记为高基数。用它们分组时,**统计方式**只剩**计数**与**求和**两项(切到这类字段时非这两项的统计方式会自动改为计数),分组旁提示「高基数字段:仅支持计数/求和,时间范围最长 7 天」。 +- 这类分组的时间范围最长 **7 天**:时间范围更长时图表不出图,提示「按高基数字段分组时,时间范围最长 7 天,请缩短时间范围」。 + +同一套规则也适用于[自定义看板](../analytics/custom-dashboards)的面板:保存时由服务端再次校验,错误为 `cannot group by an event ID` 与 `grouping by a high-cardinality field supports only count and sum`。 + ## 图表类型 分析视图提供五种图表类型: @@ -80,6 +90,14 @@ RUM 查看器提供**列表**与**分析**两种查看方式。**分析**视图 - **下载图片**:导出为 2 倍清晰度的 PNG。表格和不分组的单个数值不支持。 - **CSV**:导出图表数据。时序图每行是「时间、分组、统计值」,其他图表每个分组一行。 +### 保存到看板 + +同一排的 **保存到看板** 把当前这个分析问题固化成一个[自定义看板](../analytics/custom-dashboards)面板: + +- 图表类型可选**时序 / 表格 / 单值**;**饼图**与**分布图**还不能保存,按钮会提示「饼图和分布图还不能保存到看板」。问题不完整(如没选统计字段)时提示「选择要统计的字段」。 +- 可以**追加到现有看板**(打开该看板的编辑态并追加这个面板,保存前不写入任何数据),也可以**新建看板**(先创建只含这个面板的看板,再打开它的编辑态)。 +- 面板沿用当前的应用、时间范围与过滤条件,并自动带上看板的 `env` / `service` / `version` 变量;标题按查询自动生成。 + ## 分布图 **分布图**按一个数值字段的取值区间分桶,展示每个区间的事件数,不使用统计方式和分组。 diff --git a/zh/rum/explorer/data-query.mdx b/zh/rum/explorer/data-query.mdx index f96e055e..ce04d963 100644 --- a/zh/rum/explorer/data-query.mdx +++ b/zh/rum/explorer/data-query.mdx @@ -141,6 +141,22 @@ RUM 查看器的查询由**词项**(terms)和**操作符**(operators)组 +## 路径字段与 URL 归一化 + +`view_url_path` 与 `view_url_path_group`(以及会话、错误、资源、操作、长任务事件上同名的路径字段)由页面 URL 归一化得到,两者都可以用于筛选、分组和图表: + +| 原始 URL | `view_url_path` | `view_url_path_group` | +|----------|-----------------|------------------------| +| `https://a.com/orders/123?tab=1` | `/orders/123` | `/orders/?` | +| `https://a.com/app/#/list?page=2` | `/app/#/list` | `/app#/list` | +| `https://a.com/docs/page#section-2` | `/docs/page` | `/docs/page` | + +- 查询参数(`?tab=1`、`?page=2`)不进入这两个字段。 +- **hash 路由保留在路径里**:以 `/` 开头的 fragment(hash 模式 SPA 路由的写法)会拼进路径与路径分组,因此这类应用的每个页面是不同的取值。按页筛选或分组时要把 `#/...` 段一起写上,例如 `view_url_path:/app/#/list`。 +- 路由自带的查询会被丢弃(`#/list?page=2` 记成 `#/list`),空的 `#/` 与普通锚点(如 `#section-2`)被忽略。 +- 数字、UUID、对象 ID、trace ID、长哈希、日期这类 ID 段在 `view_url_path_group` 中归一化为 `?`,所以「每个对象一个地址」的详情页会归到同一组;`view_url_path` 保留原值。 +- 资源 URL 不走这套规则:`resource_url_path` 只取路径,资源地址上的 `#...` 一直被丢掉。 + ## 微信小程序专属属性 针对微信小程序平台采集的 View 事件,除通用的 `view_*` 字段外,还支持以下可查询属性,便于排查小程序页面冷启动、`setData` 调用和生命周期阶段耗时: From ec7a043e6da193190737a0997cc06176df20d3f5 Mon Sep 17 00:00:00 2001 From: ysyneu Date: Mon, 19 Oct 2026 04:53:21 -0700 Subject: [PATCH 2/2] docs: align templates, WeCom, Feishu, Teams, RUM dashboards and engine self-monitoring pages with the console and product behavior - Templates: describe preview notices, test-send prompts and native-table fallbacks with the console's own wording; fix which limits demote every table versus only the table over the limit; stable anchors; restrict the no-table-rendering list to the channels that show raw text - WeCom: use the console's English labels, drop internal mechanism wording, note hosted domains take no port - Feishu/Lark: show permission group titles instead of identifiers; note an App ID used by another account is refused - Teams, custom actions, custom forms: match console English labels and trim internals - RUM: grouping by view_url_path is a high-cardinality grouping; trim implementation detail from custom dashboards; promote a lone H3 - Monitors: MySQL read-only check wording; self-monitoring alert expressions per instance, never-synced rule sync case, anchored link - Changelog, permission design, CLI, session recall: wording and label fixes --- en/ai-sre/im.mdx | 2 +- en/ai-sre/sessions.mdx | 6 +- en/changelog/changelog.mdx | 2 +- en/developer/cli.mdx | 2 +- en/monitors/alert-rules/mysql.mdx | 2 +- en/monitors/engine/self-monitoring.mdx | 15 +-- en/on-call/configuration/custom-forms.mdx | 2 +- en/on-call/configuration/templates.mdx | 94 +++++++++---------- .../integration/instant-messaging/lark.mdx | 40 ++++---- .../instant-messaging/microsoft-teams.mdx | 20 ++-- .../integration/instant-messaging/wecom.mdx | 58 ++++++------ .../integration/webhooks/custom-actions.mdx | 2 +- en/platform/permission-design.mdx | 2 +- en/rum/analytics/custom-dashboards.mdx | 27 +++--- en/rum/explorer/analytics.mdx | 4 +- en/rum/explorer/data-query.mdx | 2 +- zh/ai-sre/sessions.mdx | 4 +- zh/changelog/changelog.mdx | 4 +- zh/developer/cli.mdx | 2 +- zh/monitors/alert-rules/mysql.mdx | 2 +- zh/monitors/engine/self-monitoring.mdx | 15 +-- zh/on-call/configuration/custom-forms.mdx | 2 +- zh/on-call/configuration/templates.mdx | 92 +++++++++--------- .../integration/instant-messaging/lark.mdx | 36 +++---- .../instant-messaging/microsoft-teams.mdx | 2 +- .../integration/instant-messaging/wecom.mdx | 12 +-- zh/rum/analytics/custom-dashboards.mdx | 27 +++--- zh/rum/explorer/analytics.mdx | 4 +- zh/rum/explorer/data-query.mdx | 2 +- 29 files changed, 242 insertions(+), 242 deletions(-) diff --git a/en/ai-sre/im.mdx b/en/ai-sre/im.mdx index 96a6bf3b..38f2d801 100644 --- a/en/ai-sre/im.mdx +++ b/en/ai-sre/im.mdx @@ -113,7 +113,7 @@ Once you hand AI SRE an investigation, every message that appears in the group i - **Result**: an answer to your concrete question, or an actionable interim finding. - **Progress**: a one-or-two-sentence update while work is still going on: confirmed progress, what it is waiting on, a real blocker. A progress message is **not the answer**: it does not finish the turn, and the result is posted separately as usual. -When AI SRE hands the work to a background sub-agent (a Subagent or an A2A delegation) and nothing has been sent to the chat yet in that turn, it first posts a short **acknowledgement** saying what it dispatched and that the conclusion will follow when the sub-agent reports back, so the group is not left silent until the child finishes. This is a progress message too: it does not end the turn. +When AI SRE hands the work to a background Subagent (or an A2A delegation) and nothing has been sent to the chat yet in that turn, it first posts a short **acknowledgement** saying what it dispatched and that the conclusion will follow when the Subagent reports back, so the group is not left silent until the child finishes. This is a progress message too: it does not end the turn. This reply style applies only to **human-initiated IM requests**: console sessions still answer with streaming text, and war-room auto-diagnosis is an unattended background turn that also keeps native streaming output. diff --git a/en/ai-sre/sessions.mdx b/en/ai-sre/sessions.mdx index 99238994..57027fa4 100644 --- a/en/ai-sre/sessions.mdx +++ b/en/ai-sre/sessions.mdx @@ -277,12 +277,12 @@ A summary paraphrases the conversation, but the text before it is not lost: the | --- | --- | | `seg_NNN.md` | The text replaced by the Nth summary | | `tail.md` | Everything after the latest summary (always present, may be empty) | -| `index.md` | How many rows each file covers, its time span, and a checksum hash | +| `index.md` | How many rows each file covers and its time span | -What gets recorded is **what the agent was given at the time**: images and attachments leave only an `[inline …]` / `[file …]` placeholder, a truncated result keeps only the `file_path` of its full output, and a sub-agent's internal steps stay in its own session rather than in these files. +What gets recorded is **what the agent was given at the time**: images and attachments leave only an `[inline …]` / `[file …]` placeholder, a truncated result keeps only the `file_path` of its full output, and a Subagent's internal steps stay in its own session rather than in these files. -This recall only works for agents that **carry the tool**: the main session and a `general` sub-agent with full tools get the recall note after a summary, while a restricted sub-agent such as the read-only `explore` does not have the tool and can only go by the summary after compaction. +This recall only works for agents that **have the tool**: the main session and a Subagent with full tools can use it, while a restricted Subagent such as a read-only one does not have the tool and can only go by the summary after compaction. ## Choosing a runtime environment diff --git a/en/changelog/changelog.mdx b/en/changelog/changelog.mdx index d636b4c7..31e02af7 100644 --- a/en/changelog/changelog.mdx +++ b/en/changelog/changelog.mdx @@ -8,7 +8,7 @@ keywords: ["Changelog", "Product Release", "Feature Updates", "Flashduty", "Vers ### RUM custom dashboards: build your own under Insights -The Insights tab bar no longer holds only the built-in dashboards: the **+** menu at its right end offers **New dashboard** (requires the Dashboard management permission) and **Manage dashboards**, and a custom dashboard opens as an extra tab (up to 100 tabs are listed); **Save to dashboard** on an analytics chart also turns the current question into one full-width panel. A panel asks the same aggregation question as the explorer — application (or the built-in `$app` variable, which follows the page's current application), event type, aggregation and field, group by and top N, plus a DQL filter — and draws as a time series, table, stat or text panel. Dashboard variables come in field-value and custom kinds, referenced in a panel's DQL as `${name}`, and a new dashboard starts with `env` / `service` / `version` field variables. Every save leaves a revision (the last 20 are kept), and a deleted dashboard goes to the trash for 30 days and can be restored from the manage page. See [RUM custom dashboards](/en/rum/analytics/custom-dashboards). +The Insights tab bar no longer holds only the built-in dashboards: the **+** menu at its right end offers **New dashboard** (requires the **Dashboards Manage** permission) and **Manage dashboards**, and a custom dashboard opens as an extra tab (up to 100 tabs are listed); **Save to dashboard** on an analytics chart also turns the current question into one full-width panel. A panel asks the same aggregation question as the explorer — application (or the built-in `$app` variable, which follows the page's current application), event type, aggregation and field, group by and top N, plus a DQL filter — and draws as a time series, table, stat or text panel. Dashboard variables come in field-value and custom kinds, referenced in a panel's DQL as `${name}`, and a new dashboard starts with `env` / `service` / `version` field variables. Every save leaves a revision (the last 20 are kept), and a deleted dashboard goes to the trash for 30 days and can be restored from the manage page. See [RUM custom dashboards](/en/rum/analytics/custom-dashboards). ### A redesigned template detail page, with send test diff --git a/en/developer/cli.mdx b/en/developer/cli.mdx index 6145468a..08fdc7c8 100644 --- a/en/developer/cli.mdx +++ b/en/developer/cli.mdx @@ -911,7 +911,7 @@ Fields available on create and update: | `--is-test` | Runs the plugin connectivity test. Omitted on create means no test; on update it only runs when `settings` is actually submitted, so a name/description-only update performs no test. Read `test.success` and `test.message` from the response: an HTTP success is not a passing test, and an absent `test` means no test was run | | `--data` | Full JSON request body; the type-specific `settings` block can only be passed here, and the accepted keys depend on `plugin_type` | -How `settings` reads back, and the secret rules (the same handling as `monit` datasources above): +How `settings` reads back, and the secret rules: - In the `settings` returned by `list` and `info`, every **saved secret** — passwords, app secrets, tokens, encryption keys, webhook endpoints, HTTP pull URL and body, headers, custom bodies — is shown as `******`; unset values stay empty. **`integration_key` is the exception and comes back as is** (it authenticates inbound pushes and is not the account-level `app_key`). A script that reads credentials from `integration info --json` therefore gets the mask; for a new key, rotate it with `key-rotate`, which invalidates the previous key immediately. - The `settings` submitted by `update` replaces the type-specific configuration as a whole: **a known sensitive field that is omitted or sent as `******` keeps its stored value**, and an explicit empty `headers: {}` clears headers. But **a stored secret cannot be kept while its destination changes** — changing `endpoint`, `url`, or a database host or port, for example, requires sending the real secret in the same call, otherwise the request is rejected. diff --git a/en/monitors/alert-rules/mysql.mdx b/en/monitors/alert-rules/mysql.mdx index e78e3c28..a5fd7f02 100644 --- a/en/monitors/alert-rules/mysql.mdx +++ b/en/monitors/alert-rules/mysql.mdx @@ -19,7 +19,7 @@ Related queries and recovery queries support the `${label_name}` variable and th Variables require alert engine `v0.76.0` or later; if any instance in the cluster serving the rule's datasources is older, saving or enabling the rule is rejected with an error that starts with `MySQL relate or recovery queries with variables require` and names the cluster and the required version. Rules that use no variables are not subject to this version requirement. -The SQL after variable substitution is only known at runtime, so it can't be pre-checked at save time. The alert engine renders the variables per alert instance and then checks that the rendered text is a read-only query; if it isn't a plain read-only SELECT, that evaluation run fails. +The SQL after variable substitution is only known at runtime, so it can't be pre-checked at save time. The alert engine renders the variables per alert instance and then checks that the rendered text is a read-only query; if it isn't a read-only query, that evaluation run fails. ## Threshold evaluation mode diff --git a/en/monitors/engine/self-monitoring.mdx b/en/monitors/engine/self-monitoring.mdx index 0a52fb2c..85e17534 100644 --- a/en/monitors/engine/self-monitoring.mdx +++ b/en/monitors/engine/self-monitoring.mdx @@ -22,7 +22,7 @@ scrape_configs: Flashduty does not scrape these metrics for you. The **Alert Engine Status** page in the console shows the instance list, heartbeat, and version, but none of these metrics; they exist only in your own time series database. -**Don't let the engine you are monitoring evaluate alerts on its own metrics.** When the engine process exits, its lease is not `ACTIVE`, or it has lost the connection to the center, rules running on that engine neither evaluate nor deliver events. Use the center-side [engine disconnection alert](/en/monitors/engine/engine-lost-alert) to catch an engine going down; the metric alerts below belong on your own Prometheus/Alertmanager, or on another engine cluster. +**Don't let the engine you are monitoring evaluate alerts on its own metrics.** When the engine process exits, its lease is not `ACTIVE` (for example because heartbeats to the center stopped), rules running on that engine neither evaluate nor deliver events. Use the center-side [engine disconnection alert](/en/monitors/engine/engine-lost-alert) to catch an engine going down; the metric alerts below belong on your own Prometheus/Alertmanager, or on another engine cluster. ## Connection to the center @@ -32,7 +32,7 @@ Flashduty does not scrape these metrics for you. The **Alert Engine Status** pag | `monit_edge_websocket_connected` | Gauge | — | Whether the engine currently holds a WebSocket connection to the center: 1 connected, 0 not. Console query previews and entity runtime status go over this connection | | `monit_edge_websocket_connects_total` | Counter | — | Successful dials, including the first connection. A steady increase means the connection keeps being rebuilt | | `monit_edge_websocket_dial_failures_total` | Counter | — | Failed dials. If this keeps climbing while `connects_total` doesn't, the engine can't reach the center (network, proxy, or the center refusing it) | -| `monit_edge_lease_info` | Gauge | `session_mode`, `phase` | Always 1; read the lease phase from `phase`: `BOOT`, `INITIALIZING`, `ACTIVE`, `FENCED`. Only `ACTIVE` evaluates rules and delivers events. `session_mode` is currently always `lease_v1` | +| `monit_edge_lease_info` | Gauge | `session_mode`, `phase` | Always 1; read the lease phase from `phase`: `BOOT`, `INITIALIZING`, `ACTIVE`, `FENCED`. Only `ACTIVE` evaluates rules and delivers events | | `monit_edge_lease_renewal_age_seconds` | Gauge | — | Seconds since the last successful heartbeat renewal. Heartbeats default to every 10 seconds, adjustable with `alerter.heartbeatInterval`; the lease lifetime comes from the center | | `monit_edge_webapi_access_blocked` | Gauge | — | 1 means the center permanently refused this process on authorization or license grounds, so it has stopped talking to the center. Restart the engine after the authorization is restored | @@ -40,8 +40,8 @@ Flashduty does not scrape these metrics for you. The **Alert Engine Status** pag # Connection down for more than 5 minutes monit_edge_websocket_connected == 0 -# Lease not ACTIVE for more than 2 minutes: the engine neither evaluates nor delivers -(sum(monit_edge_lease_info{phase="ACTIVE"}) or vector(0)) == 0 +# Lease not ACTIVE for more than 2 minutes: the engine neither evaluates nor delivers (per instance) +monit_edge_lease_info{phase!="ACTIVE"} == 1 # Any dial failure in the last 10 minutes increase(monit_edge_websocket_dial_failures_total[10m]) > 0 @@ -57,7 +57,7 @@ The engine syncs rules from the center every 10 seconds by default. | Metric | Type | Labels | Description | |------|------|------|------| -| `monit_edge_alert_rule_sync_age_seconds` | Gauge | — | Seconds since the last successful sync | +| `monit_edge_alert_rule_sync_age_seconds` | Gauge | — | Seconds since the last successful sync; 0 if it has never succeeded, in which case check `monit_edge_alert_rule_sync_status{outcome="pending"}` | | `monit_edge_alert_rule_sync_status` | Gauge | `outcome` | Result of the latest sync, always 1, present only on the current `outcome`: `success`, `error`, or `pending` (never synced yet) | | `monit_edge_alert_rule_sync_rules` | Gauge | — | Number of rules the center assigned to this engine at the last successful sync. If it drops to 0 or far below what you expect, first check whether rules were disabled and whether datasource bindings or instance sharding changed | @@ -99,7 +99,7 @@ sum by (error_type) (increase(monit_edge_alert_rule_evaluations_total{outcome="e ``` -When `monit_edge_alert_rule_failing_duration_seconds` climbs, check the scope with `monit_edge_alert_rule_scopes{state="failing"}`. Many scopes failing on one datasource usually means that datasource is unavailable; only one or two failing means the query or threshold of that rule is the problem. Per-rule execution state and error summary are in the console under **Runtime Details**; see [Rule runtime details](/en/monitors/quickstart/quickstart). +When `monit_edge_alert_rule_failing_duration_seconds` climbs, check the scope with `monit_edge_alert_rule_scopes{state="failing"}`. Many scopes failing on one datasource usually means that datasource is unavailable; only one or two failing means the query or threshold of that rule is the problem. Per-rule execution state and error summary are in the console under **Runtime Details**; see [Rule runtime details](/en/monitors/quickstart/quickstart#rule-runtime-details). ## Query concurrency and build @@ -122,10 +122,11 @@ The table below gives rules you can copy as-is, as a metric plus a sustained con | Alert | PromQL | For | What to do | |------|--------|------|------| | WebSocket disconnected | `monit_edge_websocket_connected == 0` | 5 minutes | The console can't query through this engine; check the network and proxy to the center, and use the growth of `dial_failures_total` to confirm if it keeps reconnecting | -| Lease not ACTIVE | `(sum(monit_edge_lease_info{phase="ACTIVE"}) or vector(0)) == 0` | 2 minutes | The engine neither evaluates nor delivers; check `monit_edge_lease_renewal_age_seconds` and heartbeats | +| Lease not ACTIVE | `monit_edge_lease_info{phase!="ACTIVE"} == 1` | 2 minutes | The engine neither evaluates nor delivers, evaluated per instance; check `monit_edge_lease_renewal_age_seconds` and heartbeats | | Center permanently refused | `monit_edge_webapi_access_blocked == 1` | 1 occurrence | Authorization or license problem; restart the engine once it is restored | | Rule sync stalled | `monit_edge_alert_rule_sync_age_seconds > 300` | 3 minutes | Rules created or changed in the center don't take effect; check the network to the center | | Rule sync failing | `monit_edge_alert_rule_sync_status{outcome="error"} == 1` | 3 minutes | Same, plus the engine log for the sync error | +| Rule sync never succeeded | `monit_edge_alert_rule_sync_status{outcome="pending"} == 1` | 3 minutes | `sync_age_seconds` is 0 when no sync has ever succeeded, so the alert above does not fire; check the network to the center and the engine log | | Rules keep failing to evaluate | `max by (datasource_id) (monit_edge_alert_rule_failing_duration_seconds) > 300` | 1 occurrence | Use `monit_edge_alert_rule_scopes{state="failing"}` to tell an unavailable datasource from a broken rule query | | Query admission timed out | `sum(increase(monit_edge_alert_query_admission_rejected_total{reason="timeout"}[10m])) > 0` | 3 minutes | A slow datasource has taken all the query concurrency and an evaluation round was skipped | diff --git a/en/on-call/configuration/custom-forms.mdx b/en/on-call/configuration/custom-forms.mdx index 9a7c4642..b3624698 100644 --- a/en/on-call/configuration/custom-forms.mdx +++ b/en/on-call/configuration/custom-forms.mdx @@ -51,7 +51,7 @@ Flashduty skips the form when the action has no form elements, or when no elemen When a **Close incident** form is configured, users can't complete the closure directly from an IM incident card: selecting close has no effect, and the bot replies with a guide message stating that the incident needs a resolve form, along with an **Open the resolve form** link. The link opens that incident's detail page and starts the close form automatically; the incident closes once the form is filled in and submitted. -Feishu/Lark, Dingtalk, WeCom, Slack, and Microsoft Teams all support this guide. Feishu/Lark adds two touches: the guide message is delivered to the person who clicked as an ephemeral message that others in the chat can't see, and the card shows a toast reminding the clicker to complete the form through the link in the guide message. +Feishu/Lark, Dingtalk, WeCom, Slack, and Microsoft Teams all support this guide. Feishu/Lark adds two touches: the guide message is delivered to the person who clicked as an ephemeral message that others in the chat can't see, and the card reminds the clicker to complete the form through the link in the guide message. ## Batch acknowledge or close diff --git a/en/on-call/configuration/templates.mdx b/en/on-call/configuration/templates.mdx index c4d294f3..82dd581c 100644 --- a/en/on-call/configuration/templates.mdx +++ b/en/on-call/configuration/templates.mdx @@ -44,7 +44,7 @@ Click **Save** to complete creation -**System preset templates and read-only**: System preset templates (Rendering Example, Default Template) and templates the current member has no permission to manage are always read-only on the detail page, marked **Built-in · Read-only** next to the title (a template you lack permission for shows no badge, but is equally uneditable). To make one your own, click **Copy as new template** in the top right and edit the copy, whose name is pre-filled as "Copy of *original template name*"; copying requires the **Templates Manage** permission and On-call Pro. +**System preset templates and read-only**: System preset templates and templates the current member has no permission to manage are always read-only on the detail page, marked **Built-in · Read-only** next to the title (a template you lack permission for shows no badge, but is equally uneditable). To make one your own, click **Copy as new template** in the top right and edit the copy, whose name is pre-filled as "Copy *original template name*"; copying requires the **Templates Manage** permission and On-call Pro. For a template you can edit, the top right shows **Template settings** (gear icon), which changes the template name, team and description only — not the body. Delete sits in the **More actions** ⋯ menu; system preset templates have no such menu and cannot be deleted. @@ -165,7 +165,9 @@ Complete variable list for incident objects: ## Preview and test send -The right side of the template detail page is the preview pane: it renders the current channel's content with the selected incident, followed by a line of diagnostics and any warnings the send engine reports. The preview's title bar also holds **Send test**, which sends the editor's current content for real so you can see what the recipient gets. +The right side of the template detail page is the preview pane: it renders the current channel's content with the selected incident, followed by a line of diagnostics and any warnings the preview raises. The preview's title bar also holds **Send test**, which sends the editor's current content for real so you can see what the recipient gets. + + ### What the preview tells you @@ -179,23 +181,21 @@ One line under the preview explains how this content was rendered: | Native tables: N / Markdown tables: N | How many tables in this render became native tables and how many stayed Markdown tables; a zero count is hidden | | Used / limit | The content's byte count and the channel's size limit, e.g. `12.3 KB / 30.0 KB`; when the channel has no known limit, only the used bytes are shown | -The yellow notices above that line come from the send engine. Any of them means the template will be reshaped or rejected on a real send — trim it before saving: +The yellow notices above that line mean the template will be reshaped or rejected on a real send. Trim it before saving: | Notice | Meaning | | :--- | :--- | -| `table_not_rendered` | This channel doesn't render Markdown tables, so recipients see the raw `\|` rows; consider a list instead | -| `native_table_fallback` | **Native tables** is on for the Feishu/Lark app, but a table here misses the native-table conditions and will be sent as a Markdown table | -| `content_too_large` | The content exceeds the channel's size limit | - -`native_table_fallback` has six reasons: `too_many_tables`, `too_many_columns`, `too_many_rows`, `cell_too_long`, `unsupported_cell`, `card_too_large` — see [Native tables](#native-tables). +| This channel doesn't render Markdown tables, so recipients see the raw text. Consider a list instead. | The Dingtalk, Slack and Telegram bots, the WeCom bot on Markdown v1, and the Dingtalk, Slack, WeCom and Teams apps do not render tables, so recipients see the unrendered table text; consider a list instead | +| Some tables can't be sent as native tables and will be sent as Markdown tables. | **Native tables** is on for the Feishu/Lark app, but a table here misses a native-table condition; see [Native tables](#native-tables) for the specific notices | +| The content exceeds the channel's size limit | There are three variants, listed below | -`content_too_large` has three outcomes: +When the content exceeds the channel's size limit, the notice is one of three: -| Outcome | Meaning | +| Notice | Meaning | | :--- | :--- | -| `rejected` | The channel refuses the message, so this notification is not delivered — real alerts included. You must trim the template | -| `truncated` | The excess is truncated and the notification still goes out; per-channel limits are listed below | -| `compact_fallback` | A WeCom bot Markdown v2 message over 4096 bytes is replaced by a compact summary, so nothing is lost | +| The content exceeds the channel's size limit, so sending will fail, including real alerts. Please trim the template. | The channel refuses the message, so this notification is not delivered. You must trim the template, for example a Feishu/Lark card over 30 KB | +| The content exceeds the channel's size limit; the excess will be truncated. | The excess is truncated and the notification still goes out; per-channel limits are listed below | +| The content exceeds 4096 bytes, so a compact summary will be sent instead. | A WeCom bot Markdown v2 message over 4096 bytes is replaced by a compact summary, so nothing is lost | ### Send test @@ -213,7 +213,7 @@ Where each channel can send: | Group chat bot | Enter the robot address | The same address the notification rule takes, either a bare token or a full URL; Telegram also needs a Chat ID, Zoom a Verify token. These values are used for this send only and are never saved | | Email | Your own account email | Cannot send when your account has no email address | -- **Myself**: requires your account to be linked to that IM integration; when it is not, the send button is disabled and members with permission can click the link to finish linking in the onboarding flow. The Slack app is the exception — no per-person linking is needed (it DMs workspace members directly), but the send still resolves you to a member of that workspace and returns `im_not_linked` if it cannot. +- **Myself**: requires your account to be linked to that IM integration; when it is not, the send button is disabled and members with permission can click the link to finish linking in the onboarding flow. The Slack app is the exception — no per-person linking is needed (it DMs workspace members directly), but the send still resolves you to a member of that workspace and shows "Your account isn't linked to this IM" if it cannot. - **Group chat**: lists only chats this integration's bot has **already joined** (Slack by channel, Teams by team channel or group chat). Everyone in the chat receives the test message. - When the channel has several integrations, pick one under **Integration** first; only enabled integrations are listed. @@ -223,36 +223,34 @@ Test message markings: - A footer is appended: `Test message · Template "" · Sent by `. - Buttons on the card only answer "This is a test message. Its buttons do not change any incident." and neither acknowledge nor close anything; the **Create War Room** button does not appear in a test message. -The result is one of two values: +The result is one of two: -| Result | Channel | Prompt | -| :--- | :--- | :--- | -| `sent` | IM app | `Sent to your Feishu DM` / `Sent to ""` | -| `queued` | Group chat bots and email | "Submitted. It usually arrives within seconds; if not, check the address" | +| Channel | Prompt | +| :--- | :--- | +| IM app | `Sent to your Feishu DM` / `Sent to ""` | +| Group chat bots and email | "Submitted. It usually arrives within seconds; if not, check the address" | -IM apps call the platform's API synchronously, so a success is `sent`; bots and email go to the notification queue and are delivered asynchronously, so `queued` only means submitted — a wrong address does not fail immediately. +For IM apps, "Sent to ..." means the message went out. For bots and email, "Submitted" only means the request was accepted, and a wrong address does not fail immediately. Limits and common failures: -- Each member can send at most 5 test messages per minute; beyond that the call returns `rate_limited`. -- Failures are reported by error code: +- Each member can send at most 5 test messages per minute; beyond that the console shows "Too many test sends. Try again later". +- A failure shows one of these messages: -| Error code | Meaning | +| Message | Meaning | | :--- | :--- | -| `template_invalid` | The template failed to render (parse or execution error). Fix the template first | -| `channel_not_supported` | This channel does not support test sends | -| `invalid_target` | The target is incomplete or malformed: no robot address, a Telegram test without a Chat ID, a Zoom test without a Verify token, or a chat that does not belong to the integration | -| `integration_not_found` | The integration was not found — it may be deleted or disabled, or not an integration of this channel | -| `im_not_linked` | Your account is not linked to this IM, so it cannot DM you | -| `rate_limited` | Sending too often | -| `webhook_url_blocked` | Robot addresses must be public `http`/`https`; a full URL pointing into a private network is refused. A bare token is joined to the vendor's URL by the platform and is not checked | -| `content_too_large` | The content exceeds the channel's size limit, such as the 30 KB Feishu card | -| `send_failed` | The channel returned a failure; the message carries the channel's own error | +| The template failed to render. Fix it first | Parse or execution error in the template | +| This channel doesn't support test sends | The channel does not offer test sends | +| The target is incomplete or invalid | No robot address, a Telegram test without a Chat ID, a Zoom test without a Verify token, or a chat that does not belong to the integration | +| The integration was not found. It may have been deleted or disabled | The integration is deleted or disabled, or is not an integration of this channel | +| Your account isn't linked to this IM | Your account is not linked to this IM, so it cannot DM you | +| Too many test sends. Try again later | More than 5 test messages in a minute | +| Address not allowed: only public http/https is supported | Robot addresses must be public `http`/`https`; a full URL pointing into a private network is refused. A bare token is joined to the vendor's URL by the platform and is not checked. This applies to SaaS only; private deployments are not restricted | +| The content exceeds the channel's size limit | The content exceeds the channel's size limit, such as the 30 KB Feishu card | +| The channel rejected the message: the channel's own error | The channel returned a failure; the message carries the channel's own error | -How test sends differ from real notifications: when the template has a syntax error, a real notification silently falls back to the system default template and is still delivered, while a test send returns `template_invalid` — that is the point, to surface template problems. Conversely, a test send writes no notification record, takes no send lock and adds nothing to the notification feed, so it never acknowledges or closes an incident and never creates a war room. - -The console calls `POST /template/preview/send` with `template_id`, `type`, the editor's current `content`, `incident_id` and the channel render switches, and receives either `status` (`sent` / `queued`) or `error_code` + `message`. +How test sends differ from real notifications: when the template has a syntax error, a real notification silently falls back to the system default template and is still delivered, while a test send shows "The template failed to render. Fix it first" — that is the point, to surface template problems. Conversely, a test send leaves no notification record on the incident, so it never acknowledges or closes an incident and never creates a war room. ## Channel templates @@ -312,11 +310,13 @@ Requires [Feishu/Lark integration](/en/on-call/integration/instant-messaging/lar On Feishu/Lark app cards, **More Actions** at the bottom is a dropdown menu with custom actions, snooze durations, and unacknowledge; the **👥 Create War Room** button sits outside that menu. + + ### Native tables **Feishu Card 2.0** adds a **Native tables** switch (off by default, saved with the template). When it is on, GFM Markdown tables in the template are recognised and rendered as the card's native table component, so recipients see a pageable table instead of rows of text with `|`. -Native tables have limits, and when any condition fails **every table on that card** falls back to Markdown text: +Native tables have the limits below. If a card has more than 5 native tables, or the card exceeds 30 KB, **every table on that card** is sent as Markdown text. Any other limit affects only the table that exceeds it, which is sent as Markdown text while the other tables stay native: | Limit | Value | | :--- | :--- | @@ -326,18 +326,18 @@ Native tables have limits, and when any condition fails **every table on that ca | Cell content | At most 2000 bytes per cell | | Rows per page | 10 rows per page; the rest is paged inside the card | -Why a table falls back (shown as a `native_table_fallback` warning in [What the preview tells you](#what-the-preview-tells-you)): +When a table falls back, the preview shows a notice (see [What the preview tells you](#preview-notices)): -| Reason | Trigger | +| Notice | Trigger | | :--- | :--- | -| `too_many_tables` | The card would carry more than 5 native tables | -| `too_many_columns` | A table has more than 8 columns | -| `too_many_rows` | A table has more than 100 rows | -| `cell_too_long` | A cell is longer than 2000 bytes | -| `unsupported_cell` | A cell holds Markdown beyond plain text and `
` line breaks, a header cell is empty, or a row's cell count differs from the header's | -| `card_too_large` | Native tables would push the card past Feishu's size limit | - -The whole Feishu card is capped at **30 KB**. Beyond that Feishu refuses the message outright and the preview reports `content_too_large` + `rejected`; trim the template and send again. +| There are more tables than native tables allow, so all of them will be sent as Markdown tables. | The card would carry more than 5 native tables | +| A table has more columns than native tables allow, so it will be sent as a Markdown table. | A table has more than 8 columns | +| A table has more rows than native tables allow, so it will be sent as a Markdown table. | A table has more than 100 rows | +| A cell is too long, so the table will be sent as a Markdown table. | A cell is longer than 2000 bytes | +| A cell contains content native tables don't support, so the table will be sent as a Markdown table. | A cell holds Markdown beyond plain text and `
` line breaks, a header cell is empty, or a row's cell count differs from the header's | +| Native tables would push the card over its size limit, so Markdown tables will be sent instead. | Native tables would push the card past Feishu's size limit, so every table on the card is sent as Markdown | + +The whole Feishu card is capped at **30 KB**. Beyond that Feishu refuses the message outright and the preview shows "The content exceeds the channel's size limit, so sending will fail, including real alerts. Please trim the template."; trim the template and send again. @@ -445,7 +445,7 @@ For the switches on the **Additional info** tab, see [Card Fields and Bottom Act Template formats supported by group chat bots. -Only the Feishu/Lark app (card 2.0, via **Native tables**) and the WeCom bot's **Markdown v2** render Markdown tables. Every other channel sends tables as raw text with `|`: the Dingtalk, Slack and Telegram bots, the WeCom bot on Markdown v1, and the Dingtalk, Slack, WeCom and Teams apps. The preview flags this. For those channels, rewrite tables as lists. +These channels do not render Markdown tables and send them as raw text: the Dingtalk, Slack and Telegram bots, the WeCom bot on Markdown v1, and the Dingtalk, Slack, WeCom and Teams apps. The preview flags this. For those channels, rewrite tables as lists. diff --git a/en/on-call/integration/instant-messaging/lark.mdx b/en/on-call/integration/instant-messaging/lark.mdx index 76151852..6a52fa9e 100644 --- a/en/on-call/integration/instant-messaging/lark.mdx +++ b/en/on-call/integration/instant-messaging/lark.mdx @@ -39,11 +39,11 @@ A private Flashduty deployment cannot reach the Feishu QR-code endpoints, so it While a review is pending the details page checks the result with Feishu every 10 seconds. You can also click **Copy message for admin** to send the admin what needs approval, or **Review in Feishu Admin** to open the app review page in the Feishu admin console. -Flashduty generates and keeps the credentials of a managed app, so the page shows only **App ID**, **Current version**, and **Status**, and the edit page says "Feishu app xxx is managed by Flashduty. Flashduty keeps its credentials, so there is nothing to fill in." The backend owns these credential fields, so changing them in Flashduty has no effect. +Flashduty generates and keeps the credentials of a managed app, so the page shows only **App ID**, **Current version**, and **Status**, and the edit page says "Feishu app xxx is managed by Flashduty. Flashduty keeps its credentials, so there is nothing to fill in." Flashduty keeps these credentials; you do not and cannot edit them in Flashduty. ### Use an existing app -Choose **Use an existing app** under **Feishu/Lark app authorization**, enter the app's `App ID`, and click **Save**. Then scan with the Feishu account of the app's owner or a collaborator to authorize Flashduty to manage it. The two reviews and the automatic configuration are the same as for a new app. +Choose **Use an existing app** under **Feishu/Lark app authorization**, enter the app's `App ID`, and click **Save**. Then scan with the Feishu account of the app's owner or a collaborator to authorize Flashduty to manage it. The two reviews and the automatic configuration are the same as for a new app. An `App ID` already used by another Flashduty account is refused. ### Managed app states @@ -114,24 +114,24 @@ The table covers basic notifications, War Room, AI SRE conversations, and AI-gen | Group | Official permission name | Purpose | | :--- | :--- | :--- | -| `base` (required) | `im:chat` | Create and manage War Room chats, get chat information, and add chat members | -| `base` (required) | `im:message` | Read and send direct and group messages | -| `base` (required) | `im:message:send_as_bot` | Send AI SRE replies and War Room messages as the bot | -| `base` (required) | `im:message:update` | Update messages sent by the app for AI SRE streaming replies | -| `base` (required) | `im:resource` | Upload and download images, files, and other message resources | -| `base` (required) | `contact:user.id:readonly` | Get user IDs by phone number or email for automatic linking and member invitation | -| `war_room_history`, `ai_sre` | `im:message.group_msg` | Read group chat history; required for AI SRE context and AI-generated post-mortem reports | -| `ai_sre` | `im:message.p2p_msg:readonly` | Receive direct messages sent by users to the bot; without this permission, AI SRE cannot receive direct-chat questions | -| `ai_sre` | `im:message.group_at_msg:readonly` | Receive group messages that mention the bot; required for group-chat AI SRE conversations | -| `ai_sre` | `im:message.reactions:read` | Query message reactions for AI SRE processing status confirmation | -| `ai_sre` | `im:message.reactions:write_only` | Add or delete message reactions for AI SRE processing status confirmation | -| `ai_sre` | `cardkit:card:write` | Create and update CardKit cards for AI SRE streaming replies | -| `ai_sre` | `contact:contact.base:readonly` | Get basic contact information for reading user and department metadata | -| `ai_sre` | `contact:user.base:readonly` | Get basic user information for displaying sender names | - -What the three groups are for: `base` is required for alert notifications, card buttons, member linking, and War Room; `war_room_history` is only needed when a post-mortem imports the war room chat history; `ai_sre` is only needed to use AI SRE in Feishu. - -The platform checks this step for you: it is done once the whole **base** group is granted, shown as "All N basic permissions granted", and as "N basic permission(s) missing" while any is missing; grant the other two groups as needed, they do not affect the check mark. +| Basic (required) | `im:chat` | Create and manage War Room chats, get chat information, and add chat members | +| Basic (required) | `im:message` | Read and send direct and group messages | +| Basic (required) | `im:message:send_as_bot` | Send AI SRE replies and War Room messages as the bot | +| Basic (required) | `im:message:update` | Update messages sent by the app for AI SRE streaming replies | +| Basic (required) | `im:resource` | Upload and download images, files, and other message resources | +| Basic (required) | `contact:user.id:readonly` | Get user IDs by phone number or email for automatic linking and member invitation | +| War room post-mortem, AI SRE | `im:message.group_msg` | Read group chat history; required for AI SRE context and AI-generated post-mortem reports | +| AI SRE | `im:message.p2p_msg:readonly` | Receive direct messages sent by users to the bot; without this permission, AI SRE cannot receive direct-chat questions | +| AI SRE | `im:message.group_at_msg:readonly` | Receive group messages that mention the bot; required for group-chat AI SRE conversations | +| AI SRE | `im:message.reactions:read` | Query message reactions for AI SRE processing status confirmation | +| AI SRE | `im:message.reactions:write_only` | Add or delete message reactions for AI SRE processing status confirmation | +| AI SRE | `cardkit:card:write` | Create and update CardKit cards for AI SRE streaming replies | +| AI SRE | `contact:contact.base:readonly` | Get basic contact information for reading user and department metadata | +| AI SRE | `contact:user.base:readonly` | Get basic user information for displaying sender names | + +What the three groups are for: Basic is required for alert notifications, card buttons, member linking, and war rooms; War room post-mortem is only needed when a post-mortem imports the war room chat history; AI SRE is only needed to use AI SRE in Feishu/Lark. + +The platform checks this step for you: it is done once the whole **Basic** group is granted, shown as "All N basic permissions granted", and as "N basic permission(s) missing" while any is missing; grant the other two groups as needed, they do not affect the check mark. ![Feishu/Lark: Request App Permissions screenshot](https://docs-cdn.flashcat.cloud/images/png/d919be62107f6b9d0c662f440d620e61.png) diff --git a/en/on-call/integration/instant-messaging/microsoft-teams.mdx b/en/on-call/integration/instant-messaging/microsoft-teams.mdx index 78bc2e07..03d1cfec 100644 --- a/en/on-call/integration/instant-messaging/microsoft-teams.mdx +++ b/en/on-call/integration/instant-messaging/microsoft-teams.mdx @@ -27,13 +27,13 @@ In the commands below, replace `{ID}` with the Flashduty Microsoft Teams integra ## Notification targets -The top of the **Configuration** tab on the integration detail page is the **Notification targets** card: Flashduty sends incident notifications only to the teams and group chats listed here. The card has two tabs, **Teams** and **Group chats**, and the number after each tab label is how many targets are linked: +The top of the **Configuration** tab on the integration detail page is the **Notification targets** card: Flashduty sends incident notifications only to the teams and group chats listed here. The card has two tabs, **Teams** and **Group chat**, and the number after each tab label is how many targets are linked: -- **Teams** lists each linked team with the channels the Flashduty app was read from in that team (shown as `Channels: General`). When no channel can be read, the row warns that the Flashduty app may have been removed from the team; select **Re-read** to bypass the cache and read that team again. +- **Teams** lists each linked team with the channels the Flashduty app was read from in that team (shown as `Channel: General`). When no channel can be read, the row warns that the Flashduty app may have been removed from the team; select **Retry** to read that team again. - Both tabs embed a three-step guide for adding a target: install the app in Teams and pick the destination (a team must use the General channel), send the link command in the target conversation, then select **Link Now** in the bot reply. -- **Unlink** on a row opens a confirmation dialog that states the team or group chat stops receiving incident notifications once unlinked; select **Confirm** in the dialog to actually unlink. The dialog also folds in "How do I remove the Flashduty bot after unlinking?". +- **Unlink** on a row opens a confirmation dialog that states the team or group chat stops receiving incident notifications once unlinked; select **Confirm** in the dialog to actually unlink. The dialog also folds in "How to remove Flashduty Bot after unlinking?". -Below that card is the **Advanced features** card, which configures this integration's AI SRE capabilities. +Below that card is the **Advanced Features** card, which configures this integration's AI SRE capabilities. ## Link team @@ -79,15 +79,15 @@ Add the app to the target Chat. -First fill in **Group chat name** in the input below the step (it is only used to display the chat in Flashduty, and the command on the page can't be copied until it's filled in). Then, in the group chat, type @Flashduty and send `linkChat {ID} {ChatName}`, and select **Link Now** in the bot reply. You must type @Flashduty yourself and pick the bot from the popup list; a pasted @ is not a mention. +First fill in **Chat name** in the input below the step (it is only used to display the chat in Flashduty, and the command on the page can't be copied until it's filled in). Then, in the group chat, type @Flashduty and send `linkChat {ID} {ChatName}`, and select **Link Now** in the bot reply. You must type @Flashduty yourself and pick the bot from the popup list; a pasted @ is not a mention. -Selecting **Link Now** brings you back to the integration detail page and completes the link automatically, so the group chat appears under **Notification targets** > **Group chats**. +Selecting **Link Now** brings you back to the integration detail page and completes the link automatically, so the group chat appears under **Notification targets** > **Group chat**. ![Microsoft Teams: Send Link Command screenshot](https://docs-cdn.flashcat.cloud/images/png/d0beee141db63714ccecb095affee79b.png) -To remove this chat's link, go to **Integration Center → Instant Messaging → Microsoft Teams → Configuration → Notification targets → Group chats**, select **Unlink** on the group chat, and then select **Confirm** in the dialog. You can also send `@Flashduty unlinkChat {ID}` in the same group chat and follow the prompts on the response card. +To remove this chat's link, go to **Integration Center → Instant Messaging → Microsoft Teams → Configuration → Notification targets → Group chat**, select **Unlink** on the group chat, and then select **Confirm** in the dialog. You can also send `@Flashduty unlinkChat {ID}` in the same group chat and follow the prompts on the response card. @@ -120,7 +120,7 @@ Click **Open App**. Send `linkUser {ID}` in your personal chat with Flashduty, then select **Link Now** in the bot reply. -Selecting **Link Now** brings you back to the integration detail page, landing on the **Linked Users** tab where the link completes. +Selecting **Link Now** brings you back to the integration detail page, landing on the **Linked users** tab where the link completes. ![Microsoft Teams: Send Link Command screenshot](https://docs-cdn.flashcat.cloud/images/png/671ae7883bbba839419e539762db99de.png) @@ -142,10 +142,10 @@ Go to Integration Center → Instant Messaging → **Microsoft Teams**, check if -Go to Integration Center → Instant Messaging → **Microsoft Teams** → the **Configuration** tab, and view linked teams and group chats under **Notification targets** > **Teams** and **Notification targets** > **Group chats**. Users are listed on the **Linked Users** tab. +Go to Integration Center → Instant Messaging → **Microsoft Teams** → the **Configuration** tab, and view linked teams and group chats under **Notification targets** > **Teams** and **Notification targets** > **Group chat**. Users are listed on the **Linked users** tab. -Unlink teams and group chats on the **Configuration** tab under **Notification targets**: open the **Teams** or **Group chats** tab, select **Unlink** on the entry, and then select **Confirm** in the dialog. You can also use the matching command in its original Teams context: `unlinkTeam {ID}` in a Team channel, `unlinkChat {ID}` in a group chat, or `unlinkUser {ID}` in your personal chat, and follow the prompts on the response card. If your account is already linked, you can also select **Unlink me** under **Linked Users**, then select **Confirm** in the dialog. +Unlink teams and group chats on the **Configuration** tab under **Notification targets**: open the **Teams** or **Group chat** tab, select **Unlink** on the entry, and then select **Confirm** in the dialog. You can also use the matching command in its original Teams context: `unlinkTeam {ID}` in a Team channel, `unlinkChat {ID}` in a group chat, or `unlinkUser {ID}` in your personal chat, and follow the prompts on the response card. If your account is already linked, you can also select **Unlink me** under **Linked users**, then select **Confirm** in the dialog. diff --git a/en/on-call/integration/instant-messaging/wecom.mdx b/en/on-call/integration/instant-messaging/wecom.mdx index 964fe8ac..d6e47fa1 100644 --- a/en/on-call/integration/instant-messaging/wecom.mdx +++ b/en/on-call/integration/instant-messaging/wecom.mdx @@ -5,7 +5,7 @@ description: "By integrating a WeCom third-party app or a custom app, you can re **Plan requirement**: IM integration requires an On-call Pro or higher subscription. [Learn more](https://flashcat.cloud/flashduty/price/) -Choose one of two methods: [Third-Party App Integration](#third-party) or [Custom App Integration](#self). When you create a WeCom integration, **App Version** defaults to **Custom app (recommended)**; the other option is **Third-party app (QR code)**. War Room and @ AI SRE in group chats are available only with a custom app. +Choose one of two methods: [Third-Party App Integration](#third-party) or [Custom App Integration](#self). When you create a WeCom integration, **Application version** defaults to **Self-built app (recommended)**; the other option is **Third-party app (scan to install)**. War Room and @ AI SRE in group chats are available only with a custom app.
@@ -25,7 +25,7 @@ Choose one of two methods: [Third-Party App Integration](#third-party) or [Custo As a WeCom service provider, Flashduty provides a long-term free version of the Flashduty app. This app requires WeCom API call authorization to use (passwordless login + message sending). This authorization currently supports **up to 60 days** free; beyond this usage period, Flashduty needs to purchase WeCom licenses for you before you can continue using it. -The third-party app mode is not available for private deployments. In this mode the enterprise ID is neither typed nor typeable: the enterprise an integration belongs to is set only by a QR bind session that WeCom vouches for. +The third-party app mode is not available for private deployments. In this mode the enterprise ID is neither typed nor typeable: the enterprise an integration belongs to is bound only through the WeCom QR confirmation. ### Install the Flashduty app in WeCom @@ -51,15 +51,15 @@ You can skip installing beforehand: choose **Flashduty app not installed yet** w - Go to **Integration Center** → **New Integration**, choose **WeCom**, select **Third-party app (QR code)** as the **App Version**, and fill in **Name**. + Go to **Integration Center** → **New Integration**, choose **WeCom**, select **Third-party app (scan to install)** as the **Application version**, and fill in **Name**. - Pick one of the two options under **Enterprise binding method**, depending on whether the enterprise has the app installed: + Pick one of the two options under **How to bind your enterprise**, depending on whether the enterprise has the app installed: | Option | When to choose it | What happens after saving | | :--- | :--- | :--- | | **Flashduty app not installed yet** (default) | The enterprise has never installed the Flashduty app | The WeCom authorization page opens; a WeCom admin scans to install the app, and the enterprise is bound once installation finishes | - | **Enterprise already installed the Flashduty app** | The app is already installed | Any member of that enterprise scans with the WeCom app to confirm; the enterprise is bound once the confirmation passes | + | **Flashduty app already installed** | The app is already installed | Any member of that enterprise scans with the WeCom app to confirm; the enterprise is bound once the confirmation passes | Click **Save**; a WeCom authorization dialog opens: @@ -67,22 +67,22 @@ You can skip installing beforehand: choose **Flashduty app not installed yet** w - For "not installed yet", click **Open the WeCom authorization page** and complete the authorized install as a WeCom admin. If you are not the admin, send the link in the dialog to your admin; the dialog continues automatically once the admin finishes. - For "already installed", scan the QR code with the WeCom app, or send the link to any member of that enterprise to open in WeCom. - Once WeCom identifies the enterprise, the dialog shows "Enterprise xxx identified, waiting for you to click Confirm binding on the WeCom page…". On the **Confirm WeCom binding** page opened in WeCom, check the Flashduty account, the requester and the enterprise, then click **Confirm**. The console saves the integration automatically; you do not need to click Save again. + Once WeCom identifies the enterprise, the dialog shows "Found enterprise xxx. Waiting for Confirm to be clicked on the WeCom page…". On the **Confirm WeCom binding** page opened in WeCom, check the Flashduty account, the requester and the enterprise, then click **Confirm**. The console saves the integration automatically; you do not need to click Save again. After binding: -- **Enterprise ID** becomes read-only and shows "Confirmed through WeCom, it cannot be changed". Switching enterprises requires scanning again; there is no field to edit the ID directly. -- A bind session is valid for 30 minutes and can be used once. After it expires the dialog says "The link has expired, please regenerate" — click **Regenerate** and scan again. -- When binding fails the dialog explains why: `This enterprise has not installed the Flashduty app yet. Close this and choose "Not installed yet" to complete the authorized install.` (the enterprise has no app but "already installed" was chosen), `Binding was cancelled on the WeCom page.` (Cancel was clicked on the confirmation page), `WeCom did not complete the confirmation, please regenerate and try again.` (anything else). -- Submitting an enterprise ID in the form alone no longer works: without a confirmed bind session, saving returns `scan with WeCom to bind your enterprise first`. +- **Enterprise ID** becomes read-only and shows "Confirmed through WeCom and cannot be changed.". Switching enterprises requires scanning again; there is no field to edit the ID directly. +- A bind session is valid for 30 minutes and can be used once. After it expires the dialog says "The link has expired. Generate a new one." — click **Generate again** and scan again. +- When binding fails the dialog explains why: `This enterprise has not installed the Flashduty app. Close this dialog, choose "Flashduty app not installed yet", and install it first.` (the enterprise has no app but "already installed" was chosen), `The binding was cancelled on the WeCom page.` (Cancel was clicked on the confirmation page), `WeCom did not complete the confirmation. Generate a new link and try again.` (anything else). +- Submitting an enterprise ID in the form alone no longer works: without a confirmed bind session, an API submission is refused with `scan with WeCom to bind your enterprise first`; the console flow never shows this message. ## Custom app integration -War Room and @ AI SRE in group chats work only in this mode, which is also the default **App Version** when you create a WeCom integration. +War Room and @ AI SRE in group chats work only in this mode, which is also the default **Application version** when you create a WeCom integration. ### Create the integration and fill in the credentials @@ -102,7 +102,7 @@ War Room and @ AI SRE in group chats work only in this mode, which is also the d If your WeCom is a private deployment, also fill in `Endpoint`. The Flashduty service must be able to reach that `Endpoint`; you can set up **whitelist authorization** for it. - **Access Domain** takes one domain only: no `https://`, no path, for example `flashduty.example.com`. + **Access Domain** takes one domain only: no `https://`, no path, and no port when Flashduty hosts it, for example `flashduty.example.com`. Flashduty issues and renews the HTTPS certificate for this domain and proxies the traffic to it, so you do not need your own Nginx or your own certificate. WeCom requires the trusted domain and the callback URL to use this domain. @@ -113,11 +113,11 @@ War Room and @ AI SRE in group chats work only in this mode, which is also the d Click **Save**. The `Token` and `EncodingAESKey` for receive messages and for the smart robot are all generated by Flashduty; you do not fill them in when creating the integration, you generate them in the matching step on the detail page. - After saving, the **Complete the configuration in WeCom** section on the detail page walks you through the rest. + After saving, the **Finish setup in WeCom** section on the detail page walks you through the rest. -## Complete the configuration in WeCom +## Finish setup in WeCom The detail page lists six steps in this order, numbered the same as this section. Steps whose outcome WeCom reports are ticked automatically; for the steps nothing reports back, click the confirm button inside the step once you have done it, which requires permission to edit the integration. @@ -131,22 +131,22 @@ Add the CNAME records shown in step 1 of the detail page at the DNS provider tha | Purpose | Type | Host record | Value | | :--- | :--- | :--- | :--- | -| Certificate hosting | CNAME | `_acme-challenge.` | The dns-01 delegation target shown on the page | -| Mainland China entry | CNAME | `` | `cn.wecom.flashduty.com` | +| Certificate | CNAME | `_acme-challenge.` | The dns-01 delegation target shown on the page | +| Mainland entry | CNAME | `` | `cn.wecom.flashduty.com` | | International entry | CNAME | `` | `wecom.flashduty.com` | -Use the values shown on the page. The **certificate hosting** record proves the domain belongs to you, and Flashduty uses it to issue the HTTPS certificate through DNS-01 validation; the two **access entry** records route traffic to Flashduty, and one of them is enough. +Use the values shown on the page. The **Certificate** record proves the domain belongs to you, and Flashduty uses it to issue the HTTPS certificate through DNS-01 validation; the two **access entry** records route traffic to Flashduty, and one of them is enough. Which entry to use: the international entry `wecom.flashduty.com` (Hong Kong) is the default and works for any domain; if the domain has completed ICP access filing on Alibaba Cloud, you can use the mainland entry `cn.wecom.flashduty.com` instead for faster access inside China; if your DNS provider supports region-based resolution, configure both. A domain without Alibaba Cloud ICP access filing that points at the mainland entry is blocked by Alibaba Cloud. -Once the records resolve and the certificate is issued, step 1 is ticked automatically and shows "In effect · certificate issued". While records are missing it shows "N record(s) not in effect"; when every record resolves but the certificate is still being issued it shows "Issuing certificate". Click **Check again** to read the state again. +Once the records resolve and the certificate is issued, step 1 is ticked automatically and shows "Resolving · Certificate issued". While records are missing it shows "N record(s) not resolving yet"; when every record resolves but the certificate is still being issued it shows "Issuing certificate". Click **Check again** to read the state again. -If a status page or another Flashduty account already holds this domain, Flashduty cannot host it for this integration and step 1 shows "Domain already in use". To host it, edit the integration and use a different access domain; if the domain really is yours, contact Flashduty support. +If a status page or another Flashduty account already holds this domain, Flashduty cannot host it for this integration and step 1 shows "Domain in use". To host it, edit the integration and use a different access domain; if the domain really is yours, contact Flashduty support. -An older integration that proxies to Flashduty through a gateway you run yourself does not point at the entries above. When Flashduty finds that `https:///ping` answers `pong`, step 1 shows "Own proxy · reachable" and skips DNS records and certificate hosting; the remaining steps still apply, except that in step 2 the verification file goes to the document root of your own server. +An older integration that proxies to Flashduty through a gateway you run yourself does not point at the entries above. When Flashduty finds that `https:///ping` answers `pong`, step 1 shows "Self-hosted proxy · Reachable" and skips DNS records and certificate hosting; the remaining steps still apply, except that in step 2 the verification file goes to the document root of your own server. ### 2. Set the trusted domain @@ -157,7 +157,7 @@ On the app details page, go to **Developer Interface** → **Web Authorization & Then on that page click **Request domain verification**, download `WW_verify_xxx.txt`, and back in step 2 of the detail page click **Upload verification file** to upload it. WeCom fetches this file from the document root of the domain, and Flashduty serves it there (WeCom follows no redirect, so both http and https must serve it directly). -Once WeCom has fetched it, step 2 is ticked automatically and shows "Verification file fetched by WeCom"; while waiting for WeCom to fetch an uploaded file it shows "Waiting for WeCom to fetch", and before uploading it shows "Waiting for the verification file". +Once WeCom has fetched it, step 2 is ticked automatically and shows "WeCom fetched the verification file"; while waiting for WeCom to fetch an uploaded file it shows "Waiting for WeCom to fetch it", and before uploading it shows "Verification file not uploaded". Back in WeCom, click **OK** to finish the verification. Fill the same domain into the OAuth2.0 web authorization callback domain on the same page. @@ -166,10 +166,10 @@ WeCom requires the domain's ICP filing entity to be the same enterprise or a rel -For a private deployment, and for the older self-proxied integrations described above, put the verification file in the document root yourself: both http and https must serve it directly, with no redirect. Then click **I have verified** to record the step. +For a private deployment, and for the older self-proxied integrations described above, put the verification file in the document root yourself: both http and https must serve it directly, with no redirect. Then click **I've verified it** to record the step. -### 3. Set the API receive messages +### 3. Set up API message receiving On the app details page, go to **Receive Messages** → **Set API Receive**, fill in the three items shown in step 3 of the detail page, and click **Save**: @@ -185,7 +185,7 @@ WeCom calls the URL immediately when you save. Once the verification passes, ste Do not click **Random generate** in WeCom: use the `Token` and `EncodingAESKey` Flashduty generates, so both sides match. -`Token` and `EncodingAESKey` are shown only once and only a masked value remains after you leave the page, so copy them into WeCom right away. To replace them, click **Regenerate**: the pair already saved stops working immediately, so any old value still in WeCom must be replaced and saved, otherwise callbacks stop arriving; because this step records a fingerprint of the credentials, it goes back to "Waiting for WeCom to verify" and is ticked again only after WeCom verifies the new pair. +`Token` and `EncodingAESKey` are shown only once and only a masked value remains after you leave the page, so copy them into WeCom right away. To replace them, click **Generate again**: the pair already saved stops working immediately, so any old value still in WeCom must be replaced and saved, otherwise callbacks stop arriving; it goes back to "Waiting for WeCom to verify" and is ticked again only after WeCom verifies the new pair. ![WeCom: Custom App Integration screenshot](https://docs-cdn.flashcat.cloud/images/png/919bd2722c75513ce1d301779b39c3bf.png) @@ -197,7 +197,7 @@ At the bottom of the app details page, click **Trusted Enterprise IP** to config WeCom only allows configuring the trusted IP after step 2 or step 3 is done. -Step 4 is determined by Flashduty calling one WeCom API with the app's `access_token`: a successful call shows "In effect"; WeCom answering `60020` shows "Not configured yet", meaning the trusted IP is still missing; any other error code is shown as WeCom returned it; an unreachable WeCom shows "Check failed". Click **Check again** to read the state again. +Step 4 is determined by Flashduty running one check against WeCom: a successful check shows "Active"; WeCom answering `60020` shows "Not set up", meaning the trusted IP is still missing; any other error code is shown as WeCom returned it; an unreachable WeCom shows "Check failed". Click **Check again** to read the state again. ![WeCom: Custom App Integration screenshot](https://docs-cdn.flashcat.cloud/images/png/fe3b2b788dda5d331148ba0946631b91.png) @@ -207,9 +207,9 @@ The IP shown on the detail page is the correct egress IP only while no private ` ### 5. Set the workspace app homepage -On the app details page, click set on **Workspace App Homepage**, choose **Web page**, and enter the address shown in step 5 of the detail page. That address is an OAuth2 authorization link built from the access domain, with `redirect_uri` pointing at `https://?app_type=wecom_app...`: Flashduty forwards requests carrying `app_type=wecom_app` on the access domain to the console, which signs the member in with the OAuth code, so there is no separate "frontend trusted domain" to configure anymore. +On the app details page, click set on **Workspace App Homepage**, choose **Web page**, and enter the address shown in step 5 of the detail page. A newly created integration has no separate "frontend trusted domain" to configure. -WeCom does not report this setting back, so click **I have set it** after configuring it. +WeCom does not report this setting back, so click **I've set it** after configuring it. ![WeCom: Custom App Integration screenshot](https://docs-cdn.flashcat.cloud/images/png/595a71dd5624a37312676e83c45d79c4.png) @@ -277,7 +277,7 @@ The Smart Robot uses its **own set of credentials** (Token and Encoding-AESKey), **Do not click "Random generate" in WeCom**: both sides must use the same pair Flashduty generated. - The plaintext is shown only at generation time; afterwards only a masked value remains. Clicking **Regenerate** makes the previous pair stop working immediately: replace the old values in WeCom, save, and verify the callback URL again (the step is ticked again only after that verification passes). + The plaintext is shown only at generation time; afterwards only a masked value remains. Clicking **Generate again** makes the previous pair stop working immediately: replace the old values in WeCom, save, and verify the callback URL again (the step is ticked again only after that verification passes). @@ -315,7 +315,7 @@ Check in order: -This usually means the `Token` or `Encoding-AESKey` is wrong. The Smart Robot's credentials and the custom app's **Receive Messages** credentials are **two independent sets**. Regenerate the matching set in step 6 (Smart Robot) or step 3 (Receive Messages) of the detail page, enter the new values in WeCom and save, then verify the callback URL again. +This usually means the `Token` or `Encoding-AESKey` is wrong. The Smart Robot's credentials and the custom app's **Receive Messages** credentials are **two independent sets**. Generate the matching set again in step 6 (Smart Robot) or step 3 (Receive Messages) of the detail page, enter the new values in WeCom and save, then verify the callback URL again. diff --git a/en/on-call/integration/webhooks/custom-actions.mdx b/en/on-call/integration/webhooks/custom-actions.mdx index b9b31f38..496c1e3a 100644 --- a/en/on-call/integration/webhooks/custom-actions.mdx +++ b/en/on-call/integration/webhooks/custom-actions.mdx @@ -30,7 +30,7 @@ Each channel can have a maximum of 5 custom actions. You can't select a channel ## Edit a saved action -Once saved, the Endpoint, the custom Headers values, and the custom request body are treated as secrets and are never shown back: the integration detail page shows a **Saved; hidden for security reasons** hint in place of the Endpoint, and the Headers and custom Payload values read as `******`. +Once saved, the Endpoint, the custom Headers values, and the custom request body are treated as secrets and are never shown back: the integration detail page shows a **Saved. Hidden for security.** hint in place of the Endpoint, and the Headers and custom Payload values read as `******`. The edit form pre-fills those fields with `******`, and saving follows these rules: diff --git a/en/platform/permission-design.mdx b/en/platform/permission-design.mdx index 90fc8dfe..fa20a080 100644 --- a/en/platform/permission-design.mdx +++ b/en/platform/permission-design.mdx @@ -145,7 +145,7 @@ For creating and managing custom menus themselves, use **Platform Management → | Permission | Type | Description | | --- | --- | --- | | **Applications Manage** | Manage | Create, configure and manage frontend monitoring apps, set sampling rules and alert policies, and so on | - | **Dashboards Manage** | Manage | Create, edit and delete custom dashboards in Insights, and restore deleted ones from the recycle bin (kept for 30 days). See [Custom Dashboards](/en/rum/analytics/custom-dashboards) | + | **Dashboards Manage** | Manage | Create, edit and delete custom dashboards in Insights, and restore deleted ones from the trash (kept for 30 days). See [Custom dashboards](/en/rum/analytics/custom-dashboards) | | **Performance Read** | Read | View frontend performance monitoring issues, and so on | | **Error Tracking Read** | Read | View frontend issues, and so on | | **Session Explorer Read** | Read | View event details, and so on | diff --git a/en/rum/analytics/custom-dashboards.mdx b/en/rum/analytics/custom-dashboards.mdx index a6e5f3a7..47bbd240 100644 --- a/en/rum/analytics/custom-dashboards.mdx +++ b/en/rum/analytics/custom-dashboards.mdx @@ -17,7 +17,6 @@ A custom dashboard is an extra tab in the **Insights** tab bar, next to the buil | Edit mode | `/rum/dashboard/custom/:id?edit=1` | Opens straight into editing; changes go into a browser draft first | | New dashboard | `/rum/dashboard/custom/new?edit=1` | From the **+** menu at the right of the tab bar | | Dashboard management | `/rum/dashboard/manage` | The dashboard list: search, delete, trash | -| Legacy addresses | `/rum/dashboards`, `/rum/dashboards/:id` | Redirect to the addresses above | The **+** menu at the right of the tab bar has two items: @@ -37,12 +36,10 @@ The panel editor's **Visualization** offers four kinds: | Type | Description | |------|-------------| -| **Time series** (`time_series`) | The measure over time, drawn as lines or bars | -| **Table** (`table`) | One row per group, with drill-down (see [Table drill-down](#table-drill-down)) | -| **Stat** (`stat`) | One key number, with optional threshold colors; a stat does not use a group-by | -| **Text** (`text`) | Markdown text, bound to no query | - -The dashboard definition also accepts **bar** (`bar`) and **top list** (`top_list`) panels, which a dashboard written through the API can contain, but the RUM console does not render those two charts yet: the type picker shows them as "Coming in a later release" and they cannot be selected. +| **Time series** | The measure over time, drawn as lines or bars | +| **Table** | One row per group, with drill-down (see [Table drill-down](#table-drill-down)) | +| **Stat** | One key number, with optional threshold colors; a stat does not use a group-by | +| **Text** | Markdown text, bound to no query | ## Adding a panel @@ -78,7 +75,7 @@ A panel query has the same parts as the Explorer's analytics view: Group-by fields are limited by field cardinality, exactly as in the Explorer (see [Analytics · Group-by field](../explorer/analytics#group-by-field)): a field that is an event's own ID cannot be grouped by, and any other high-cardinality field takes Count or Sum only, with a time range of at most 7 days. -A panel's query is compiled in the browser and sent to `/rum/data/query`; when the result is estimated from a sample, the panel shows a **Sampled** badge and its explanation. +When the result is estimated from a sample, the panel shows a **Sampled** badge and its explanation. ## Dashboard variables @@ -87,8 +84,8 @@ The variable bar sits at the top of the dashboard. A new dashboard starts with t | Variable kind | Description | |---------------|-------------| | Built-in `$app` | The page's current application. Choosing **Page application** in a panel query is the same as using `$app`; only an application's `application_id` can reference it | -| Field variable (`rum_field`) | Candidates are the values of one RUM field; multi-select, All allowed | -| Custom variable (`custom`) | Candidates you type in (one per line, `label=value` or a bare value); single or multi-select | +| Field variable | Candidates are the values of one RUM field; multi-select, All allowed | +| Custom variable | Candidates you type in (one per line, `label=value` or a bare value); single or multi-select | - Reference a variable in a panel's DQL as `${name}` or `$name`, such as `env:${env}`. A variable with several values expands to `(a OR b)`; with **All** selected, or with no value, the whole condition holding the reference (`env:${env}`, `-env:${env}`, `NOT env:${env}`, and a value list containing it) is dropped rather than queried as an empty value. - References are replaced in the browser before the query runs, so a panel's DQL has to fill in into a complete statement to return data. @@ -137,7 +134,7 @@ RUM dashboards have no folders: they are not grouped, and the management page ha ## Permissions -Creating, editing, deleting and restoring a dashboard needs the **Dashboards Manage** permission (`rumDashboard:manage`). Without it: +Creating, editing, deleting and restoring a dashboard needs the **Dashboards Manage** permission. Without it: - the **+** menu has no **New dashboard**, and the management page has no **New dashboard** button and no delete or restore entry; - a dashboard has no **Edit**, and its **⋯** menu has no **Clone** or **Delete**; @@ -156,12 +153,12 @@ Creating, editing, deleting and restoring a dashboard needs the **Dashboards Man | Variables | 20 | | Candidates per variable | 1000 | | Data links per panel | 5 | -| Queries in a time series / top list panel | 1–26 | -| Queries in a table / stat / bar panel | exactly 1 | -| Values in a top list panel | 1–100 | +| Queries in a time series panel | 1–26 | +| Queries in a table / stat panel | exactly 1 | +| Markdown size in a text panel | 64 KB | | Title length | 189 characters | | Description length | 1024 characters | -| Dashboard definition size | 1 MB (request body 2 MB) | +| Dashboard definition size | 1 MB | | Default time ranges | `now-15m`, `now-30m`, `now-1h`, `now-3h`, `now-6h`, `now-12h`, `now-24h`, `now-7d` | | Auto refresh options | off, `30s`, `1m`, `5m` | diff --git a/en/rum/explorer/analytics.mdx b/en/rum/explorer/analytics.mdx index 1539e56a..acf278ec 100644 --- a/en/rum/explorer/analytics.mdx +++ b/en/rum/explorer/analytics.mdx @@ -39,6 +39,8 @@ In the legend, table and CSV, Count and Distribution measures read "Events"; oth The **Group-by field** splits the measure so each value is shown separately. Use **top** to keep the top `5`, `10` (default), `20` or `50` groups; without a group-by, the result is a single number. Array fields cannot be grouped by, and a **Distribution** does not use a group-by. + + ### Cardinality limits on the group-by field A group-by is also limited by field cardinality, because a high-cardinality field makes for an expensive grouping: @@ -90,7 +92,7 @@ From the top-right corner of the chart: - **Download image**: exports a PNG at 2× resolution. Not available for tables or a single ungrouped number. - **CSV**: exports the chart data. A timeseries has one row per time, group and measure; other charts have one row per group. -### Saving to a dashboard +## Saving to a dashboard **Save to dashboard** in the same row turns the current question into a [custom dashboard](../analytics/custom-dashboards) panel: diff --git a/en/rum/explorer/data-query.mdx b/en/rum/explorer/data-query.mdx index 234dd1e7..9b0668aa 100644 --- a/en/rum/explorer/data-query.mdx +++ b/en/rum/explorer/data-query.mdx @@ -142,7 +142,7 @@ For numeric type attributes, comparison operators can be used: ## Path fields and URL normalization -`view_url_path` and `view_url_path_group` (and the same-named path fields on session, error, resource, action and long task events) are derived by normalizing the page URL, and both can be filtered, grouped and charted on: +`view_url_path` and `view_url_path_group` (and the same-named path fields on session, error, resource, action and long task events) are derived by normalizing the page URL, and both can be filtered and charted on. Grouping by `view_url_path` is a high-cardinality grouping (Count and Sum only, time range up to 7 days; see [Cardinality limits on the group-by field](/en/rum/explorer/analytics#cardinality-limits)); to summarize by page, group by `view_url_path_group`: | Raw URL | `view_url_path` | `view_url_path_group` | |---------|-----------------|------------------------| diff --git a/zh/ai-sre/sessions.mdx b/zh/ai-sre/sessions.mdx index 873f25dd..3cad6225 100644 --- a/zh/ai-sre/sessions.mdx +++ b/zh/ai-sre/sessions.mdx @@ -277,12 +277,12 @@ IM 和 API 会话不显示草稿卡,Agent 会在回复开头给出“审阅并 | --- | --- | | `seg_NNN.md` | 被第 N 次摘要替换掉的那段原文 | | `tail.md` | 最近一次摘要之后的全部内容(始终存在,可能为空) | -| `index.md` | 每个文件覆盖的条数、起止时间与校验哈希 | +| `index.md` | 每个文件覆盖的条数与起止时间 | 记录的是**当时交给 Agent 的内容**:图片与附件只留下 `[inline …]` / `[file …]` 占位,没有原文;被截断的大输出只保留完整结果的 `file_path`;子代理的内部步骤在它自己的会话里,不写进这些文件。 -原文召回只对**携带该工具**的 Agent 生效:主会话与拥有完整工具的 `general` 子代理会在摘要后拿到召回提示;像只读的 `explore` 这类受限子代理没有该工具,压缩后只能依靠摘要。 +原文召回只对**带有该工具**的 Agent 生效:主会话和拥有完整工具的子代理可以用;只读等受限子代理没有该工具,压缩后只能依靠摘要。 ## 选择运行环境 diff --git a/zh/changelog/changelog.mdx b/zh/changelog/changelog.mdx index 9c1cd5d1..c49732d3 100644 --- a/zh/changelog/changelog.mdx +++ b/zh/changelog/changelog.mdx @@ -22,11 +22,11 @@ keywords: ["更新日志", "产品发布", "功能更新", "Flashduty", "版本 ### 企业微信自建应用:访问域名由 Flashduty 托管 -集成企业自建应用只需要填一个必填的 **访问域名**(只填域名本身,且须是本企业备案主体的域名)。保存后详情页第 1 步给出 DNS 记录:一条「证书托管」的 dns-01 委派,证明域名归你们所有,由 Flashduty 据此签发并自动续期 HTTPS 证书;一条访问入口 CNAME,大陆入口指向 `cn.wecom.flashduty.com`、国际入口指向 `wecom.flashduty.com`,配其中一条即可,之后由 Flashduty 直接反代,不用自己搭 Nginx 或申请证书。第 2 步在企业微信「设置可信域名 → 申请校验域名」下载 `WW_verify_xxx.txt` 后上传到详情页,由 Flashduty 在该域名根路径返回(企业微信不跟随重定向,http 与 https 都要能取)。域名已被状态页或其他 Flashduty 账户占用时不能托管;私有化部署没有第 1 步。详见[企业微信](/zh/on-call/integration/instant-messaging/wecom)。 +集成企业自建应用只需要填一个必填的 **访问域名**(只填域名本身,且须是本企业备案主体的域名)。保存后详情页第 1 步给出 DNS 记录:一条「证书托管」的 dns-01 委派,证明域名归您所有,由 Flashduty 据此签发并自动续期 HTTPS 证书;一条访问入口 CNAME,大陆入口指向 `cn.wecom.flashduty.com`、国际入口指向 `wecom.flashduty.com`,配其中一条即可,之后由 Flashduty 直接反代,不用自己搭 Nginx 或申请证书。第 2 步在企业微信「设置可信域名 → 申请校验域名」下载 `WW_verify_xxx.txt` 后上传到详情页,由 Flashduty 在该域名根路径返回(企业微信不跟随重定向,http 与 https 都要能取)。域名已被状态页或其他 Flashduty 账户占用时不能托管;私有化部署没有第 1 步。详见[企业微信](/zh/on-call/integration/instant-messaging/wecom)。 ### 飞书集成:扫码创建由 Flashduty 托管的应用 -新建飞书集成时表单给出三选一:**创建新应用(推荐)** / **使用已有应用** / **改为手动填写凭证**。选前两者保存后弹出飞书二维码,扫码后 Flashduty 在你的飞书企业里创建(或接管)应用并完成全部配置——事件与回调的加密策略和请求地址、机器人与网页应用能力、已启用功能需要的权限,然后发布一个新版本;凭证由 Flashduty 保管,详情页只显示 App ID、当前版本与状态。飞书要求管理员审批两次(先审应用,再审配置版本),详情页与集成列表据此显示「待管理员审核(1/2)」「待管理员审核配置(2/2)」「新版本审核中」等状态,每 10 秒自动核对,也可以 **复制消息给管理员**。手动填凭证接入的老集成可在详情页点 **升级为自动管理** 扫码升级,App Secret 与推送地址不变、通知不中断;私有化部署连不到飞书扫码接口,只能手动填写凭证。详见[飞书/Lark](/zh/on-call/integration/instant-messaging/lark)。 +新建飞书集成时表单给出三选一:**创建新应用(推荐)** / **使用已有应用** / **改为手动填写凭证**。选前两者保存后弹出飞书二维码,扫码后 Flashduty 在您的飞书企业里创建(或接管)应用并完成全部配置——事件与回调的加密策略和请求地址、机器人与网页应用能力、已启用功能需要的权限,然后发布一个新版本;凭证由 Flashduty 保管,详情页只显示 App ID、当前版本与状态。飞书要求管理员审批两次(先审应用,再审配置版本),详情页与集成列表据此显示「待管理员审核(1/2)」「待管理员审核配置(2/2)」「新版本审核中」等状态,每 10 秒自动核对,也可以 **复制消息给管理员**。手动填凭证接入的老集成可在详情页点 **升级为自动管理** 扫码升级,App Secret 与推送地址不变、通知不中断;私有化部署连不到飞书扫码接口,只能手动填写凭证。详见[飞书/Lark](/zh/on-call/integration/instant-messaging/lark)。 diff --git a/zh/developer/cli.mdx b/zh/developer/cli.mdx index fef395e1..6a7f5450 100644 --- a/zh/developer/cli.mdx +++ b/zh/developer/cli.mdx @@ -900,7 +900,7 @@ flashduty integration delete # 删除;仍被其他 | `--is-test` | 执行插件的连通性测试。创建时省略即不测试;更新时只在确实提交了 `settings` 的调用中测试,只改名称/备注不会发测试。结果看响应的 `test.success` 与 `test.message`:HTTP 调用成功不等于测试通过,响应中没有 `test` 表示本次没有执行测试 | | `--data` | 完整 JSON 请求体;类型专属配置 `settings` 只能通过它传入,可接受的键由 `plugin_type` 决定 | -`settings` 的读回与密钥语义(与上文 `monit` 数据源的处理一致): +`settings` 的读回与密钥语义: - `list` 与 `info` 返回的 `settings` 中,每个**已保存的密钥类值**——密码、App Secret、Token、加密密钥、Webhook 地址、HTTP 拉取的 URL 与 Body、Headers、自定义 Body——一律显示为 `******`;未设置的值保持为空。**`integration_key` 是例外,按原值返回**(它用于接入推送,与账户级 `app_key` 不是一回事)。依赖 `integration info --json` 读取凭据的脚本拿到的是掩码;需要新密钥时用 `key-rotate` 轮换,旧密钥会立即失效。 - `update` 提交的 `settings` 是整体替换:**已知敏感字段被省略或传 `******` 时保留已存密钥**,显式传空 `headers: {}` 会清空 Headers。但**改密钥的发送目标时不能沿用已存密钥**——例如改 `endpoint`、`url`,或改数据库的主机/端口——必须同时传入真实密钥,否则请求被拒。 diff --git a/zh/monitors/alert-rules/mysql.mdx b/zh/monitors/alert-rules/mysql.mdx index 04f5dd39..c746787a 100644 --- a/zh/monitors/alert-rules/mysql.mdx +++ b/zh/monitors/alert-rules/mysql.mdx @@ -20,7 +20,7 @@ Monitors 支持使用标准 SQL 语法对 MySQL 进行查询,并根据查询 变量需要告警引擎 `v0.76.0` 或以上版本;服务该规则数据源的集群内有实例低于此版本时,保存或启用会被拒绝,错误提示以 `MySQL relate or recovery queries with variables require` 开头并指明集群与所需版本。不使用变量的规则不受该版本限制。 -变量替换后的 SQL 到运行期才确定,保存时无法预检。告警引擎按告警实例渲染变量,再检查渲染结果是否为只读查询,不是纯只读 SELECT 时该次评估失败。 +变量替换后的 SQL 到运行期才确定,保存时无法预检。告警引擎按告警实例渲染变量,再检查渲染结果是否为只读查询,不是只读查询时该次评估失败。 ## 阈值判定模式 diff --git a/zh/monitors/engine/self-monitoring.mdx b/zh/monitors/engine/self-monitoring.mdx index 88f09f0a..602e2a55 100644 --- a/zh/monitors/engine/self-monitoring.mdx +++ b/zh/monitors/engine/self-monitoring.mdx @@ -22,7 +22,7 @@ scrape_configs: Flashduty 不会代为抓取这些指标。控制台的**告警引擎状态**页展示的是实例清单、心跳与版本,不含这些指标;指标只存在于您自己的时序库中。 -**不要让被监控的这台引擎评估它自己的指标告警。** 引擎进程退出、租约不处于 `ACTIVE` 或与中心断连时,跑在这台引擎上的规则既不进行评估也发不出事件。引擎是否存活请用中心侧的[引擎失联告警](/zh/monitors/engine/engine-lost-alert)兜底;下文这些指标告警适合放在您自己的 Prometheus/Alertmanager 上,或另一个引擎集群上评估。 +**不要让被监控的这台引擎评估它自己的指标告警。** 引擎进程退出、租约不处于 `ACTIVE`(例如与中心的心跳中断)时,跑在这台引擎上的规则既不进行评估也发不出事件。引擎是否存活请用中心侧的[引擎失联告警](/zh/monitors/engine/engine-lost-alert)兜底;下文这些指标告警适合放在您自己的 Prometheus/Alertmanager 上,或另一个引擎集群上评估。 ## 与中心的连接 @@ -32,7 +32,7 @@ Flashduty 不会代为抓取这些指标。控制台的**告警引擎状态**页 | `monit_edge_websocket_connected` | Gauge | — | 当前是否持有到中心的 WebSocket 连接,1 为在线、0 为不在线。控制台的查询预览、实体运行状态都走这条连接 | | `monit_edge_websocket_connects_total` | Counter | — | 拨号成功的累计次数,含首次连接。持续增长说明连接在反复重建 | | `monit_edge_websocket_dial_failures_total` | Counter | — | 拨号失败的累计次数。它一直涨而 `connects_total` 不涨,就是连不上中心(网络、代理或中心拒绝) | -| `monit_edge_lease_info` | Gauge | `session_mode`、`phase` | 恒为 1,用 `phase` 读租约阶段:`BOOT`、`INITIALIZING`、`ACTIVE`、`FENCED`。只有 `ACTIVE` 阶段才评估规则并投递事件;`session_mode` 目前恒为 `lease_v1` | +| `monit_edge_lease_info` | Gauge | `session_mode`、`phase` | 恒为 1,用 `phase` 读租约阶段:`BOOT`、`INITIALIZING`、`ACTIVE`、`FENCED`。只有 `ACTIVE` 阶段才评估规则并投递事件 | | `monit_edge_lease_renewal_age_seconds` | Gauge | — | 距上一次心跳续租成功过去的秒数。心跳默认每 10 秒一次,可用 `alerter.heartbeatInterval` 调整;租约有效期由中心下发 | | `monit_edge_webapi_access_blocked` | Gauge | — | 1 表示中心因授权或 license 原因永久拒绝,该进程已停止与中心通信,恢复授权后需要重启引擎 | @@ -40,8 +40,8 @@ Flashduty 不会代为抓取这些指标。控制台的**告警引擎状态**页 # 连接断了 5 分钟以上 monit_edge_websocket_connected == 0 -# 租约不在 ACTIVE 超过 2 分钟:引擎既不评估也不投递 -(sum(monit_edge_lease_info{phase="ACTIVE"}) or vector(0)) == 0 +# 租约不在 ACTIVE 超过 2 分钟:引擎既不评估也不投递(按实例分别判断) +monit_edge_lease_info{phase!="ACTIVE"} == 1 # 最近 10 分钟内出现过拨号失败 increase(monit_edge_websocket_dial_failures_total[10m]) > 0 @@ -57,7 +57,7 @@ increase(monit_edge_websocket_dial_failures_total[10m]) > 0 | 指标 | 类型 | 标签 | 说明 | |------|------|------|------| -| `monit_edge_alert_rule_sync_age_seconds` | Gauge | — | 距上一次同步成功过去的秒数 | +| `monit_edge_alert_rule_sync_age_seconds` | Gauge | — | 距上一次同步成功过去的秒数;从未同步成功时为 0,此时看 `monit_edge_alert_rule_sync_status{outcome="pending"}` | | `monit_edge_alert_rule_sync_status` | Gauge | `outcome` | 最近一次同步的结果,值恒为 1,只在当前结果的 `outcome` 上出现一条:`success`、`error`,或 `pending`(还没同步过) | | `monit_edge_alert_rule_sync_rules` | Gauge | — | 最近一次同步成功时中心分配给这台引擎的规则数。它掉到 0 或远小于预期时,先确认规则是否被停用、数据源绑定或实例分片是否变化 | @@ -99,7 +99,7 @@ sum by (error_type) (increase(monit_edge_alert_rule_evaluations_total{outcome="e ``` -`monit_edge_alert_rule_failing_duration_seconds` 涨起来时,对照 `monit_edge_alert_rule_scopes{state="failing"}` 看范围:同一数据源下大量作用域同时失败,通常是这个数据源不可用;只有个别作用域失败,问题在那条规则的查询或阈值配置上。单条规则的执行状态和错误摘要见控制台的**运行详情**,说明见[规则运行详情](/zh/monitors/quickstart/quickstart)。 +`monit_edge_alert_rule_failing_duration_seconds` 涨起来时,对照 `monit_edge_alert_rule_scopes{state="failing"}` 看范围:同一数据源下大量作用域同时失败,通常是这个数据源不可用;只有个别作用域失败,问题在那条规则的查询或阈值配置上。单条规则的执行状态和错误摘要见控制台的**运行详情**,说明见[规则运行详情](/zh/monitors/quickstart/quickstart#规则运行详情)。 ## 查询并发与版本 @@ -122,10 +122,11 @@ sum(increase(monit_edge_alert_query_admission_rejected_total{reason="timeout"}[1 | 告警 | PromQL | 持续 | 处置 | |------|--------|------|------| | WebSocket 断连 | `monit_edge_websocket_connected == 0` | 5 分钟 | 控制台无法通过该引擎查询;检查到中心的网络与代理,用 `dial_failures_total` 的增长确认是否在反复重连 | -| 租约不在 ACTIVE | `(sum(monit_edge_lease_info{phase="ACTIVE"}) or vector(0)) == 0` | 2 分钟 | 引擎既不评估也不投递;看 `monit_edge_lease_renewal_age_seconds` 与心跳 | +| 租约不在 ACTIVE | `monit_edge_lease_info{phase!="ACTIVE"} == 1` | 2 分钟 | 引擎既不评估也不投递,按实例分别判断;看 `monit_edge_lease_renewal_age_seconds` 与心跳 | | 中心永久拒绝 | `monit_edge_webapi_access_blocked == 1` | 1 次 | 授权或 license 问题,恢复后重启引擎 | | 规则同步停滞 | `monit_edge_alert_rule_sync_age_seconds > 300` | 3 分钟 | 中心新建或修改的规则不生效;检查到中心的网络 | | 规则同步失败 | `monit_edge_alert_rule_sync_status{outcome="error"} == 1` | 3 分钟 | 同上,并结合引擎日志看同步报错 | +| 规则从未同步成功 | `monit_edge_alert_rule_sync_status{outcome="pending"} == 1` | 3 分钟 | `sync_age_seconds` 在从未同步成功时为 0,上一条告警不会触发;检查到中心的网络与引擎日志 | | 规则持续评估失败 | `max by (datasource_id) (monit_edge_alert_rule_failing_duration_seconds) > 300` | 1 次 | 对照 `monit_edge_alert_rule_scopes{state="failing"}` 判断是数据源不可用还是单条规则查询有问题 | | 查询准入排队超时 | `sum(increase(monit_edge_alert_query_admission_rejected_total{reason="timeout"}[10m])) > 0` | 3 分钟 | 某个数据源变慢占满了查询并发,这一轮评估被跳过 | diff --git a/zh/on-call/configuration/custom-forms.mdx b/zh/on-call/configuration/custom-forms.mdx index 53dc1a0d..4a4e9748 100644 --- a/zh/on-call/configuration/custom-forms.mdx +++ b/zh/on-call/configuration/custom-forms.mdx @@ -51,7 +51,7 @@ keywords: ["自定义表单", "故障创建", "故障认领", "故障关闭", " 配置了 **关闭故障** 表单后,用户无法在 IM 故障卡片上直接完成关闭:点关闭不会生效,机器人会回复一条引导消息,说明该故障需要填写关闭表单,并附一个 **前往填写关闭表单** 链接。该链接直达这条故障的详情页并自动打开关闭表单,填写提交后才完成关闭。 -飞书、钉钉、企业微信、Slack 和 Microsoft Teams 都支持这项引导。飞书另外做了两处处理:引导消息以临时消息的形式发给点击者,同群其他人看不到;同时在卡片上弹一条 toast,提示通过引导消息中的链接填写表单。 +飞书、钉钉、企业微信、Slack 和 Microsoft Teams 都支持这项引导。飞书另外做了两处处理:引导消息以临时消息的形式发给点击者,同群其他人看不到,并在卡片上提示通过引导消息里的链接填写表单。 ## 批量认领或关闭 diff --git a/zh/on-call/configuration/templates.mdx b/zh/on-call/configuration/templates.mdx index 304c55c1..cbbf6255 100644 --- a/zh/on-call/configuration/templates.mdx +++ b/zh/on-call/configuration/templates.mdx @@ -45,7 +45,7 @@ keywords: ["通知模板", "模板配置", "消息格式", "模板变量", "通 -**系统预置模板与只读**:系统预置模板(渲染示例、默认模板)和当前成员没有模板管理权限的模板,详情页一律是只读状态,标题旁标有 **系统预置 · 只读**(权限不足的模板不显示这个标识,但同样不能改)。要改成自己的内容,点右上角 **复制为新模板** 复制一份副本再改,副本名称预填「复制 原模板名」;复制需要「通知模板管理」权限和 On-call 专业版。 +**系统预置模板与只读**:系统预置模板和当前成员没有模板管理权限的模板,详情页一律是只读状态,标题旁标有 **系统预置 · 只读**(权限不足的模板不显示这个标识,但同样不能改)。要改成自己的内容,点右上角 **复制为新模板** 复制一份副本再改,副本名称预填「复制 原模板名」;复制需要「通知模板管理」权限和 On-call 专业版。 可编辑的模板,右上角是 **模板设置**(齿轮图标),用来改模板名称、管理团队和模板描述,不涉及正文;删除收在 **更多操作** ⋯ 菜单里,系统预置模板没有这个菜单,也不能删除。 @@ -166,7 +166,9 @@ keywords: ["通知模板", "模板配置", "消息格式", "模板变量", "通 ## 预览与发送测试 -模板详情页右侧是预览区:按所选故障渲染当前渠道的内容,下方带一行诊断信息和发送引擎给出的警告。预览区标题栏还有 **发送测试**,可以把编辑器里的当前内容真实发到渠道里看效果。 +模板详情页右侧是预览区:按所选故障渲染当前渠道的内容,下方带一行诊断信息和预览给出的警告。预览区标题栏还有 **发送测试**,可以把编辑器里的当前内容真实发到渠道里看效果。 + + ### 预览会提示什么 @@ -180,23 +182,21 @@ keywords: ["通知模板", "模板配置", "消息格式", "模板变量", "通 | 原生表格 N / Markdown 表格 N | 这次渲染里有几张表画成原生表格、几张画成 Markdown 表格;为 0 时不显示 | | 已用 / 上限 | 内容字节数和该渠道的大小上限,如 `12.3 KB / 30.0 KB`;渠道没有已知上限时只显示已用字节数 | -元信息上方的黄色提示来自发送引擎,出现任意一条就说明这条模板在真实发送时会变形或超限,保存前按提示精简: +元信息上方的黄色提示说明这条模板在真实发送时会变形或超限,出现任意一条,保存前按提示精简: | 提示 | 含义 | | :--- | :--- | -| `table_not_rendered` | 该渠道不渲染 Markdown 表格,接收方看到的是一行行带 `\|` 的原文,建议改写成列表 | -| `native_table_fallback` | 飞书应用开了 **表格转原生表格**,但这次有表格达不到原生表格的条件,会按 Markdown 表格发送 | -| `content_too_large` | 内容超过渠道大小上限 | - -`native_table_fallback` 的原因有 6 种:`too_many_tables`、`too_many_columns`、`too_many_rows`、`cell_too_long`、`unsupported_cell`、`card_too_large`,含义见[表格转原生表格](#表格转原生表格)。 +| 该渠道不渲染 Markdown 表格,接收方会看到原文。可以改写成列表。 | 钉钉、Slack、Telegram 机器人,企业微信机器人(Markdown v1),以及钉钉、Slack、企业微信、Teams 应用不渲染表格,接收方看到的是未渲染的表格原文,建议改写成列表 | +| 部分表格不能用原生表格发送,会改用 Markdown 表格。 | 飞书应用开了 **表格转原生表格**,但这次有表格达不到原生表格的条件,具体提示见[表格转原生表格](#native-tables) | +| 内容超过渠道大小上限 | 提示分 3 种,见下表 | -`content_too_large` 的处理分 3 种: +内容超过渠道大小上限时,提示分 3 种: -| 处理 | 含义 | +| 提示 | 含义 | | :--- | :--- | -| `rejected` | 渠道直接拒收,这条通知发不出去,真实告警也一样,必须精简模板 | -| `truncated` | 超出上限的部分被截断,通知仍会发出,各渠道的具体上限见下文 | -| `compact_fallback` | 企业微信机器人 Markdown v2 超过 4096 字节时改发精简摘要,不丢通知 | +| 内容超过渠道大小上限,发送会失败,真实告警也一样。请精简模板。 | 渠道直接拒收,这条通知发不出去,必须精简模板,例如飞书卡片超过 30 KB | +| 内容超过渠道大小上限,超出部分会被截断。 | 超出上限的部分被截断,通知仍会发出,各渠道的具体上限见下文 | +| 内容超过 4096 字节,发送时会改为精简摘要。 | 企业微信机器人 Markdown v2 超过 4096 字节时改发精简摘要,不丢通知 | ### 发送测试 @@ -214,7 +214,7 @@ keywords: ["通知模板", "模板配置", "消息格式", "模板变量", "通 | 群聊机器人 | 临时填机器人地址 | 与通知规则里填的地址相同,裸 token 或完整地址都行;Telegram 另填 Chat ID,Zoom 另填 Verify Token。这些值只用于这一次发送,不会保存 | | 邮件 | 自己的账号邮箱 | 账号没有邮箱时不能发送 | -- **我自己**:要求当前账号已关联该 IM 集成,未关联时发送按钮不可点,有权限的成员可以点 **去关联** 到接入向导完成关联。Slack 应用例外,不需要逐人关联(按工作区成员直接私信),但发送时仍要把你解析成该工作区的成员,解析不到会返回 `im_not_linked`。 +- **我自己**:要求当前账号已关联该 IM 集成,未关联时发送按钮不可点,有权限的成员可以点 **去关联** 到接入向导完成关联。Slack 应用例外,不需要逐人关联(按工作区成员直接私信),但发送时仍要把你解析成该工作区的成员,解析不到会提示「你的账号还没有关联这个 IM,无法私信」。 - **群聊**:只列出该机器人在这个集成里**已加入**的群(Slack 按频道,Teams 按团队下的频道或群聊)。群里所有成员都会收到这条测试消息。 - 同一渠道配了多个集成时,先在 **集成** 里选一个;只列已启用的集成。 @@ -226,34 +226,32 @@ keywords: ["通知模板", "模板配置", "消息格式", "模板变量", "通 发送结果有两种: -| 结果 | 渠道 | 提示 | -| :--- | :--- | :--- | -| `sent` | IM 应用 | 「已发送到你的飞书私聊」/「已发送到「群名」」 | -| `queued` | 群聊机器人与邮件 | 「已提交发送,通常几秒内送达;没收到请检查地址」 | +| 渠道 | 提示 | +| :--- | :--- | +| IM 应用 | 「已发送到你的飞书私聊」/「已发送到「群名」」 | +| 群聊机器人与邮件 | 「已提交发送,通常几秒内送达;没收到请检查地址」 | -IM 应用是同步调用平台接口,成功即 `sent`;机器人和邮件进通知队列异步投递,`queued` 只表示已提交,地址填错时不会有即时报错。 +IM 应用提示「已发送」才表示消息已送达;机器人和邮件的「已提交发送」只表示已提交,地址填错时不会有即时报错。 限制与常见失败原因: -- 每个成员每分钟最多发 5 条测试消息,超过返回 `rate_limited`。 -- 失败时控制台按错误码给出提示: +- 每个成员每分钟最多发 5 条测试消息,超过会提示「发送太频繁,请稍后再试」。 +- 失败时控制台给出下面的提示: -| 错误码 | 含义 | +| 提示 | 含义 | | :--- | :--- | -| `template_invalid` | 模板渲染失败(语法或执行出错),先修正模板 | -| `channel_not_supported` | 该渠道不支持发送测试 | -| `invalid_target` | 发送目标不完整或格式不对:机器人没填地址、Telegram 缺 Chat ID、Zoom 缺 Verify Token、群聊不属于该集成 | -| `integration_not_found` | 找不到这个集成,可能已被删除或停用,或不是该渠道的集成 | -| `im_not_linked` | 你的账号没有关联这个 IM,无法私信自己 | -| `rate_limited` | 发送太频繁 | -| `webhook_url_blocked` | 机器人地址只支持公网 `http`/`https`;填完整地址时内网地址会被拒绝。裸 token 由平台拼成厂商地址,不做这项检查 | -| `content_too_large` | 内容超过渠道大小上限,例如飞书卡片 30 KB | -| `send_failed` | 渠道返回失败,提示里带渠道的原始报错 | +| 模板渲染失败,请先修正模板 | 模板语法或执行出错 | +| 这个渠道不支持发送测试 | 该渠道不提供发送测试 | +| 发送目标填写不完整或格式不对 | 机器人没填地址、Telegram 缺 Chat ID、Zoom 缺 Verify Token,或群聊不属于该集成 | +| 找不到这个集成,可能已被删除或停用 | 集成已被删除或停用,或不是该渠道的集成 | +| 你的账号还没有关联这个 IM,无法私信 | 你的账号没有关联这个 IM,无法私信自己 | +| 发送太频繁,请稍后再试 | 超过每分钟 5 条的限制 | +| 地址不允许:只支持公网 http/https | 机器人地址只支持公网 `http`/`https`,填完整地址时内网地址会被拒绝;裸 token 由平台拼成厂商地址,不做这项检查。仅 SaaS 版本有此限制,私有化部署不受限 | +| 内容超过渠道大小上限 | 内容超过渠道大小上限,例如飞书卡片 30 KB | +| 渠道返回失败:渠道的原始报错 | 渠道返回失败,提示里带渠道的原始报错 | -测试发送与真实通知的差别:模板语法有错时,真实通知会静默回退成系统默认模板继续发,而发送测试直接报 `template_invalid`,把模板问题暴露出来。反过来,测试发送不写故障通知记录、不占发送锁、不进通知流水,所以它不会认领、关闭故障或创建作战室。 - -控制台调用的是 `POST /template/preview/send`,请求带 `template_id`、`type`、编辑器当前 `content`、`incident_id` 和渠道渲染开关,返回 `status`(`sent` / `queued`)或 `error_code` + `message`。 +测试发送与真实通知的差别:模板语法有错时,真实通知会静默回退成系统默认模板继续发,而发送测试直接提示「模板渲染失败,请先修正模板」,把模板问题暴露出来。反过来,测试发送不会在故障上留下通知记录,所以它不会认领、关闭故障或创建作战室。 ## 渠道模板 @@ -313,11 +311,13 @@ IM 应用是同步调用平台接口,成功即 `sent`;机器人和邮件进 飞书应用卡片底部的 **更多操作** 是一个下拉菜单,包含自定义操作、暂缓时长与取消认领等选项;**👥 创建作战室** 按钮单独显示,不在该菜单中。 + + ### 表格转原生表格 **飞书卡片 2.0** 下另有一个开关 **表格转原生表格**(默认关闭,随模板保存)。开启后,模板里的 GFM Markdown 表格会被识别出来,渲染成飞书卡片的原生表格组件,接收方看到的是可翻页的表格,而不是一行行带 `|` 的文本。 -原生表格是有限额的,任一条件不满足时,**这张卡片上的所有表格**一起退回 Markdown 文本发送: +原生表格有以下限额。单张卡片的原生表格超过 5 个,或卡片超过 30 KB 时,**这张卡片上的所有表格**都按 Markdown 文本发送;其余限额只影响超限的那一张表,它按 Markdown 文本发送,其他表格不受影响: | 限制 | 上限 | | :--- | :--- | @@ -327,18 +327,18 @@ IM 应用是同步调用平台接口,成功即 `sent`;机器人和邮件进 | 单元格内容 | 每格最多 2000 字节 | | 每页行数 | 每页显示 10 行,超出部分在卡片里翻页 | -触发退回的原因(本页[预览会提示什么](#预览会提示什么)里以 `native_table_fallback` 警告显示): +触发退回时,预览会给出对应提示(见[预览会提示什么](#preview-notices)): -| 原因 | 触发条件 | +| 提示 | 触发条件 | | :--- | :--- | -| `too_many_tables` | 这张卡片上的原生表格超过 5 个 | -| `too_many_columns` | 某张表超过 8 列 | -| `too_many_rows` | 某张表超过 100 行 | -| `cell_too_long` | 某个单元格超过 2000 字节 | -| `unsupported_cell` | 单元格里除纯文本和 `
` 换行外还有其它 Markdown 标记,表头有空单元格,或某行的列数与表头不一致 | -| `card_too_large` | 用原生表格后卡片会超过飞书的大小上限 | - -飞书卡片整体上限是 **30 KB**。超过时飞书直接拒收这条消息,预览会给 `content_too_large` + `rejected`,需要精简模板后再发。 +| 表格数量超过原生表格上限,发送时会全部改用 Markdown 表格。 | 这张卡片上的原生表格超过 5 个 | +| 表格列数超过原生表格上限,发送时会改用 Markdown 表格。 | 某张表超过 8 列 | +| 表格行数超过原生表格上限,发送时会改用 Markdown 表格。 | 某张表超过 100 行 | +| 有单元格内容过长,发送时会改用 Markdown 表格。 | 某个单元格超过 2000 字节 | +| 有单元格包含原生表格不支持的内容,发送时会改用 Markdown 表格。 | 单元格里除纯文本和 `
` 换行外还有其它 Markdown 标记,表头有空单元格,或某行的列数与表头不一致 | +| 用原生表格后卡片会超过大小上限,发送时会改用 Markdown 表格。 | 用原生表格后卡片会超过飞书的大小上限,这张卡片上的所有表格都改用 Markdown 表格 | + +飞书卡片整体上限是 **30 KB**。超过时飞书直接拒收这条消息,预览会提示「内容超过渠道大小上限,发送会失败,真实告警也一样。请精简模板。」,需要精简模板后再发。 @@ -446,7 +446,7 @@ IM 应用是同步调用平台接口,成功即 `sent`;机器人和邮件进 群聊机器人支持的模板格式。 -除飞书应用(卡片 2.0,开启 **表格转原生表格** 后走原生表格)和企业微信机器人的 **Markdown v2** 之外,各渠道都不渲染 Markdown 表格:钉钉、Slack、Telegram 机器人、企业微信机器人(Markdown v1),以及钉钉、Slack、企业微信、Teams 应用,都会把表格按带 `|` 的原文发出去,预览里也会给出提示。发给这些渠道的模板建议把表格改写成列表。 +以下渠道不渲染 Markdown 表格,会把表格按带 `|` 的原文发出去,预览里也会给出提示:钉钉、Slack、Telegram 机器人,企业微信机器人(Markdown v1),以及钉钉、Slack、企业微信、Teams 应用。发给这些渠道的模板建议把表格改写成列表。 diff --git a/zh/on-call/integration/instant-messaging/lark.mdx b/zh/on-call/integration/instant-messaging/lark.mdx index 33ef19f7..105fe143 100644 --- a/zh/on-call/integration/instant-messaging/lark.mdx +++ b/zh/on-call/integration/instant-messaging/lark.mdx @@ -40,11 +40,11 @@ keywords: ["飞书", "Lark", "即时消息", "告警通知", "IM集成"] 审核中详情页每 10 秒自动向飞书核对一次结果;也可以点 **复制消息给管理员**,把要审批的内容发给飞书管理员,或点 **去飞书管理后台审核** 直接打开飞书管理后台的应用审核页。 -托管应用的凭证由 Flashduty 生成和保管,页面只显示 **App ID**、**当前版本** 和 **状态**,编辑页会提示「飞书应用 xxx 由 Flashduty 自动管理,凭证由 Flashduty 保管,不需要填写」。这些凭证字段由后端维护,在 Flashduty 里修改不会生效。 +托管应用的凭证由 Flashduty 生成和保管,页面只显示 **App ID**、**当前版本** 和 **状态**,编辑页会提示「飞书应用 xxx 由 Flashduty 自动管理,凭证由 Flashduty 保管,不需要填写」。这些凭证由 Flashduty 保管,不需要也无法在 Flashduty 里修改。 ### 使用已有应用 -在 **飞书应用授权** 里选 **使用已有应用**,填入应用的 `App ID`,点击 **保存** 后用应用所有者或协作者的飞书账号扫码,授权 Flashduty 管理这个应用。之后的两次审批和自动配置与创建新应用相同。 +在 **飞书应用授权** 里选 **使用已有应用**,填入应用的 `App ID`,点击 **保存** 后用应用所有者或协作者的飞书账号扫码,授权 Flashduty 管理这个应用。之后的两次审批和自动配置与创建新应用相同。已被其他 Flashduty 账户使用的 `App ID` 会被拒绝。 ### 托管应用的状态与处理 @@ -114,22 +114,22 @@ keywords: ["飞书", "Lark", "即时消息", "告警通知", "IM集成"] | 分组 | 官方权限名称 | 用途 | | :--- | :--- | :--- | -| `base`(必需) | `im:chat` | 创建和管理作战室群、获取群信息、添加群成员 | -| `base`(必需) | `im:message` | 读取和发送单聊、群组消息 | -| `base`(必需) | `im:message:send_as_bot` | 以机器人身份发送 AI SRE 回复和作战室消息 | -| `base`(必需) | `im:message:update` | 更新应用已发送的消息,用于 AI SRE 流式回复更新 | -| `base`(必需) | `im:resource` | 上传和下载消息中的图片、文件等资源 | -| `base`(必需) | `contact:user.id:readonly` | 通过手机号或邮箱获取用户 ID,用于自动关联和邀请成员 | -| `war_room_history`、`ai_sre` | `im:message.group_msg` | 读取群聊历史消息;AI SRE 上下文和 AI 生成复盘报告需要该权限 | -| `ai_sre` | `im:message.p2p_msg:readonly` | 接收用户发给机器人的单聊消息;缺少该权限时,AI SRE 无法收到单聊提问 | -| `ai_sre` | `im:message.group_at_msg:readonly` | 接收群聊中 @ 机器人的消息;群聊 AI SRE 对话需要该权限 | -| `ai_sre` | `im:message.reactions:read` | 查询消息表情反应,用于 AI SRE 处理状态确认 | -| `ai_sre` | `im:message.reactions:write_only` | 添加或删除消息表情反应,用于 AI SRE 处理状态确认 | -| `ai_sre` | `cardkit:card:write` | 创建和更新 CardKit 卡片,用于 AI SRE 流式回复 | -| `ai_sre` | `contact:contact.base:readonly` | 获取通讯录基本信息,用于读取用户和部门基础资料 | -| `ai_sre` | `contact:user.base:readonly` | 获取用户基础信息,用于显示发送者名称 | - -三组权限的用途:`base` 是告警通知、卡片按钮、关联成员和作战室所必需;`war_room_history` 只在作战室复盘要导入群聊记录时需要;`ai_sre` 只在飞书里使用 AI SRE 时需要。 +| 基础(必需) | `im:chat` | 创建和管理作战室群、获取群信息、添加群成员 | +| 基础(必需) | `im:message` | 读取和发送单聊、群组消息 | +| 基础(必需) | `im:message:send_as_bot` | 以机器人身份发送 AI SRE 回复和作战室消息 | +| 基础(必需) | `im:message:update` | 更新应用已发送的消息,用于 AI SRE 流式回复更新 | +| 基础(必需) | `im:resource` | 上传和下载消息中的图片、文件等资源 | +| 基础(必需) | `contact:user.id:readonly` | 通过手机号或邮箱获取用户 ID,用于自动关联和邀请成员 | +| 作战室复盘、AI SRE | `im:message.group_msg` | 读取群聊历史消息;AI SRE 上下文和 AI 生成复盘报告需要该权限 | +| AI SRE | `im:message.p2p_msg:readonly` | 接收用户发给机器人的单聊消息;缺少该权限时,AI SRE 无法收到单聊提问 | +| AI SRE | `im:message.group_at_msg:readonly` | 接收群聊中 @ 机器人的消息;群聊 AI SRE 对话需要该权限 | +| AI SRE | `im:message.reactions:read` | 查询消息表情反应,用于 AI SRE 处理状态确认 | +| AI SRE | `im:message.reactions:write_only` | 添加或删除消息表情反应,用于 AI SRE 处理状态确认 | +| AI SRE | `cardkit:card:write` | 创建和更新 CardKit 卡片,用于 AI SRE 流式回复 | +| AI SRE | `contact:contact.base:readonly` | 获取通讯录基本信息,用于读取用户和部门基础资料 | +| AI SRE | `contact:user.base:readonly` | 获取用户基础信息,用于显示发送者名称 | + +三组权限的用途:「基础」是告警通知、卡片按钮、关联成员和作战室所必需;「作战室复盘」只在复盘要导入群聊记录时需要;「AI SRE」只在飞书里使用 AI SRE 时需要。 这一步由平台自动检测:**基础** 一组全部开通就算完成,显示「基础 N 项已开通」,缺权限时显示「缺 N 项基础权限」;后两组按需开通,不影响打勾。 diff --git a/zh/on-call/integration/instant-messaging/microsoft-teams.mdx b/zh/on-call/integration/instant-messaging/microsoft-teams.mdx index 006fc29c..e307325b 100644 --- a/zh/on-call/integration/instant-messaging/microsoft-teams.mdx +++ b/zh/on-call/integration/instant-messaging/microsoft-teams.mdx @@ -30,7 +30,7 @@ keywords: ["Microsoft Teams", "Teams", "即时消息", "告警通知", "IM集成 集成详情页 **配置** 页签的顶部是 **通知位置** 卡片,Flashduty 只往其中列出的团队和群聊发故障通知。卡片里是 **团队** 和 **群聊** 两个页签,页签名后的数字是已关联的数量: -- **团队** 按团队列出,每个团队下方显示 Flashduty 应用在该团队里读到的频道(形如 `频道:General`)。读不到频道时会提示 Flashduty 应用可能已被移出这个团队,点 **重新读取** 可跳过缓存重新读取该团队。 +- **团队** 按团队列出,每个团队下方显示 Flashduty 应用在该团队里读到的频道(形如 `频道:General`)。读不到频道时会提示 Flashduty 应用可能已被移出这个团队,点 **重新读取** 可重新读取该团队。 - 两个页签都内嵌了新增目标的三步引导:在 Teams 里安装应用并选中目标(团队必须选 General 频道),在目标会话里 @Flashduty 发送关联指令,再点机器人回复里的 **Link Now**。 - 列表每一项右侧的 **取消关联** 会先弹出确认框,写明取消关联该团队或群聊后它将收不到故障通知;在弹窗里选择 **确认** 才真正解除。弹窗里还折叠了「取消关联后如何移除 Flashduty Bot ?」的说明。 diff --git a/zh/on-call/integration/instant-messaging/wecom.mdx b/zh/on-call/integration/instant-messaging/wecom.mdx index 7372de16..f3b3f599 100644 --- a/zh/on-call/integration/instant-messaging/wecom.mdx +++ b/zh/on-call/integration/instant-messaging/wecom.mdx @@ -26,7 +26,7 @@ keywords: ["企业微信", "WeCom", "即时消息", "告警通知", "IM集成"] Flashduty 作为企业微信服务商,为您提供 Flashduty 应用的长期免费版本。该应用需要获得企业微信接口调用许可才能使用(免密登录 + 消息发送)。该许可目前支持 **最多 60 天** 免费,超出该使用时长后,Flashduty 需要为您购买企业微信许可,您方可继续使用。 -第三方应用模式不支持私有化部署。这个模式下的企业 ID 不用填、也不能填:集成的企业归属只能由一次经过企业微信证明的扫码绑定会话确定。 +第三方应用模式不支持私有化部署。这个模式下的企业 ID 不用填、也不能填:集成的企业归属只能通过企业微信扫码确认来绑定。 ### 在企业微信中安装 Flashduty 应用 @@ -77,7 +77,7 @@ Flashduty 作为企业微信服务商,为您提供 Flashduty 应用的长期 - **企业 ID** 变为只读,并提示「已通过企业微信确认,不能修改」。换企业只能重新扫码,没有直接改 ID 的入口。 - 绑定会话 30 分钟内有效,且只能用一次。过期后窗口提示「链接已过期,请重新生成」,点 **重新生成** 后重新扫码。 - 绑定失败时窗口给出原因:`该企业还没有安装 Flashduty 应用,请关闭后改选「还没有安装」,先完成授权安装。`(企业其实没装应用,却选了「已安装」)、`已在企业微信页面上取消绑定。`(在确认页点了取消)、`企业微信没有完成确认,请重新生成后再试。`(其他情况)。 -- 只在表单里提交企业 ID 不再生效:没有经过确认的绑定会话,保存会提示 `scan with WeCom to bind your enterprise first`。 +- 只在表单里提交企业 ID 不再生效:没有经过确认的绑定会话,通过 API 提交时,保存会提示 `scan with WeCom to bind your enterprise first`;控制台的流程不会出现这条提示。 @@ -103,7 +103,7 @@ Flashduty 作为企业微信服务商,为您提供 Flashduty 应用的长期 企业微信为私有化部署版本时,还要填写 `Endpoint`。Flashduty 服务必须能访问该 `Endpoint`,您可以为它设置 **白名单授权**。 - **访问域名** 只填一个域名:不带 `https://`、不带路径,例如 `flashduty.example.com`。 + **访问域名** 只填一个域名:不带 `https://`、不带路径,由 Flashduty 托管时也不带端口,例如 `flashduty.example.com`。 这个域名的 HTTPS 证书由 Flashduty 签发并自动续期,访问也由 Flashduty 转发,不用自己搭 Nginx、不用自己申请证书。企业微信要求可信域名和回调地址都使用这个域名。 @@ -186,7 +186,7 @@ Flashduty 作为企业微信服务商,为您提供 Flashduty 应用的长期 不要在企业微信里点 **随机获取**:`Token` 和 `EncodingAESKey` 用 Flashduty 生成的这一套,两边才一致。 -`Token` 和 `EncodingAESKey` 只显示一次,离开页面后只看到掩码,请当次复制到企业微信。需要换一套时点 **重新生成**:重新生成后已保存的那一套立即失效,企业微信里填了旧值的要换成新值并保存,否则收不到回调;因为这一步记的是凭据指纹,换过凭据后该步骤会退回「等待企业微信校验」,等企业微信重新校验通过后再打勾。 +`Token` 和 `EncodingAESKey` 只显示一次,离开页面后只看到掩码,请当次复制到企业微信。需要换一套时点 **重新生成**:重新生成后已保存的那一套立即失效,企业微信里填了旧值的要换成新值并保存,否则收不到回调;换过凭据后该步骤会退回「等待企业微信校验」,等企业微信重新校验通过后再打勾。 ![企业微信:集成企业自建应用 界面截图](https://docs-cdn.flashcat.cloud/images/png/919bd2722c75513ce1d301779b39c3bf.png) @@ -198,7 +198,7 @@ Flashduty 作为企业微信服务商,为您提供 Flashduty 应用的长期 企业微信要求先完成第 2 或第 3 步,才允许配置可信 IP。 -第 4 步由 Flashduty 用应用的 `access_token` 调一次企业微信接口来判断:调用成功显示「已生效」;企业微信返回 `60020` 显示「待配置」,表示可信 IP 还没配好;其他错误码原样显示企业微信返回的码;调不通企业微信显示「检测失败」。可以点 **重新检测** 重读。 +第 4 步由 Flashduty 向企业微信发起一次检测:检测成功显示「已生效」;企业微信返回 `60020` 显示「待配置」,表示可信 IP 还没配好;其他错误码原样显示企业微信返回的码;调不通企业微信显示「检测失败」。可以点 **重新检测** 重读。 ![企业微信:集成企业自建应用 界面截图](https://docs-cdn.flashcat.cloud/images/png/fe3b2b788dda5d331148ba0946631b91.png) @@ -208,7 +208,7 @@ Flashduty 作为企业微信服务商,为您提供 Flashduty 应用的长期 ### 5. 设置工作台应用主页 -在应用详情页的 **工作台应用主页** 点设置,选 **网页**,填入详情页第 5 步给出的地址。该地址是以访问域名拼出的 OAuth2 授权链接,`redirect_uri` 指到 `https://<访问域名>?app_type=wecom_app...`:Flashduty 在访问域名上把带 `app_type=wecom_app` 的请求转到控制台,用 OAuth 拿到的 code 完成免密登录。不需要再单独配置「前端可信域名」。 +在应用详情页的 **工作台应用主页** 点设置,选 **网页**,填入详情页第 5 步给出的地址。新建的集成不需要再单独配置「前端可信域名」。 企业微信不回传这项配置,设置完成后点 **我已设置** 记录这一步。 diff --git a/zh/rum/analytics/custom-dashboards.mdx b/zh/rum/analytics/custom-dashboards.mdx index 39ff08c5..a4458393 100644 --- a/zh/rum/analytics/custom-dashboards.mdx +++ b/zh/rum/analytics/custom-dashboards.mdx @@ -17,7 +17,6 @@ keywords: ["RUM", "自定义看板", "分析看板", "面板", "图表", "看板 | 编辑态 | `/rum/dashboard/custom/:id?edit=1` | 打开即进入编辑,改动先落在浏览器里的草稿 | | 新建看板 | `/rum/dashboard/custom/new?edit=1` | 由页签栏右侧的 **+** 菜单进入 | | 看板管理 | `/rum/dashboard/manage` | 看板列表:搜索、删除、回收站 | -| 旧地址 | `/rum/dashboards`、`/rum/dashboards/:id` | 自动重定向到上面的地址 | 页签栏右侧的 **+** 菜单有两项: @@ -37,12 +36,10 @@ keywords: ["RUM", "自定义看板", "分析看板", "面板", "图表", "看板 | 类型 | 说明 | |------|------| -| **时序**(`time_series`) | 统计值随时间的走势,可选折线或柱状画法 | -| **表格**(`table`) | 按分组的明细,一行一个分组,可点击下钻(见[表格下钻](#表格下钻)) | -| **单值**(`stat`) | 一个关键数字,可设置阈值着色;单值面板不使用分组 | -| **文本**(`text`) | Markdown 说明文字,不绑定查询 | - -看板定义同时接受**柱状**(`bar`)与**排行**(`top_list`)两种面板类型(通过 API 写入的看板可以有),但 RUM 控制台暂未渲染这两种图表:类型选择器里它们显示为「后续版本支持」,不可选择。 +| **时序** | 统计值随时间的走势,可选折线或柱状画法 | +| **表格** | 按分组的明细,一行一个分组,可点击下钻(见[表格下钻](#表格下钻)) | +| **单值** | 一个关键数字,可设置阈值着色;单值面板不使用分组 | +| **文本** | Markdown 说明文字,不绑定查询 | ## 添加面板 @@ -78,7 +75,7 @@ keywords: ["RUM", "自定义看板", "分析看板", "面板", "图表", "看板 分组字段受字段基数限制,规则与查看器相同,见[聚合分析 · 分组字段](../explorer/analytics#分组字段):事件的 ID 字段不能用于分组,其他高基数字段只能配计数或求和且时间范围最长 7 天。 -面板的查询在浏览器里编译后发往 `/rum/data/query`,结果按抽样估算时面板会显示**采样估算**标记与说明。 +结果按抽样估算时,面板会显示**采样估算**标记与说明。 ## 看板变量 @@ -87,8 +84,8 @@ keywords: ["RUM", "自定义看板", "分析看板", "面板", "图表", "看板 | 变量类型 | 说明 | |----------|------| | 内置 `$app` | 页面的当前应用。面板查询里选**跟随页面应用**就等于用 `$app`;只有应用的 `application_id` 能引用它 | -| 字段变量(`rum_field`) | 候选值取自某个 RUM 字段的取值,多选,「全部」可用 | -| 自定义变量(`custom`) | 手动填写候选值(每行一个,`显示名=值` 或纯值),单选或多选 | +| 字段变量 | 候选值取自某个 RUM 字段的取值,多选,「全部」可用 | +| 自定义变量 | 手动填写候选值(每行一个,`显示名=值` 或纯值),单选或多选 | - 在面板的 DQL 里用 `${name}` 或 `$name` 引用变量,例如 `env:${env}`。一个变量选了多个值时展开为 `(a OR b)`;选中**全部**或没有取值时,包含该引用的整条条件(`env:${env}`、`-env:${env}`、`NOT env:${env}`,以及含有它的值列表)会被整条去掉,而不是当成空值查询。 - 引用在浏览器里替换后才查询,因此面板的 DQL 必须能替换成完整语句才有结果。 @@ -137,7 +134,7 @@ RUM 看板没有目录(文件夹):看板不做分组,管理页也没有 ## 权限 -新建、编辑、删除与恢复看板需要**看板管理**权限(`rumDashboard:manage`)。没有该权限时: +新建、编辑、删除与恢复看板需要**看板管理**权限。没有该权限时: - 页签栏 **+** 菜单里没有**新建看板**,看板的管理页没有**新建看板**按钮与删除、恢复入口; - 看板右上角没有**编辑**,**⋯** 菜单里没有**克隆**与**删除**; @@ -156,12 +153,12 @@ RUM 看板没有目录(文件夹):看板不做分组,管理页也没有 | 变量数 | 20 | | 每个变量的候选值 | 1000 | | 单个面板的数据链接 | 5 | -| 时序 / 排行面板的查询数 | 1–26 | -| 表格 / 单值 / 柱状面板的查询数 | 恰好 1 | -| 排行面板的取值个数 | 1–100 | +| 时序面板的查询数 | 1–26 | +| 表格 / 单值面板的查询数 | 恰好 1 | +| 文本面板的 Markdown 大小 | 64 KB | | 标题长度 | 189 字符 | | 描述长度 | 1024 字符 | -| 看板定义大小 | 1 MB(请求体 2 MB) | +| 看板定义大小 | 1 MB | | 默认时间范围可选值 | `now-15m`、`now-30m`、`now-1h`、`now-3h`、`now-6h`、`now-12h`、`now-24h`、`now-7d` | | 自动刷新可选值 | 不刷新、`30s`、`1m`、`5m` | diff --git a/zh/rum/explorer/analytics.mdx b/zh/rum/explorer/analytics.mdx index 1c5b52e6..238fa157 100644 --- a/zh/rum/explorer/analytics.mdx +++ b/zh/rum/explorer/analytics.mdx @@ -39,6 +39,8 @@ RUM 查看器提供**列表**与**分析**两种查看方式。**分析**视图 **分组字段**决定按什么拆开,每个取值单独展示。可以用**取前**选择保留前 `5`、`10`(默认)、`20` 或 `50` 组;不选分组字段时,结果汇总为单个数值。数组字段不能用于分组,**分布图**不使用分组。 + + ### 分组字段的基数限制 分组还受字段基数约束,基数越高的字段分组代价越大: @@ -90,7 +92,7 @@ RUM 查看器提供**列表**与**分析**两种查看方式。**分析**视图 - **下载图片**:导出为 2 倍清晰度的 PNG。表格和不分组的单个数值不支持。 - **CSV**:导出图表数据。时序图每行是「时间、分组、统计值」,其他图表每个分组一行。 -### 保存到看板 +## 保存到看板 同一排的 **保存到看板** 把当前这个分析问题固化成一个[自定义看板](../analytics/custom-dashboards)面板: diff --git a/zh/rum/explorer/data-query.mdx b/zh/rum/explorer/data-query.mdx index ce04d963..610f9134 100644 --- a/zh/rum/explorer/data-query.mdx +++ b/zh/rum/explorer/data-query.mdx @@ -143,7 +143,7 @@ RUM 查看器的查询由**词项**(terms)和**操作符**(operators)组 ## 路径字段与 URL 归一化 -`view_url_path` 与 `view_url_path_group`(以及会话、错误、资源、操作、长任务事件上同名的路径字段)由页面 URL 归一化得到,两者都可以用于筛选、分组和图表: +`view_url_path` 与 `view_url_path_group`(以及会话、错误、资源、操作、长任务事件上同名的路径字段)由页面 URL 归一化得到,两者都可以用于筛选和图表。按 `view_url_path` 分组属于高基数分组(统计方式仅限计数与求和,时间范围最长 7 天,见[分组字段的基数限制](/zh/rum/explorer/analytics#cardinality-limits));按页面汇总请用 `view_url_path_group`: | 原始 URL | `view_url_path` | `view_url_path_group` | |----------|-----------------|------------------------|