前言
在前一篇使用 OpenTracing - Jaeger之中,我們介紹如何快速安裝及透過程式將相關的資訊記錄到 Jaeger 系統之中。
一個使用者的操作,後端可能會串連很多個服務,如下圖,
透過 OpenTracing 可以用時間軸的方式來看整個過程每個部份所花費的時間,如下圖,
而從 Bot 的使用來看也是相同的,
使用者 -> Bot Connector(Local) -> Bot App(BFv3) -> Bot Connector(Local) -> 使用者
所以,接下來,我們要將 Bot Connector(NodeJS) <-> Bot App(BFv3) 串接起來。
實作
NodeJS 使用
因為我們的 Bot Connector 是使用 NodeJS,所以先看一下如何在 NodeJS 中使用 Jaeger 。
- 安裝 Opentracing, Jaeger Client 套件
1 | npm install opentracing jaeger-client --save |
- 建立 Library 來取得共用的 Tracer (tracing.ts)
1 | // jaeger client |
- NodeJS import Tracer
建立好產生 Tracer 的 Module 後,就可以在要使用的地方 import ,然後取得 tracer,如下,
1 | //OpenTracing |
NodeJS API 入口 (express)
在 NodeJS 中使用大約有 5 個部份,- 是建立 Span
這裡需要判斷 Request 中的 Header 是否包含傳過來的 Parent Span (TracerId),透過 tracer.extract 取得,然後決定是否要設定為它的 Child。 - 記錄 Tag
我們可以透過 Tag 來 Search 資料,例如 userId=”Rainmaker” - Log 程式中一些需要記錄的訊息
- 記錄錯誤
發生錯誤時,將 Tags.ERROR 設定為 true ,在查詢 UI 中就會有所記錄。也建議將錯誤訊息記錄下來。 - 呼叫 span.finish method 它才會寫入
- 是建立 Span
所以大約的程式碼如下,
1 | /** 回覆訊息 (ReplyToActivity) */ |
- 被呼叫 Method 的做法
在前面 api 入口會建立 span ,並將該 span 放到 ctx 物件之中,並傳進來,所以被呼叫的 Method 需要多一個可選參數來接收父 Span。如果該 Method 要透過 Http Call 外部的 api ,則需要將目前 span 的資料放到 Http Header 之中(透過 tracer.inject 取得 header 資料),如下,
1 | /** 將訊息送給各個Channel */ |
.net framework (以 BFv3 訂便當 Bot 為範例)
.net 使用方式在 使用 OpenTracing - Jaeger之中已有說明。
接下來要在 MessagesController 中接收 Bot Connector 傳進來的 Parent Span 資料(一樣是 tracer.Extract),程式碼如下,
1 | private async Task<Activity> HandleMessage(Activity activity) |
註 在 c#中是因為我們用 using 去包,所以就不需要特別地寫 span.finish 。
- 被呼叫的 Method ,只要判斷 tracer.ActiveSpan != null ,就設定 tracer.ActiveSpan 為 Parent Span,如下,
1 | var tracer = GlobalTracer.Instance; |
- 透過 HttpClient 呼叫外部 api 時,一樣將 tracer.ActiveSpan 放入 Header 傳過去即可,如下,
1 | //add opentracing |
運行結果
透過 WebChat 來測試訂便當 Bot,
我們在 訂便當 Bot 中輸入代號,產生訂便當的 Menu,它的過程為,
WebChat -> Bot Connector -> 訂便當 Bot -> Bot Connector -> WebChat
.net debug
在 MessageController 中,可以看到從 Header 中多了一個 uber-tracer-id header,如下,
Jaeger UI 查詢
Search by Tag
我的使用者為 Rainmaker ,所以我們可以在 Jaeger UI 中的 Tag ,輸入 userId=”Rainmaker” 就可以查出我的資訊,查看這個 Trace 資料
點進去查看那個 Trace ,就可以發現,整個 Path 都串接起來了哦,如下,然後再展開那個花費 3.79 秒,我們 Log 它的 actionType 是 ShowMenuAction,如下,
結論
從上面的 Demo 之中,在不同語言實作的系統中,可以將程式呼叫的過程透過 OpenTracing 一致的 API ,將它送到 Jaeger 之中。
所以我們可以從 Jaeger UI 來查詢到系統效能的瓶頸在那裡。
一開始建議先將在 api 入口,去記錄較大的 span 。當發現某個 span 不夠詳細時,再到該 span 去加入 child 的 span 。
列如在上面的 ActionStrategyResolver-DoActionAsync Span 需要再有詳細的資料,我們可以在那個 span 中再加入 child span。