

# 编写金丝雀脚本
<a name="CloudWatch_Synthetics_Canaries_WritingCanary"></a>

以下部分介绍了如何编写 Canary 脚本，以及如何将 Canary 与其他 AWS 服务及外部依赖项和库集成。

**Topics**
+ [使用 Java 运行时编写金丝雀脚本](Synthetics_WritingCanary_Java.md)
+ [使用 Playwright 运行时编写 Node.js Canary 脚本](Synthetics_WritingCanary_Nodejs_Playwright.md)
+ [使用 Puppeteer 运行时编写 Node.js Canary 脚本](CloudWatch_Synthetics_Canaries_WritingCanary_Nodejs_Pup.md)
+ [编写 Python 金丝雀脚本](CloudWatch_Synthetics_Canaries_WritingCanary_Python.md)
+ [为 Node.js 多重检查蓝图编写 JSON 配置](CloudWatch_Synthetics_WritingCanary_Multichecks.md)

# 使用 Java 运行时编写金丝雀脚本
<a name="Synthetics_WritingCanary_Java"></a>

**Topics**
+ [金丝雀的 Java 项目结构](#Synthetics_canary_Java_package)
+ [为金丝雀打包项目](#Synthetics_canary_Java_package_canary)
+ [处理程序名称](#Synthetics_canary_Java_handler)
+ [CloudWatch Synthetics 配置](#Synthetics_canary_Java_config)
+ [CloudWatch Synthetics 环境变量](#Synthetics_canary_Java_variables)

## 金丝雀的 Java 项目结构
<a name="Synthetics_canary_Java_package"></a>

若要使用 Java 创建金丝雀，您需要编写和编译代码，并将编译后的构件部署到 Synthetics。您可以通过多种方式初始化 Java Lambda 项目。例如，您可以在 IntelliJ IDEA 或 Visual Studio Code 等首选 IDE 中使用标准的 Java 项目设置。或者，您也可以手动创建所需的文件结构。

Synthetics Java 项目包含以下常规结构：

```
/project-root
    └ src
        └ main
            └ java
                └ canarypackage // name of package
                |    └ ExampleCanary.java // Canary code file
                |    └ other_supporting_classes
                - resources
                     └ synthetics.json // Synthetics configuration file    
     └ build.gradle OR pom.xml
```

您可以使用 Maven 或 Gradle 来构建项目和管理依赖项。

在上面的结构中，`ExampleCanary` 类是金丝雀的入口点或处理程序。

 **Java 金丝雀类示例** 

此示例是让金丝雀向存储在 *TESTING\$1URL* Lambda 环境变量中的 URL 发出 GET 请求。该金丝雀不使用 Synthetics 运行时提供的任何方法。

```
package canarypackage;

import java.net.HttpURLConnection;
import java.net.URL;

// Handler value: canary.ExampleCanary::canaryCode
public class ExampleCanary { 
  public void canaryCode() throws Exception{ 
      URL url = new URL(System.getenv("TESTING_URL"));
      HttpURLConnection con=(HttpURLConnection)url.openConnection();
      con.setRequestMethod("GET");
      con.setConnectTimeout(5000);
      con.setReadTimeout(5000);
      int status=con.getResponseCode();
      if(status!=200){
        throw new Exception("Failed to load " + url + ", status code: " + status);
      }
  }
}
```

强烈建议使用 Synthetics 提供的库函数 `executeStep` 对该金丝雀进行模块化处理。该金丝雀会对从 URL1 和 URL2 环境变量获取的两个单独的 URL 进行 `get` 调用。

**注意**  
要使用 `executeStep` 功能，金丝雀的处理程序方法应采用类型为 Synthetics 的参数，如下所示。

```
package canarypackage;

import com.amazonaws.synthetics.Synthetics;
import java.net.HttpURLConnection;
import java.net.URL;

// Handler value: canary.ExampleCanary::canaryCode
public class ExampleCanary {
  public void canaryCode(Synthetics synthetics) throws Exception {
    createStep("Step1", synthetics, System.getenv("URL1"));
    createStep("Step2", synthetics, System.getenv("URL2"));
    return;
  }
  
  private void createStep(String stepName, Synthetics synthetics, String url) throws Exception{
    synthetics.executeStep(stepName,()->{
      URL obj=new URL(url);
      HttpURLConnection con=(HttpURLConnection)obj.openConnection();
      con.setRequestMethod("GET");
      con.setConnectTimeout(5000);
      con.setReadTimeout(5000);
      int status=con.getResponseCode();
      if(status!=200){
        throw new Exception("Failed to load" + url + "status code:" + status);
      }
      return null;
    }).get();
  }
}
```

## 为金丝雀打包项目
<a name="Synthetics_canary_Java_package_canary"></a>

Synthetics 接受以 *zip* 格式打包的 Java 金丝雀代码。zip 文件包含金丝雀代码的类文件、任何第三方依赖项的 jar 和 Synthetics 配置文件。

Synthetics Java zip 包含以下常规结构。

```
example-canary
    └ lib
    |  └ //third party dependency jars
       └ java-canary.jar
    └ synthetics.json
```

要根据上面的项目结构构建此 zip，您可以使用 gradle (build.gradle) 或 maven (pom.xml)。见下列。

有关 Synthetics 库的编译时依赖项或接口的信息，请参阅 [aws-aws-cloudwatch-synthetics-sdk-java](https://github.com/aws/aws-cloudwatch-synthetics-sdk-java/tree/main) 下的自述文件。

```
plugins {
    id 'java'
}

repositories {
    mavenCentral()
}

dependencies {
    // Third party dependencies 
    // example: implementation 'software.amazon.awssdk:s3:2.31.9'
    
    // Declares dependency on Synthetics interfaces for compiling only
    // Refer https://github.com/aws/aws-cloudwatch-synthetics-sdk-java for building from source.
    compileOnly 'software.amazon.synthetics:aws-cloudwatch-synthetics-sdk-java:1.0.0'}

test {
    useJUnitPlatform()
}

// Build the zip to be used as Canary code.
task buildZip(type: Zip) {

    archiveFileName.set("example-canary.zip")
    destinationDirectory.set(file("$buildDir"))
    
    from processResources
    into('lib') {
        from configurations.runtimeClasspath
        from(tasks.named("jar"))
    }
    from "src/main/java/resources/synthetics.json"
    
    doLast {
        println "Artifact written to: ${archiveFile.get().asFile.absolutePath}"
    }
}

java {
    toolchain {
        languageVersion = JavaLanguageVersion.of(21)
    }
}

tasks.named("build") {
    dependsOn "buildZip"
}
```

## 处理程序名称
<a name="Synthetics_canary_Java_handler"></a>

处理程序名称是金丝雀的入口点。对于 Java 运行时，处理程序采用以下格式。

```
<<full qualified name for canary class>>::<<name of the method to start the execution from>>
// for above code: canarypackage.ExampleCanary::canaryCode
```

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

您可以通过提供一个名为 `synthetics.json` 的可选 JSON 配置文件来配置 Synthetics Java 运行时的行为。此文件应打包到 zip 包的根目录中。尽管配置文件是可选的，但如果您未提供配置文件或缺少配置密钥，CloudWatch 将采用默认值。

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

```
{
    "step": {
        "stepSuccessMetric": true,
        "stepDurationMetric": true,
        "continueOnStepFailure": false,
        "stepsReport": true
    },
    "logging": {
        "logRequest": false,
        "logResponse": false
    },
    "httpMetrics": {
        "metric_2xx": true,
        "metric_4xx": true,
        "metric_5xx": true,
        "aggregated2xxMetric": true,
        "aggregated4xxMetric": true,
        "aggregated5xxMetric": true
    },
    "canaryMetrics": {
        "failedCanaryMetric": true,
        "aggregatedFailedCanaryMetric": true
    }
}
```

 **步骤配置** 
+ *continueOnStepFailure* – 确定是否即使步骤失败，脚本仍应继续。默认值为 false。
+ *stepSuccessMetric* – 确定是否发出步骤的 ` SuccessPercent` 指标。对于金丝雀运行，如果步骤成功，步骤的 `SuccessPercent` 指标为 *100*；如果步骤失败，则为 *0*。默认值为 *true*。
+ *stepDurationMetric* – 确定是否发出步骤的 *Duration* 指标。*Duration* 指标以步骤运行的持续时间（以毫秒为单位）发出。默认值为 *true*。

 **日志记录配置** 

适用于 CloudWatch Synthetics 生成的日志。控制请求和响应日志的详细程度。
+ *logRequest* – 指定是否将每个请求都记录在金丝雀日志中。默认值为 false。
+ *logResponse* – 指定是否将每个响应都记录在金丝雀日志中。默认值为 false。

 **HTTP 指标配置** 

与 CloudWatch Synthetics 针对此 Canary 发出的具有不同 HTTP 状态代码的网络请求数量相关的指标的配置。
+ *metric\$12xx* – 指定是否发出此金丝雀的 *2xx* 指标（含 CanaryName 维度）。默认值为 *true*。
+ *metric\$14xx* – 指定是否发出此金丝雀的 *4xx* 指标（含 CanaryName 维度）。默认值为 *true*。
+ *metric\$15xx* – 指定是否发出此金丝雀的 *5xx* 指标（含 CanaryName 维度）。默认值为 *true*。
+ *aggregated2xxMetric* – 指定是否发出此金丝雀的 *2xx* 指标（不含 CanaryName 维度）。默认值为 *true*。
+ *aggregated4xxMetric* – 指定是否发出此金丝雀的 *4xx* 指标（不含 CanaryName 维度）。默认值为 *true*。
+ *aggregated5xxMetric* – 指定是否发出此金丝雀的 *5xx* 指标（不含 CanaryName 维度）。默认值为 *true*。

 **Canary 指标配置** 

对 CloudWatch Synthetics 发出的其他指标的配置。
+ *failedCanaryMetric* – 网络访问分析器指定是否发出此金丝雀的 *Failed* 指标（含 CanaryName 维度）。默认值为 *true*。
+ *aggatedFailedCanaryMetric* – 指定是否发出此金丝雀的 *Failed* 指标（不含 CanaryName 维度）。默认值为 *true*。

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

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

 **日志格式** 

CloudWatch Synthetics Java 运行时会为每一次金丝雀运行创建 CloudWatch 日志。日志以 JSON 格式编写，便于查询。您还可以选择将日志格式更改为 *TEXT*。
+ *环境变量名称* – CW\$1SYNTHETICS\$1LOG\$1FORMAT
+ *支持的值* – JSON、TEXT
+ *默认值* – JSON

 **日志级别** 
+ *环境变量名称* – CW\$1SYNTHETICS\$1LOG\$1LEVEL
+ *支持的值* – TRACE、DEBUG、INFO、WARN、ERROR、FATAL
+ *默认值* – INFO

除了以上环境变量外，Java 运行时还会为函数添加一个默认环境变量 `AWS_LAMBDA-EXEC_WRAPPER`，并将其值设置为 `/opt/synthetics-otel-instrument`。此环境变量会修改函数用于遥测的启动行为。如果此环境变量已存在，则确保将其设置为所需值。

# 使用 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

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

**Topics**
+ [从 Scratch 创建 CloudWatch Synthetics 金丝雀](#CloudWatch_Synthetics_Canaries_write_from_scratch)
+ [将 Node.js 金丝雀文件打包](#CloudWatch_Synthetics_Canaries_package)
+ [更改现有 Puppeteer 脚本以将其用作 Synthetics 金丝雀](#CloudWatch_Synthetics_Canaries_modify_puppeteer_script)
+ [环境变量](#CloudWatch_Synthetics_Environment_Variables)
+ [将您的金丝雀与其他 AWS 服务集成](#CloudWatch_Synthetics_Canaries_AWS_integrate)
+ [强制金丝雀使用静态 IP 地址](#CloudWatch_Synthetics_Canaries_staticIP)

## 从 Scratch 创建 CloudWatch Synthetics 金丝雀
<a name="CloudWatch_Synthetics_Canaries_write_from_scratch"></a>

这里是一个极简 Synthetics 金丝雀脚本示例。此脚本将成功通过一次运行，并返回一个字符串。要查看失败的金丝雀示例，请将 `let fail = false;` 更改为 `let fail = true;`。

您必须为金丝雀脚本定义入口点函数。要查看如何将文件上传到指定作为 Canary 的 `ArtifactS3Location` 的 Amazon S3 位置，请在 `/tmp` 文件夹下创建这些文件。所有 Canary 构件都应存储在 `/tmp` 中，因为这是唯一可写入的目录。请务必将脚本创建的所有屏幕截图或其他文件的屏幕截图路径设置为 `/tmp`。Synthetics 会自动将 ` /tmp` 中的文件上传到 S3 存储桶。

```
/tmp/<name>
```

脚本运行后，将“通过/失败”状态和持续时间指标发布到 CloudWatch，并将 `/tmp` 下的文件上传到 S3 存储桶。

```
const basicCustomEntryPoint = async function () {

    // Insert your code here

    // Perform multi-step pass/fail check

    // Log decisions made and results to /tmp

    // Be sure to wait for all your code paths to complete 
    // before returning control back to Synthetics.
    // In that way, your canary will not finish and report success
    // before your code has finished executing

    // Throw to fail, return to succeed
    let fail = false;
    if (fail) {
        throw "Failed basicCanary check.";
    }

    return "Successfully completed basicCanary checks.";
};

exports.handler = async () => {
    return await basicCustomEntryPoint();
};
```

接下来，我们将扩展脚本以使用 Synthetics 日志记录并使用 AWS SDK 进行调用。出于演示目的，此脚本将创建 Amazon DynamoDB 客户端并调用 DynamoDB listTables API。它会记录对请求的响应，并根据请求是否成功来记录通过还是失败。

```
const log = require('@aws/synthetics-logger');
const AWS = require('aws-sdk');
// Require any dependencies that your script needs
// Bundle additional files and dependencies into a .zip file with folder structure
// nodejs/node_modules/additional files and folders

const basicCustomEntryPoint = async function () {

    log.info("Starting DynamoDB:listTables canary.");
    
    let dynamodb = new AWS.DynamoDB();
    var params = {};
    let request = await dynamodb.listTables(params);
    try {
        let response = await request.promise();
        log.info("listTables response: " + JSON.stringify(response));
    } catch (err) {
        log.error("listTables error: " + JSON.stringify(err), err.stack);
        throw err;
    }

    return "Successfully completed DynamoDB:listTables canary.";
};

exports.handler = async () => {
    return await basicCustomEntryPoint();
};
```

## 将 Node.js 金丝雀文件打包
<a name="CloudWatch_Synthetics_Canaries_package"></a>

 **对于 syn-nodejs-puppeteer-11.0 及以上版本** 

 较新版本仍然支持较旧的打包结构（适用于 syn-nodejs-puppeteer-10.0 及以下版本）。

使用以下某个选项创建脚本：
+ .js 文件（CommonJS 语法）
+ .mjs 文件（ES 模块语法）

对于 ES 模块，请使用以下某个选项：
+ .js 文件（CommonJS 语法）
+ .mjs 文件（ES 模块语法）

软件包结构定义如下：
+ 根级别处理程序文件（index.js/index.mjs）
+ 可选配置文件（synthetics.json）
+ node\$1modules 中的其他依赖项（如果需要）

打包结构示例：

```
  my_function/
├── index.mjs
├── synthetics.json
├── helper-utils.mjs
└── node_modules/
    └── dependencies
```

要进行打包，请按照以下步骤操作：

1. 安装依赖项（如有）。

   ```
   npm install
   ```

1. 创建一个 .zip 包。

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

 **对于 syn-nodejs-puppeteer-11.0 及以下版本** 

在使用 Amazon S3 时需要以下结构：

```
  nodejs/
└── node_modules/
    └── myCanaryFilename.js
```

 **要在 syn-nodejs-puppeteer-3.4\$1 中添加可选的子文件夹支持：**

```
nodejs/
└── node_modules/
    └── myFolder/
        └── myCanaryFilename.js
```

**注意**  
配置中的处理程序路径必须与文件位置匹配。

 **处理程序名称** 

请务必将您的金丝雀脚本入口点（处理程序）设置为 ` myCanaryFilename.functionName`，以匹配脚本入口点的文件名。如果您使用 `syn-nodejs-puppeteer-3.4` 之前版本的运行时，则 `functionName` 必须是 `handler`。如果您使用 ` syn-nodejs-puppeteer-3.4` 或之后版本，您可以选择任何函数名称作为处理程序。如果您使用 `syn-nodejs-puppeteer-3.4` 或之后版本，您也可以选择将金丝雀存储在单独的文件夹中，例如 ` nodejs/node_modules/myFolder/my_canary_filename`。如果将其存储在单独的文件夹中，请在脚本入口点中指定该路径，例如 ` myFolder/my_canary_filename.functionName`。

## 更改现有 Puppeteer 脚本以将其用作 Synthetics 金丝雀
<a name="CloudWatch_Synthetics_Canaries_modify_puppeteer_script"></a>

本节介绍如何对 Puppeteer 脚本进行修改，以将其作为 Synthetics 金丝雀脚本运行。有关 Puppeteer 的更多信息，请参阅 [Puppeteer API v1.14.0](https://github.com/puppeteer/puppeteer/blob/v1.14.0/docs/api.md)。

我们从这个示例 Puppeteer 开始：

```
const puppeteer = require('puppeteer');

(async () => {
  const browser = await puppeteer.launch();
  const page = await browser.newPage();
  await page.goto('https://example.com');
  await page.screenshot({path: 'example.png'});

  await browser.close();
})();
```

转换步骤如下：
+ 创建和导出 `handler` 函数。处理程序是脚本的入口点函数。如果您使用 ` syn-nodejs-puppeteer-3.4` 之前版本的运行时，处理程序函数必须命名为 `handler`。如果您使用 `syn-nodejs-puppeteer-3.4` 或之后版本，函数可以有任何名称，但必须与脚本中使用的名称相同。此外，如果您使用 `syn-nodejs-puppeteer-3.4` 或之后版本，您可以将脚本存储在任何文件夹下，并将该文件夹指定为处理程序名称的一部分。

  ```
  const basicPuppeteerExample = async function () {};
  
  exports.handler = async () => {
      return await basicPuppeteerExample();
  };
  ```
+ 使用 `Synthetics` 依赖项。

  ```
  var synthetics = require('@aws/synthetics-puppeteer');
  ```
+ 使用 `Synthetics.getPage` 函数获取 Puppeteer `Page` 对象。

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

  Synthetics.getPage 函数返回的页面对象指示需要记录 **page.on** `request`、`response` 和 ` requestfailed` 事件。Synthetics 还为页面上的请求和响应设置 HAR 文件生成，并将金丝雀 ARN 添加到页面上的传出请求的 user-agent 标头。

该脚本现已准备好作为 Synthetics 金丝雀运行。更新的脚本如下：

```
var synthetics = require('@aws/synthetics-puppeteer');  // Synthetics dependency

const basicPuppeteerExample = async function () {
    const page = await synthetics.getPage(); // Get instrumented page from Synthetics
    await page.goto('https://example.com');
    await page.screenshot({path: '/tmp/example.png'}); // Write screenshot to /tmp folder
};

exports.handler = async () => {  // Exported handler function 
    return await basicPuppeteerExample();
};
```

## 环境变量
<a name="CloudWatch_Synthetics_Environment_Variables"></a>

您可以在创建金丝雀时使用环境变量。这样，您就能够编写单个金丝雀脚本，然后将该脚本与不同的值相结合，快速创建具有类似任务的多个金丝雀。

例如，假定您的企业具有用于不同软件开发阶段的 `prod`、` dev` 和 `pre-release` 端点，而您需要创建金丝雀来测试其中每个端点。您可以编写一个测试软件的金丝雀脚本，然后在分别创建三个金丝雀时为端点环境变量指定不同的值。之后，在创建金丝雀时，您可以指定要用于环境变量的脚本和值。

环境变量的名称可以包含字母、数字和下划线字符，必须以字母开头，并且至少为两个字符。环境变量的总大小不能超过 4 KB。不能将任何 Lambda 预留环境变量指定为环境变量的名称。有关预留环境变量的更多信息，请参阅[运行时环境变量](https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html#configuration-envvars-runtime)。

**重要**  
使用 AWS 自有 AWS KMS 密钥对环境变量密钥和值进行静态加密。但是，并未在客户端对环境变量进行加密。请勿在其中存储敏感信息。

下面的示例脚本使用两个环境变量。此脚本用于检查网页是否可用的金丝雀。该金丝雀使用环境变量来参数化所检查的 URL 和所使用的 CloudWatch Synthetics 日志级别。

以下函数将 `LogLevel` 设置为 ` LOG_LEVEL` 环境变量的值。

```
 synthetics.setLogLevel(process.env.LOG_LEVEL);
```

此函数将 `URL` 设置为 `URL` 环境变量的值。

```
const URL = process.env.URL;
```

以下是完整的脚本。当您使用此脚本创建金丝雀时，您可以指定 `LOG_LEVEL` 和 `URL` 环境变量的值。

```
var synthetics = require('@aws/synthetics-puppeteer');
const log = require('@aws/synthetics-logger');

const pageLoadEnvironmentVariable = async function () {

    // Setting the log level (0-3)
    synthetics.setLogLevel(process.env.LOG_LEVEL);
    // INSERT URL here
    const URL = process.env.URL;

    let page = await synthetics.getPage();
    //You can customize the wait condition here. For instance,
    //using 'networkidle2' may be less restrictive.
    const response = await page.goto(URL, {waitUntil: 'domcontentloaded', timeout: 30000});
    if (!response) {
        throw "Failed to load page!";
    }
    //Wait for page to render.
    //Increase or decrease wait time based on endpoint being monitored.
    await page.waitFor(15000);
    await synthetics.takeScreenshot('loaded', 'loaded');
    let pageTitle = await page.title();
    log.info('Page title: ' + pageTitle);
    log.debug('Environment variable:' + process.env.URL);

    //If the response status code is not a 2xx success code
    if (response.status() < 200 || response.status() > 299) {
        throw "Failed to load page!";
    }
};

exports.handler = async () => {
    return await pageLoadEnvironmentVariable();
};
```

### 将环境变量传递到脚本
<a name="CloudWatch_Synthetics_Canaries_pass_variables"></a>

要在控制台中创建金丝雀时将环境变量传递给脚本，请在控制台上的 **Environment variables（环境变量）**部分指定环境变量的密钥和值。有关更多信息，请参阅 [创建金丝雀](CloudWatch_Synthetics_Canaries_Create.md)。

要通过 API 或 AWS CLI 传递环境变量，请使用 `RunConfig` 部分中的 ` EnvironmentVariables` 参数。以下为 AWS CLI 命令示例，该命令创建一个使用两个环境变量（具有 `Environment` 和 `Region` 密钥）的金丝雀。

```
aws synthetics create-canary --cli-input-json '{
   "Name":"nameofCanary",
   "ExecutionRoleArn":"roleArn",
   "ArtifactS3Location":"s3://amzn-s3-demo-bucket-123456789012-us-west-2",
   "Schedule":{
      "Expression":"rate(0 minute)",
      "DurationInSeconds":604800
   },
   "Code":{
      "S3Bucket": "canarycreation",
      "S3Key": "cwsyn-mycanaryheartbeat-12345678-d1bd-1234-abcd-123456789012-12345678-6a1f-47c3-b291-123456789012.zip",
      "Handler":"pageLoadBlueprint.handler"
   },
   "RunConfig": {
      "TimeoutInSeconds":60,
      "EnvironmentVariables": {
         "Environment":"Production",
         "Region": "us-west-1"
      }
   },
   "SuccessRetentionPeriodInDays":13,
   "FailureRetentionPeriodInDays":13,
   "RuntimeVersion":"syn-nodejs-2.0"
}'
```

## 将您的金丝雀与其他 AWS 服务集成
<a name="CloudWatch_Synthetics_Canaries_AWS_integrate"></a>

所有金丝雀都可以使用 AWS SDK 库。在编写金丝雀时，您可以使用此库将金丝雀与其他 AWS 服务集成。

为此，您需要将以下代码添加到您的金丝雀中。对于这些例子，AWS Secrets Manager 用作与金丝雀集成的服务。
+ 导入 AWS SDK。

  ```
  const AWS = require('aws-sdk');
  ```
+ 为要集成的 AWS 服务创建客户端。

  ```
  const secretsManager = new AWS.SecretsManager();
  ```
+ 使用客户端对该服务进行 API 调用。

  ```
  var params = {
    SecretId: secretName
  };
  return await secretsManager.getSecretValue(params).promise();
  ```

下面的金丝雀脚本代码段更详细地演示了与 Secrets Manager 集成的示例。

```
var synthetics = require('@aws/synthetics-puppeteer');
const log = require('@aws/synthetics-logger');
 
const AWS = require('aws-sdk');
const secretsManager = new AWS.SecretsManager();
 
const getSecrets = async (secretName) => {
    var params = {
        SecretId: secretName
    };
    return await secretsManager.getSecretValue(params).promise();
}
 
const secretsExample = async function () {
    let URL = "<URL>";
    let page = await synthetics.getPage();
    
    log.info(`Navigating to URL: ${URL}`);
    const response = await page.goto(URL, {waitUntil: 'domcontentloaded', timeout: 30000});
    
    // Fetch secrets
    let secrets = await getSecrets("secretname")
   
    /**
    * Use secrets to login. 
    *
    * Assuming secrets are stored in a JSON format like:
    * {
    *   "username": "<USERNAME>",
    *   "password": "<PASSWORD>"
    * }
    **/
    let secretsObj = JSON.parse(secrets.SecretString);
    await synthetics.executeStep('login', async function () {
        await page.type(">USERNAME-INPUT-SELECTOR<", secretsObj.username);
        await page.type(">PASSWORD-INPUT-SELECTOR<", secretsObj.password);
        
        await Promise.all([
          page.waitForNavigation({ timeout: 30000 }),
          await page.click(">SUBMIT-BUTTON-SELECTOR<")
        ]);
    });
   
    // Verify login was successful
    await synthetics.executeStep('verify', async function () {
        await page.waitForXPath(">SELECTOR<", { timeout: 30000 });
    });
};

exports.handler = async () => {
    return await secretsExample();
};
```

## 强制金丝雀使用静态 IP 地址
<a name="CloudWatch_Synthetics_Canaries_staticIP"></a>

您可以设置金丝雀，使其使用静态 IP 地址。

**强制金丝雀使用静态 IP 地址**

1. 创建新的 VPC。有关更多信息，请参阅[在您的 VPC 中使用 DNS](https://docs.aws.amazon.com/vpc/latest/userguide/vpc-dns.html)。

1. 创建新的互联网网关。有关更多信息，请参阅[在您的 VPC 中添加互联网网关](https://docs.aws.amazon.com/vpc/latest/userguide/VPC_Internet_Gateway.html#working-with-igw)。

1. 在新的 VPC 内创建公有子网。

1. 向 VPC 添加新的路由表。

1. 在新路由表中添加一条从 `0.0.0.0/0` 到互联网网关的路由。

1. 将新的路由表与公有子网关联。

1. 创建弹性 IP 地址。有关更多信息，请参阅[弹性 IP 地址](https://docs.aws.amazon.com/AWSEC2/latest/UserGuide/elastic-ip-addresses-eip.html)。

1. 创建一个新的 NAT 网关，并将其分配给公有子网和弹性 IP 地址。

1. 在 VPC 中创建私有子网。

1. 向 VPC 默认路由表中添加一条从 `0.0.0.0/0` 到 NAT 网关的路由

1. 创建金丝雀。

# 编写 Python 金丝雀脚本
<a name="CloudWatch_Synthetics_Canaries_WritingCanary_Python"></a>

此脚本将成功通过一次运行，并返回一个字符串。要查看失败的金丝雀示例，请将 fail = False 更改为 fail = True

```
def basic_custom_script():
    # Insert your code here
    # Perform multi-step pass/fail check
    # Log decisions made and results to /tmp
    # Be sure to wait for all your code paths to complete 
    # before returning control back to Synthetics.
    # In that way, your canary will not finish and report success
    # before your code has finished executing
    fail = False
    if fail:
        raise Exception("Failed basicCanary check.")
    return "Successfully completed basicCanary checks."
def handler(event, context):
    return basic_custom_script()
```

## 将 Python 金丝雀文件打包
<a name="CloudWatch_Synthetics_Canaries_WritingCanary_Python_package"></a>

如果您有多个 .py 文件或脚本具有依赖项，则您可以将它们捆绑到单个 ZIP 格式文件中。如果您使用 `syn-python-selenium-1.1` 运行时，此 ZIP 格式文件必须将主金丝雀 .py 文件包含在 `python` 文件夹中，例如 `python/my_canary_filename.py`。如果您使用 ` syn-python-selenium-1.1` 或之后版本，您可以选择使用其他文件夹，例如 `python/myFolder/my_canary_filename.py`。

此 ZIP 格式文件应包含所有必要的文件夹和文件，但其他文件不需要位于 `python` 文件夹中。

请务必将您的金丝雀脚本入口点设置为 ` my_canary_filename.functionName`，以匹配脚本入口点的文件名和函数名称。如果您使用 `syn-python-selenium-1.0` 运行时，则 `functionName` 必须是 `handler`。如果您使用 ` syn-python-selenium-1.1` 或之后版本，该处理程序名称限制不适用，您也可以选择将金丝雀存储在单独的文件夹中，例如 ` python/myFolder/my_canary_filename.py`。如果将其存储在单独的文件夹中，请在脚本入口点中指定该路径，例如 ` myFolder/my_canary_filename.functionName`。

## 更改现有 Puppeteer 脚本以使用 Synthetics 金丝雀
<a name="CloudWatch_Synthetics_Canaries_WritingCanary_Python_Selenium"></a>

您可以快速修改用于 Python 和 Selenium 的现有脚本以用作金丝雀。有关 Selenium 的更多信息，请参阅 [www.selenium.dev/](https://www.selenium.dev/)。

在本示例中，将从以下 Selenium 脚本开始：

```
from selenium import webdriver

def basic_selenium_script():
    browser = webdriver.Chrome()
    browser.get('https://example.com')
    browser.save_screenshot('loaded.png')

basic_selenium_script()
```

转换步骤如下。

**转换 Selenium 脚本以用作金丝雀**

1. 更改 `import` 语句以使用 ` aws_synthetics` 模块中的 Selenium：

   ```
   from aws_synthetics.selenium import synthetics_webdriver as webdriver
   ```

   `aws_synthetics` 模块中的 Selenium 确保金丝雀可以发出指标和日志、生成 HAR 文件及使用其他 CloudWatch Synthetics 功能。

1. 创建一个处理程序函数并调用您的 Selenium 方法。处理程序是脚本的入口点函数。

   如果您使用 `syn-python-selenium-1.0`，处理程序函数必须命名为 `handler`。如果您使用 `syn-python-selenium-1.1` 或之后版本，函数可以有任何名称，但必须与脚本中使用的名称相同。此外，如果您使用 `syn-python-selenium-1.1` 或之后版本，您可以将脚本存储在任何文件夹下，并将该文件夹指定为处理程序名称的一部分。

   ```
   def handler(event, context):
       basic_selenium_script()
   ```

脚本现已更新为 CloudWatch Synthetics 金丝雀。更新的脚本如下：

`webdriver` 是 [SyntheticsWebDriver](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_Library_Python.html#CloudWatch_Synthetics_Library_Python_SyntheticsWebDriver) 类的实例，而由 `webdriver.Chrome()` 返回的浏览器则是 [SyntheticsBrowser](https://docs.aws.amazon.com/AmazonCloudWatch/latest/monitoring/CloudWatch_Synthetics_Canaries_Library_Python.html#CloudWatch_Synthetics_Library_Python_SyntheticsBrowser) 类的实例。

```
from aws_synthetics.selenium import synthetics_webdriver as webdriver

def basic_selenium_script():
    browser = webdriver.Chrome()
    browser.get('https://example.com')
    browser.save_screenshot('loaded.png')

def handler(event, context):
    basic_selenium_script()
```

## 更改现有的 Puppeteer Synthetics 脚本以验证非标准证书
<a name="Canaries_Non-Standard_Certificates"></a>

Synthetics Canary 的一个重要用例是让您监测自己的端点。如果您想监测尚未准备好接收外部流量的端点，这种监测有时可能意味着您尚未拥有由可信的第三方证书颁发机构签署的正确证书。

对于这种情况，有如下两种可能的解决方案：
+ 要对客户端证书进行身份验证，请参阅 [How to validate authentication using Amazon CloudWatch Synthetics – Part 2](https://aws.amazon.com/blogs/mt/how-to-validate-authentication-using-amazon-cloudwatch-synthetics-part-2/)。
+ 要对自签名证书进行身份验证，请参阅 [ How to validate authentication with self-signed certificates in Amazon CloudWatch Synthetics](https://aws.amazon.com/blogs/mt/how-to-validate-authentication-with-self-signed-certificates-in-amazon-cloudwatch-synthetics/)

使用 CloudWatch Synthetics Canary 时，您的选择并不局限于以上两种。您可以通过扩展 Canary 代码来扩展这些功能并添加业务逻辑。

**注意**  
在 Python 运行时上运行的 Synthetics Canary 本身就启用了 ` --ignore-certificate-errors` 标志，因此这些 Canary 在访问具有非标准证书配置的网站时，不应出现任何问题。

# 为 Node.js 多重检查蓝图编写 JSON 配置
<a name="CloudWatch_Synthetics_WritingCanary_Multichecks"></a>

借助 Node.js 多重检查蓝图，您可以创建可在单次金丝雀运行中执行多项验证检查的金丝雀。当您想要测试多个端点、验证应用程序的不同方面，或按顺序执行一系列相关检查时，此蓝图非常有用。

**Topics**
+ [根配置结构](#root-configuration-structure)
+ [全局设置](#global-settings)
+ [变量和数据管理](#variables-data-management)
+ [步骤定义](#step-definitions)
+ [检查类型](#check-types)
+ [身份验证方法](#authentication-methods)
+ [断言与验证](#assertions-validation)
+ [数据提取](#data-extraction)

## 根配置结构
<a name="root-configuration-structure"></a>

根配置定义了高级 API 蓝图金丝雀的整体结构。


**架构属性**  

| 属性 | Type | 必需 | 说明 | 
| --- | --- | --- | --- | 
|  globalSettings  | 对象 | 否 | 应用于所有步骤的默认配置 | 
|  variables  | 对象 | 否 | 跨步骤（最多 10 个）可重复使用的值 | 
|  steps  | 对象 |  是  | 监控步骤集合（1-10 个步骤） | 

 **示例** 

```
{
  "globalSettings": {
    "stepTimeout": 30000,
    "userAgent": "CloudWatch-Synthetics-Advanced/1.0"
  },
  "variables": {
    "baseUrl": "https://api.example.com",
    "apiVersion": "v1"
  },
  "steps": {
    "1": {
      "stepName": "healthCheck",
      "checkerType": "HTTP",
      "url": "${baseUrl}/health",
      "httpMethod": "GET"
    }
  }
}
```

 **验证规则** 
+ 必须包含至少一个步骤
+ 最多允许 10 个步骤
+ 除 `globalSettings`、` variables` 和 `steps` 外，不允许使用其他属性

## 全局设置
<a name="global-settings"></a>

全局设置提供了应用于所有步骤的默认配置，除非在步骤级别被覆盖。

 **属性** 


**全局设置属性**  

| 属性 | Type | 默认 | Range | 说明 | 
| --- | --- | --- | --- | --- | 
|  stepTimeout  | 整数 | 30000 | 5000-300000 | 所有步骤的默认超时（毫秒） | 

 **示例** 

```
{
  "globalSettings": {
    "stepTimeout": 60000,
            
  }
}
```

## 变量和数据管理
<a name="variables-data-management"></a>

通过变量，您可以定义可重复使用的值，并在整个配置中使用 `${variableName}` 语法来引用它们。

 **变量属性** 


| 属性 | Type | 说明 | 
| --- | --- | --- | 
| 变量名称 | 字符串 | 必须匹配模式 ^[a-zA-Z][a-zA-Z0-9\$1]\$1\$1 | 
| 变量值 | 字符串 | 任意字符串值 | 

 **限制** 
+ 每个配置最多 10 个变量
+ 变量名称必须以字母开头
+ 变量名称只能包含字母、数字和下划线
+ 架构中未指定最大长度

 **示例** 

```
{
  "variables": {
    "baseUrl": "https://api.example.com",
    "apiKey": "${AWS_SECRET:my-api-key}",
    "timeout": "30000",
    "userEmail": "test@example.com"
  }
}
```

 **配置使用** 

```
{
  "steps": {
    "1": {
      "url": "${baseUrl}/users",
      "timeout": "${timeout}",
      "headers": {
        "Authorization": "Bearer ${apiKey}"
      }
    }
  }
}
```

## 步骤定义
<a name="step-definitions"></a>

步骤定义了单独的监控操作。每个步骤从 1 到 10 编号，包含特定类型的检查。

 *常用步骤属性* 


| 属性 | Type | 必需 | 描述 | 
| --- | --- | --- | --- | 
|  stepName  | 字符串 |  是  | 步骤的唯一标识符 | 
|  checkerType  | 字符串 |  是  | 检查类型：HTTP、DNS、SSL、 TCP | 
|  extractors  | array | 否 | 数据提取配置 | 

 *步骤名称验证* 
+ 模式：^[a-zA-Z][a-zA-Z0-9\$1-]\$1\$1
+ 最大长度：64 个字符
+ 必须以字母开头

 *步骤编号* 
+ 步骤以字符串键的形式编号：“1”、“2”、...、“10”
+ 模式：^([1-9]\$110)\$1
+ 至少需要 1 个步骤
+ 最多允许 10 个步骤

 *示例* 

```
{
  "steps": {
    "1": {
      "stepName": "loginAPI",
      "checkerType": "HTTP",
      "url": "https://api.example.com/login",
      "httpMethod": "POST"
    },
    "2": {
      "stepName": "dnsCheck",
      "checkerType": "DNS",
      "domain": "example.com"
    }
  }
}
```

## 检查类型
<a name="check-types"></a>

### HTTP 检查
<a name="http-types"></a>

通过全面的请求和响应验证来监控 Web 端点和 API。

 **必需属性** 


| 属性 | Type | 说明 | 
| --- | --- | --- | 
|  url  | 字符串 | 目标 URL（必须是有效的 URI 格式） | 
|  httpMethod  | 字符串 | HTTP 方法：GET、POST、PUT、 PATCH、DELETE、HEAD、OPTIONS | 

 **可选属性** 


| 属性 | Type | 默认 | Range | 说明 | 
| --- | --- | --- | --- | --- | 
|  timeout  | 整数 | 30000 | 5000-300000 | 请求超时（毫秒） | 
|  waitTime  | 整数 | 0 | 0-60 | 请求前的延迟（秒） | 
|  headers  | object | - | - | 自定义 HTTP 标头 | 
|  body  | 字符串 | - | - | 用于 POST/PUT 操作的请求正文 | 
|  authentication  | object | - | - | 身份验证配置 | 
|  assertions  | array | - | - | 响应验证规则 | 

 **示例** 

```
{
  "stepName": "createUser",
  "checkerType": "HTTP",
  "url": "https://api.example.com/users",
  "httpMethod": "POST",
  "timeout": 15000,
  "headers": {
    "Content-Type": "application/json",
    "X-API-Version": "v1"
  },
  "body": "{\"name\":\"John Doe\",\"email\":\"john@example.com\"}",
  "authentication": {
    "type": "API_KEY",
    "apiKey": "${AWS_SECRET:api-credentials}",
    "headerName": "X-API-Key"
  },
  "assertions": [
    {
      "type": "STATUS_CODE",
      "operator": "EQUALS",
      "value": 201
    }
  ]
}
```

### DNS 检查
<a name="dns-types"></a>

验证 DNS 解析并记录信息。

 **必需属性** 


| 属性 | Type | 说明 | 
| --- | --- | --- | 
|  domain  | 字符串 | 要查询的域名（主机名格式） | 

 **可选属性** 


| 属性 | Type | 默认值 | 说明 | 
| --- | --- | --- | --- | 
|  recordType  | 字符串 | “A” | DNS 记录类型：A、CNAME、MX、 TXT、NS | 
|  nameserver  | 字符串 | - | 要查询的特定 DNS 服务器 | 
|  timeout  | 整数 | 30000 | 查询超时（5000-300000 毫秒） | 
|  port  | 整数 | 53 | DNS 服务器端口（1-65535） | 
|  protocol  | 字符串 | “UDP” | 协议：UDP 或 TCP | 
|  assertions  | array | - | DNS 响应验证规则 | 

 **示例** 

```
{
  "stepName": "dnsResolution",
  "checkerType": "DNS",
  "domain": "example.com",
  "recordType": "A",
  "nameserver": "8.8.8.8",
  "timeout": 10000,
  "assertions": [
    {
      "type": "RECORD_VALUE",
      "operator": "CONTAINS",
      "value": "192.168"
    }
  ]
}
```

### SSL 检查
<a name="ssl-types"></a>

监控 SSL 证书的运行状况和配置。

 **必需属性** 


| 属性 | Type | 说明 | 
| --- | --- | --- | 
|  hostname  | 字符串 | 目标主机名（主机名格式） | 

 **可选属性** 


| 属性 | Type | 默认值 | 描述 | 
| --- | --- | --- | --- | 
|  port  | 整数 | 443 | SSL 端口（1-65535） | 
|  timeout  | 整数 | 30000 | 连接超时（5000-300000 毫秒） | 
|  sni  | 布尔值 | TRUE | 服务器名称指示 | 
|  verifyHostname  | 布尔值 | TRUE | 主机名验证 | 
|  allowSelfSigned  | 布尔值 | FALSE | 接受自签名证书 | 
|  assertions  | array | - | 证书验证规则 | 

 **示例** 

```
{
  "stepName": "sslCertCheck",
  "checkerType": "SSL",
  "hostname": "secure.example.com",
  "port": 443,
  "sni": true,
  "verifyHostname": true,
  "assertions": [
    {
      "type": "CERTIFICATE_EXPIRY",
      "operator": "GREATER_THAN",
      "value": 30,
      "unit": "DAYS"
    }
  ]
}
```

### TCP 检查
<a name="tcp-types"></a>

测试 TCP 端口连接性和响应验证。

 **必需属性** 


| 属性 | Type | 说明 | 
| --- | --- | --- | 
|  hostname  | 字符串 | 目标主机名（主机名格式） | 
|  port  | 整数 | 目标端口（1-65535） | 

 **可选属性** 


| 属性 | Type | 默认值 | 描述 | 
| --- | --- | --- | --- | 
|  timeout  | 整数 | 30000 | 总超时（5000-300000 毫秒） | 
|  connectionTimeout  | 整数 | 3000 | 连接超时（5000-300000 毫秒） | 
|  readTimeout  | 整数 | 2000 | 数据读取超时（5000-300000 毫秒） | 
|  sendData  | 字符串 | - | 连接后要发送的数据 | 
|  expectedResponse  | 字符串 | - | 预期响应数据 | 
|  encoding  | 字符串 | “UTF-8” | 数据编码：UTF-8、ASCII、HEX | 
|  assertions  | array | - | 连接和响应验证 | 

 **示例** 

```
{
  "stepName": "databaseConnection",
  "checkerType": "TCP",
  "hostname": "db.example.com",
  "port": 3306,
  "connectionTimeout": 5000,
  "sendData": "SELECT 1",
  "expectedResponse": "1",
  "assertions": [
    {
      "type": "CONNECTION_SUCCESSFUL",
      "value": true
    }
  ]
}
```

## 身份验证方法
<a name="authentication-methods"></a>

 **不使用身份验证** 

```
{
  "type": "NONE"
}
```

 **基本身份验证** 


| 属性 | Type | 必需 | 描述 | 
| --- | --- | --- | --- | 
|  type  | 字符串 |  是  | 必须是 "BASIC" | 
|  username  | 字符串 |  是  | 用于身份验证的用户名 | 
|  password  | 字符串 |  是  | 用于身份验证的密码 | 

 **示例** 

```
{
  "type": "BASIC",
  "username": "admin",
  "password": "${AWS_SECRET:basic-auth:password}"
}
```

 **API 密钥身份验证** 


| 属性 | Type | 必需 | 默认值 | 说明 | 
| --- | --- | --- | --- | --- | 
|  type  | 字符串 |  是  | - | 必须是 "API\$1KEY" | 
|  apiKey  | 字符串 |  是  | - | API 密钥值 | 
|  headerName  | 字符串 | 否 | “X-API-Key” | API 密钥的标头名称 | 

 **示例** 

```
{
  "type": "API_KEY",
  "apiKey": "${AWS_SECRET:api-credentials}",
  "headerName": "Authorization"
}
```

 **OAuth 客户端凭证** 


| 属性 | Type | 必需 | 默认值 | 说明 | 
| --- | --- | --- | --- | --- | 
|  type  | 字符串 |  是  | - | 必须是 "OAUTH\$1CLIENT\$1CREDENTIALS" | 
|  tokenUrl  | 字符串 |  是  | - | OAuth 令牌端点 URL | 
|  clientId  | 字符串 |  是  | - | OAuth 客户端 ID | 
|  clientSecret  | 字符串 |  是  | - | OAuth 客户端密钥 | 
|  scope  | 字符串 | 否 | - | OAuth 范围 | 
|  audience  | 字符串 | 否 | - | OAuth 受众 | 
|  resource  | 字符串 | 否 | - | OAuth 资源 | 
|  tokenApiAuth  | array | 否 | - | 令牌 API 身份验证方法：BASIC\$1AUTH\$1HEADER、REQUEST\$1BODY | 
|  tokenCacheTtl  | 整数 | 否 | 3600 | 令牌缓存 TTL（至少 60 秒） | 

 **示例** 

```
{
  "type": "OAUTH_CLIENT_CREDENTIALS",
  "tokenUrl": "https://auth.example.com/oauth/token",
  "clientId": "${AWS_SECRET:oauth-creds:client_id}",
  "clientSecret": "${AWS_SECRET:oauth-creds:client_secret}",
  "scope": "read write",
  "tokenCacheTtl": 7200
}
```

 **AWS 签名（版本 4）** 


| 属性 | Type | 必需 | 描述 | 
| --- | --- | --- | --- | 
|  type  | 字符串 |  是  | 必须是 "SIGV4" | 
|  service  | 字符串 |  是  | AWS 服务的名称（例如“execute-api”） | 
|  region  | 字符串 |  是  | AWS 区域 | 
|  roleArn  | 字符串 |  是  | 用于签名的 IAM 角色 ARN | 

 **示例** 

```
{
  "type": "SIGV4",
  "service": "execute-api",
  "region": "us-east-1",
  "roleArn": "arn:aws:iam::123456789012:role/SyntheticsRole"
}
```

## 断言与验证
<a name="assertions-validation"></a>

### HTTP 断言
<a name="http-assertions"></a>

 **状态代码断言** 


| 属性 | Type | 必需 | 描述 | 
| --- | --- | --- | --- | 
|  type  | 字符串 |  是  | 必须是 "STATUS\$1CODE" | 
|  operator  | 字符串 |  是  | EQUALS, NOT\$1EQUALS, GREATER\$1THAN,  LESS\$1THAN, IN\$1RANGE | 
|  value  | 整数 | 有条件 | HTTP 状态代码（100-599） | 
|  rangeMin  | 整数 | 有条件 | 最小范围值（适用于 IN\$1RANGE） | 
|  rangeMax  | 整数 | 有条件 | 最大范围值（适用于 IN\$1RANGE） | 

```
{
  "type": "STATUS_CODE",
  "operator": "EQUALS",
  "value": 200
}
```

 **响应时间断言** 


| 属性 | Type | 必需 | 默认值 | 说明 | 
| --- | --- | --- | --- | --- | 
|  type  | 字符串 |  是  | - | 必须是 "RESPONSE\$1TIME" | 
|  operator  | 字符串 |  是  | - | LESS\$1THAN, GREATER\$1THAN, EQUALS | 
|  value  | 数字 |  是  | - | 时间值（最小 0） | 
|  unit  | 字符串 | 否 | “MILLISECONDS” | 必须是 "MILLISECONDS" | 

```
{
  "type": "RESPONSE_TIME",
  "operator": "LESS_THAN",
  "value": 500,
  "unit": "MILLISECONDS"
}
```

 **标头断言** 


| 属性 | Type | 必需 | 描述 | 
| --- | --- | --- | --- | 
|  type  | 字符串 |  是  | 必须是 "HEADER" | 
|  headerName  | 字符串 |  是  | 要验证的标头名称 | 
|  operator  | 字符串 |  是  | EQUALS, NOT\$1EQUALS, CONTAINS,  NOT\$1CONTAINS, REGEX\$1MATCH, EXIST | 
|  value  | 字符串/布尔值 | 有条件 | 预期值（EXIST 运算符为布尔值） | 

```
{
  "type": "HEADER",
  "headerName": "Content-Type",
  "operator": "CONTAINS",
  "value": "application/json"
}
```

 **正文断言** 


| 属性 | Type | 必需 | 默认值 | 说明 | 
| --- | --- | --- | --- | --- | 
|  type  | 字符串 |  是  | - | 必须是 "BODY" | 
|  target  | 字符串 | 否 | “JSON” | JSON 或 TEXT | 
|  path  | 字符串 | 有条件 | - | JSONPath（对于 JSON 目标是必需的） | 
|  operator  | 字符串 |  是  | - | CONTAINS, NOT\$1CONTAINS, EQUALS,  NOT\$1EQUALS, EXISTS | 
|  value  | 字符串/布尔值 |  是  | - | 预期值（EXISTS 运算符为布尔值） | 

```
{
  "type": "BODY",
  "target": "JSON",
  "path": "$.users[0].name",
  "operator": "EQUALS",
  "value": "John Doe"
}
```

### DNS 断言
<a name="dns-assertions"></a>

 **记录价值断言** 


| 属性 | Type | 必需 | Range | 说明 | 
| --- | --- | --- | --- | --- | 
|  type  | 字符串 |  是  | - | 必须是 "RECORD\$1VALUE" | 
|  operator  | 字符串 |  是  | - | EQUALS, NOT\$1EQUALS, CONTAINS,  NOT\$1CONTAINS, REGEX\$1MATCH | 
|  value  | 字符串 |  是  | - | 预期记录值 | 

 **记录计数断言** 


| 属性 | Type | 必需 | Range | 说明 | 
| --- | --- | --- | --- | --- | 
|  type  | 字符串 |  是  | - | 必须是 "RECORD\$1COUNT" | 
|  operator  | 字符串 |  是  | - | EQUALS, GREATER\$1THAN, LESS\$1THAN | 
|  value  | 整数 |  是  | ≥ 0 | 预期计数（最小 0） | 

 **授权断言** 


| 属性 | Type | 必需 | Range | 说明 | 
| --- | --- | --- | --- | --- | 
|  type  | 字符串 |  是  | - | 必须是 "AUTHORITATIVE" | 
|  value  | 布尔值 |  是  | - | 预期授权状态 | 

 **TTL 断言** 


| 属性 | Type | 必需 | Range | 说明 | 
| --- | --- | --- | --- | --- | 
|  type  | 字符串 |  是  | - | 必须是 "TTL" | 
|  operator  | 字符串 |  是  | - | EQUALS, GREATER\$1THAN, LESS\$1THAN | 
|  value  | 整数 |  是  | ≥ 0 | 预期 TTL（最小 0） | 

### SSL 断言
<a name="ssl-assertions"></a>

 **证书到期断言** 


| 属性 | Type | 必需 | 默认值 | 说明 | 
| --- | --- | --- | --- | --- | 
|  type  | 字符串 |  是  | - | 必须是 "CERTIFICATE\$1EXPIRY" | 
|  operator  | 字符串 |  是  | - | GREATER\$1THAN, LESS\$1THAN | 
|  value  | 整数 |  是  | - | 时间值（最小 0） | 
|  unit  | 字符串 | 否 | “DAYS” | DAYS, HOURS | 

 **证书主题断言** 


| 属性 | Type | 必需 | 默认值 | 说明 | 
| --- | --- | --- | --- | --- | 
|  type  | 字符串 |  是  | - | 必须是 "CERTIFICATE\$1SUBJECT" | 
|  field  | 字符串 |  是  | - | 主题字段：CN、O、OU、C、ST、L | 
|  operator  | 字符串 |  是  | - | CONTAINS, EQUALS, REGEX\$1MATCH | 
|  value  | 字符串 |  是  | - | 预期字段值 | 

 **证书颁发者断言** 


| 属性 | Type | 必需 | 默认值 | 说明 | 
| --- | --- | --- | --- | --- | 
|  type  | 字符串 |  是  | - | 必须是 "CERTIFICATE\$1ISSUER" | 
|  field  | 字符串 |  是  | - | 颁发者字段：CN、O | 
|  operator  | 字符串 |  是  | - | CONTAINS, EQUALS | 
|  value  | 字符串 |  是  | - | 预期字段值 | 

### TCP 断言
<a name="tcp-assertions"></a>

 **连接成功断言** 


| 属性 | Type | 必需 | 默认值 | 说明 | 
| --- | --- | --- | --- | --- | 
|  type  | 字符串 |  是  | - | 必须是 "CONNECTION\$1SUCCESSFUL" | 
|  value  | 布尔值 |  是  | - | 预期连接状态 | 

 **响应数据断言** 


| 属性 | Type | 必需 | 默认值 | 说明 | 
| --- | --- | --- | --- | --- | 
|  type  | 字符串 |  是  | - | 必须是 "RESPONSE\$1DATA" | 
|  operator  | 字符串 |  是  | - | CONTAINS, EQUALS, NOT\$1CONTAINS,  REGEX\$1MATCH, STARTS\$1WITH, ENDS\$1WITH | 
|  value  | 字符串 |  是  | - | 预期响应数据 | 
|  encoding  | 字符串 | 否 | “UTF-8” | UTF-8, ASCII, HEX | 

## 数据提取
<a name="data-extraction"></a>

借助提取器，您可以从响应中捕获数据，用于后续步骤或报告目的。

 **提取属性** 


| 属性 | Type | 必需 | 默认值 | 说明 | 
| --- | --- | --- | --- | --- | 
|  name  | 字符串 |  是  | - | 提取数据的变量名称 | 
|  type  | 字符串 |  是  | - | 提取类型：BODY | 
|  path  | 字符串 | 否 | - | 用于正文提取的 JSONPath | 
|  regex  | 字符串 | 否 | - | 正则表达式模式 | 
|  regexGroup  | 整数 | 否 | 0 | 正则表达式捕获组（最小 0） | 

 **提取名称验证** 
+ 模式：`^[a-zA-Z][a-zA-Z0-9_]*$`
+ 必须以字母开头
+ 可包含字母、数字和下划线

**限制**：替换不适用于架构中具有特定 ENUM 值的字段

 **提取类型** 

```
{
  "name": "userId",
  "type": "BODY",
  "path": "$.user.id"
}
```

```
{
  "stepName": "loginAndExtract",
  "checkerType": "HTTP",
  "url": "https://api.example.com/login",
  "httpMethod": "POST",
  "body": "{\"username\":\"test\",\"password\":\"pass\"}",
  "extractors": [
    {
      "name": "textVariable",
      "type": "BODY",
      "path": "$.myvalue"
    }
  ]
},
{
  "stepName": "substituteVariable",
  "checkerType": "HTTP",
  "url": "https://api.example.com/get/${textVariable}",
  "httpMethod": "GET",
  "assertions": [
    {
    "type": "BODY",
    "target": "JSON",
    "path": "$.users[0].name",
    "operator": "EQUALS",
    "value": "${textVariable}"
    }
  ]
}
```