opentelemetry(apps/app/src/features/opentelemetry/ の大局的メンテナンス spec)。custom-metrics/application-metrics.ts, user-counts-metrics.ts, page-counts-metrics.ts, system-metrics.ts。addXxxMetrics(): void を export する。metrics.getMeter('growi-<scope>-metrics', '1.0.0') で Meter を取得し、meter.createObservableGauge(name, { description, unit }) で gauge を作る。meter.addBatchObservableCallback(async (result) => { try { ... } catch (e) { loggerDiag.error(...) } }, [gauge, ...]) で登録。loggerFactory('growi:opentelemetry:custom-metrics:<scope>')(pino)と diag.createComponentLogger({ namespace: 'growi:custom-metrics:<scope>' })(OTel diag)の 2 つ。canHandle の衝突を避ける必要がある。anonymization/handlers/index.ts、各 handler の canHandle 実装。canHandle は副作用無しで判定のみ、handle は失敗時に null を返すか元の URL を維持する。canHandle ロジックで確認する。process.constrainedMemory() の挙動system.memory.limit の skip 条件を確定する必要がある。0 を返す(v24 でも継続)。value > 0(falsy)で判定すれば、macOS・Windows・cgroup なし Linux すべてで一貫した「skip」挙動になる。_resource への private アクセス@opentelemetry/sdk-node の TypeScript 型定義、node-sdk-resource.ts。sdk-node 0.217.0 時点)。_resource プロパティを直接書き換えることで、start() 前に Resource を差し替えられる。(sdk as any)._resource への reflective アクセスを getResource / setResource で隔離。SDK のメジャー更新時に public API が出ていないか Revalidation Trigger として確認する。| Option | Description | Strengths | Risks / Limitations | Notes |
|---|---|---|---|---|
| Custom ObservableGauge per layer | 自前で 4 Meter / 7+ gauge を実装し、@opentelemetry/host-metrics を採用しない |
完全制御、cgroup / V8 対応、追加 dep ゼロ、Meter ごとに spec 単位でテスト可能 | コード量増(〜500 行) | 採用 |
@opentelemetry/host-metrics 採用 |
system / process メトリクスをコミュニティパッケージで自動 emit | 既製、ネットワーク・CPU も追加 | cgroup 未対応、V8 ヒープ非対応、不要メトリクス強制 emit、semconv 古い | 不採用(要件 5 未充足) |
| Single Meter, all metrics | 全 7+ メトリクスを単一 Meter で束ねる | コードが小さい | 観測スコープ(business vs system)の責務が混在、テスト分離困難 | 不採用 |
| 2-stage Resource initialization | DB 非依存 → DB 初期化後 の 2 段階で Resource を構築 | 循環依存回避、DB 接続前に SDK 部分起動可能 | _resource private アクセス必要 |
採用 |
| Single-stage Resource | すべての Resource を DB 初期化後に作る | private アクセス不要 | OpenTelemetry の起動が DB 接続まで遅延、service.name などの基本属性も遅れる |
不採用 |
| Module-based anonymization | AnonymizationModule interface + 配列順評価 |
新規パス追加が局所変更で済む、handler ごとに spec | 配列順への暗黙依存 | 採用 |
| Centralized anonymization (switch / regex map) | 1 ファイルで if/else または map で振り分け | フローが見やすい | 拡張ごとに 1 ファイルが肥大化、spec が結合 | 不採用 |
growi.configs info gauge ラベルgrowi.* / system.* / process.* ObservableGaugehttp.target 等 incubating semconvsystem.memory.limit と system.host.memory.total を別メトリクスに分離system.memory.limit を cgroup → fallback で os.totalmem にする。system.memory.limit と system.host.memory.total を別メトリクスにする。system.memory.limit は cgroup limit が取れたときのみ観測、system.host.memory.total は常に観測。system.memory.limit が emit されない」を明記する。attachment.type 等)は growi.configs のラベルへ統合wiki_type, external_auth_types, attachment_type 等)を Resource Attribute に載せるか、専用 info-gauge のラベルに載せるかという選択。growi.configs ObservableGauge(値は常に 1)のラベルへ統合(Prometheus info パターン)。growi.configs のラベル数は機能追加と共に増える。各値が固定 enum 由来のためカーディナリティ影響は限定的。'' フォールバックで統一する(undefined ラベル attribute が emit されないことを利用しない)。growi.deployment.type は OTel 標準 deployment.environment.name に寄せないdeployment.environment.name("production"/"staging" 等)があるが、GROWI の growi.deployment.type("docker"/"k8s"/"growi-docker-compose" 等)はランタイム形態を表し、環境分類とは別概念。growi.deployment.type のまま据え置く(Resource Attribute)。deployment.environment.name を追加導入する。growi-system-metrics で system / process / V8 を束ねるgrowi-system-metrics 単一 Meter で束ねる。system.*/process.* の prefix で十分名前空間が分離できる。Meter を分けると addBatchObservableCallback の呼び出しと spec も二重になり管理コスト増。http.target 経由でトレースに残るリスクを下げたいが、auto-instrumentation の挙動を完全に制御することはできない。otel:anonymizeInBestEffort が true のときのみ startIncomingSpanHook を注入。AnonymizationModule interface に従い、canHandle で対象選別 / handle で attribute を返す。Object.assign でマージ。semconv.ts にコピー@opentelemetry/semantic-conventions/incubating は minor リリースで破壊的変更を含む可能性があるとアナウンスされている。service.instance.id, http.target をローカル定数として保持し、ランタイムコードからは local file のみを import する。PeriodicExportingMetricReader の exportIntervalMillis を 300000(5 分)に設定。getNodeAutoInstrumentations() を全有効化すると pino log と fs operation がトレース化される。@opentelemetry/instrumentation-pino と @opentelemetry/instrumentation-fs を enabled: false で明示的に無効化。service.instance.id は config 値の passthrough、自動生成しないservice.instance.id を UUID 等で自動生成する resource detector があるが、GROWI ではどう扱うか。otel:serviceInstanceId(env: OPENTELEMETRY_SERVICE_INSTANCE_ID)を優先、フォールバックで app:serviceInstanceId(DB 由来)を使用。両方 undefined の場合は emit しない。process.constrainedMemory() のプラットフォーム依存: Linux cgroup v1/v2 のみサポートで、macOS/Windows では常に 0 を返す。Mitigation: 0 のときは system.memory.limit を観測しない挙動が、そのまま非対応プラットフォームの振る舞いと一致するため追加対策不要。growi.configs ラベル経由)。_resource プロパティが SDK メジャー更新で消滅する可能性。Mitigation: Revalidation Trigger として SDK バージョンアップ時にチェック。public API が出たら即座に切り替え。anonymization/handlers/ の更新有無を確認する文化を維持。handlers/index.ts の anonymizationModules 配列が単一の真実ソース。service.instance.id / http.target が stable 化されているのに local 定数を放置すると、最新 OTLP 受信側との互換性が崩れる可能性。Mitigation: @opentelemetry/semantic-conventions メジャー / minor 更新時に Revalidation Trigger で見直す。apps/app/src/features/opentelemetry/server/custom-metrics/application-metrics.ts — ObservableGauge + addBatchObservableCallback のリファレンス実装。