Deno KVを活用してDeno deployにホストしたサービスのアクセスログを記録する
目的は公開しているサービスの利用状況をしっかり確認したかったというところから、アクセスログ記録するかという流れ
もともとやりたいとは思っていたがぐだぐだしていたらかなり時間が経ってしまった
とりあえず取れる情報はすべて取っておきたいのでrequest
オブジェクトの内容はすべて記録するようにした
Denoサーバ側の実装
簡単な例
- logger.ts
import{ ulid }from"jsr:@std/ulid"; const EXPIRE_LOGS_DAYS = 90; const logObject = async (now:Date, req:Request)=>{const ts = Math.floor(now.getTime() / 1000); return{method: req.method, url: req.url, redirect: req.redirect, bodyUsed: req.bodyUsed, ...{ts: ts }, headers: Object.fromEntries(req.headers.entries()), ...(req.body ? {body: await req.text() } : {}), }; }; const log = async (request:Request, additionalData)=>{const kv = await Deno.openKv(); const now = newDate(); const logRecord = { ...(await logObject(now, request)), ...additionalData }; returnawait kv.set(["logs", now.getFullYear(), now.getMonth() + 1, now.getDate(), ulid()], logRecord, {expireIn: 1000 * 60 * 60 * 24 * EXPIRE_LOGS_DAYS, }); }; export{ log };
キーの値は['logs', 2024, 9, 17, '01J81GWK767S8Y6THBEP1Y6V6T']
といった感じの内容となる
毎度使うパラメータや重要な指標などを含めておけるよう、log
を呼び出す際に第2引数でオプショナルなデータを追加できるようにした
- server.ts
import{ log }from"./logger.ts"; const port = 8080; const handler = async (request:Request):Promise<Response>=>{// ...何かしらの処理 log(request, {}); // ...何かしらの処理returnnewResponse('message', {status: 200}); }; console.log(`HTTP webserver running. Access it at: http://localhost:8080/`); Deno.serve({port }, handler);
これでサービスへのアクセス時の情報がKVに記録される
永続的なデータ保存
KVからGCSやS3へのExportでもよいかと思ったけど
アクセスキーを発行してDeno Deploy側に保存しないといけないのか…と思ったので
記録はKVにして(Expire指定)GoogleCloudやGitHubActionsなどからKVへアクセスして参照、ExportしてBigQueryへ取り込むのが良さそうということでそうした
Deno側のアクセスキーが必要となるが保存先はGitHubかGoogleCloudになる
logviewer
実際にBigQueryへ取り込む前にサーッと中身みたいなと思ったので簡単なCLIを書いた
出力するキーはオプションで指定可能にしたので用途によって使い分けできるはず
とりあえず自分が使う分には不自由していない
--json
オプションを用意したのでExportするときはJSON形式で出力してbq load
するだけみたいなのがサクッとできる
サンプルイメージ
{id}の箇所の値はDeno DeployのProjectのKVタブから取得する
ザーッと眺めてみるパターン
$ deno run --allow-net --allow-env --unstable-kv logviewer.ts --url=https://api.deno.com/databases/{id}/connect --prefix=logs,2024,9 --exclude=headers,value,versionstamp,bodyUsed,redirect,ua ┌───────┬──────────────────────────────────────────────────────┬────────┬─────────────────────────────────────────────────────────────┬────────────────────────────┬────────────┬──────────────┬─────────┬────────────────────────────────┐ │ (idx) │ key │ method │ url │ ts │ user │ to │ theme │ referer │ ├───────┼──────────────────────────────────────────────────────┼────────┼─────────────────────────────────────────────────────────────┼────────────────────────────┼────────────┼──────────────┼─────────┼────────────────────────────────┤ │ 0 │ [ "logs", 2024, 9, 5, "01J70480GF97GVKTQ4JTV7NERY" ] │ "GET" │ "https://kusa-image.deno.dev/swfz" │ "2024-09-05T03:39:28.000Z" │ "swfz" │ undefined │ "light" │ undefined │ │ 1 │ [ "logs", 2024, 9, 5, "01J70487J7CQ32WXK8R973SP9A" ] │ "GET" │ "https://kusa-image.deno.dev/swfz?to=2023-01-01" │ "2024-09-05T03:39:35.000Z" │ "swfz" │ "2023-01-01" │ "light" │ undefined │ │ 2 │ [ "logs", 2024, 9, 5, "01J7048CAKF7P2W9ZE4QM75B6V" ] │ "GET" │ "https://kusa-image.deno.dev/swfz?to=2023-01-01&theme=dark" │ "2024-09-05T03:39:40.000Z" │ "swfz" │ "2023-01-01" │ "dark" │ undefined │ ```
console.table
でこんなによしなに出力してくれるの知らなかったのでちょっとびっくりした
実際に眺めてみて
Kusa Imageとterminal-imageで使っている
ほぼ自分だけが利用者って感じだったがごく少数だけどGitHubのREADMEに貼り付けたりしてくれている人がいた
利用されていることがわかったので良かった、励みになる
あとはterminal-imageの方はSlackのImageProxyというUserAgentからアクセスがちょいちょいあってどこかで共有されているのか?という感じでちょっとテンションが上った
作ってすぐのころはブログに書いて宣伝もしていたので、もしかしたらもうちょい使ってもらえてたかもしれないと考えると最初から入れておけばよかったと後悔している
まとめ
- Deno Deployで動いているサービスのアクセスログをKVへ保存した
- KVの中身を簡単に見るためのDenoスクリプトを書いた
- 意外と作ったサービスを使ってくれている人もいることがわかって良かった