@exabugs/dynamodb-client 1.10.0 → 1.11.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/CHANGELOG.md +6 -0
- package/README.md +1 -0
- package/dist/notify/channels/email.d.ts +32 -0
- package/dist/notify/channels/email.d.ts.map +1 -0
- package/dist/notify/channels/email.js +67 -0
- package/dist/notify/channels/email.js.map +1 -0
- package/dist/notify/channels/slack.d.ts +12 -0
- package/dist/notify/channels/slack.d.ts.map +1 -0
- package/dist/notify/channels/slack.js +46 -0
- package/dist/notify/channels/slack.js.map +1 -0
- package/dist/notify/format.d.ts +7 -0
- package/dist/notify/format.d.ts.map +1 -0
- package/dist/notify/format.js +29 -0
- package/dist/notify/format.js.map +1 -0
- package/dist/notify/handler.d.ts +42 -0
- package/dist/notify/handler.d.ts.map +1 -0
- package/dist/notify/handler.js +175 -0
- package/dist/notify/handler.js.map +1 -0
- package/dist/notify/index.d.ts +18 -0
- package/dist/notify/index.d.ts.map +1 -0
- package/dist/notify/index.js +14 -0
- package/dist/notify/index.js.map +1 -0
- package/dist/notify/logger.d.ts +6 -0
- package/dist/notify/logger.d.ts.map +1 -0
- package/dist/notify/logger.js +11 -0
- package/dist/notify/logger.js.map +1 -0
- package/dist/notify/normalize.d.ts +11 -0
- package/dist/notify/normalize.d.ts.map +1 -0
- package/dist/notify/normalize.js +245 -0
- package/dist/notify/normalize.js.map +1 -0
- package/dist/notify/policy.d.ts +15 -0
- package/dist/notify/policy.d.ts.map +1 -0
- package/dist/notify/policy.js +36 -0
- package/dist/notify/policy.js.map +1 -0
- package/dist/notify/store.d.ts +22 -0
- package/dist/notify/store.d.ts.map +1 -0
- package/dist/notify/store.js +119 -0
- package/dist/notify/store.js.map +1 -0
- package/dist/notify/types.d.ts +63 -0
- package/dist/notify/types.d.ts.map +1 -0
- package/dist/notify/types.js +8 -0
- package/dist/notify/types.js.map +1 -0
- package/dist/server/handler.cjs +3 -3
- package/dist/server/media-handler.cjs +2 -2
- package/dist/server/media-process-handler-dlq.cjs +2 -2
- package/dist/server/media-process-handler.cjs +2 -2
- package/dist/sharp-layer.zip +0 -0
- package/docs/NOTIFY.md +103 -0
- package/package.json +8 -3
package/docs/NOTIFY.md
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# 運用通知(`@exabugs/dynamodb-client/notify`)
|
|
2
|
+
|
|
3
|
+
ログ(`warn` 以上)・CloudWatch アラーム・直接呼び出しを、管理者へ通知するための**部品**。配線(購読フィルタ・Lambda・Terraform・秘密の置き場)は、各アプリが行う。既存の挙動は変えない(サブパス `./notify` の追加のみ)。
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
ログ(購読フィルタ) ─┐
|
|
7
|
+
アラーム(EventBridge) ─┼→ 通知ハンドラ → Notice に正規化 → 重複抑制 → 流量制限 → Slack / メール
|
|
8
|
+
直接呼び出し ────────┘
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## ログの仕様
|
|
12
|
+
|
|
13
|
+
JSON 1 行。`level`(`debug` / `info` / `warn` / `error` / `fatal`)、`message`、任意で `event`(機械可読の種別)、`resource` / `target`(対象)、`action` / `actions`(対策の案内)。**`warn` 以上は通知の対象**にする。
|
|
14
|
+
|
|
15
|
+
- 既存の logger(`createLogger`)は変えていない。`fatal` は `logFatal(message, meta)` で出す
|
|
16
|
+
- Lambda の実行基盤の失敗(`Task timed out` など)は、`fatal`(`LAMBDA_FAILURE`)として扱う
|
|
17
|
+
- ログ行は 3 種類を読む。どれも、**アプリの JSON の `level` と各フィールドを優先**する(Lambda の外側の `level` は、`console.log` で出した warn が `INFO` になる)
|
|
18
|
+
1. アプリの JSON 1 行
|
|
19
|
+
2. Lambda のテキスト形式: `時刻<TAB>RequestId<TAB>INFO<TAB>{JSON}`
|
|
20
|
+
3. Lambda の JSON 形式(`log_format = JSON`): `{"level":"INFO","requestId":…,"message":"{アプリの JSON 文字列}"}`
|
|
21
|
+
- JSON でない行(`console.error` の文字列など)は、外側の `level` が `WARN` 以上のときだけ、`LOG_WARN` / `LOG_ERROR` として通知する
|
|
22
|
+
- タイムアウト・クラッシュは、テキスト形式の行(`Task timed out after …` / `Runtime exited …` / `REPORT … Status: timeout`)と、JSON 形式のシステムログ(`platform.report` / `platform.runtimeDone` の `status`。Lambda のシステムログレベルを `INFO` 以上にしたときだけ出る)の両方を読む
|
|
23
|
+
- Lambda の実行基盤の失敗(`Invoke Error` / `Unhandled Promise Rejection` / `Uncaught Exception` / `Task timed out` / `Runtime exited`)は `fatal`(`LAMBDA_FAILURE`)。stack は持ち出さない
|
|
24
|
+
|
|
25
|
+
## Notice
|
|
26
|
+
|
|
27
|
+
外部へ送るのは、許可したフィールドだけ(`level` / `event` / `message` / `app` / `env` / `target` / `action` / `occurredAt` / `suppressed`)。文字数を制限し、制御文字を除く。ログ全体は転送しない。
|
|
28
|
+
|
|
29
|
+
## 重複抑制と流量制限
|
|
30
|
+
|
|
31
|
+
指紋は `app#env#level#event#target`(level を含めるので、warn の窓が後の error を抑制しない)。`event` の無いログは、メッセージの先頭 60 文字を `target` にして、別々のエラーを 1 つにしない。専用の DynamoDB テーブル(パーティションキー `pk` 文字列、TTL 属性 `ttl`)に、条件付き更新で記録する。
|
|
32
|
+
|
|
33
|
+
| level | 窓 |
|
|
34
|
+
| -------------------------------- | -------------- |
|
|
35
|
+
| `warn` | 3600 秒に 1 回 |
|
|
36
|
+
| `error` | 600 秒に 1 回 |
|
|
37
|
+
| `fatal` / `ok`(アラームの復旧) | 抑制しない |
|
|
38
|
+
|
|
39
|
+
- 同じバッチ(1 回の購読の呼び出し)の同じ指紋は 1 通にまとめ、件数(`suppressed`)を付ける。確保・計数もまとめて 1 回
|
|
40
|
+
- 抑制した件数は、窓が明けた次の通知に付く(再発しない場合は報告されない)
|
|
41
|
+
- アラームの状態変化(`ALARM:` で始まる `event`)は、変化したときにしか届かないので、抑制しない(ALARM → OK → ALARM を、すべて通知する)
|
|
42
|
+
- 確保 → 送信 → **どのチャネルにも届かなかった通知は確保を解放する**(次の発生で、窓を待たずに通知できる。解放は自分の確保だけ。失敗した 1 件も件数に戻す)。例外で終わったときも同じ
|
|
43
|
+
- `failOnAnyChannelError` で例外にしても、届いた通知の確保は残る(失敗したチャネルへは、再送や失敗先からの再実行でも届かない)。再試行を前提にせず、失敗はハンドラのログとアラームで知る
|
|
44
|
+
- 宛先(管理者)の取得に失敗したら、そのチャネルの失敗として扱い、他のチャネルは送る
|
|
45
|
+
- `event` の無いログは、メッセージの先頭 60 文字を指紋に使う。ID や数値を含むメッセージは毎回違う指紋になり、重複抑制が効かない(流量制限が頼り)。`event` を付けること
|
|
46
|
+
- 全チャネルが失敗したら例外にする(Lambda の失敗先に流せる)。一部のチャネルだけの失敗は、`failedChannels` に入り、ハンドラ自身のログに出る(`failOnAnyChannelError: true` で、例外にもできる)
|
|
47
|
+
- 流量制限: チャネルごとに、600 秒で 20 件まで。超えた最初のバッチにだけ、「省略」の 1 通を出す。省かれて、どのチャネルにも届かなかった通知の確保は解放する
|
|
48
|
+
- `NotifyResult.sent` は、1 つ以上のチャネルに届いた通知の数(省略の通知は含まない)。`throttled` は、流量制限で省いた延べ数
|
|
49
|
+
|
|
50
|
+
## チャネル
|
|
51
|
+
|
|
52
|
+
- `createSlackChannel({ getWebhookUrl })` — チャンネル宛(Incoming Webhook)。URL が `https://hooks.slack.com/`(`urlPrefix` で変更可)で始まらなければ送らない。URL はエラーにもログにも出さない
|
|
53
|
+
- `createEmailChannel({ from, send })` — 宛先は **Bcc だけ**に 50 件ずつまとめる(To に送信元を入れない。受信できないアドレスだとバウンスする)。`sesSender({ client, SendEmailCommand })` は、アプリが `@aws-sdk/client-sesv2` を静的に import して渡す(バンドラが依存を解決でき、Lambda のバンドルに含まれる)。他のリージョンの SES も指定できる。失敗の原因(SES のエラー名・メッセージ)はエラーに残り、宛先は出さない
|
|
54
|
+
- `Channel` インターフェースを実装すれば、他の経路も足せる
|
|
55
|
+
- 宛先(管理者)は `AdminDirectory.listEmails()`。スキーマは強制しない。結果は 300 秒キャッシュする
|
|
56
|
+
|
|
57
|
+
## 使い方
|
|
58
|
+
|
|
59
|
+
```ts
|
|
60
|
+
import { DynamoDBClient } from '@aws-sdk/client-dynamodb';
|
|
61
|
+
import { SESv2Client, SendEmailCommand } from '@aws-sdk/client-sesv2';
|
|
62
|
+
import { DynamoDBDocumentClient } from '@aws-sdk/lib-dynamodb';
|
|
63
|
+
|
|
64
|
+
import {
|
|
65
|
+
DynamoNoticeStore,
|
|
66
|
+
createEmailChannel,
|
|
67
|
+
createNotifyHandler,
|
|
68
|
+
createSlackChannel,
|
|
69
|
+
sesSender,
|
|
70
|
+
} from '@exabugs/dynamodb-client/notify';
|
|
71
|
+
|
|
72
|
+
export const handler = createNotifyHandler({
|
|
73
|
+
app: 'myapp',
|
|
74
|
+
env: process.env.ENV_NAME ?? 'dev',
|
|
75
|
+
store: new DynamoNoticeStore(
|
|
76
|
+
DynamoDBDocumentClient.from(new DynamoDBClient({})),
|
|
77
|
+
process.env.NOTICE_TABLE!
|
|
78
|
+
),
|
|
79
|
+
admins: {
|
|
80
|
+
listEmails: async () => [
|
|
81
|
+
/* 管理者のメール */
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
channels: [
|
|
85
|
+
createSlackChannel({ getWebhookUrl: async () => /* SSM などから */ '' }),
|
|
86
|
+
createEmailChannel({
|
|
87
|
+
from: 'noreply@example.com',
|
|
88
|
+
send: sesSender({ client: new SESv2Client({ region: 'us-east-1' }), SendEmailCommand }),
|
|
89
|
+
}),
|
|
90
|
+
],
|
|
91
|
+
});
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## 配線の注意(各アプリ)
|
|
95
|
+
|
|
96
|
+
- **通知ハンドラのロググループは購読しない**(自分のログで無限ループになる)。失敗は Lambda の失敗先(SNS など)→ 固定の運用者のアドレスへ届ける
|
|
97
|
+
- 購読フィルタは**広めの語句**にする。ハンドラが厳密に判定する(通知しない行は捨てる)。`log_format`(テキスト / JSON)で `level` の位置が変わり、`console.log` の warn は外側が `INFO` になるので、`level` の JSON フィルタは使わない:
|
|
98
|
+
`?warn ?error ?fatal ?WARN ?ERROR ?FATAL ?"Task timed out" ?"Runtime exited" ?Unhandled ?Uncaught ?"Invoke Error" ?"Status: timeout" ?"Status: error" ?timeout`
|
|
99
|
+
- 一部のチャネルだけの失敗(Webhook の失効など)に気づくには、ハンドラ自身のログ(`通知の送信に失敗しました`)のメトリクスフィルタ → アラーム → SNS(固定の運用者のアドレス)を配線する。または `failOnAnyChannelError: true` にして、Lambda の失敗先に流す
|
|
100
|
+
- 訓練は `dryRun: true`(送らず、store にも触れない。宛先つきの計画が `plan` に入る)
|
|
101
|
+
- アラームは、ログを出さないもの(Lambda の Errors / Throttles、DynamoDB のスロットル、WAF など)に使う。ログと同じ事象(SPARSE_SCAN など)をアラームでも通知すると二重になる
|
|
102
|
+
- Lambda の非同期の再試行は 0 にする(重複を避ける。失敗は失敗先で受ける)
|
|
103
|
+
- SES の送信元ドメインは、検証と本番アクセスが済んだリージョンのものを使う
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@exabugs/dynamodb-client",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.11.0",
|
|
4
4
|
"description": "DynamoDB Single-Table Client SDK with MongoDB-like API, Shadow Records, and Lambda implementation for serverless applications",
|
|
5
5
|
"author": "exabugs",
|
|
6
6
|
"license": "MIT",
|
|
@@ -41,7 +41,8 @@
|
|
|
41
41
|
"docs/SCAN_WARNING.md",
|
|
42
42
|
"README.md",
|
|
43
43
|
"LICENSE",
|
|
44
|
-
"CHANGELOG.md"
|
|
44
|
+
"CHANGELOG.md",
|
|
45
|
+
"docs/NOTIFY.md"
|
|
45
46
|
],
|
|
46
47
|
"exports": {
|
|
47
48
|
".": {
|
|
@@ -117,7 +118,11 @@
|
|
|
117
118
|
"types": "./dist/server/media/types.d.ts"
|
|
118
119
|
},
|
|
119
120
|
"./terraform": "./terraform/main.tf",
|
|
120
|
-
"./terraform/media": "./terraform/media/main.tf"
|
|
121
|
+
"./terraform/media": "./terraform/media/main.tf",
|
|
122
|
+
"./notify": {
|
|
123
|
+
"import": "./dist/notify/index.js",
|
|
124
|
+
"types": "./dist/notify/index.d.ts"
|
|
125
|
+
}
|
|
121
126
|
},
|
|
122
127
|
"peerDependencies": {
|
|
123
128
|
"react-admin": "^5.0.0"
|