

本文為英文版的機器翻譯版本，如內容有任何歧義或不一致之處，概以英文版為準。

# 撰寫 Canary 指令碼
<a name="CloudWatch_Synthetics_Canaries_WritingCanary"></a>

下列各節說明如何撰寫 Canary 指令碼，以及如何將 Canary 與其他 AWS 服務以及外部相依性和程式庫整合。

**Topics**
+ [使用 Java 執行時期撰寫 Canary 指令碼](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 Canary 指令碼](CloudWatch_Synthetics_Canaries_WritingCanary_Python.md)
+ [撰寫 Node.js 多重檢查藍圖的 JSON 組態](CloudWatch_Synthetics_WritingCanary_Multichecks.md)

# 使用 Java 執行時期撰寫 Canary 指令碼
<a name="Synthetics_WritingCanary_Java"></a>

**Topics**
+ [Canary 的 Java 專案結構](#Synthetics_canary_Java_package)
+ [封裝 Canary 的專案](#Synthetics_canary_Java_package_canary)
+ [處理常式名稱](#Synthetics_canary_Java_handler)
+ [CloudWatch Synthetics 組態](#Synthetics_canary_Java_config)
+ [CloudWatch Synthetics 環境變數](#Synthetics_canary_Java_variables)

## Canary 的 Java 專案結構
<a name="Synthetics_canary_Java_package"></a>

若要在 Java 中建立 Canary，需要撰寫程式碼、編譯程式碼，然後將編譯的成品部署到 Synthetics。可以透過各種方式初始化 Java Lambda 專案。舉例來說，您可以在偏好的 IDE 中使用標準 Java 專案設定，例如 IntelliJ IDEA 或 Visual Studio Code。或者，您可以手動建立所需的檔案結構。

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` 類別是 Canary 的進入點或處理常式。

 **Java Canary 類別範例** 

此範例可讓 Canary 對存放在 *TESTING\$1URL* Lambda 環境變數中的 URL 提出取得請求。Canary 不會使用 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` 模組化您的 Canary。Canary 會向從環境變數 URL1 和 URL2 取得的兩個獨立 URL 發起 `get` 呼叫。

**注意**  
若要使用 `executeStep` 功能，Canary 的處理常式方法應採用 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();
  }
}
```

## 封裝 Canary 的專案
<a name="Synthetics_canary_Java_package_canary"></a>

Synthetics 接受 *zip* 格式的 Java Canary 程式碼。zip 包含 Canary 程式碼的類別檔案、任何第三方相依項的 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-cloudwatch-synthetics-sdk-java ](https://github.com/aws/aws-cloudwatch-synthetics-sdk-java/tree/main)下的 README。

```
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>

處理常式名稱是 Canary 的進入點。對於 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* – 判斷是否發出步驟的*持續時間*指標。*持續時間*指標以步驟執行的持續時間發出，以毫秒為單位。預設值為 * true*。

 **記錄組態** 

適用於 CloudWatch Synthetics 產生的日誌。控制請求和回應日誌的詳盡程度。
+ *logRequest*：指定是否要在 Canary 日誌中記錄每個請求。預設值為 false。
+ *logResponse*：指定是否要在 Canary 日誌中記錄每個回應。預設值為 false。

 **HTTP 指標組態** 

與具有不同 HTTP 狀態碼之網路請求計數相關的指標組態，由 CloudWatch Synthetics 針對此 Canary 發出。
+ *metric\$12xx* – 指定是否要為此 Canary 發出 * 2xx* 指標 （使用 CanaryName 維度）。預設值為 *true*。
+ *metric\$14xx* – 指定是否要為此 Canary 發出 * 4xx* 指標 （使用 CanaryName 維度）。預設值為 *true*。
+ *metric\$15xx* – 指定是否要為此 Canary 發出 * 5xx* 指標 （使用 CanaryName 維度）。預設值為 *true*。
+ *aggregated2xxMetric* – 指定是否要為此 Canary 發出 * 2xx* 指標 （不含 CanaryName 維度）。預設值為 *true*。
+ *aggregated4xxMetric* – 指定是否要為此 Canary 發出 * 4xx* 指標 （不含 CanaryName 維度）。預設值為 *true*。
+ *aggregated5xxMetric* – 指定是否要為此 Canary 發出 * 5xx* 指標 （不含 CanaryName 維度）。預設值為 *true*。

 **Canary 指標組態** 

CloudWatch Synthetics 發出之其他指標的組態。
+ *failedCanaryMetric* Network Access Analyzer 指定是否要為此 Canary 發出*失敗*指標 （使用 CanaryName 維度）。預設值為 *true*。
+ *aggregatedFailedCanaryMetric*：指定是否要為此 Canary 發出*失敗*指標 (不含 CanaryName 維度)。預設值為 *true*。

## CloudWatch Synthetics 環境變數
<a name="Synthetics_canary_Java_variables"></a>

您可以使用環境變數來設定記錄層級和格式。

 **日誌格式** 

CloudWatch Synthetics Java 執行時期會為每個 Canary 執行建立 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**
+ [為 Playwright 執行時期封裝 Node.js Canary 檔案](#Synthetics_canary_Nodejs_Playwright_package)
+ [變更現有 Playwright 指令碼以用作 CloudWatch Synthetics Canary](#CloudWatch_Synthetics_canary_edit_Playwright_script)
+ [CloudWatch Synthetics 組態](#Synthetics_canary_configure_Playwright_script)

## 為 Playwright 執行時期封裝 Node.js Canary 檔案
<a name="Synthetics_canary_Nodejs_Playwright_package"></a>

 Canary 指令碼由 `.js` (CommonJS 語法) 或 `.mjs`(ES 語法) 檔案組成，其中包含您的 Synthetics 處理常式程式碼，以及程式碼所依賴的任何其他套件和模組。以 ES (ECMAScript) 格式建立的指令碼應使用 .mjs 作為副檔名，或包含具有「類型」：「模組」欄位集的 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` 檔案，並將其儲存在與進入點或處理常式檔案相同的路徑。

或者，也可以將進入點檔案儲存在您選擇的資料夾結構中。不過，請確定已在處理常式名稱中指定資料夾路徑。

 **處理常式名稱** 

請務必將 Canary 的指令碼進入點 (處理常式) 設定為 ` 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 Canary，這會記錄瀏覽器傳送的每個請求。預設值為 ` false`。
+ `logResponse` – 是否要在 Canary 日誌中記錄每個回應。對於 UI Canary，這會記錄瀏覽器收到的每個回應。預設值為 ` false`。
+ `logRequestBody` – 是否要在 Canary 日誌中記錄請求內文及請求。此組態僅在 `logRequest` 為 true 時適用。預設值為 `false`。
+ `logResponseBody` – 是否要在 Canary 日誌中記錄回應內文及請求。此組態僅在 `logResponse` 為 true 時適用。預設值為 `false`。
+ `logRequestHeaders` – 是否要在 Canary 日誌中記錄請求標頭及請求。此組態僅在 ` logRequest` 為 true 時適用。預設值為 `false`。
+ `logResponseHeaders` – 是否要在 Canary 日誌中記錄回應標頭及回應。此組態僅在 ` logResponse` 為 true 時適用。預設值為 `false`。

 **HTTP 指標組態** 

與具有不同 HTTP 狀態碼之網路請求計數相關的指標組態，由 CloudWatch Synthetics 針對此 Canary 發出。
+ `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**
+ [從頭開始建立 CloudWatch Synthetics Canary](#CloudWatch_Synthetics_Canaries_write_from_scratch)
+ [封裝 Node.js Canary 檔案](#CloudWatch_Synthetics_Canaries_package)
+ [變更現有的 Puppeteer 指令碼以作為 Synthetics Canary 使用](#CloudWatch_Synthetics_Canaries_modify_puppeteer_script)
+ [環境變數](#CloudWatch_Synthetics_Environment_Variables)
+ [將您的 Canary 與其他 AWS 服務整合](#CloudWatch_Synthetics_Canaries_AWS_integrate)
+ [強制您的 Canary 使用靜態 IP 地址](#CloudWatch_Synthetics_Canaries_staticIP)

## 從頭開始建立 CloudWatch Synthetics Canary
<a name="CloudWatch_Synthetics_Canaries_write_from_scratch"></a>

下面是基本 Synthetics Canary 指令碼的範例。此指令碼作為成功執行傳遞，並傳回一個字串。若想查看失敗的 Canary 看起來是什麼樣子，請將 `let fail = false;` 變更為 `let fail = true;`。

您必須定義 Canary 指令碼的進入點函數。若要檢視檔案如何上傳到指定為 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 Canary 檔案
<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
```

**注意**  
組態中的處理常式路徑必須與您的檔案位置相符。

 **處理常式名稱** 

請務必將 Canary 的指令碼進入點 (處理常式) 設定為 ` myCanaryFilename.functionName`，以符合指令碼進入點的檔案名稱。如果您使用的執行時間早於 `syn-nodejs-puppeteer-3.4`，則 `functionName` 必須為 `handler`。如果您使用的是 ` syn-nodejs-puppeteer-3.4` 或更高版本，您可以選擇任何函數名稱作為處理常式。如果您使用的是 `syn-nodejs-puppeteer-3.4` 或更高版本，您還可以選擇將 Canary 存放在單獨的資料夾 (例如 ` nodejs/node_modules/myFolder/my_canary_filename`) 中。如果將其存放在單獨的資料夾中，請在指令碼進入點中指定該路徑，例如 ` myFolder/my_canary_filename.functionName`。

## 變更現有的 Puppeteer 指令碼以作為 Synthetics Canary 使用
<a name="CloudWatch_Synthetics_Canaries_modify_puppeteer_script"></a>

本節介紹如何採用 Puppeteer 指令碼和修改它們以作為 Synthetics Canary 指令碼執行。如需 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 檔案產生，並將 Canary ARN 新增至頁面上傳出請求的 user-agent 標頭。

指令碼現在已準備好作為 Synthetics Canary 執行。這是更新後的指令碼：

```
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>

建立 Canary 時，您可以使用環境變數。這允許您編寫單一 Canary 指令碼，然後使用具有不同數值的指令碼來快速建立具有類似任務的多個 Canary。

例如，假設您的組織在軟體開發的不同階段擁有諸如 `prod`、` dev` 和 `pre-release` 之類的端點，並且您需要建立 Canary 來測試這些端點。您可以撰寫測試軟體的單一 Canary 指令碼，然後在建立三個 Canary 的每一個 Canary 時，為端點環境變數指定不同的數值。然後，當您建立 Canary 時，您可以指定要用於環境變數的指令碼和數值。

環境變數名稱可包含字母、數字和底線字元。其必須以字母開頭，且至少有兩個字元。環境變數的總大小不能超過 4 KB。您無法指定任何 Lambda 保留環境變數作為環境變數的名稱。如需有關保留環境變數的詳細資訊，請參閱[執行時間環境變數](https://docs.aws.amazon.com/lambda/latest/dg/configuration-envvars.html#configuration-envvars-runtime)。

**重要**  
環境變數金鑰和值會使用 AWS 擁有 AWS KMS 的金鑰進行靜態加密。然而，環境變數在用戶端並未經過加密處理。請勿在其中存放敏感資訊。

以下範例指令碼使用了兩個環境變數。這個指令碼可用於檢查網頁是否可用的 Canary。它使用環境變數來參數化其檢查的 URL 和其使用的 CloudWatch Synthetics 日誌層級。

以下函數會將 `LogLevel` 設定為 ` LOG_LEVEL` 環境變數的數值。

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

此函數會將 `URL` 設定為 `URL` 環境變數的數值。

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

這是完整的指令碼。當您使用此指令碼建立 Canary 時，您可以指定要用於 `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>

若要在主控台中建立 Canary 時將環境變數傳遞至指令碼，請在主控台的 **Environment variables** (環境變數) 區段中指定環境變數的金鑰和數值。如需詳細資訊，請參閱[建立 Canary](CloudWatch_Synthetics_Canaries_Create.md)。

若要透過 API 或 傳遞環境變數 AWS CLI，請使用 `RunConfig`區段中的 ` EnvironmentVariables` 參數。以下是建立 Canary 的範例 AWS CLI 命令，該 Canary 使用兩個具有 `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"
}'
```

## 將您的 Canary 與其他 AWS 服務整合
<a name="CloudWatch_Synthetics_Canaries_AWS_integrate"></a>

所有 Canary 都可以使用 AWS SDK 程式庫。您可以在撰寫 Canary 時使用此程式庫，將 Canary 與其他 AWS 服務整合。

要這麼做，您需要新增下面的程式碼到您的 Canary。對於這些範例， AWS Secrets Manager 用作 Canary 正在整合的服務。
+ 匯入 AWS SDK。

  ```
  const AWS = require('aws-sdk');
  ```
+ 為您整合 AWS 的服務建立用戶端。

  ```
  const secretsManager = new AWS.SecretsManager();
  ```
+ 使用用戶端對該服務進行 API 呼叫。

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

下面的 Canary 指令碼程式碼片段示範了與 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();
};
```

## 強制您的 Canary 使用靜態 IP 地址
<a name="CloudWatch_Synthetics_Canaries_staticIP"></a>

您可以設定 Canary，以便使用靜態 IP 地址。

**若要強制 Canary 使用靜態 IP 地址**

1. 建立新 VPC 如需詳細資訊，請參閱[使用 DNS 與您的 VPC 搭配](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. 建立 Canary。

# 撰寫 Python Canary 指令碼
<a name="CloudWatch_Synthetics_Canaries_WritingCanary_Python"></a>

此指令碼作為成功執行傳遞，並傳回一個字串。若想查看失敗的 Canary 看起來是什麼樣子，請將 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 Canary 檔案
<a name="CloudWatch_Synthetics_Canaries_WritingCanary_Python_package"></a>

如果您有多個 .py 檔案，或者指令碼具有相依性，則可將其全部封裝到單一 ZIP 檔案中。如果您使用 `syn-python-selenium-1.1` 執行時間，ZIP 檔案在 `python` 資料夾中必須包含主要 Canary .py 檔案，例如 `python/my_canary_filename.py`。如果您使用的是 ` syn-python-selenium-1.1` 或更高版本，您可以選擇使用其他資料夾，例如 `python/myFolder/my_canary_filename.py`。

此 ZIP 檔案應包含所有必要的資料夾和檔案，但其他檔案不需要位於 `python` 資料夾中。

請務必將 Canary 的指令碼進入點設定為 ` my_canary_filename.functionName`，以符合指令碼進入點的檔案名稱和函數名稱。如果您使用的是 `syn-python-selenium-1.0` 執行時間，則 `functionName` 必須為 `handler`。如果您使用的是 ` syn-python-selenium-1.1` 或更高版本，此處理常式名稱限制就不適用，您還可以選擇將 Canary 存放在單獨的資料夾 (例如 ` python/myFolder/my_canary_filename.py`) 中。如果將其存放在單獨的資料夾中，請在指令碼進入點中指定該路徑，例如 ` myFolder/my_canary_filename.functionName`。

## 變更現有的 Selenium 指令碼以作為 Synthetics Canary 使用
<a name="CloudWatch_Synthetics_Canaries_WritingCanary_Python_Selenium"></a>

您可以快速修改 Python 和 Selenium 用作 Canary 的現有指令碼。如需 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 指令碼轉換可用作 Canary**

1. 將變更 `import` 陳述式，以使用來自 ` aws_synthetics` 模組的 Selenium：

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

   來自 `aws_synthetics` 的 Selenium 模組可確保 Canary 能發射指標和日誌，產生 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 Canary。這是更新後的指令碼：

`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 的一個重要使用案例是您監控自己的端點。如果您想要監控尚未準備好處理外部流量的端點，此類監控有時意味著您可能未持有由可信第三方憑證授權機構簽署的有效憑證。

針對這種情況，有兩種可行的解決方案如下：
+ 若要驗證用戶端憑證，請參閱[如何使用 Amazon CloudWatch Synthetics 驗證身分驗證 – 第 2 部分](https://aws.amazon.com/blogs/mt/how-to-validate-authentication-using-amazon-cloudwatch-synthetics-part-2/)。
+ 若要驗證自我簽署憑證，請參閱[如何在 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 多重檢查藍圖可讓您建立 Canary，在單一 Canary 執行中執行多個驗證檢查。當您想要測試多個端點、驗證應用程式的不同層面，或依序執行一系列相關檢查時，此藍圖非常有用。

**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 藍圖 Canary 的整體結構。


**結構描述屬性**  

| 屬性 | Type | 必要 | Description | 
| --- | --- | --- | --- | 
|  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 | 預設 | 範圍 | Description | 
| --- | --- | --- | --- | --- | 
|  stepTimeout  | integer | 30000 | 5000-300000 | 所有步驟的預設逾時 （毫秒） | 

 **範例** 

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

## 變數和資料管理
<a name="variables-data-management"></a>

變數可讓您定義可重複使用的值，這些值可以使用`${variableName}`語法在整個組態中參考。

 **變數屬性** 


| 屬性 | Type | 說明 | 
| --- | --- | --- | 
| 變數名稱 | string | 必須符合模式 ^[a-zA-Z][a-zA-Z0-9\$1]\$1\$1 | 
| 變數值 | string | 任何字串值 | 

 **限制** 
+ 每個組態最多 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  | string |  是  | 步驟的唯一識別符 | 
|  checkerType  | string |  是  | 檢查類型：HTTP、DNS、SSL、  TCP | 
|  extractors  | 陣列 | 否 | 資料擷取組態 | 

 *步驟名稱驗證* 
+ 模式 - ^【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 端點和 APIs。

 **必要屬性** 


| 屬性 | Type | Description | 
| --- | --- | --- | 
|  url  | string | 目標 URL （必須是有效的 URI 格式） | 
|  httpMethod  | string | HTTP 方法：GET、POST、PUT、 PATCH、DELETE、HEAD、 OPTIONS | 

 **選用屬性** 


| 屬性 | Type | 預設 | 範圍 | Description | 
| --- | --- | --- | --- | --- | 
|  timeout  | integer | 30000 | 5000-300000 | 請求逾時 （毫秒） | 
|  waitTime  | integer | 0 | 0-60 | 請求之前的延遲 （秒） | 
|  headers  | object | - | - | 自訂 HTTP 標頭 | 
|  body  | string | - | - | POST/PUT 操作的請求內文 | 
|  authentication  | object | - | - | 身分驗證組態 | 
|  assertions  | 陣列 | - | - | 回應驗證規則 | 

 **範例** 

```
{
  "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 | Description | 
| --- | --- | --- | 
|  domain  | string | 要查詢的網域名稱 （主機名稱格式） | 

 **選用屬性** 


| 屬性 | Type | 預設 | Description | 
| --- | --- | --- | --- | 
|  recordType  | string | 「A」 | DNS 記錄類型：A、CNAME、MX、 TXT、 NS | 
|  nameserver  | string | - | 要查詢的特定 DNS 伺服器 | 
|  timeout  | integer | 30000 | 查詢逾時 (5000-300000ms) | 
|  port  | integer | 53 | DNS 伺服器連接埠 (1-65535) | 
|  protocol  | string | 「UDP」 | 通訊協定： UDP 或 TCP | 
|  assertions  | 陣列 | - | 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 | Description | 
| --- | --- | --- | 
|  hostname  | string | 目標主機名稱 （主機名稱格式） | 

 **選用屬性** 


| 屬性 | Type | 預設 | 描述 | 
| --- | --- | --- | --- | 
|  port  | integer | 443 | SSL 連接埠 (1-65535) | 
|  timeout  | integer | 30000 | 連線逾時 (5000-300000ms) | 
|  sni  | boolean | TRUE | 伺服器名稱指示 | 
|  verifyHostname  | boolean | TRUE | 主機名稱驗證 | 
|  allowSelfSigned  | boolean | FALSE | 接受自我簽署憑證 | 
|  assertions  | 陣列 | - | 憑證驗證規則 | 

 **範例** 

```
{
  "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 | Description | 
| --- | --- | --- | 
|  hostname  | string | 目標主機名稱 （主機名稱格式） | 
|  port  | integer | 目標連接埠 (1-65535) | 

 **選用屬性** 


| 屬性 | Type | 預設 | 描述 | 
| --- | --- | --- | --- | 
|  timeout  | integer | 30000 | 整體逾時 (5000-300000ms) | 
|  connectionTimeout  | integer | 3000 | 連線逾時 (5000-300000ms) | 
|  readTimeout  | integer | 2000 | 資料讀取逾時 (5000-300000ms) | 
|  sendData  | string | - | 連線後要傳送的資料 | 
|  expectedResponse  | string | - | 預期的回應資料 | 
|  encoding  | string | 「UTF-8」 | 資料編碼：UTF-8、ASCII、 HEX | 
|  assertions  | 陣列 | - | 連線和回應驗證 | 

 **範例** 

```
{
  "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  | string |  是  | 必須為 "BASIC" | 
|  username  | string |  是  | 身分驗證的使用者名稱 | 
|  password  | string |  是  | 用於身分驗證的密碼 | 

 **範例** 

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

 **API 金鑰身分驗證** 


| 屬性 | Type | 必要 | 預設 | Description | 
| --- | --- | --- | --- | --- | 
|  type  | string |  是  | - | 必須為 "API\$1KEY" | 
|  apiKey  | string |  是  | - | API 金鑰值 | 
|  headerName  | string | 否 | "X-API-Key" | API 金鑰的標頭名稱 | 

 **範例** 

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

 **OAuth 用戶端登入資料** 


| 屬性 | Type | 必要 | 預設 | Description | 
| --- | --- | --- | --- | --- | 
|  type  | string |  是  | - | 必須為 "OAUTH\$1CLIENT\$1CREDENTIALS" | 
|  tokenUrl  | string |  是  | - | OAuth 權杖端點 URL | 
|  clientId  | string |  是  | - | OAuth 用戶端 ID | 
|  clientSecret  | string |  是  | - | OAuth 用戶端秘密 | 
|  scope  | string | 否 | - | OAuth 範圍 | 
|  audience  | string | 否 | - | OAuth 對象 | 
|  resource  | string | 否 | - | OAuth 資源 | 
|  tokenApiAuth  | 陣列 | 否 | - | Token API 驗證方法：BASIC\$1AUTH\$1HEADER、 REQUEST\$1BODY | 
|  tokenCacheTtl  | integer | 否 | 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 Signature （第 4 版）** 


| 屬性 | Type | 必要 | 描述 | 
| --- | --- | --- | --- | 
|  type  | string |  是  | 必須為 "SIGV4" | 
|  service  | string |  是  |  AWS 服務的名稱 （例如，「execute-api」) | 
|  region  | string |  是  | AWS 區域 | 
|  roleArn  | string |  是  | 用於簽署的 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  | string |  是  | 必須為 "STATUS\$1CODE" | 
|  operator  | string |  是  | EQUALS, NOT\$1EQUALS, GREATER\$1THAN,  LESS\$1THAN, IN\$1RANGE | 
|  value  | integer | 有條件 | HTTP 狀態碼 (100-599) | 
|  rangeMin  | integer | 有條件 | 最小範圍值 （適用於 IN\$1RANGE) | 
|  rangeMax  | integer | 有條件 | 最大範圍值 （適用於 IN\$1RANGE) | 

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

 **回應時間聲明** 


| 屬性 | Type | 必要 | 預設 | Description | 
| --- | --- | --- | --- | --- | 
|  type  | string |  是  | - | 必須為 "RESPONSE\$1TIME" | 
|  operator  | string |  是  | - | LESS\$1THAN, GREATER\$1THAN, EQUALS | 
|  value  | 數字 |  是  | - | 時間值 （最小值為 0) | 
|  unit  | string | 否 | 「MILLISECONDS」 | 必須為 "MILLISECONDS" | 

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

 **頭部聲明** 


| 屬性 | Type | 必要 | 描述 | 
| --- | --- | --- | --- | 
|  type  | string |  是  | 必須為 "HEADER" | 
|  headerName  | string |  是  | 要驗證的標頭名稱 | 
|  operator  | string |  是  | EQUALS, NOT\$1EQUALS, CONTAINS,  NOT\$1CONTAINS, REGEX\$1MATCH, EXIST | 
|  value  | string/boolean | 有條件 | 預期值 (EXIST運算子的布林值） | 

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

 **內文聲明** 


| 屬性 | Type | 必要 | 預設 | Description | 
| --- | --- | --- | --- | --- | 
|  type  | string |  是  | - | 必須為 "BODY" | 
|  target  | string | 否 | 「JSON」 | JSON 或 TEXT | 
|  path  | string | 有條件 | - | JSONPath (JSON 目標需要） | 
|  operator  | string |  是  | - | CONTAINS, NOT\$1CONTAINS, EQUALS,  NOT\$1EQUALS, EXISTS | 
|  value  | string/boolean |  是  | - | 預期值 (EXISTS運算子的布林值） | 

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

### DNS 聲明
<a name="dns-assertions"></a>

 **記錄值聲明** 


| 屬性 | Type | 必要 | 範圍 | Description | 
| --- | --- | --- | --- | --- | 
|  type  | string |  是  | - | 必須為 "RECORD\$1VALUE" | 
|  operator  | string |  是  | - | EQUALS, NOT\$1EQUALS, CONTAINS,  NOT\$1CONTAINS, REGEX\$1MATCH | 
|  value  | string |  是  | - | 預期的記錄值 | 

 **記錄計數宣告** 


| 屬性 | Type | 必要 | 範圍 | Description | 
| --- | --- | --- | --- | --- | 
|  type  | string |  是  | - | 必須為 "RECORD\$1COUNT" | 
|  operator  | string |  是  | - | EQUALS, GREATER\$1THAN, LESS\$1THAN | 
|  value  | integer |  是  | ≥ 0 | 預期計數 （最低 0) | 

 **授權聲明** 


| 屬性 | Type | 必要 | 範圍 | Description | 
| --- | --- | --- | --- | --- | 
|  type  | string |  是  | - | 必須為 "AUTHORITATIVE" | 
|  value  | boolean |  是  | - | 預期的授權狀態 | 

 **TTL 聲明** 


| 屬性 | Type | 必要 | 範圍 | Description | 
| --- | --- | --- | --- | --- | 
|  type  | string |  是  | - | 必須為 "TTL" | 
|  operator  | string |  是  | - | EQUALS, GREATER\$1THAN, LESS\$1THAN | 
|  value  | integer |  是  | ≥ 0 | 預期的 TTL （最低 0) | 

### SSL 聲明
<a name="ssl-assertions"></a>

 **憑證過期聲明** 


| 屬性 | Type | 必要 | 預設 | Description | 
| --- | --- | --- | --- | --- | 
|  type  | string |  是  | - | 必須為 "CERTIFICATE\$1EXPIRY" | 
|  operator  | string |  是  | - | GREATER\$1THAN, LESS\$1THAN | 
|  value  | integer |  是  | - | 時間值 （最小值為 0) | 
|  unit  | string | 否 | 「天」 | DAYS, HOURS | 

 **憑證主體聲明** 


| 屬性 | Type | 必要 | 預設 | Description | 
| --- | --- | --- | --- | --- | 
|  type  | string |  是  | - | 必須為 "CERTIFICATE\$1SUBJECT" | 
|  field  | string |  是  | - | 主旨欄位：CN、O、OU、C、ST、 L | 
|  operator  | string |  是  | - | CONTAINS, EQUALS, REGEX\$1MATCH | 
|  value  | string |  是  | - | 預期的欄位值 | 

 **憑證發行者聲明** 


| 屬性 | Type | 必要 | 預設 | Description | 
| --- | --- | --- | --- | --- | 
|  type  | string |  是  | - | 必須為 "CERTIFICATE\$1ISSUER" | 
|  field  | string |  是  | - | 發行者欄位：CN、 O | 
|  operator  | string |  是  | - | CONTAINS, EQUALS | 
|  value  | string |  是  | - | 預期的欄位值 | 

### TCP 聲明
<a name="tcp-assertions"></a>

 **連線成功聲明** 


| 屬性 | Type | 必要 | 預設 | Description | 
| --- | --- | --- | --- | --- | 
|  type  | string |  是  | - | 必須為 "CONNECTION\$1SUCCESSFUL" | 
|  value  | boolean |  是  | - | 預期的連線狀態 | 

 **回應資料聲明** 


| 屬性 | Type | 必要 | 預設 | Description | 
| --- | --- | --- | --- | --- | 
|  type  | string |  是  | - | 必須為 "RESPONSE\$1DATA" | 
|  operator  | string |  是  | - | CONTAINS, EQUALS, NOT\$1CONTAINS,  REGEX\$1MATCH, STARTS\$1WITH, ENDS\$1WITH | 
|  value  | string |  是  | - | 預期的回應資料 | 
|  encoding  | string | 否 | 「UTF-8」 | UTF-8, ASCII, HEX | 

## 資料擷取
<a name="data-extraction"></a>

擷取器可讓您從回應擷取資料，以用於後續步驟或報告目的。

 **擷取屬性** 


| 屬性 | Type | 必要 | 預設 | Description | 
| --- | --- | --- | --- | --- | 
|  name  | string |  是  | - | 擷取資料的變數名稱 | 
|  type  | string |  是  | - | 擷取類型： BODY | 
|  path  | string | 否 | - | 用於內文擷取的 JSONPath  | 
|  regex  | string | 否 | - | 規則表達式模式 | 
|  regexGroup  | integer | 否 | 0 | Regex 擷取群組 （最低 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}"
    }
  ]
}
```