💡

Tail Worker をテストしてみた

2024/04/15に公開

Tail Worker とは Producer Worker と指定される親Worker実行時に自動でバックグランドで起動され、HTTP ステータス、渡されたデータ、またはキャッチされなかった例外などの実行に関する情報を受け取ります。Alertやデバッグ、実行状況の分析などに利用されます。従来wrangler tailで特定Workerの実行状況を管理することができましたが、その環境が不要となりWorkerを用いて監視が可能となります。

https://developers.cloudflare.com/workers/observability/logging/tail-workers/

また2024年4月第一週に開催されたDeveloper Week 2024 では、Tail Worker がProducer Worker の実行失敗(Exceptionエラー時)にそのスタックトレースとソースマップによるサポートが発表され、キャッチされなかった例外がスローされると、ソース マップがフェッチされ、それを使用して例外のスタック トレースがワーカーの元のソース コードの行にマップされます。これによりエラーの原因解析が容易になります。またソースマップは作成不要で自動でProducer Worker のDeploy時に作成されます。この機能は4月15日以降順次利用可能となる予定です。
https://developers.cloudflare.com/workers/observability/source-maps

この記事ではスタックトレースとソースマップが利用可能となる前にTail Worker のおさらいをしておきます。

さっそくやってみる

まずはいつも通りWorkersのHello Worldを実行します。
以下を参考にしてパラメータを指定してください。

wrangler init helloworldproducer
Delegating to locally-installed wrangler@3.10.0 over global wrangler@2.14.0...
Run `npx wrangler init helloworldproducer` to use the local version directly.

 ⛅️ wrangler 3.10.0 (update available 3.50.0)
-------------------------------------------------------
Using npm as package manager.
▲ [WARNING] The `init` command is no longer supported. Please use `npm create cloudflare@2 -- helloworldproducer` instead.

  The `init` command will be removed in a future version.


Running `npm create cloudflare@2 -- helloworldproducer`...

using create-cloudflare version 2.0.9

╭ Create an application with Cloudflare Step 1 of 3
│
├ Where do you want to create your application?
│ dir helloworldproducer
│
├ What type of application do you want to create?
│ type "Hello World" script
│
├ Do you want to use TypeScript?
│ typescript no
│
├ Copying files from "simple" template
│
├ Do you want to use git?
│ git no
│
╰ Application created 

╭ Installing dependencies Step 2 of 3
│
├ Installing dependencies
│ installed via `npm install`
│
╰ Dependencies Installed

╭ Deploy with Cloudflare Step 3 of 3
│
├ Do you want to deploy your application?
│ no deploying via `npm run deploy`
│
├  APPLICATION CREATED  Deploy your application with npm run deploy
│
│ Run the development server npm run start
│ Deploy your application npm run deploy
│ Read the documentation https://developers.cloudflare.com/workers
│ Stuck? Join us at https://discord.gg/cloudflaredev
│
╰ See you again soon!

この例ではhelloworldproducerとしてWorkerを作成しています。
まずは普通にwrangler deployを実行してブラウザでアクセスを行いHello Worldが表示されていることを確認します。

cd .\helloworldproducer\
PS C:\Users\HarunobuKameda\helloworldproducer> wrangler deploy
Delegating to locally-installed wrangler@3.50.0 over global wrangler@2.14.0...
Run `npx wrangler deploy` to use the local version directly.

 ⛅️ wrangler 3.50.0
-------------------
√ Select an account » Harunobukameda Demo account
Total Upload: 0.19 KiB / gzip: 0.16 KiB
Uploaded helloworldproducer (1.14 sec)
Published helloworldproducer (3.68 sec)
  https://helloworldproducer.harunobukameda.workers.dev
Current Deployment ID: 57586c4c-ef6c-422b-a2a3-f18fffc918c8


NOTE: "Deployment ID" in this output will be changed to "Version ID" in a future version of Wrangler. To learn more visit: https://developers.cloudflare.com/workers/configuration/versions-and-deployments

次にこのWorker を追跡するTail Worker を作ります。名前をtailworkerとします。
作成されたらworker.jsを以下に変更します。

tailworker - worker.js
export default {
	async tail(events,env,ctx) {
      console.log(events);
	  await fetch("https://httpbin.org ", {
		method: "POST",
		body: JSON.stringify(events),
	  })
	}
  }

eventsとしてProducer Workerから情報が引き渡されconsole(クラウドフレアのマネージメントコンソールで確認ができます)に情報を出力しつつ、外部のエンドポイントにログをPOSTします。SIEMなどを指定します。httpbin.orgはPOST/GETなどのテストが簡単に行える超便利ツールですが今回は利用しません。
どうもCloudflareのドキュメントのサンプルのままでは正しく動作しない(ほとんどのケースpostがなされない)ようで、fetch/post にawaitを入れたら動作しました。

次にProducer Worker で起動の度tailworkerを起動する指定を行います。

tailproducer - wrangler.toml
name = "tailproducer"
main = "src/worker.js"
compatibility_date = "2024-04-15"

tail_consumers = [{service = "tailworker"}]

tail_consumers = [{service = "tailworker"}]がポイントです。起動の度にtailworker が起動してtailproducerの実行を追跡します。
再度wrangler deployで両方のworkerをdeployしておきます。

マネージメントコンソールでtailworkerをクリックします。

Logsのタブをクリックします。

Begin log streamをクリックしてブラウザはそのままにしておきます。

別タブでproducerworkerにアクセスすると以下のようにログがtailworker側で表示され追跡していることがわかります。

tailworkerconsole.log(event)が以下 "logs": [行で出力されていることがわかります。

{
  "outcome": "ok",
  "scriptVersion": {
    "id": "b0d3b584-241b-49f4-81c8-8a6d67e3e091"
  },
  "scriptName": "tailworker",
  "diagnosticsChannelEvents": [],
  "exceptions": [],
  "logs": [
    {
      "message": [
        [
          {
            "outcome": "ok",
            "scriptVersion": {
              "id": "5bb00bdf-b41e-41e0-8c46-df87b14d6c6c"
            },
            "scriptName": "tailproducer",
            "diagnosticsChannelEvents": [],
            "exceptions": [],
            "logs": [],
            "eventTimestamp": 1713165296311,
            "event": {
              "request": {
                "url": "https://tailproducer.harunobukameda.workers.dev/favicon.ico",
                "method": "GET",
                "headers": {
                  "accept": "image/avif,image/webp,image/apng,image/svg+xml,image/*,*/*;q=0.8",
                  "accept-encoding": "gzip, br",
                  "accept-language": "ja,en-US;q=0.9,en;q=0.8",
                  "cf-connecting-ip": "2a09:bac5:4305:1000::198:da",
                  "cf-ipcountry": "JP",
                  "cf-ray": "874a243daac0e06e",
                  "cf-visitor": "{\"scheme\":\"https\"}",
                  "connection": "Keep-Alive",
                  "host": "tailproducer.harunobukameda.workers.dev",
                  "referer": "https://tailproducer.harunobukameda.workers.dev/",
                  "sec-ch-ua": "\"Google Chrome\";v=\"123\", \"Not:A-Brand\";v=\"8\", \"Chromium\";v=\"123\"",
                  "sec-ch-ua-mobile": "?0",
                  "sec-ch-ua-platform": "\"Windows\"",
                  "sec-fetch-dest": "image",
                  "sec-fetch-mode": "no-cors",
                  "sec-fetch-site": "same-origin",
                  "user-agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/123.0.0.0 Safari/537.36",
                  "x-forwarded-proto": "https",
                  "x-real-ip": "2a09:bac5:4305:1000::198:da"
                },
                "cf": {
                  "longitude": "139.68990",
                  "httpProtocol": "HTTP/2",
                  "tlsCipher": "AEAD-AES128-GCM-SHA256",
                  "continent": "AS",
                  "asn": 13335,
                  "clientAcceptEncoding": "gzip, deflate, br, zstd",
                  "country": "JP",
                  "tlsClientExtensionsSha1": "Derf3oIFqxKiuPdK3n2H1YP8iaQ=",
                  "verifiedBotCategory": "",
                  "tlsClientAuth": {
                    "certIssuerDNLegacy": "",
                    "certIssuerSKI": "",
                    "certSubjectDNRFC2253": "",
                    "certSubjectDNLegacy": "",
                    "certFingerprintSHA256": "",
                    "certNotBefore": "",
                    "certSKI": "",
                    "certSerial": "",
                    "certIssuerDN": "",
                    "certVerified": "NONE",
                    "certNotAfter": "",
                    "certSubjectDN": "",
                    "certPresented": "0",
                    "certRevoked": "0",
                    "certIssuerSerial": "",
                    "certIssuerDNRFC2253": "",
                    "certFingerprintSHA1": ""
                  },
                  "tlsExportedAuthenticator": {
                    "clientFinished": "d86e3d59c4669dc623632ba402aa13b41839717c8e296992b739f3ffc1a9a340",
                    "clientHandshake": "40ed9bf657409cf3efb87434f69843817def4620390d654f55f66d54eefd39fc",
                    "serverHandshake": "260bdf33b96f1d272ac9d549323de3457e5e725d6b763f461a9712ff07b76205",
                    "serverFinished": "fd1f624fc382d186c3efac949eabdf5853ed44a44b7abd704cf0744ca8712b33"
                  },
                  "tlsVersion": "TLSv1.3",
                  "city": "Tokyo",
                  "timezone": "Asia/Tokyo",
                  "colo": "NRT",
                  "tlsClientHelloLength": "520",
                  "edgeRequestKeepAliveStatus": 1,
                  "postalCode": "102-0082",
                  "region": "Tokyo",
                  "latitude": "35.68930",
                  "requestPriority": "weight=16;exclusive=0;group=0;group-weight=0",
                  "regionCode": "13",
                  "asOrganization": "Cloudflare Warp",
                  "tlsClientRandom": "kmz4Pvi7txCqgVuvADvgDqXcp/iOnAxTsyRcIantsyw="
                }
              },
              "response": {
                "status": 200
              }
            }
          }
        ]
      ],
      "level": "log",
      "timestamp": 1713165296311
    }
  ],
  "eventTimestamp": 1713165296311,
  "event": {
    "consumedEvents": [
      {
        "scriptName": "tailproducer"
      }
    ]
  },
  "id": 1
}

日本時間で2024/04/15 に記事を執筆していますが、明日にはスタックトレースとソースマップがサポートされるはずなので次回続報をお届けする予定です。

Discussion