> 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/traces-guan-cha-ying-yong-cheng-shi-de-xiao-neng-ping-jing/tou-guo-apm-agents-shou-ji-bing-chuan-song-hou-duan-fu-wu-yun-zuo-de-ji-lu.md).

# 透過 APM Agents 收集並傳送後端服務運作的記錄

### 本篇學習重點

* APM Agents 要解決的問題是什麼？
* APM Agents 提供什麼功能？
* 如何使用 APM Agents 的簡介
* 使用 APM Agents 要注意的事項。

## APM Agents 要解決的問題

Elastic APM Agents 的任務主要有兩件事：

* 協助『應用程式』或『服務』收集 Performance (效能) 相關的 Metrics，傳送給 APM Server。
* 當 『應用程式』或『服務』 發生 Error (錯誤) 時，收集 Error Logs，傳送給 APM Server。

要做到以上的兩件事，代表我們需要程式運作時，收集這兩種的資訊，以下分別針對這兩種情境來說明一般的做法。

### 傳統收集 Performance Metrics 的問題

要分析某段程式運作效能時，最簡單與古老的做法，就是在程式開始時，先記錄當下的時間，在程式結束之後，記錄結束的時間，也因為這種需求太普遍，各種語言當中的 Framework 或 Library 也都有支援這種計算 `time elapsed` 的工具，不過當記錄這些 Performance Metrics 時，常會有以下的問題：

* 『只有有埋 `time elapsed` 的地方，才收集得到數據』，但是要埋的地方可能有非常多，一開始不會都埋好，實務上常常會變成『發生問題時，才改 code 來埋 log、重新出 build、deploy、想辦法讓問題再次發生時，取得 log 』，這樣會變成是背動的狀況，甚至遇到不容易 reproduce (重製) 的例外狀況時，也會很不容易收集到能協助判斷及解決問題的資訊。
* 收集到的資訊不足，在不同的情境下，會需要收集的資訊會不同，例如 Database 存取時的執行速度太慢，這時要能進一步盤查原因，可能會需要執行當下的 SQL statement 並且包含執行時帶入的參數，也可能會需要知道是哪一個請求，才產生出這個 DB 的 query，資訊不足時要花更多的時間才能推測與盤查。

### 傳統收集 Error 的問題

使用例如 `try` `catch` 等 Error handling 的方法，並且在錯誤發生時寫 Logs，而這樣的做法常會遇到：

* Logs 沒有結構化的格式，都只純文字的方式在解讀，有時要找尋相關的 Logs 時，只能用 grap 的方式去篩選，但複雜的條件要處理會很花時間。
* 沒有抓到的錯誤，被丟出時，記錄到的資訊也不足，可能只有 stack trace，但是缺少能更進一步判斷的商業資訊，例如使用者 ID、訂單編號…等。
* 收到的 Error，不容易與前、後發生的處理所產生的 Logs 串連在一起，在分析問題的原因時，不容易掌握整體處理流程發生狀況的全貌。

## APM Agents 的功能介紹

Elastic APM Agent 在協助收集 Performance Metrics 時，提供幾種的方式：

### Framework Integration (框架整合)

也可以稱為 Build-in Instrumentation (內建檢測)，不論是哪種程式語言，在開發的時候常會使用各種 Framework，並且在 Framework 透過已經建立好的一些機制，例如：HTTP 請求的處理、Database 的存取，Logging 的機制、Scheduling (排程) 的功能…等。

APM Agents 在各種語言的支援上，都有盡量整合最熱門的一些 Framework，讓使用者簡單的設定後就能直接使用，自動依照 Framework 的功能，收集相關的資訊，並且以結構化的方式，將收集到的數據的格式定義在 **Elastic Common Schema** 之中。

### Instrumentation (檢測)

在沒有使用支援的 Framework 的時候，APM Agents 也有提供 Instrument (檢測) 資料收集的工具，讓使用者能很簡單的將自己程式中要觀察的某個處理行為，能包裝成為 `Transaction` 或 `Span`，並且透過已經定義好的架構與提供的 Utility，能輕易的收集 Performance 相關的 Metrics 並且加上要額外記錄的資訊，並且由 APM 幫我們將這收集到的事件，與其他前後的事件連結在一起。

### Background Collection (背景收集)

APM Agents 在運作時，會在背景定期的收集系統的 Metrics，能夠配合我們所收集的 `Transaction` 或 `Span` 的資訊，來協助掌握某個要觀察的時間點，系統整體的狀況。

### APM Agents 支援的語言

目前有支援的語言，後端相關的如下：

* Golang
* Java
* .Net
* Node.js
* PHP
* Python
* Ruby

前端相關的有兩個

* iOS
* RUM (Real Time Monitoring) Javascript

## APM Agents 使用方式簡介

以下使用 Golang 為例：

1. 安裝 `Elastic APM` 套件

```
go get -u go.elastic.co/apm
```

1. 使用在 Build-in framework 或 Library，例如 **Gin Web Framework**：

```
import (
	"go.elastic.co/apm/module/apmgin"
)

func main() {
	engine := gin.New()
	engine.Use(apmgin.Middleware(engine))
	...
}
```

1. 透過 Environment Variable 定義相關的設定

* `ELASTIC_APM_SERVER_URL`：APM Server 的位置
* `ELASTIC_APM_SERVICE_NAME`：目前的服務名稱，這個會是之後在 APM UI 中用來識別服務的重要設定。
* `ELASTIC_APM_ENVIRONMENT`：目前的 Environment，這也是在 Kibana APM UI 中篩選的主要功能之一。
* `ELASTIC_APM_GLOBAL_LABELS`：有一些自訂的 Labes 是屬於全域型的，也就是在這個服務裡全都要加上的，可以定在這裡。
* ...其它可參考 [官方文件 - APM Go Agents - Configuration](https://www.elastic.co/guide/en/apm/agent/go/current/configuration.html)。

1. 指定自定義的 Instrument

建立自己定義的 `Transaction`：

```
tx := apm.DefaultTracer.StartTransaction("GET /api/v1", "request")
defer tx.End()
...
tx.Result = "HTTP 2xx"
tx.Context.SetLabel("region", "us-east-1")
```

在 `Transaction` 當中加上 `Span`：

```
span, ctx := apm.StartSpan(ctx, "SELECT FROM foo", "db.mysql.query")
defer span.End()
```

由於每一種語言實作的方式都會有些不同，每一種 Framework 的行為有不同時，支援的方式也會有所差異，以上只是最簡短介紹 APM Agents 的使用方式，讓大家有個感覺，詳細的使用方式，請參考 [官方文件 - APM Agents](https://www.elastic.co/guide/en/apm/agent/index.html) \[1] 每個語言的版本。

## 使用 APM Agents 時要注意的事項

### 對於原本服務的影響？

APM Agents 在運作時，一定會佔用到原本服務需使用的系統資源，這些處理會使用到：

* CPU
* Memory
* 頻寬

另外再從兩個部份來分析：

* Latency (延遲)：APM Agents 收集資訊對於原本服務的 latency 影響程度非常的小，一般只有個位數的微秒 (microseconds)，所以單純是考量 Latency 的部份的話，埋的量不是非常多的話，其實不會有太大的影響。
* Background tasks (背景處理)：APM 在收集到 Instrument 資訊之後，會需要序列化 (serialize) 以及壓縮 (gzipping) 的處理，這部份會佔用到 CPU 的運算，如果服務本身是使用 CPU 為主的服務 (CPU bound)，這部份會影響到原本的性能，但如果不是 CPU bound 的話，影響會較少。

另外 APM Server 如果無法正常接受 APM Agents 所傳送的資料時，APM Agents 最終會選擇放棄傳送，設計上也是以不影響原本服務執行為主。

> 特別注意：先前專案的團隊實際在使用 PHP 版本的 APM Agents 時，在壓測時有明確的因為加上 APM Agents 後，原本服務的 QPS 下降將近一半，猜測和 PHP 的實作版本沒有實作 async call 可能有關，若有使用 PHP 版本的話會需要特別留意。

### Sample Rate (取樣率)

既然使用 APM Agents 收集 Instruments 數據時，一定會佔用到系統的資源，也就是多少都會影響到原本服務的效能，這時候 Sample Rate (取樣率) 就是一個很重要的設定，不過這個值沒有一定的標準，但是在量級非常大的系統之中，1 \~ 3% 的取樣率一般已經足夠有一定的代表性，也就是若是有問題發生時，一般都會能取得到樣本，當然這個設定值還是會依照使用的情境與硬體的規格需進行壓測及調整。

### 減少 APM Agents 處理的資料量

* 如果某些資料沒有必要都要收集，例如 `capture_header` 、 `capture_body` ，這個在 Sample Rate 較高、量級較大的環境之中，應該考慮關掉 `capture_body` ，這個會於效能會有明顯的影響。
* 如果一個 Transaction 中包含了過多個 Span，這也會讓整包處理的量非常的大，例如有寫了個跑非常多次的迴圈，裡面產生非常多的 `span`，這種情況應該設定好 `transaction_max_span` 來避免這種意外發生。
* 另外 APM Agents 會將收集到的 Instruments 資料分批的往 Elasticsearch 傳送，這個每次處理的量也會佔用到系統的效能，如果記憶體佔用太多，應該要嘗試調整傳送的間隔 `api_request_time` 、或是每批的大小 `api_request_size`。

## 參考資料

1. [官方文件 - APM Agents](https://www.elastic.co/guide/en/apm/agent/index.html)
