> For the complete documentation index, see [llms.txt](https://training.onedoggo.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://training.onedoggo.com/tech-sharing/uncle-joe-teach-es-elastc-observability/uptime-zhang-wo-xi-tong-de-sheng-ming-zhi-xiang/shi-yong-he-cheng-jian-kong-synthetics-monitor-cong-shi-yong-zhe-qing-jing-yan-zheng-fu-wu-de-yun-zu.md).

# 使用合成監控 (Synthetics Monitor) 從使用者情境驗證服務的運作狀態

### 本篇學習重點

* 了解 Uptime 的最新功能合成監控 (Synthetic Monitor) 的能力
* 學習 Elastic Synthetic 的架構與使用方法

## 什麼是合成監控 (Synthetic Monitor)

Elastic Observability Uptime 中 Synthetic Monitor 功能的推出，主要是針對使用者體驗 (User Experience) 的角度所發展出來的功能，設計的目的是『**能夠在使用者遇到問題之前，先發現他們可能將遇到的問題**』，因此透過分析使用者存取網頁的情境，產生並模擬出這些情境的操作步驟，並且將這些步驟記錄下來，當作監控的依據，同時在發生異常時，也因為保留了這些步驟的歷程資料，能夠更即時的協助開發與運維的人員解決問題。

> 注意：Synthetic Monitor 是在 Elastic Observability 7.10 版發佈時推出的，推出的時間是在 2020 年 11 月，寫這篇文章時，已經到 Elastic Observability 7.14 版了，不過 Synthetic 還是在實驗 (Experimental) 的階段，也就是官方不建議直接使用在 Production 上，如果要使用的話，未來正式版推出時不見得會確保向前相容，這部份的風險要自己承擔。

## Elastic Synthetic 的運作架構

Elastic Synthetic 底層是使用 [Playwright](https://github.com/microsoft/playwright) 這個 Node.js 的函式庫，透過 Playwright 能使用簡單的 API 來操作 Chromium, Firefox, Webkit 等瀏覽器的核心，進而模擬出使用者操作瀏覽器的行為，因此 `@elastic/synthetics` 也就是使用 javascript 開發的函式庫，並且與 Heartbeat 所延伸開發出來的 `Heartbeat synthetics module` 進行整合，讓我們能夠直接使用 Heartbeat 來透過 `@elastic/synthetics` 執行指定的腳本，並且將這些結果透過 Heartbeat 記錄回 Elasticsearch。

Elastic Observability 同時也在 Kibana 的 Uptime app 之中深度的整合並支援 Synthetic Monitor，讓透過 Synthetic Monitor 記錄的資訊，也就能和其他 Heartbeat 所接收到的資訊一樣，可以直接在單一的入口網站 Uptime 能查看結果、也能使用 Alert 機制在異常的時候主動通知我們。

![06-synthetics-overview](https://i.imgur.com/dL1gKun.png)

## 使用 Synthetic

這邊建議大家參考 [官方文件 Synthetics Quickstart](https://www.elastic.co/guide/en/observability/current/synthetics-quickstart.html) \[2] 來參考如何進行配置及快速上手，主要的概念我這邊整理了一下，協助大家理解，但細節的請參考官方文件。

### 執行的環境

由於 Synthetic 的使用環境較複雜，因此官方建議使用 Docker 的環境來運作，不過在開發的階段，我們可以直接使用本地端來執行。

> 注意：由於官方提供的 Docker Image 目前喬叔使用 Apple M1 CPU 無法正常使用，應該是 Synthetic 裡所使用到的套件有支援度的問題，所以在花了許多時間嘗試卻失敗之後，我在使用 Docker 環境執行 Synthetic 的時候是使用 Windows + Linux VM 的環境來運作。

要在學會執行的話，最簡單的方式就是直接將官方 [Synthetic Github 的專案](https://github.com/elastic/synthetics/tree/master/) Clone 下來，學習他怎麼做：

```
git clone https://github.com/elastic/synthetics/tree/master/
```

在裡面的 `examples` 路徑下，分別有

* `docker`：使用 `run.sh` 可以運作一個 docker container 並執行路徑下的 `heartbeat.docker.yml` 所定義的腳本，或是使用 `bash.sh` 會運作 docker container 並且進入 docker 環境，可以直接在裡面執行要測試的腳本。
* `e-commerce`：這是一個較完整的電商示範網站，裡面也有一些較完整的腳本，不過目前電商網站掛了，要跑的話要自己架起來。
* `in-line`：裡面有幾個簡單的腳本，可以直接透過下方所教的執行 Synthetic 的方法來使用。
* `todos`：這是一個簡單的 ToDo 的 App，裡面也有包含兩種執行 Synthetic 方式的實作，可以供參考。

### 執行 Synthetic 方式

執行的方式分為兩種：

#### 使用 `@elastic/synthetic` 執行

可以透過

```
npm install @elastic/synthetic
```

的方式安裝，或是在上面提到的 example 的專案中，透過 `npm install` 來安裝 `package.json` 裡面指定好的相依的套件。

再來可以透過

```
npx @elastic/synthetics .
```

執行當下目錄的所有的 `.journey.ts` 或是 `.journey.js` 的腳本。

或是透過以下的指定，使用 `inline` 的方式執行 `xxx.js` 裡面所定義的腳本。

```
cat xxx.js | npx @elastic/synthetics --inline
```

> 請注意！這種方式只是單純的執行腳本的結果，並不會把結果傳送到 Elasticsearch 去，也就是在 Uptime 裡是看不到結果的。

#### 使用 `Heartbeat` 執行

若我們要透過 Heartbeat 執行時，要先定義好 `heartbeat.yml` 的設定，裡面可以使用兩種執行的方式 - `inline` 的腳本以及 `指定腳本檔案下載路徑` 的方式，例如 `todos` 的 examples 裡面的 `heartbeat.yml` 就有包含這兩種的例子。

```
heartbeat.monitors:
- type: browser
  id: elastic-website 
  name: Elastic website
  schedule: "@every 1m"
  source:
    inline:
      script: |- 
        step("load homepage", async () => {
            await page.goto('https://www.elastic.co');
        });
        step("hover over products menu", async () => {
            await page.hover('css=[data-nav-item=products]');
        });
- name: Todos
  id: todos
  type: browser
  schedule: "@every 1m"
  source:
    zip_url: 
      url: "https://github.com/elastic/synthetics/archive/refs/heads/master.zip" 
      folder: "examples/todos" 
      username: "" 
      password: ""
```

### 編寫 Synthetic 腳本

要編寫 Synthetic 腳本，主要是要了解 Playwright 的使用方式，主要編寫測試時會用到的語法如下：

* `journey`：用來測試一個主要的功能情境但擁有多個分散步驟的案例。
* `step`：一個 `journey` 裡面所執行的某個驟步，可以是開啟網頁、滑動到某個元件、點選某個按鈕…等。
* `beforeAll`、`before`、`afterAll`、`after`：這些分別用來定義執行 `journey` 之前、或之後要執行的動作。

一個 `journey` 裡面包含幾個重要的元件：

* `browser`：瀏覽器物件，瀏覽器層級的功能會在這邊可以使用。
* `page`：頁面，可跳轉頁面到指定的 URL，或是執行 screenshot，或是在某些頁面的事件事做事。
* `context`：`browser` 的某個 context，當有多個使用瀏覽器的情境、並且不希望共用 cookie, cache 時，就會建立多個 context。
* `params`：使用者定義的參數。

例如 examples `e-commerce` 裡面的一段範例：

```
journey({ name: 'Delete cart items', tags: ['cart'] }, ({ page, params }) => {
  navigateToProductDetail(page, params);

  step('Add items to cart', async () => {
    await page.selectOption('select[name="quantity"]', '2');
    await Promise.all([
      page.waitForNavigation({
        url: /cart/,
        waitUntil: 'networkidle',
      }),
      page.click('text=Add to Cart'),
    ]);
  });

  step('empty cart items', async () => {
    const headline = await page.$('.container h3');
    expect(await headline.textContent()).toContain(
      '1 items in your Shopping Cart'
    );
    await Promise.all([
      page.waitForNavigation({ waitUntil: 'networkidle' }),
      page.click('text=Empty cart'),
    ]);
  });

  step('verify empty shopping cart', async () => {
    await Promise.all([
      page.waitForNavigation({ url: /cart/, waitUntil: 'networkidle' }),
      page.click('text=View Cart'),
    ]);
    await page.waitForSelector('.container');
    const headline = await page.$('.container h3');
    expect(await headline.textContent()).toContain(
      'Your shopping cart is empty'
    );
  });
});
```

## 透過 Kibana Uptime 檢視 Synthetic Monitor 的結果

我們回到 Kibana 的 Uptime 主畫面，在這個畫面之中，我們可以看到有兩個 `Browser` 類型的 Monitor，這個就是使用 `@elastic/synthetic` 所收集到的資料，也就是專門是以使用者行為模擬出的前端網頁操作情境。

![06-kibana-uptime-browse](https://i.imgur.com/El9m1Gc.png)

### Synthetic Monitor 的內容頁面

接下來我們點選其中一個 **13th iTHome Ironeman OneDoggo - inline** 的 Monitor，觀看裡面的記錄細節，與一般的 Monitor 的結果差不多，不過可以發現在 History 的部份，多了畫面的截圖。

![06-kibana-Browser-Monitor-Overview](https://i.imgur.com/a5OZte3.png)

### 從 Uptime 查看 Synthetic 錄製的 `journey` 結果

點選 History 當中的某一筆記錄之後，我們可以看到這個 `journey` 的結果，總共包含三個部份：

* load OneDoggo team page
* goto Joe's page
* navigate to day 1 article

![06-kibana-monitor-journey](https://i.imgur.com/U0eTPDH.png)

### 可以展開 `step` 的執行細節

任何一個 `journey` 的 `step` 展開後，都可以看到這個 `step` 的執行語法與執行結果，甚至 Console output 也會記錄起來，對於要記錄資訊查問題，非常的方便。

![06-kibana-browser-step-detail](https://i.imgur.com/uY1JPU2.png)

### 針對 `step` 檢視效能的分析

點選某一個 `step` 的 `performance breakdown` 之後，可以查看這個頁面在載入時的細節。

![06-kibana-browser-performance-breakdown](https://i.imgur.com/lrJMDwH.png)

點選任何一個資源請求，可以查看當時完整的細節資訊，雖然不像 **Dev Tools** 這麼強大，但這是定期依照我們要的情境所記錄下來的資訊，能夠還原當時發生了什麼事，是不是某些資源當下載入的時間超過預期。

![06-kibana-browser-step-performance-breakdown-detail](https://i.imgur.com/hiBN9Rn.png)

***

這個 Synthetic 的功能實在是讓人很驚豔，本來想順便做一個監控團員們大家有沒有準時發文的 `journey`，但後來發現我最近花在寫鐵人賽文章的時間太久了，我家的狗一直關在家心情不是很好，所以只好收手，好好當個狗奴才去遛狗，期待 Elastic 正式推出 Synthetics Monitor 的功能！

## 參考資料

1. [Playwright Github](https://github.com/microsoft/playwright)
2. [官方文件 Synthetics Quickstart](https://www.elastic.co/guide/en/observability/current/synthetics-quickstart.html)
