這篇要來介紹 Effect 與 Observability , Observability 中文叫可觀測性,意思是你的程式的執行的過程是可以被觀察的,特別是在分散式的系統或是微服務上,因為資料會在不同的地方被傳遞,因此如何觀察到這些資料是被如何處理的就變的至關重要了
一般講到 Observability 一般都會看到類似這樣的圖
(圖片取自 opentelemetry.io)
上面的橫桿稱為 span ,每一條代表的是一段程式的執行,透過這樣的程式可以明確的看到程式間是如何互相呼叫的,與每段的執行時間等等
那麼回到 Effect ,如果你之前有點開過這系列的文章中的那些我放在 Effect playground 的範例,你可能有注意到,左邊的側邊欄上一直有個 DevTools.ts
的檔案,那到底是做什麼用的呢?我們實際來使用一下,把裡面提供的 DevToolsLive
引入進來,並加入到我們上一篇中的 logging 的範例中,像這樣子
pipe(
Effect.gen(function*() {
yield* Effect.log('first')
yield* Effect.sleep('500 millis')
yield* Effect.log('second')
yield* Effect.sleep('500 millis')
yield* Effect.log('done')
}),
Effect.withLogSpan('span'),
// 這個 withSpan 一定要加才有效果
Effect.withSpan('main'),
// 加入 DevToolsLive
Effect.provide(DevToolsLive),
Effect.runPromise
)
神奇的事發生了,我們看到下面的 trace viewer
下面顯示出了我們程式 main
的執行時間,還有 log ,我們再來一個更複雜的範例
const taskA = Effect.fn("taskA")(function*() {
yield* Effect.sleep("500 millis")
})
const taskC = Effect.fn("taskC")(function*() {
yield* Effect.sleep("200 millis")
})
const taskB = Effect.fn("taskB")(function*(n: number) {
// 將資料加到目前的 span 上
yield* Effect.annotateCurrentSpan('n', n)
yield* Effect.sleep("500 millis")
yield* taskC()
})
pipe(
Effect.gen(function*() {
yield* Effect.log("first")
yield* taskA()
yield* Effect.log("second")
yield* taskB(42)
yield* Effect.log("done")
}),
Effect.withLogSpan("span"),
Effect.withSpan("main"),
Effect.provide(DevToolsLive),
Effect.runPromise
)
這個範例給出了更複雜的 tracing ,我們還可以看到我們加在程式中的 n
被標記在 tracing 中
那上面的東西只有在 Effect playground 可以看到嗎?並不是的,如果你是本機開發使用,你可以安裝 Effect 的 vscode extension
那如果是正式環境呢?我們來看下去
如果是正式的環境,你會需要一個集中收集的服務,這邊我們使用 Axiom 這個免費額度還不錯的線上服務來 demo ,另外如果你想要自架,可以考慮看看 Grafana, OpenObserve 等也都不錯
如果要收集紀錄到遠端的服務的話,除了 effect 本體,你需要安裝不少的套件
$ pnpm add @effect/opentelemetry @opentelemetry/exporter-trace-otlp-http @opentelemetry/sdk-metrics @opentelemetry/sdk-trace-base @opentelemetry/sdk-trace-node @opentelemetry/sdk-trace-web
然後我們把 DevTool 改成 provide NodeSdk
import { NodeSdk } from '@effect/opentelemetry'
import { BatchSpanProcessor } from '@opentelemetry/sdk-trace-base'
import { OTLPTraceExporter } from '@opentelemetry/exporter-trace-otlp-http'
Effect.provide(
NodeSdk.layer(() => ({
resource: { serviceName: 'example' },
spanProcessor: new BatchSpanProcessor(
new OTLPTraceExporter({
url: 'https://api.axiom.co/v1/traces',
headers: {
Authorization: 'Bearer <你的 API key>',
'x-axiom-dataset': '<你的 dataset>',
},
}),
),
})),
)
事實上你沒看錯,這邊就是在使用 Open Telemetry 這個標準的 tracing 方法,這邊設定好後我們將程式執行一次,並到 Axiom 上看一下
像這樣,我們就可以收集正式環境的紀錄,並且使用這種方法了解程式內部是如何執行的,下一篇我們要來聊的是 Effect 內建的 Schema 驗證