@ah-monica/cloudflare 0.2.1 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,8 +1,20 @@
1
1
  # @ah-monica/cloudflare
2
2
 
3
- Cloudflare WorkersからMONICAへ、捕捉した例外をbest effortで送るadapter。
4
- Node.js APIへ依存せず、Module Workerの`ExecutionContext.waitUntil()`へ渡せる
5
- Promiseを提供する。
3
+ Cloudflare Workers から MONICA へ、捕捉した例外を送る adapter。
4
+ Node.js API に依存しないので `nodejs_compat` フラグは要らない。
5
+
6
+ 共通の使い方・オプション・制約は [ルートの README](../README.md) にある。
7
+
8
+ ## インストール
9
+
10
+ ```bash
11
+ npm install @ah-monica/cloudflare
12
+ ```
13
+
14
+ ## 初期化
15
+
16
+ DSN には secret key(`msk_...`)を使い、Cloudflare の secret に保存する(`vars` には置かない)。
17
+ staging と production では project・鍵・`environment` を分ける。
6
18
 
7
19
  ```ts
8
20
  import { createCloudflareClient } from "@ah-monica/cloudflare";
@@ -13,26 +25,67 @@ interface Env {
13
25
  MONICA_RELEASE?: string;
14
26
  }
15
27
 
28
+ function createClient(env: Env) {
29
+ return createCloudflareClient({
30
+ dsn: env.MONICA_DSN,
31
+ environment: env.MONICA_ENVIRONMENT,
32
+ release: env.MONICA_RELEASE,
33
+ beforeSend(item) {
34
+ // どの値が個人情報かはアプリケーション固有。送ってよい値だけを残す。
35
+ delete item.user;
36
+ if (item.request) delete item.request.headers;
37
+ return item;
38
+ },
39
+ });
40
+ }
41
+ ```
42
+
43
+ `MONICA_RELEASE` には deploy する git の commit sha を入れる(例: `wrangler deploy --var MONICA_RELEASE:$(git rev-parse HEAD)`)。
44
+ ブラウザ側の SDK にも同じ値を渡すと、Worker とブラウザの event が同じ release に揃う。
45
+
46
+ DSN を Secrets Store に置く場合、binding の値は `get()` で非同期に読むので、client は
47
+ `catch` の中で作る([制約](#制約) のとおり request ごとに作ってよい)。`get()` は secret が
48
+ 無いと例外を投げるので、`undefined` に落として何も送らない client にする。
49
+
50
+ ```ts
51
+ interface Env {
52
+ MONICA_DSN: SecretsStoreSecret;
53
+ MONICA_ENVIRONMENT: string;
54
+ MONICA_RELEASE?: string;
55
+ }
56
+
16
57
  export default {
17
58
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
18
59
  try {
19
60
  return await handleRequest(request, env);
20
61
  } catch (error) {
21
62
  const monica = createCloudflareClient({
22
- dsn: env.MONICA_DSN,
63
+ dsn: await env.MONICA_DSN.get().catch(() => undefined),
23
64
  environment: env.MONICA_ENVIRONMENT,
24
65
  release: env.MONICA_RELEASE,
25
- beforeSend(item) {
26
- // PIIの定義はアプリケーション固有。送信可能な値だけを残す。
27
- delete item.user;
28
- if (item.request) delete item.request.headers;
29
- return item;
30
- },
31
66
  });
67
+ monica.captureExceptionInBackground(ctx, error);
68
+ return new Response("Internal Server Error", { status: 500 });
69
+ }
70
+ },
71
+ };
72
+ ```
73
+
74
+ ## 使い方
32
75
 
33
- monica.captureExceptionInBackground(ctx, error, {
76
+ `captureException()` は capture と flush の両方を終えてから解決する Promise を返す。
77
+ handler の応答を待たせたくない場合は `captureExceptionInBackground(ctx, ...)` で
78
+ `ExecutionContext.waitUntil()` に載せる。
79
+
80
+ ```ts
81
+ export default {
82
+ async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
83
+ try {
84
+ return await handleRequest(request, env);
85
+ } catch (error) {
86
+ createClient(env).captureExceptionInBackground(ctx, error, {
34
87
  tags: { operation: "handle-request" },
35
- // URL pathにもPIIが入り得るため、利用側で安全と判断した値だけを渡す。
88
+ // URL の path にも個人情報が入り得るので、安全と判断した値だけを渡す。
36
89
  request: { method: request.method, url: new URL(request.url).origin },
37
90
  });
38
91
  return new Response("Internal Server Error", { status: 500 });
@@ -41,53 +94,50 @@ export default {
41
94
  };
42
95
  ```
43
96
 
44
- `captureException()`はcaptureとflushを完了するPromiseを返す。レスポンスを待たせたく
45
- ないHTTP handlerでは`captureExceptionInBackground(ctx, ...)`を使う。CronやQueueの
46
- handlerで送信完了を処理結果に含めたい場合は、直接`await`してよい。
97
+ Cron や Queue の handler のように送信完了を処理結果に含めたい場合は、直接 `await` する。
47
98
 
48
99
  ```ts
100
+ const monica = createClient(env);
49
101
  await monica.captureException(error, { tags: { trigger: "scheduled" } });
50
102
  ```
51
103
 
52
- ## 送信が拒否されたとき
104
+ `captureMessage()` も同じく capture と flush を終えてから解決するので、`flush()` を
105
+ 別途呼ぶ必要は無い。scope(`setUser` / `addBreadcrumb` / `withScope`)は持たず、
106
+ 文脈は capture の第 2 引数で渡す。
53
107
 
54
- `422`(envelope schema 不正)のとき、既定で`console.warn`へ1行出す(Workersのログに出る)。
108
+ Worker の外側で起きた未捕捉例外まで集めたい場合は Tail Worker も検討する。この adapter は、
109
+ アプリケーションが捕捉して業務上の文脈を選んで送る例外を対象にする。
55
110
 
56
- ```
57
- monica: ingest rejected the envelope with 422 (invalid_envelope): 1 issue(s); $.items[0].request.method: Invalid type: Expected string
58
- ```
59
-
60
- 出力先を変える場合は`onDiagnostic`を渡す。`null`を渡すと何も出さない。
61
- `flush()`の戻り値の`status` / `issues` / `error`からも取得できる。
111
+ ## オプション
62
112
 
63
- `401`(鍵の失効・種別違い)を受けると、そのclientからは以後1回もPOSTしない。
64
- `flush()`の戻り値の`stopped: true`と次の1行で分かる。
113
+ | option | 型 | default | 説明 |
114
+ | --- | --- | --- | --- |
115
+ | `dsn` | `string \| null` | なし | `https://msk_...@<ingest-host>`。未指定・空文字なら何も送らない |
116
+ | `environment` | `string` | 必須 | 1〜128 文字 |
117
+ | `release` | `string` | なし | item の `release` に載る |
118
+ | `sampleRate` | `number` | `1` | 0〜1 |
119
+ | `requestTimeoutMs` | `number` | `2000` | 1 回の HTTP request の上限 |
120
+ | `flushTimeoutMs` | `number` | `2000` | `captureException` / `captureMessage` が内部で待つ flush の上限 |
121
+ | `maxRetries` | `number` | `0` | `429` / `5xx` / network 障害の再送回数 |
122
+ | `maxCauseDepth` | `number` | `10` | 辿る `cause` の段数 |
123
+ | `maxStackFrames` | `number` | `200` | 送る stack frame 数 |
124
+ | `onDiagnostic` | `(diagnostic) => void \| null` | `console.warn` に 1 行 | 拒否されたときの診断の受け取り先。`null` で無効 |
125
+ | `beforeSend` | `(item, hint) => item \| null \| Promise<...>` | なし | `null` を返すと破棄 |
126
+ | `fetch` | `typeof fetch` | `globalThis.fetch` | 送信に使う fetch |
65
127
 
66
- ```
67
- monica: ingest rejected the envelope with 401 (invalid_key); no further envelopes will be sent
68
- ```
69
-
70
- `413`(body上限超過)を受けると`items`を半分に割って送り直す。SDKは契約(gzip後
71
- 1 MiB)を下回るサイズしか送らないので、返るのは経路上のproxyやgatewayが契約より
72
- 低い上限を持っているとき。分割して受理されても`flush()`の戻り値の`status`は`413`に
73
- なり、次の1行をtransportにつき1回だけ出す。
74
-
75
- ```
76
- monica: ingest rejected the envelope with 413 (unknown); splitting and resending. A size limit on the path may be below the 1 MiB (gzip) contract
77
- ```
128
+ ## 送信結果と診断
78
129
 
79
- ## PII方針
130
+ 拒否されたときは既定で `console.warn` に 1 行出る(Workers のログに出る。`422` / `401` / `413`)。
131
+ `flush()` の戻り値の `status` / `issues` / `error` / `stopped` からも取れる。
132
+ 詳しくは [TROUBLESHOOTING.md](../TROUBLESHOOTING.md)。
80
133
 
81
- SDKは、例外message、stack、request、contextがPIIかどうかを推測せず、自動除去も
82
- しない。送信値の選択と`beforeSend`での除去は利用アプリケーションの責任。
83
- MONICAサーバの既知credentialフィルターは防御層であり、任意のPIIが除去される保証
84
- ではない。
134
+ ## 制約
85
135
 
86
- ## API keyと環境
136
+ - `captureExceptionInBackground` を使わず、`waitUntil()` にも載せずに handler を返すと、
137
+ 送信は途中で打ち切られる。
138
+ - client は request ごとに作ってよい。`401` で止まるのはその client だけなので、
139
+ 鍵が失効しても次の request で再び 1 回 POST する。
87
140
 
88
- `dsn`にはCloudflare secretへ保存したsecret project key(`msk_...`)を使う。
89
- stagingとproductionではproject、key、`environment`を分離し、package versionは共通で
90
- よい。secretを`vars`、ソースコード、ログへ置かない。
141
+ ## ライセンス
91
142
 
92
- 未捕捉例外をWorkerの外側から網羅的に収集する用途にはTail Workerも検討する。この
93
- adapterは、アプリケーションが捕捉し、業務contextを選んで送る例外を対象にする。
143
+ [Apache-2.0](LICENSE)
package/dist/types.d.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  import type { BeforeSend, FetchLike, FlushResult, MonicaBreadcrumb, MonicaLevel, MonicaRequest, MonicaUser, TransportDiagnosticHandler } from "@ah-monica/core";
2
2
  export interface CloudflareClientOptions {
3
- dsn: string;
3
+ /** 未指定・空文字・空白だけなら何も送らない */
4
+ dsn?: string | null;
4
5
  environment: string;
5
6
  release?: string;
6
7
  sampleRate?: number;
package/dist/version.d.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  /** package.json の version と同じ値。bun run version X.Y.Z が書き換える */
2
- export declare const SDK_VERSION = "0.2.1";
2
+ export declare const SDK_VERSION = "0.3.0";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  /** package.json の version と同じ値。bun run version X.Y.Z が書き換える */
2
- export const SDK_VERSION = "0.2.1";
2
+ export const SDK_VERSION = "0.3.0";
package/package.json CHANGED
@@ -1,11 +1,13 @@
1
1
  {
2
2
  "name": "@ah-monica/cloudflare",
3
- "version": "0.2.1",
3
+ "version": "0.3.0",
4
4
  "description": "Cloudflare Workers adapter for sending application errors to MONICA",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
7
7
  "sideEffects": false,
8
8
  "files": ["dist", "README.md"],
9
+ "main": "./dist/index.js",
10
+ "types": "./dist/index.d.ts",
9
11
  "exports": {
10
12
  ".": {
11
13
  "types": "./dist/index.d.ts",
@@ -30,7 +32,7 @@
30
32
  "directory": "cloudflare"
31
33
  },
32
34
  "dependencies": {
33
- "@ah-monica/core": "0.2.1"
35
+ "@ah-monica/core": "0.3.0"
34
36
  },
35
37
  "devDependencies": {
36
38
  "@types/bun": "^1.2.21",