From 583a9360c4f8717d38b7a70ed8ccfe7f912adefb Mon Sep 17 00:00:00 2001 From: Duncan Mackenzie Date: Tue, 28 Jul 2026 15:52:24 -0700 Subject: [PATCH 1/3] fix(sdk-metrics): correct metric name, availability, and anchors Rename local_activity_succeeded_endtoend_latency to local_activity_succeed_endtoend_latency to match Core, Go, and Java. Fix the swapped resource_slots_cpu_usage and resource_slots_mem_usage anchors. Add Core to the four Nexus metric sections and to sticky_cache_total_forced_eviction, add Java to num_pollers, and drop the "Go and Java only" note from nexus_task, LocalActivityWorker, and NexusWorker. Remove the "in seconds" claims that contradict the units note, fix task-queue to task_queue, and drop two trailing commas. --- docs/references/sdk-metrics.mdx | 249 ++++++++++++++++---------------- 1 file changed, 124 insertions(+), 125 deletions(-) diff --git a/docs/references/sdk-metrics.mdx b/docs/references/sdk-metrics.mdx index 64d543ee12..dcaf0fd221 100644 --- a/docs/references/sdk-metrics.mdx +++ b/docs/references/sdk-metrics.mdx @@ -2,7 +2,7 @@ id: sdk-metrics title: Temporal SDK metrics reference sidebar_label: SDK metrics -description: Temporal SDKs emit metrics covering Client usage and Worker Processes. Metrics can be tuned to improve Worker performance and are prefixed with temporal_ before export. +description: Reference for the metrics that Temporal SDKs emit from Client usage and Worker Processes. toc_max_heading_level: 4 keywords: - reference @@ -15,13 +15,13 @@ tags: The information on this page is relevant to [Temporal SDKs](/encyclopedia/architecture/temporal-sdks). -See [Cloud metrics](/cloud/metrics/) for metrics emitted by [Temporal Cloud](/cloud/overview). +See [Cloud metrics](/cloud/metrics) for metrics emitted by [Temporal Cloud](/cloud/overview). -See [Cluster metrics](/references/cluster-metrics) for metrics emitted by the [OSS Cluster](/temporal-service). +See [Temporal Service metrics](/references/cluster-metrics) for metrics emitted by a [self-hosted Temporal Service](/temporal-service). -Some SDKs may emit metrics beyond what is listed in this SDK Metrics reference. -Only metrics included in this Metrics reference have guaranteed, defined behavior. -Other metrics are considered deprecated, inconsistent or experimental. +Some SDKs emit metrics beyond what this reference lists. +Only the metrics on this page have guaranteed, defined behavior. +Treat any others as deprecated, inconsistent, or experimental. ::: @@ -36,8 +36,8 @@ The Temporal SDKs emit a set of metrics from Temporal Client usage and Worker Pr - [How to tune Worker performance based on metrics](/develop/worker-performance) All metrics are prefixed with `temporal_` before being exported to their configured destination. -(The prefix has been removed in parts of this reference.) -Currently, some metrics are specific to certain SDKs. +Section headings on this page omit the prefix. +Some metrics are specific to certain SDKs. TypeScript, Python, .NET, and Ruby SDK metrics are defined in the Core SDK. @@ -66,76 +66,76 @@ The Core SDK is a shared common core library used by several Temporal SDKs, incl Each metric may have some combination of the following keys attached to them: -- `task-queue`: Task Queue that the Worker Entity is polling +- `task_queue`: Task Queue that the Worker Entity is polling - `namespace`: Namespace the Worker is bound to - `poller_type`: One of the following: - `workflow_task` - `activity_task` - - `nexus_task` (Go and Java only) + - `nexus_task` - `sticky_workflow_task` - `worker_type`: One of the following: - `ActivityWorker` - `WorkflowWorker` - - `LocalActivityWorker` (Go and Java only) - - `NexusWorker` (Go and Java only) + - `LocalActivityWorker` + - `NexusWorker` - `activity_type`: The name of the Activity Function the metric is associated with - `workflow_type`: The name of the Workflow Function the metric is associated with - `operation`: RPC method name; available for metrics related to Temporal Client gRPC requests Some keys may not be available in every SDK, and Histogram metrics may have different buckets in each SDK. -| Metric name | Emitted by | Metric type | Availability | -| ------------------------------------------------------------------------------------------------ | -------------- | ----------- | -------------- | -| [temporal_activity_execution_cancelled](#activity_execution_cancelled) | Worker | Counter | Java | -| [temporal_activity_execution_failed](#activity_execution_failed) | Worker | Counter | Core, Go, Java | -| [temporal_activity_execution_latency](#activity_execution_latency) | Worker | Histogram | Core, Go, Java | -| [temporal_activity_poll_no_task](#activity_poll_no_task) | Worker | Counter | Core, Go, Java | -| [temporal_activity_schedule_to_start_latency](#activity_schedule_to_start_latency) | Worker | Histogram | Core, Go, Java | -| [temporal_activity_succeed_endtoend_latency](#activity_succeed_endtoend_latency) | Worker | Histogram | Core, Go, Java | -| [temporal_activity_task_error](#activity_task_error) | Worker | Counter | Go | -| [temporal_corrupted_signals](#corrupted_signals) | Worker | Counter | Go, Java | -| [temporal_local_activity_execution_cancelled](#local_activity_execution_cancelled) | Worker | Counter | Core, Go, Java | -| [temporal_local_activity_execution_failed](#local_activity_execution_failed) | Worker | Counter | Core, Go, Java | -| [temporal_local_activity_execution_latency](#local_activity_execution_latency) | Worker | Histogram | Core, Go, Java | -| [temporal_local_activity_succeeded_endtoend_latency](#local_activity_succeeded_endtoend_latency) | Worker | Histogram | Core, Go, Java | -| [temporal_local_activity_total](#local_activity_total) | Worker | Counter | Core, Go, Java | -| [temporal_long_request](#long_request) | Service Client | Counter | Core, Go, Java | -| [temporal_long_request_failure](#long_request_failure) | Service Client | Counter | Core, Go, Java | -| [temporal_long_request_latency](#long_request_latency) | Service Client | Histogram | Core, Go, Java | -| [temporal_nexus_poll_no_task](#nexus_poll_no_task) | Worker | Counter | Core, Go, Java | -| [temporal_nexus_task_schedule_to_start_latency](#nexus_task_schedule_to_start_latency) | Worker | Histogram | Core, Go, Java | -| [temporal_nexus_task_execution_failed](#nexus_task_execution_failed) | Worker | Counter | Core, Go, Java | -| [temporal_nexus_task_execution_latency](#nexus_task_execution_latency) | Worker | Histogram | Core, Go, Java | -| [temporal_nexus_task_endtoend_latency](#nexus_task_endtoend_latency) | Worker | Histogram | Core, Go, Java | -| [temporal_num_pollers](#num_pollers) | Worker | Gauge | Core, Go | -| [temporal_poller_start](#poller_start) | Worker | Counter | Go, Java | -| [temporal_request](#request) | Service Client | Counter | Core, Go, Java | -| [temporal_request_failure](#request_failure) | Service Client | Counter | Core, Go, Java | -| [temporal_request_latency](#request_latency) | Service Client | Histogram | Core, Go, Java | -| [temporal_resource_slots_mem_usage](#resource_slots_cpu_usage) | Worker | Gauge | Core, Java | -| [temporal_resource_slots_cpu_usage](#resource_slots_mem_usage) | Worker | Gauge | Core, Java | -| [temporal_sticky_cache_hit](#sticky_cache_hit) | Worker | Counter | Core, Go, Java | -| [temporal_sticky_cache_miss](#sticky_cache_miss) | Worker | Counter | Core, Go, Java | -| [temporal_sticky_cache_size](#sticky_cache_size) | Worker | Gauge | Core, Go, Java | -| [temporal_sticky_cache_total_forced_eviction](#sticky_cache_total_forced_eviction) | Worker | Counter | Go, Java | -| [temporal_unregistered_activity_invocation](#unregistered_activity_invocation) | Worker | Counter | Go | -| [temporal_worker_start](#worker_start) | Worker | Counter | Core, Go, Java | -| [temporal_worker_task_slots_available](#worker_task_slots_available) | Worker | Gauge | Core, Go, Java | -| [temporal_worker_task_slots_used](#worker_task_slots_used) | Worker | Gauge | Core, Go, Java | -| [temporal_workflow_active_thread_count](#workflow_active_thread_count) | Worker | Gauge | Java | -| [temporal_workflow_canceled](#workflow_canceled) | Worker | Counter | Core, Go, Java | -| [temporal_workflow_completed](#workflow_completed) | Worker | Counter | Core, Go, Java | -| [temporal_workflow_continue_as_new](#workflow_continue_as_new) | Worker | Counter | Core, Go, Java | -| [temporal_workflow_endtoend_latency](#workflow_endtoend_latency) | Worker | Histogram | Core, Go, Java | -| [temporal_workflow_failed](#workflow_failed) | Worker | Counter | Core, Go, Java | -| [temporal_workflow_task_execution_failed](#workflow_task_execution_failed) | Worker | Counter | Core, Go, Java | -| [temporal_workflow_task_execution_latency](#workflow_task_execution_latency) | Worker | Histogram | Core, Go, Java | -| [temporal_workflow_task_queue_poll_empty](#workflow_task_queue_poll_empty) | Worker | Counter | Core, Go, Java | -| [temporal_workflow_task_queue_poll_succeed](#workflow_task_queue_poll_succeed) | Worker | Counter | Core, Go, Java | -| [temporal_workflow_task_replay_latency](#workflow_task_replay_latency) | Worker | Histogram | Core, Go, Java | -| [temporal_workflow_task_schedule_to_start_latency](#workflow_task_schedule_to_start_latency) | Worker | Histogram | Core, Go, Java | - -### activity_execution_cancelled +| Metric name | Emitted by | Metric type | Availability | +| -------------------------------------------------------------------------------------------- | -------------- | ----------- | -------------- | +| [temporal_activity_execution_cancelled](#activity_execution_cancelled) | Worker | Counter | Java | +| [temporal_activity_execution_failed](#activity_execution_failed) | Worker | Counter | Core, Go, Java | +| [temporal_activity_execution_latency](#activity_execution_latency) | Worker | Histogram | Core, Go, Java | +| [temporal_activity_poll_no_task](#activity_poll_no_task) | Worker | Counter | Core, Go, Java | +| [temporal_activity_schedule_to_start_latency](#activity_schedule_to_start_latency) | Worker | Histogram | Core, Go, Java | +| [temporal_activity_succeed_endtoend_latency](#activity_succeed_endtoend_latency) | Worker | Histogram | Core, Go, Java | +| [temporal_activity_task_error](#activity_task_error) | Worker | Counter | Go | +| [temporal_corrupted_signals](#corrupted_signals) | Worker | Counter | Go, Java | +| [temporal_local_activity_execution_cancelled](#local_activity_execution_cancelled) | Worker | Counter | Core, Go, Java | +| [temporal_local_activity_execution_failed](#local_activity_execution_failed) | Worker | Counter | Core, Go, Java | +| [temporal_local_activity_execution_latency](#local_activity_execution_latency) | Worker | Histogram | Core, Go, Java | +| [temporal_local_activity_succeed_endtoend_latency](#local_activity_succeed_endtoend_latency) | Worker | Histogram | Core, Go, Java | +| [temporal_local_activity_total](#local_activity_total) | Worker | Counter | Core, Go, Java | +| [temporal_long_request](#long_request) | Service Client | Counter | Core, Go, Java | +| [temporal_long_request_failure](#long_request_failure) | Service Client | Counter | Core, Go, Java | +| [temporal_long_request_latency](#long_request_latency) | Service Client | Histogram | Core, Go, Java | +| [temporal_nexus_poll_no_task](#nexus_poll_no_task) | Worker | Counter | Core, Go, Java | +| [temporal_nexus_task_schedule_to_start_latency](#nexus_task_schedule_to_start_latency) | Worker | Histogram | Core, Go, Java | +| [temporal_nexus_task_execution_failed](#nexus_task_execution_failed) | Worker | Counter | Core, Go, Java | +| [temporal_nexus_task_execution_latency](#nexus_task_execution_latency) | Worker | Histogram | Core, Go, Java | +| [temporal_nexus_task_endtoend_latency](#nexus_task_endtoend_latency) | Worker | Histogram | Core, Go, Java | +| [temporal_num_pollers](#num_pollers) | Worker | Gauge | Core, Go, Java | +| [temporal_poller_start](#poller_start) | Worker | Counter | Go, Java | +| [temporal_request](#request) | Service Client | Counter | Core, Go, Java | +| [temporal_request_failure](#request_failure) | Service Client | Counter | Core, Go, Java | +| [temporal_request_latency](#request_latency) | Service Client | Histogram | Core, Go, Java | +| [temporal_resource_slots_cpu_usage](#resource_slots_cpu_usage) | Worker | Gauge | Core, Java | +| [temporal_resource_slots_mem_usage](#resource_slots_mem_usage) | Worker | Gauge | Core, Java | +| [temporal_sticky_cache_hit](#sticky_cache_hit) | Worker | Counter | Core, Go, Java | +| [temporal_sticky_cache_miss](#sticky_cache_miss) | Worker | Counter | Core, Go, Java | +| [temporal_sticky_cache_size](#sticky_cache_size) | Worker | Gauge | Core, Go, Java | +| [temporal_sticky_cache_total_forced_eviction](#sticky_cache_total_forced_eviction) | Worker | Counter | Core, Go, Java | +| [temporal_unregistered_activity_invocation](#unregistered_activity_invocation) | Worker | Counter | Go | +| [temporal_worker_start](#worker_start) | Worker | Counter | Core, Go, Java | +| [temporal_worker_task_slots_available](#worker_task_slots_available) | Worker | Gauge | Core, Go, Java | +| [temporal_worker_task_slots_used](#worker_task_slots_used) | Worker | Gauge | Core, Go, Java | +| [temporal_workflow_active_thread_count](#workflow_active_thread_count) | Worker | Gauge | Java | +| [temporal_workflow_canceled](#workflow_canceled) | Worker | Counter | Core, Go, Java | +| [temporal_workflow_completed](#workflow_completed) | Worker | Counter | Core, Go, Java | +| [temporal_workflow_continue_as_new](#workflow_continue_as_new) | Worker | Counter | Core, Go, Java | +| [temporal_workflow_endtoend_latency](#workflow_endtoend_latency) | Worker | Histogram | Core, Go, Java | +| [temporal_workflow_failed](#workflow_failed) | Worker | Counter | Core, Go, Java | +| [temporal_workflow_task_execution_failed](#workflow_task_execution_failed) | Worker | Counter | Core, Go, Java | +| [temporal_workflow_task_execution_latency](#workflow_task_execution_latency) | Worker | Histogram | Core, Go, Java | +| [temporal_workflow_task_queue_poll_empty](#workflow_task_queue_poll_empty) | Worker | Counter | Core, Go, Java | +| [temporal_workflow_task_queue_poll_succeed](#workflow_task_queue_poll_succeed) | Worker | Counter | Core, Go, Java | +| [temporal_workflow_task_replay_latency](#workflow_task_replay_latency) | Worker | Histogram | Core, Go, Java | +| [temporal_workflow_task_schedule_to_start_latency](#workflow_task_schedule_to_start_latency) | Worker | Histogram | Core, Go, Java | + +### `activity_execution_cancelled` An Activity Execution was canceled. @@ -143,7 +143,7 @@ An Activity Execution was canceled. - Available in: Java - Tags: `activity_type`, `namespace`, `task_queue` -### activity_execution_failed +### `activity_execution_failed` An Activity Execution failed. This does not include local Activity Failures in the Go and Java SDKs (see [local_activity_execution_failed](#local_activity_execution_failed)). @@ -152,7 +152,7 @@ This does not include local Activity Failures in the Go and Java SDKs (see [loca - Available in: Core, Go, Java - Tags: `activity_type`, `namespace`, `task_queue` -### activity_execution_latency +### `activity_execution_latency` Time to complete an Activity Execution, from the time the Activity Task is generated to the time the language SDK responded with a completion (failure or success). @@ -160,7 +160,7 @@ Time to complete an Activity Execution, from the time the Activity Task is gener - Available in: Core, Go, Java - Tags: `activity_type`, `namespace`, `task_queue` -### activity_poll_no_task +### `activity_poll_no_task` An Activity Worker poll for an Activity Task timed out, and no Activity Task is available to pick from the Task Queue. @@ -168,19 +168,18 @@ An Activity Worker poll for an Activity Task timed out, and no Activity Task is - Available in: Core, Go, Java - Tags: `namespace`, `task_queue` -### activity_schedule_to_start_latency +### `activity_schedule_to_start_latency` -The Schedule-To-Start time of an Activity Task in seconds. +The Schedule-To-Start time of an Activity Task. A [Schedule-To-Start Timeout](/encyclopedia/detecting-activity-failures#schedule-to-start-timeout) can be set when an Activity Execution is spawned. -This metric is useful for ensuring Activity Tasks are being processed from the queue in a timely manner. Some SDKs may include -the `activity_type` label, but the metric should not vary by type, as it does not influence the rate at which tasks are pulled -from the queue. +Use this metric to check that Activity Tasks are being processed from the queue in a timely manner. +Some SDKs include the `activity_type` label, but the metric should not vary by type, because the type does not influence the rate at which tasks are pulled from the queue. - Type: Histogram - Available in: Core, Go, Java - Tags: `namespace`, `task_queue` -### activity_succeed_endtoend_latency +### `activity_succeed_endtoend_latency` Total latency of successfully finished Activity Executions from the time they are scheduled to the time they are completed. This metric is not recorded for async Activity completion. @@ -189,15 +188,15 @@ This metric is not recorded for async Activity completion. - Available in: Core, Go, Java - Tags: `activity_type`, `namespace`, `task_queue` -### activity_task_error +### `activity_task_error` An internal error or panic occurred during Activity Task handling or execution. - Type: Counter -- Available in: Go, +- Available in: Go - Tags: `activity_type`, `namespace`, `task_queue`, `workflow_type` -### corrupted_signals +### `corrupted_signals` Number of Signals whose payload could not be deserialized. @@ -205,7 +204,7 @@ Number of Signals whose payload could not be deserialized. - Available in: Go, Java - Tags: `namespace`, `task_queue`, `workflow_type` -### local_activity_execution_cancelled +### `local_activity_execution_cancelled` A Local Activity Execution was canceled. @@ -213,7 +212,7 @@ A Local Activity Execution was canceled. - Available in: Core, Go, Java - Tags: `activity_type`, `namespace`, `task_queue` -### local_activity_execution_failed +### `local_activity_execution_failed` A Local Activity Execution failed. @@ -221,7 +220,7 @@ A Local Activity Execution failed. - Available in: Core, Go, Java - Tags: `activity_type`, `namespace`, `task_queue` -### local_activity_execution_latency +### `local_activity_execution_latency` Time to complete a Local Activity Execution, from the time the first Activity Task is generated to the time the SDK responds that the execution is complete. @@ -229,7 +228,7 @@ Time to complete a Local Activity Execution, from the time the first Activity Ta - Available in: Core, Go, Java - Tags: `activity_type`, `namespace`, `task_queue` -### local_activity_succeeded_endtoend_latency +### `local_activity_succeed_endtoend_latency` Total latency of successfully finished Local Activity Executions (from schedule to completion). @@ -237,7 +236,7 @@ Total latency of successfully finished Local Activity Executions (from schedule - Available in: Core, Go, Java - Tags: `activity_type`, `namespace`, `task_queue` -### local_activity_total +### `local_activity_total` Total number of [Local Activity Executions](/local-activity). @@ -245,7 +244,7 @@ Total number of [Local Activity Executions](/local-activity). - Available in: Core, Go, Java - Tags: `activity_type`, `namespace`, `task_queue` -### long_request +### `long_request` Temporal Client made an RPC long poll request. @@ -253,7 +252,7 @@ Temporal Client made an RPC long poll request. - Available in: Core, Go, Java - Tags: `namespace`, `operation` -### long_request_failure +### `long_request_failure` Temporal Client made an RPC long poll request that failed. This number is included into the total `long_request` counter for long poll RPC requests. @@ -262,7 +261,7 @@ This number is included into the total `long_request` counter for long poll RPC - Available in: Core, Go, Java - Tags: `namespace`, `operation` -### long_request_latency +### `long_request_latency` Latency of a Temporal Client gRPC long poll request. @@ -270,18 +269,18 @@ Latency of a Temporal Client gRPC long poll request. - Available in: Core, Go, Java - Tags: `namespace`, `operation` -### nexus_poll_no_task +### `nexus_poll_no_task` A Nexus Worker poll for a Nexus Task timed out, and no Nexus Task is available to pick from the Task Queue. - Type: Counter -- Available in: Go, Java +- Available in: Core, Go, Java - Tags: `namespace`, `task_queue` -### nexus_task_schedule_to_start_latency +### `nexus_task_schedule_to_start_latency` -The Schedule-To-Start time of a Nexus Task in seconds. The schedule time is taken from when the corresponding request -hit the Frontend service to when the SDK started processing the task. +The Schedule-To-Start time of a Nexus Task. +The schedule time is taken from when the corresponding request hit the Frontend service to when the SDK started processing the task. This time is limited by the `Request-Timeout` header given to the Frontend when handling this request. @@ -289,13 +288,13 @@ This time is limited by the `Request-Timeout` header given to the Frontend when - Available in: Core, Go, Java - Tags: `namespace`, `task_queue` -### nexus_task_execution_failed +### `nexus_task_execution_failed` Handling of a Nexus Task resulted in an error. This includes any error returned from a user handler and unexpected internal errors in the SDK. - Type: Counter -- Available in: Go, Java +- Available in: Core, Go, Java - Tags: `namespace`, `task_queue`, `nexus_service`, `nexus_operation`, `failure_reason` Valid values for the `failure_reason` tag: @@ -310,25 +309,25 @@ Valid values for the `failure_reason` tag: - `operation_canceled`: The user handler code has indicated that the operation has completed as canceled. In Go, this maps to an `UnsuccessfulOperationError` with a `canceled` state. -### nexus_task_execution_latency +### `nexus_task_execution_latency` Time to complete a Nexus Task, from the time the Nexus Task processing starts in the SDK to the time the user handler completes. - Type: Histogram -- Available in: Go, Java +- Available in: Core, Go, Java - Tags: `namespace`, `task_queue`, `nexus_service`, `nexus_operation` -### nexus_task_endtoend_latency +### `nexus_task_endtoend_latency` Total latency of Nexus Tasks from the time the corresponding request hit the Frontend to after the SDK gets acknowledgment from the server for task completion. - Type: Histogram -- Available in: Go, Java +- Available in: Core, Go, Java - Tags: `namespace`, `task_queue`, `nexus_service`, `nexus_operation` -### num_pollers +### `num_pollers` Current number of Worker Entities that are polling. @@ -336,7 +335,7 @@ Current number of Worker Entities that are polling. - Available in: Core, Go, Java - Tags: `namespace`, `poller_type`, `task_queue` -### poller_start +### `poller_start` A Worker Entity poller was started. @@ -344,7 +343,7 @@ A Worker Entity poller was started. - Available in: Go, Java - Tags: `namespace`, `task_queue` -### request +### `request` Temporal Client made an RPC request. @@ -352,7 +351,7 @@ Temporal Client made an RPC request. - Available in: Core, Go, Java - Tags: `namespace`, `operation` -### request_failure +### `request_failure` Temporal Client made an RPC request that failed. This number is included into the total `request` counter for RPC requests. @@ -361,7 +360,7 @@ This number is included into the total `request` counter for RPC requests. - Available in: Core, Go, Java - Tags: `namespace`, `operation` -### request_latency +### `request_latency` Latency of a Temporal Client gRPC request. @@ -369,7 +368,7 @@ Latency of a Temporal Client gRPC request. - Available in: Core, Go, Java - Tags: `namespace`, `operation` -### resource_slots_cpu_usage +### `resource_slots_cpu_usage` CPU usage as a value between 0 and 100. As perceived by the resource-based slots tuner, if enabled. @@ -377,7 +376,7 @@ enabled. - Type: Gauge - Available in: Core, Java -### resource_slots_mem_usage +### `resource_slots_mem_usage` Memory usage as a value between 0 and 100. As perceived by the resource-based slots tuner, if enabled. @@ -385,7 +384,7 @@ enabled. - Type: Gauge - Available in: Core, Java -### sticky_cache_hit +### `sticky_cache_hit` A Workflow Task found a cached Workflow Execution to run against. @@ -393,15 +392,15 @@ A Workflow Task found a cached Workflow Execution to run against. - Available in: Core, Go, Java - Tags: `namespace`, `task_queue` -### sticky_cache_miss +### `sticky_cache_miss` -A Workflow Task did not find a cached Workflow execution to run against. +A Workflow Task did not find a cached Workflow Execution to run against. - Type: Counter - Available in: Core, Go, Java - Tags: `namespace`, `task_queue` -### sticky_cache_size +### `sticky_cache_size` Current cache size, expressed in number of Workflow Executions. @@ -409,23 +408,23 @@ Current cache size, expressed in number of Workflow Executions. - Available in: Core, Go, Java - Tags: `namespace` (TypeScript, Java), `task_queue` (TypeScript) -### sticky_cache_total_forced_eviction +### `sticky_cache_total_forced_eviction` A Workflow Execution has been forced from the cache intentionally. - Type: Counter -- Available in: Go, Java +- Available in: Core, Go, Java - Tags: `namespace`, `task_queue` -### unregistered_activity_invocation +### `unregistered_activity_invocation` A request to spawn an Activity Execution is not registered with the Worker. - Type: Counter -- Available in: Go, +- Available in: Go - Tags: `activity_type`, `namespace`, `task_queue`, `workflow_type` -### worker_start +### `worker_start` A Worker Entity has been registered, created, or started. @@ -433,7 +432,7 @@ A Worker Entity has been registered, created, or started. - Available in: Core, Go, Java - Tags: `namespace`, `task_queue`, `worker_type` -### worker_task_slots_available +### `worker_task_slots_available` The total number of Workflow, Activity, Local Activity, or Nexus Task execution slots that are currently available. Use the `worker_type` key to differentiate execution slots. @@ -444,7 +443,7 @@ For example, Workflow Workers execute Workflow Tasks, Activity Workers execute A - Available in: Core, Go, Java - Tags: `namespace`, `task_queue`, `worker_type` -### worker_task_slots_used +### `worker_task_slots_used` The total number of Workflow, Activity, Local Activity, or Nexus Tasks execution slots in current use. Use the `worker_type` key to differentiate execution slots. @@ -455,14 +454,14 @@ For example, Workflow Workers execute Workflow Tasks, Activity Workers execute A - Available in: Core, Go, Java - Tags: `namespace`, `task_queue`, `worker_type` -### workflow_active_thread_count +### `workflow_active_thread_count` Total amount of Workflow threads in the Worker Process. - Type: Gauge - Available in: Java -### workflow_canceled +### `workflow_canceled` Workflow Execution ended because of a cancellation request. @@ -470,7 +469,7 @@ Workflow Execution ended because of a cancellation request. - Available in: Core, Go, Java - Tags: `namespace`, `task_queue`, `workflow_type` -### workflow_completed +### `workflow_completed` A Workflow Execution completed successfully. @@ -478,7 +477,7 @@ A Workflow Execution completed successfully. - Available in: Core, Go, Java - Tags: `namespace`, `task_queue`, `workflow_type` -### workflow_continue_as_new +### `workflow_continue_as_new` A Workflow ended with Continue-As-New. @@ -486,7 +485,7 @@ A Workflow ended with Continue-As-New. - Available in: Core, Go, Java - Tags: `namespace`, `task_queue`, `workflow_type` -### workflow_endtoend_latency +### `workflow_endtoend_latency` Total Workflow Execution time from schedule to completion for a single Workflow Run. (A retried Workflow Execution is a separate Run.) @@ -494,7 +493,7 @@ Total Workflow Execution time from schedule to completion for a single Workflow - Available in: Core, Go, Java - Tags: `namespace`, `task_queue`, `workflow_type` -### workflow_failed +### `workflow_failed` A Workflow Execution failed. @@ -502,7 +501,7 @@ A Workflow Execution failed. - Available in: Core, Go, Java - Tags: `namespace`, `task_queue`, `workflow_type` -### workflow_task_execution_failed +### `workflow_task_execution_failed` A Workflow Task Execution failed. @@ -515,7 +514,7 @@ Valid values for the `failure_reason` tag: - `NonDeterminismError`: The Workflow Task failed due to a non-determinism error. - `WorkflowError`: The Workflow Task failed for any other reason. -### workflow_task_execution_latency +### `workflow_task_execution_latency` Workflow Task Execution time. @@ -523,7 +522,7 @@ Workflow Task Execution time. - Available in: Core, Go, Java - Tags: `namespace`, `task_queue`, `workflow_type` -### workflow_task_queue_poll_empty +### `workflow_task_queue_poll_empty` A Workflow Worker polled a Task Queue and timed out without picking up a Workflow Task. @@ -531,7 +530,7 @@ A Workflow Worker polled a Task Queue and timed out without picking up a Workflo - Available in: Core, Go, Java - Tags: `namespace`, `task_queue` -### workflow_task_queue_poll_succeed +### `workflow_task_queue_poll_succeed` A Workflow Worker polled a Task Queue and successfully picked up a Workflow Task. @@ -539,7 +538,7 @@ A Workflow Worker polled a Task Queue and successfully picked up a Workflow Task - Available in: Core, Go, Java - Tags: `namespace`, `task_queue` -### workflow_task_replay_latency +### `workflow_task_replay_latency` Time to catch up on replaying a Workflow Task. @@ -547,7 +546,7 @@ Time to catch up on replaying a Workflow Task. - Available in: Core, Go, Java - Tags: `namespace`, `task_queue`, `workflow_type` -### workflow_task_schedule_to_start_latency +### `workflow_task_schedule_to_start_latency` The Schedule-To-Start time of a Workflow Task. From a7200b953a93388522fd5e99eb4486253083d281 Mon Sep 17 00:00:00 2001 From: Duncan Mackenzie Date: Tue, 28 Jul 2026 15:52:30 -0700 Subject: [PATCH 2/3] fix(toc): wrap long identifiers before underscores Add break opportunities before underscores in table of contents entries, tighten the leading within an entry, and hang continuation lines. --- src/css/custom.css | 13 +++++++++++++ src/theme/TOCItems/index.tsx | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 48 insertions(+) create mode 100644 src/theme/TOCItems/index.tsx diff --git a/src/css/custom.css b/src/css/custom.css index 5457b05c38..50739ebeeb 100644 --- a/src/css/custom.css +++ b/src/css/custom.css @@ -778,6 +778,19 @@ h3 { text-decoration: none; } +/* Long identifiers wrap onto several lines in the table of contents. Tighten the + leading within an entry and widen the gap between entries so a wrapped line + doesn't read as a separate entry, and hang the continuation lines. */ +.table-of-contents__link { + line-height: 1.25; + padding-left: 0.7rem; + text-indent: -0.7rem; +} + +.table-of-contents li { + margin: 0.55rem var(--ifm-toc-padding-horizontal); +} + .announcementBar_node_modules-\@docusaurus-theme-classic-lib-next-theme-AnnouncementBar-styles-module { --docusaurus-announcement-bar-height: 4rem; font-size: 24px; diff --git a/src/theme/TOCItems/index.tsx b/src/theme/TOCItems/index.tsx new file mode 100644 index 0000000000..ea384d7504 --- /dev/null +++ b/src/theme/TOCItems/index.tsx @@ -0,0 +1,35 @@ +import React, { useMemo } from 'react'; +import TOCItems from '@theme-original/TOCItems'; +import type TOCItemsType from '@theme/TOCItems'; +import type { WrapperProps } from '@docusaurus/types'; + +type Props = WrapperProps; + +// Reference pages use identifiers such as local_activity_succeed_endtoend_latency +// as headings. No CSS property breaks on underscores, so add the break opportunities +// here. is used rather than a zero-width space because it contributes no +// characters when the text is copied. +function addUnderscoreBreaks(html: string): string { + if (!html.includes('_')) { + return html; + } + return html + .split(/(<[^>]*>)/) + .map((part) => + part.startsWith('<') ? part : part.replace(/_/g, '_'), + ) + .join(''); +} + +export default function TOCItemsWrapper(props: Props): JSX.Element { + const toc = useMemo( + () => + props.toc.map((item) => ({ + ...item, + value: addUnderscoreBreaks(item.value), + })), + [props.toc], + ); + + return ; +} From c16eb6ba0340ba460aac7a7bf168cab9eb88b7c7 Mon Sep 17 00:00:00 2001 From: Duncan Mackenzie Date: Tue, 28 Jul 2026 16:14:23 -0700 Subject: [PATCH 3/3] Add consistency check for the SDK metrics reference Validate that the summary table and per-metric sections agree on anchor, type, and availability, that headings are backticked metric names, and that SDK names, metric types, emitters, and tags come from a known vocabulary. Runs on pull requests that touch the page. --- .github/workflows/check-metrics-reference.yml | 31 ++ bin/check-metrics-reference.js | 276 ++++++++++++++++++ bin/check-metrics-reference.test.js | 218 ++++++++++++++ package.json | 1 + 4 files changed, 526 insertions(+) create mode 100644 .github/workflows/check-metrics-reference.yml create mode 100644 bin/check-metrics-reference.js create mode 100644 bin/check-metrics-reference.test.js diff --git a/.github/workflows/check-metrics-reference.yml b/.github/workflows/check-metrics-reference.yml new file mode 100644 index 0000000000..a528abc84e --- /dev/null +++ b/.github/workflows/check-metrics-reference.yml @@ -0,0 +1,31 @@ +name: Check Metrics Reference + +on: + pull_request: + paths: + - "docs/references/sdk-metrics.mdx" + - "bin/check-metrics-reference.js" + - "bin/check-metrics-reference.test.js" + - ".github/workflows/check-metrics-reference.yml" + +permissions: + contents: read + +jobs: + check-metrics-reference: + name: Validate metrics reference consistency + runs-on: ubuntu-latest + steps: + - name: Check out repository + uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 + + - name: Set up Node.js + uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 + with: + node-version: "24" + + - name: Test the checker + run: node --test bin/check-metrics-reference.test.js + + - name: Check the metrics reference + run: node bin/check-metrics-reference.js diff --git a/bin/check-metrics-reference.js b/bin/check-metrics-reference.js new file mode 100644 index 0000000000..77208c38f3 --- /dev/null +++ b/bin/check-metrics-reference.js @@ -0,0 +1,276 @@ +#!/usr/bin/env node + +// Checks the internal consistency of the SDK metrics reference. +// +// The page states the same facts twice: once in the summary table and once in +// the per-metric section below it. Nothing kept the two in sync, which is how +// the table came to link two metrics to each other's anchors and to disagree +// with five sections about which SDKs emit a metric. +// +// This check is deliberately self-contained. It reads no SDK source and cannot +// tell you that a documented metric name is wrong, only that the page +// contradicts itself or uses a value outside the vocabularies below. +// +// node bin/check-metrics-reference.js + +const fs = require('fs'); +const path = require('path'); + +const PAGES = [path.join('docs', 'references', 'sdk-metrics.mdx')]; + +const METRIC_TYPES = ['Counter', 'Gauge', 'Histogram']; +const EMITTERS = ['Worker', 'Service Client']; +const SDKS = ['Core', 'Go', 'Java']; + +// Every tag any metric on the page is allowed to carry. Both the shared key +// list near the top of the page and each metric's own "Tags" line are checked +// against this, so a hyphen/underscore slip in either place is a failure. +const TAGS = [ + 'activity_type', + 'failure_reason', + 'namespace', + 'nexus_operation', + 'nexus_service', + 'operation', + 'poller_type', + 'task_queue', + 'worker_type', + 'workflow_type', +]; + +// The units admonition explains that histograms are milliseconds in Core-based +// SDKs and seconds in Go and Java, so an individual metric must not claim one. +const UNIT_CLAIM = /\bin (seconds|milliseconds)\b/i; + +const METRIC_NAME = '[a-z0-9_]+'; + +function parseTable(content) { + const lines = content.split('\n'); + const start = lines.findIndex((l) => l.startsWith('| Metric name')); + if (start === -1) return []; + + const rows = []; + for (let i = start + 2; i < lines.length; i++) { + const line = lines[i]; + if (!line.startsWith('|')) break; + + const cells = line + .slice(1, line.lastIndexOf('|')) + .split('|') + .map((c) => c.trim()); + + const link = cells[0].match( + new RegExp(`^\\[temporal_(${METRIC_NAME})\\]\\(#(${METRIC_NAME})\\)$`), + ); + + rows.push({ + line: i + 1, + raw: cells[0], + name: link ? link[1] : null, + anchor: link ? link[2] : null, + emitter: cells[1], + type: cells[2], + availability: cells[3], + }); + } + return rows; +} + +function parseSections(content) { + const parts = content.split(new RegExp(`^### \`(${METRIC_NAME})\`$`, 'm')); + const sections = []; + + for (let i = 1; i < parts.length; i += 2) { + const body = parts[i + 1]; + const field = (label) => { + const m = body.match(new RegExp(`^- ${label}: (.*)$`, 'm')); + return m ? m[1].trim() : null; + }; + + sections.push({ + name: parts[i], + type: field('Type'), + availability: field('Available in'), + tags: field('Tags'), + // Prose above the first bullet, which is where a stray unit claim lands. + description: body.split('\n- ')[0], + }); + } + return sections; +} + +// Headings must be a backticked metric name so the anchors stay predictable +// and match the sibling cluster-metrics reference. +function findMalformedHeadings(content) { + return content + .split('\n') + .map((line, i) => ({ line: i + 1, text: line })) + .filter( + ({ text }) => + text.startsWith('### ') && + !new RegExp(`^### \`${METRIC_NAME}\`$`).test(text), + ); +} + +// The shared "Each metric may have..." list, whose entries are backticked tag +// names at one indent level. Nested entries are tag *values*, not names. +function parseTagList(content) { + const start = content.indexOf('Each metric may have'); + if (start === -1) return []; + const block = content.slice(start).split('\n\n').slice(0, 2).join('\n\n'); + + return [...block.matchAll(/^- `([a-z0-9_-]+)`/gm)].map((m) => m[1]); +} + +function splitList(value) { + return value.split(',').map((v) => v.trim()); +} + +function backtickedNames(value) { + return [...value.matchAll(/`([a-z0-9_-]+)`/g)].map((m) => m[1]); +} + +function checkPage(content) { + const problems = []; + const add = (msg) => problems.push(msg); + + for (const { line, text } of findMalformedHeadings(content)) { + add(`line ${line}: heading is not a backticked metric name: ${text}`); + } + + for (const tag of parseTagList(content)) { + if (!TAGS.includes(tag)) { + add(`shared tag list: unknown tag \`${tag}\``); + } + } + + const rows = parseTable(content); + const sections = parseSections(content); + + if (rows.length === 0) add('summary table not found or empty'); + if (sections.length === 0) add('no metric sections found'); + + const sectionsByName = new Map(sections.map((s) => [s.name, s])); + + for (const row of rows) { + if (!row.name) { + add(`line ${row.line}: table row is not a temporal_ metric link: ${row.raw}`); + continue; + } + + const label = row.name; + + if (row.anchor !== row.name) { + add(`${label}: table links to #${row.anchor}`); + } + if (!EMITTERS.includes(row.emitter)) { + add(`${label}: unknown "Emitted by" value "${row.emitter}"`); + } + if (!METRIC_TYPES.includes(row.type)) { + add(`${label}: unknown metric type "${row.type}" in table`); + } + for (const sdk of splitList(row.availability)) { + if (!SDKS.includes(sdk)) { + add(`${label}: unknown SDK "${sdk}" in table availability`); + } + } + + const section = sectionsByName.get(row.anchor); + if (!section) { + add(`${label}: table links to #${row.anchor} but no such section exists`); + continue; + } + + if (section.type !== row.type) { + add( + `${label}: type disagrees (table "${row.type}", section "${section.type}")`, + ); + } + if (section.availability !== row.availability) { + add( + `${label}: availability disagrees (table "${row.availability}", section "${section.availability}")`, + ); + } + } + + const rowsByAnchor = new Set(rows.map((r) => r.anchor)); + + for (const section of sections) { + const label = section.name; + + if (!rowsByAnchor.has(section.name)) { + add(`${label}: has a section but no summary table row`); + } + + if (section.type === null) { + add(`${label}: section is missing a "Type" line`); + } else if (!METRIC_TYPES.includes(section.type)) { + add(`${label}: unknown metric type "${section.type}" in section`); + } + + if (section.availability === null) { + add(`${label}: section is missing an "Available in" line`); + } else { + for (const sdk of splitList(section.availability)) { + if (!SDKS.includes(sdk)) { + add(`${label}: unknown SDK "${sdk}" in section availability`); + } + } + } + + if (section.tags !== null) { + for (const tag of backtickedNames(section.tags)) { + if (!TAGS.includes(tag)) { + add(`${label}: unknown tag \`${tag}\``); + } + } + } + + const unit = section.description.match(UNIT_CLAIM); + if (unit) { + add( + `${label}: description says "${unit[0]}", which contradicts the units admonition`, + ); + } + } + + return problems; +} + +function main() { + let failed = false; + + for (const page of PAGES) { + const content = fs.readFileSync(path.join(process.cwd(), page), 'utf8'); + const problems = checkPage(content); + + if (problems.length === 0) { + const count = parseTable(content).length; + console.log(`${page}: ${count} metrics consistent.`); + continue; + } + + failed = true; + console.error(`${page}: ${problems.length} problem(s)\n`); + for (const problem of problems) { + console.error(` ${problem}`); + } + console.error(''); + } + + process.exit(failed ? 1 : 0); +} + +module.exports = { + PAGES, + TAGS, + parseTable, + parseSections, + parseTagList, + findMalformedHeadings, + checkPage, +}; + +if (require.main === module) { + main(); +} diff --git a/bin/check-metrics-reference.test.js b/bin/check-metrics-reference.test.js new file mode 100644 index 0000000000..14cf90a093 --- /dev/null +++ b/bin/check-metrics-reference.test.js @@ -0,0 +1,218 @@ +const { describe, it } = require('node:test'); +const assert = require('node:assert'); +const fs = require('fs'); +const path = require('path'); +const { + PAGES, + parseTable, + parseSections, + parseTagList, + findMalformedHeadings, + checkPage, +} = require('./check-metrics-reference.js'); + +// A minimal page with the same shape as the real one. Each test mutates one +// thing so a failure names the rule that broke rather than the whole page. +const VALID_PAGE = `Each metric may have some combination of the following keys attached to them: + +- \`task_queue\`: Task Queue that the Worker Entity is polling +- \`poller_type\`: One of the following: + - \`workflow_task\` +- \`namespace\`: Namespace the Worker is bound to + +Some keys may not be available in every SDK. + +| Metric name | Emitted by | Metric type | Availability | +| -------------------------------------------- | ---------- | ----------- | -------------- | +| [temporal_worker_start](#worker_start) | Worker | Counter | Core, Go, Java | +| [temporal_num_pollers](#num_pollers) | Worker | Gauge | Core, Go, Java | + +### \`num_pollers\` + +Current number of Worker Entities that are polling. + +- Type: Gauge +- Available in: Core, Go, Java +- Tags: \`namespace\`, \`poller_type\` + +### \`worker_start\` + +A Worker Entity has been registered. + +- Type: Counter +- Available in: Core, Go, Java +- Tags: \`namespace\`, \`task_queue\` +`; + +const problemsFor = (page) => checkPage(page); + +describe('parseTable', () => { + it('extracts a row per metric', () => { + const rows = parseTable(VALID_PAGE); + assert.strictEqual(rows.length, 2); + assert.deepStrictEqual( + rows.map((r) => r.name), + ['worker_start', 'num_pollers'], + ); + }); + + it('captures the anchor separately from the metric name', () => { + const [row] = parseTable(VALID_PAGE); + assert.strictEqual(row.name, 'worker_start'); + assert.strictEqual(row.anchor, 'worker_start'); + }); + + it('returns nothing when there is no table', () => { + assert.deepStrictEqual(parseTable('# Just a heading\n'), []); + }); +}); + +describe('parseSections', () => { + it('extracts type, availability, and tags', () => { + const sections = parseSections(VALID_PAGE); + assert.strictEqual(sections.length, 2); + assert.deepStrictEqual(sections[0], { + name: 'num_pollers', + type: 'Gauge', + availability: 'Core, Go, Java', + tags: '`namespace`, `poller_type`', + description: '\n\nCurrent number of Worker Entities that are polling.\n', + }); + }); + + it('reports missing fields as null', () => { + const page = VALID_PAGE.replace('- Type: Gauge\n', ''); + assert.strictEqual(parseSections(page)[0].type, null); + }); +}); + +describe('parseTagList', () => { + it('reads tag names from the shared key list', () => { + assert.deepStrictEqual(parseTagList(VALID_PAGE), [ + 'task_queue', + 'poller_type', + 'namespace', + ]); + }); + + it('ignores nested tag values', () => { + assert.ok(!parseTagList(VALID_PAGE).includes('workflow_task')); + }); +}); + +describe('findMalformedHeadings', () => { + it('accepts backticked metric names', () => { + assert.deepStrictEqual(findMalformedHeadings(VALID_PAGE), []); + }); + + it('rejects a bare heading', () => { + const page = VALID_PAGE.replace('### `num_pollers`', '### num_pollers'); + assert.strictEqual(findMalformedHeadings(page).length, 1); + }); +}); + +describe('checkPage', () => { + it('passes a well-formed page', () => { + assert.deepStrictEqual(problemsFor(VALID_PAGE), []); + }); + + // Each case below is a bug that actually shipped on the SDK metrics page. + + it('catches a table row linking to another metric anchor', () => { + const page = VALID_PAGE.replace( + '[temporal_num_pollers](#num_pollers)', + '[temporal_num_pollers](#worker_start)', + ); + const problems = problemsFor(page); + assert.ok(problems.some((p) => p.includes('table links to #worker_start'))); + }); + + it('catches the table and section disagreeing on availability', () => { + const page = VALID_PAGE.replace( + '| [temporal_num_pollers](#num_pollers) | Worker | Gauge | Core, Go, Java |', + '| [temporal_num_pollers](#num_pollers) | Worker | Gauge | Core, Go |', + ); + const problems = problemsFor(page); + assert.ok(problems.some((p) => p.includes('availability disagrees'))); + }); + + it('catches the table and section disagreeing on type', () => { + const page = VALID_PAGE.replace('- Type: Gauge', '- Type: Counter'); + assert.ok(problemsFor(page).some((p) => p.includes('type disagrees'))); + }); + + it('catches a trailing comma in an availability list', () => { + const page = VALID_PAGE.replace( + '- Available in: Core, Go, Java\n- Tags: `namespace`, `poller_type`', + '- Available in: Go,\n- Tags: `namespace`, `poller_type`', + ); + const problems = problemsFor(page); + assert.ok(problems.some((p) => p.includes('unknown SDK ""'))); + }); + + it('catches a hyphenated tag name in the shared key list', () => { + const page = VALID_PAGE.replace('- `task_queue`:', '- `task-queue`:'); + const problems = problemsFor(page); + assert.ok(problems.some((p) => p.includes('unknown tag `task-queue`'))); + }); + + it('catches a hyphenated tag name on a metric', () => { + const page = VALID_PAGE.replace( + '- Tags: `namespace`, `task_queue`', + '- Tags: `namespace`, `task-queue`', + ); + const problems = problemsFor(page); + assert.ok(problems.some((p) => p.includes('unknown tag `task-queue`'))); + }); + + it('catches a unit claim that contradicts the units admonition', () => { + const page = VALID_PAGE.replace( + 'Current number of Worker Entities that are polling.', + 'The Schedule-To-Start time of an Activity Task in seconds.', + ); + const problems = problemsFor(page); + assert.ok(problems.some((p) => p.includes('contradicts the units'))); + }); + + it('catches a section with no table row', () => { + const page = `${VALID_PAGE}\n### \`orphan_metric\`\n\nText.\n\n- Type: Counter\n- Available in: Go\n`; + const problems = problemsFor(page); + assert.ok(problems.some((p) => p.includes('no summary table row'))); + }); + + it('catches a table row with no section', () => { + const page = VALID_PAGE.replace( + '### `worker_start`', + '### `worker_started`', + ); + const problems = problemsFor(page); + assert.ok(problems.some((p) => p.includes('no such section exists'))); + }); + + it('catches a section missing its Available in line', () => { + const page = VALID_PAGE.replace('- Available in: Core, Go, Java\n- Tags: `namespace`, `poller_type`', '- Tags: `namespace`, `poller_type`'); + const problems = problemsFor(page); + assert.ok(problems.some((p) => p.includes('missing an "Available in" line'))); + }); + + it('catches an unknown SDK name', () => { + const page = VALID_PAGE.replace( + '- Available in: Core, Go, Java\n- Tags: `namespace`, `poller_type`', + '- Available in: Core, Rust\n- Tags: `namespace`, `poller_type`', + ); + const problems = problemsFor(page); + assert.ok(problems.some((p) => p.includes('unknown SDK "Rust"'))); + }); +}); + +describe('the real reference pages', () => { + for (const page of PAGES) { + it(`${page} is internally consistent`, () => { + const content = fs.readFileSync( + path.join(__dirname, '..', page), + 'utf8', + ); + assert.deepStrictEqual(checkPage(content), []); + }); + } +}); diff --git a/package.json b/package.json index 2acbb0e105..d30f9b27ac 100644 --- a/package.json +++ b/package.json @@ -16,6 +16,7 @@ "check-links": "hyperlink build/index.html --recursive --check-anchors", "validate:og-images": "node ./bin/validate-og-images.js", "check:og-budget": "node ./bin/check-og-build-budget.js", + "check:metrics": "node ./bin/check-metrics-reference.js", "test": "node --test", "lint": "vale --output=JSON docs/**/**/*.mdx > vale-output.json; vale --output=JSON docs/**/**/*.md > vale-md-output.json", "lint:go": "vale docs/develop/go/*.mdx",