

# 使用 Playwright 运行时编写 Node.js Canary 脚本
<a name="Synthetics_WritingCanary_Nodejs_Playwright"></a>

**Topics**
+ [将 Node.js Canary 文件打包用于 Playwright 运行时](#Synthetics_canary_Nodejs_Playwright_package)
+ [更改现有 Playwright 脚本以将其用作 CloudWatch Synthetics Canary](#CloudWatch_Synthetics_canary_edit_Playwright_script)
+ [CloudWatch Synthetics 配置](#Synthetics_canary_configure_Playwright_script)

## 将 Node.js Canary 文件打包用于 Playwright 运行时
<a name="Synthetics_canary_Nodejs_Playwright_package"></a>

 Canary 脚本包含一个 `.js`（CommonJS 语法）或 `.mjs`（ES 语法）文件，其中包含您的 Synthetics 处理程序代码，以及代码所依赖的任何其他包和模块。以 ES（ECMAScript）格式创建的脚本应使用 .mjs 作为扩展名，或者包含一个设置了 "type": "module" 字段的 package.json 文件。与 Node.js Puppeteer 等其他运行时不同，您无需将脚本保存在特定的文件夹结构中。您可以直接打包脚本。使用您的首选 `zip` 实用工具创建一个 `.zip` 文件，并将处理程序文件置于根目录中。如果您的 Canary 脚本依赖未包含在 Synthetics 运行时中的其他包或模块，您可以将这些依赖项添加到 `.zip` 文件中。为此，您可以通过运行 `npm install` 命令将函数所需的库安装到 `node_modules` 目录中。以下示例 CLI 命令将创建名为 `my_deployment_package.zip` 的 `.zip` 文件，其中包含 `index.js` 或 `index.mjs` 文件（Synthetics 处理程序）及其依赖项。在示例中，您要使用 `npm` 程序包管理器来安装依赖项。

```
~/my_function
├── index.mjs
├── synthetics.json
├── myhelper-util.mjs    
└── node_modules
    ├── mydependency
```

在根目录下创建一个包含您的项目文件夹内容的 `.zip` 文件。使用 `r`（递归）选项，如以下示例所示，确保 `zip` 压缩子文件夹。

```
zip -r my_deployment_package.zip .
```

添加 Synthetics 配置文件来配置 CloudWatch Synthetics 的行为。您可以创建 `synthetics.json` 文件并将其保存在与入口点或处理程序文件相同的路径下。

或者，您也可以将入口点文件存储在您选择的文件夹结构中。但是，请确保在您的处理程序名称中指定了文件夹路径。

 **处理程序名称** 

请务必将您的金丝雀脚本入口点（处理程序）设置为 ` myCanaryFilename.functionName`，以匹配脚本入口点的文件名。您也可以选择将 Canary 存储在单独的文件夹中，例如 ` myFolder/my_canary_filename.mjs`。如果将其存储在单独的文件夹中，请在脚本入口点中指定该路径，例如 ` myFolder/my_canary_filename.functionName`。

## 更改现有 Playwright 脚本以将其用作 CloudWatch Synthetics Canary
<a name="CloudWatch_Synthetics_canary_edit_Playwright_script"></a>

您可以编辑 Node.js 和 Playwright 的现有脚本以用作 Canary。有关 Playwright 的更多信息，请参阅 [Playwright 库](https://playwright.dev/docs/api/class-playwright)文档。

您可以使用保存在文件 ` exampleCanary.mjs` 中的以下 Playwright 脚本。

```
import { chromium } from 'playwright';
import { expect } from '@playwright/test';

const browser = await chromium.launch();
const page = await browser.newPage();
await page.goto('https://example.com', {timeout: 30000});
await page.screenshot({path: 'example-home.png'});

const title = await page.title();
expect(title).toEqual("Example Domain");
 
await browser.close();
```

执行以下步骤转换脚本：

1. 创建和导出 `handler` 函数。处理程序是脚本的入口点函数。您可以为处理程序函数选择任何名称，但脚本中使用的函数应与 Canary 处理程序中的函数相同。如果您的脚本名称为 `exampleCanary.mjs`，处理程序函数名称为 `myhandler`，则您的 Canary 处理程序命名为 `exampleCanary.myhandler`。在以下示例中，处理程序函数的名称为 `handler`。

   ```
   exports.handler = async () => {
     // Your script here
     };
   ```

1. 将 `Synthetics Playwright module` 作为依赖项导入。

   ```
   import { synthetics } from '@aws/synthetics-playwright';
   ```

1. 使用 Synthetics `Launch` 函数启动浏览器。

   ```
   const browser = await synthetics.launch();
   ```

1. 使用 Synthetics `newPage` 函数创建新的 Playwright 页面。

   ```
   const page = await synthetics.newPage();
   ```

您的脚本现已准备好作为 Synthetics Canary 运行。以下是更新的脚本：

 **更新了 ES6 格式的脚本** 

脚本文件以 `.mjs` 扩展名保存。

```
import { synthetics } from '@aws/synthetics-playwright';
import { expect } from '@playwright/test';

export const handler = async (event, context) => {
  try {
        // Launch a browser
        const browser = await synthetics.launch();
        
        // Create a new page
        const page = await synthetics.newPage(browser);
        
        // Navigate to a website
        await page.goto('https://www.example.com', {timeout: 30000});
        
        // Take screenshot
        await page.screenshot({ path: '/tmp/example.png' });
        
        // Verify the page title
        const title = await page.title();
        expect(title).toEqual("Example Domain");
    } finally {
        // Ensure browser is closed
        await synthetics.close();
    }
};
```

 **更新的 CommonJS 格式的脚本** 

脚本文件以 `.js` 扩展名保存。

```
const { synthetics } = require('@aws/synthetics-playwright');
const { expect } = require('@playwright/test');

exports.handler = async (event) => {
  try {
    const browser = await synthetics.launch();
    const page = await synthetics.newPage(browser);
    await page.goto('https://www.example.com', {timeout: 30000});
    await page.screenshot({ path: '/tmp/example.png' });
    const title = await page.title();
    expect(title).toEqual("Example Domain");
  } finally {
    await synthetics.close();
  }
};
```

## CloudWatch Synthetics 配置
<a name="Synthetics_canary_configure_Playwright_script"></a>

您可以通过提供一个名为 `synthetics.json` 的可选 JSON 配置文件来配置 Synthetics Playwright 运行时的行为。此文件应与处理程序文件打包在相同的位置。尽管配置文件是可选的，但如果您未提供配置文件或缺少配置密钥，CloudWatch 将采用默认值。

 **打包您的配置文件** 

以下是支持的配置值及其默认值。

```
{
    "step": {
        "screenshotOnStepStart": false,
        "screenshotOnStepSuccess": false,
        "screenshotOnStepFailure": false,
        "stepSuccessMetric": true,
        "stepDurationMetric": true,
        "continueOnStepFailure": true,
        "stepsReport": true
    },
    "report": {
        "includeRequestHeaders": true,
        "includeResponseHeaders": true,
        "includeUrlPassword": false,
        "includeRequestBody": true,
        "includeResponseBody": true,
        "restrictedHeaders": ['x-amz-security-token', 'Authorization'], // Value of these headers is redacted from logs and reports
        "restrictedUrlParameters": ['Session', 'SigninToken'] // Values of these url parameters are redacted from logs and reports
    },
    "logging": {
        "logRequest": false,
        "logResponse": false,
        "logResponseBody": false,
        "logRequestBody": false,
        "logRequestHeaders": false,
        "logResponseHeaders": false
    },
    "httpMetrics": {
        "metric_2xx": true,
        "metric_4xx": true,
        "metric_5xx": true,
        "failedRequestsMetric": true,
        "aggregatedFailedRequestsMetric": true,
        "aggregated2xxMetric": true,
        "aggregated4xxMetric": true,
        "aggregated5xxMetric": true
    },
    "canaryMetrics": {
        "failedCanaryMetric": true,
        "aggregatedFailedCanaryMetric": true
    },
    "userAgent": "",
    "har": true
}
```

 **步骤配置** 
+ `screenshotOnStepStart` – 确定 Synthetics 是否应在步骤开始之前捕获屏幕截图。默认值为 `true`。
+ `screenshotOnStepSuccess` – 确定 Synthetics 是否应在步骤成功后捕获屏幕截图。默认值为 `true`。
+ `screenshotOnStepFailure` – 确定 Synthetics 是否应在步骤失败后捕获屏幕截图。默认值为 `true`。
+ `continueOnStepFailure` – 确定是否即使步骤失败，脚本仍应继续。默认值为 `false`。
+ `stepSuccessMetric` – 确定是否发出步骤的 ` SuccessPercent` 指标。对于 Canary 运行，如果步骤成功，步骤的 `SuccessPercent` 指标为 `100`，如果步骤失败，则为 `0`。默认值为 `true`。
+ `stepDurationMetric` – 确定是否发出步骤的 `Duration` 指标。`Duration` 指标以步骤运行的持续时间（以毫秒为单位）发出。默认值为 `true`。

 **报告配置** 

包括 CloudWatch Synthetics 生成的所有报告，例如 HAR 文件和 Synthetics 步骤报告。敏感数据编辑字段 `restrictedHeaders` 和 `restrictedUrlParameters` 也适用于 Synthetics 生成的日志。
+ `includeRequestHeaders` – 是否在报告中包含请求标头。默认值为 `false`。
+ `includeResponseHeaders` – 是否在报告中包含响应标头。默认值为 `false`。
+ `includeUrlPassword` – 是否包含 URL 中显示的密码。默认情况下，URL 中显示的密码会在日志和报告中进行编辑，以防泄露敏感数据。默认为 `false`。
+ `includeRequestBody` – 是否在报告中包含请求正文。默认值为 `false`。
+ `includeResponseBody` – 是否在报告中包含响应正文。默认值为 `false`。
+ `restrictedHeaders` – 要忽略的标头值列表（如果包含标头）。这对请求标头和响应标头均适用。例如，您可以通过将 `includeRequestHeaders` 传递为 true 并将 `restrictedHeaders` 传递为 `['Authorization']` 来隐藏您的凭证。
+ `restrictedUrlParameters` – 要编辑的 URL 路径或查询参数的列表。这适用于出现在日志、报告和错误中的 URL。此参数区分大小写。您可以传递星号 (`*`) 作为值来编辑所有 URL 路径和查询参数值。默认值为空数组。
+ `har` – 确定是否应生成 HTTP 存档（HAR）。默认值为 `true`。

下面是报告配置文件的示例：

```
"includeRequestHeaders": true,
"includeResponseHeaders": true,
"includeUrlPassword": false,
"includeRequestBody": true,
"includeResponseBody": true,
"restrictedHeaders": ['x-amz-security-token', 'Authorization'], // Value of these headers is redacted from logs and reports
"restrictedUrlParameters": ['Session', 'SigninToken'] // Values of these URL parameters are redacted from logs and reports
```

 **日志记录配置** 

适用于 CloudWatch Synthetics 生成的日志。控制请求和响应日志的详细程度。
+ `logRequest` – 是否将每个请求都记录在 Canary 日志中。对于 UI 金丝雀，这会记录浏览器发送的每个请求。默认值为 ` false`。
+ `logResponse` – 是否将每个响应都记录在 Canary 日志中。对于 UI 金丝雀，这会记录浏览器收到的每个响应。默认值为 ` false`。
+ `logRequestBody` – 是否随请求一起将请求正文记录在 Canary 日志中。仅当 `logRequest` 为 true 时，此配置才适用。默认值为 `false`。
+ `logResponseBody` – 是否随请求一起将响应正文记录在 Canary 日志中。仅当 `logResponse` 为 true 时，此配置才适用。默认值为 `false`。
+ `logRequestHeaders` – 是否随请求一起将请求标头记录在 Canary 日志中。仅当 ` logRequest` 为 true 时，此配置才适用。默认值为 `false`。
+ `logResponseHeaders` – 是否随响应一起将响应标头记录在 Canary 日志中。仅当 ` logResponse` 为 true 时，此配置才适用。默认值为 `false`。

 **HTTP 指标配置** 

与 CloudWatch Synthetics 针对此 Canary 发出的具有不同 HTTP 状态代码的网络请求数量相关的指标的配置。
+ `metric_2xx` – 是否发出此 Canary 的 `2xx` 指标（带 `CanaryName` 维度）。默认值为 ` true`。
+ `metric_4xx` – 是否发出此 Canary 的 `4xx` 指标（带 `CanaryName` 维度）。默认值为 ` true`。
+ `metric_5xx` – 是否发出此 Canary 的 `5xx` 指标（带 `CanaryName` 维度）。默认值为 ` true`。
+ `failedRequestsMetric` – 是否发出此 Canary 的 ` failedRequests` 指标（带 `CanaryName` 维度）。默认值为 `true`。
+ `aggregatedFailedRequestsMetric` – 是否发出此 Canary 的 ` failedRequests` 指标（不带 `CanaryName` 维度）。默认值为 `true`。
+ `aggregated2xxMetric` – 是否发出此 Canary 的 `2xx` 指标（不带 `CanaryName` 维度）。默认值为 `true`。
+ `aggregated4xxMetric` – 是否发出此 Canary 的 `4xx` 指标（不带 `CanaryName` 维度）。默认值为 `true`。
+ `aggregated5xxMetric` – 是否发出此 Canary 的 `5xx` 指标（不带 `CanaryName` 维度）。默认值为 `true`。

 **Canary 指标配置** 

对 CloudWatch Synthetics 发出的其他指标的配置。
+ `failedCanaryMetric` – 是否发出此 Canary 的 `Failed` 指标（带 `CanaryName` 维度）。默认值为 ` true`。
+ `aggregatedFailedCanaryMetric` – 是否发出此 Canary 的 ` Failed` 指标（不带 `CanaryName` 维度）。默认值为 `true`。

 **其他配置** 
+ `userAgent` – 要附加到用户代理的字符串。用户代理是一个包含在请求标头中的字符串，用于识别您在使用无外设浏览器时访问的网站的浏览器。CloudWatch Synthetics 会自动添加 `CloudWatchSynthetics/canary-arn to the user agent`。指定的配置将附加到生成的用户代理。要附加的默认用户代理值是空字符串 (`""`)。

### CloudWatch Synthetics 环境变量
<a name="Synthetics_canary_Nodejs_Playwright_script"></a>

使用环境变量配置日志记录级别和格式。

 **日志格式** 

CloudWatch Synthetics Playwright 运行时会为每一次 Canary 运行创建 CloudWatch 日志。日志以 JSON 格式编写，便于查询。您还可以将日志格式更改为 `TEXT`。
+ `Environment variable name` – CW\$1SYNTHETICS\$1LOG\$1FORMAT 
+ `Supported values` – JSON、TEXT 
+ `Default` – JSON 

 **日志级别** 

尽管启用 `Debug` 模式会增加详细程度，但对于故障排除很有用。
+ `Environment variable name` – CW\$1SYNTHETICS\$1LOG\$1LEVEL
+ `Supported values` – TRACE、DEBUG、INFO、WARN、ERROR、FATAL 
+ `Default` – INFO