@ah-monica/cloudflare 0.3.0 → 0.4.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
@@ -44,7 +44,7 @@ function createClient(env: Env) {
44
44
  ブラウザ側の SDK にも同じ値を渡すと、Worker とブラウザの event が同じ release に揃う。
45
45
 
46
46
  DSN を Secrets Store に置く場合、binding の値は `get()` で非同期に読むので、client は
47
- `catch` の中で作る([制約](#制約) のとおり request ごとに作ってよい)。`get()` は secret が
47
+ request の入口で作る([制約](#制約) のとおり request ごとに作ってよい)。`get()` は secret が
48
48
  無いと例外を投げるので、`undefined` に落として何も送らない client にする。
49
49
 
50
50
  ```ts
@@ -56,14 +56,15 @@ interface Env {
56
56
 
57
57
  export default {
58
58
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
59
+ const monica = createCloudflareClient({
60
+ dsn: await env.MONICA_DSN.get().catch(() => undefined),
61
+ environment: env.MONICA_ENVIRONMENT,
62
+ release: env.MONICA_RELEASE,
63
+ });
64
+ ctx.waitUntil(monica.flush());
59
65
  try {
60
66
  return await handleRequest(request, env);
61
67
  } catch (error) {
62
- const monica = createCloudflareClient({
63
- dsn: await env.MONICA_DSN.get().catch(() => undefined),
64
- environment: env.MONICA_ENVIRONMENT,
65
- release: env.MONICA_RELEASE,
66
- });
67
68
  monica.captureExceptionInBackground(ctx, error);
68
69
  return new Response("Internal Server Error", { status: 500 });
69
70
  }
@@ -73,6 +74,8 @@ export default {
73
74
 
74
75
  ## 使い方
75
76
 
77
+ request の入口で client を作り、`ctx.waitUntil(monica.flush())` を呼ぶ([稼働確認](#稼働確認))。
78
+
76
79
  `captureException()` は capture と flush の両方を終えてから解決する Promise を返す。
77
80
  handler の応答を待たせたくない場合は `captureExceptionInBackground(ctx, ...)` で
78
81
  `ExecutionContext.waitUntil()` に載せる。
@@ -80,10 +83,12 @@ handler の応答を待たせたくない場合は `captureExceptionInBackground
80
83
  ```ts
81
84
  export default {
82
85
  async fetch(request: Request, env: Env, ctx: ExecutionContext): Promise<Response> {
86
+ const monica = createClient(env);
87
+ ctx.waitUntil(monica.flush());
83
88
  try {
84
89
  return await handleRequest(request, env);
85
90
  } catch (error) {
86
- createClient(env).captureExceptionInBackground(ctx, error, {
91
+ monica.captureExceptionInBackground(ctx, error, {
87
92
  tags: { operation: "handle-request" },
88
93
  // URL の path にも個人情報が入り得るので、安全と判断した値だけを渡す。
89
94
  request: { method: request.method, url: new URL(request.url).origin },
@@ -108,6 +113,16 @@ await monica.captureException(error, { tags: { trigger: "scheduled" } });
108
113
  Worker の外側で起きた未捕捉例外まで集めたい場合は Tail Worker も検討する。この adapter は、
109
114
  アプリケーションが捕捉して業務上の文脈を選んで送る例外を対象にする。
110
115
 
116
+ ## 稼働確認
117
+
118
+ 共通の仕組みは [ルートの README](../README.md#稼働確認) にある。
119
+
120
+ - isolate で最初の `flush()` / `captureException()` / `captureMessage()` の呼び出しが
121
+ `trigger: "start"` を 1 通送る。client を作っただけでは送らない。`dsn` と `environment` の組ごとに数える
122
+ - request の入口で `ctx.waitUntil(monica.flush())` を呼ぶ。`waitUntil` が無いと handler の終了で送信が打ち切られる
123
+ - Workers にはタイマーが無いので `trigger: "interval"` は送らない。isolate が作り直されるたびに `start` を送る
124
+ - 状態は isolate のメモリに持ち、ストレージには書かない
125
+
111
126
  ## オプション
112
127
 
113
128
  | option | 型 | default | 説明 |
package/dist/client.js CHANGED
@@ -3,6 +3,10 @@ import { SDK_VERSION } from "./version.js";
3
3
  const DEFAULT_FLUSH_TIMEOUT_MS = 2_000;
4
4
  const DEFAULT_MAX_CAUSE_DEPTH = 10;
5
5
  const DEFAULT_MAX_STACK_FRAMES = 200;
6
+ // client は request ごとに作られるので、start を送ったかは isolate に 1 つ持つ。
7
+ // 判定の前に印を付けるので、並行する request でも isolate ごとに 1 回になる
8
+ // (dsn と environment が違えば別に数える)
9
+ const startedInIsolate = new Set();
6
10
  export function createCloudflareClient(options) {
7
11
  assertPositiveInteger("flushTimeoutMs", options.flushTimeoutMs);
8
12
  assertPositiveInteger("maxCauseDepth", options.maxCauseDepth);
@@ -10,6 +14,7 @@ export function createCloudflareClient(options) {
10
14
  const flushTimeoutMs = options.flushTimeoutMs ?? DEFAULT_FLUSH_TIMEOUT_MS;
11
15
  const maxCauseDepth = options.maxCauseDepth ?? DEFAULT_MAX_CAUSE_DEPTH;
12
16
  const maxStackFrames = options.maxStackFrames ?? DEFAULT_MAX_STACK_FRAMES;
17
+ const presenceKey = `${options.dsn ?? ""}\n${options.environment}`;
13
18
  const core = createCoreClient({
14
19
  transport: createFetchTransport({
15
20
  dsn: options.dsn,
@@ -24,12 +29,28 @@ export function createCloudflareClient(options) {
24
29
  sampleRate: options.sampleRate,
25
30
  beforeSend: options.beforeSend,
26
31
  sdk: { name: "@ah-monica/cloudflare", version: SDK_VERSION },
32
+ presence: { platform: "javascript" },
27
33
  });
34
+ /**
35
+ * Workers にはタイマーが無く、global scope では fetch できない。生成時ではなく最初の
36
+ * flush / capture の呼び出し(request の中)で start を送る。送信中の分は flush() が
37
+ * 待つので、それを waitUntil に載せれば handler の後まで生きる。
38
+ */
39
+ function startOnce() {
40
+ if (startedInIsolate.has(presenceKey))
41
+ return;
42
+ startedInIsolate.add(presenceKey);
43
+ void core.checkPresence("start");
44
+ }
45
+ function flush(timeoutMs) {
46
+ startOnce();
47
+ return core.flush(timeoutMs);
48
+ }
28
49
  async function captureAndFlush(input, hint) {
29
50
  try {
51
+ startOnce();
30
52
  const eventId = await core.capture(input, hint);
31
- if (eventId === null)
32
- return null;
53
+ // 捨てた event でも、送信中の start は待つ
33
54
  await core.flush(flushTimeoutMs);
34
55
  return eventId;
35
56
  }
@@ -75,7 +96,7 @@ export function createCloudflareClient(options) {
75
96
  captureException,
76
97
  captureMessage,
77
98
  captureExceptionInBackground,
78
- flush: core.flush,
99
+ flush,
79
100
  close: core.close,
80
101
  };
81
102
  }
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.3.0";
2
+ export declare const SDK_VERSION = "0.4.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.3.0";
2
+ export const SDK_VERSION = "0.4.0";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ah-monica/cloudflare",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "Cloudflare Workers adapter for sending application errors to MONICA",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -32,7 +32,7 @@
32
32
  "directory": "cloudflare"
33
33
  },
34
34
  "dependencies": {
35
- "@ah-monica/core": "0.3.0"
35
+ "@ah-monica/core": "0.4.0"
36
36
  },
37
37
  "devDependencies": {
38
38
  "@types/bun": "^1.2.21",