requirements.md 14 KB

Requirements Document

Introduction

GROWI の OpenTelemetry 統合 (apps/app/src/features/opentelemetry/) を メンテナンスするための大局的な仕様。SDK ライフサイクル、Resource Attribute、Custom Metric、HTTP Anonymization の 4 レイヤがそれぞれ「何を担い、何を担わないか」を明文化し、新規メトリクスや anonymization handler の追加、SDK のバージョンアップ、設定キーの追加・改名といった将来のメンテナンス時に、本 spec を 1 か所の参照点として運用できる状態を目標とする。

本 spec は新規実装 spec ではなく、既に実装・稼働している features/opentelemetry/現状の責務境界をスナップショットとして固定化する 性格を持つ。個別機能の追加・変更は原則として本 spec の Boundary Commitments の範囲内で行われ、境界をまたぐ変更が必要なときは Revalidation Triggers として再評価される。

Boundary Context

  • In scope:
    • NodeSDK の起動・有効化制御・Resource 2 段階初期化 (node-sdk.ts, node-sdk-configuration.ts, node-sdk-resource.ts)。
    • Diag Logger の pino アダプタ (logger.ts)。
    • SemConv の不安定 attribute のローカルコピー (semconv.ts)。
    • identity 専用の Resource Attribute 供給 (custom-resource-attributes/)。
    • Custom Metric の emit と合成 (custom-metrics/、合計 4 モジュール: application / user-counts / page-counts / system)。
    • HTTP リクエストの best-effort anonymization (anonymization/、4 個の handler + utility)。
    • otel:* 設定キー 4 種の利用ポリシー。
  • Out of scope:
    • growiInfoService / configManager / loggerFactory などの上流サービスの設計や API 変更。
    • 既存メトリクス(growi.users.total / growi.users.active / growi.pages.total / growi.configs / system.* / process.*)の名称変更や再構成。
    • OpenTelemetry のログシグナル統合(log signal は現状未使用)。
    • クライアント側(ブラウザ)からの telemetry 出力。
    • Trace span への独自 attribute 追加(http.target 以外)。
    • OTLP Exporter の wire 仕様や受信側ツールチェイン。
  • Adjacent expectations:
    • 上流: growiInfoService.getGrowiInfo({ includeAttachmentInfo, includeUserCountInfo, includePageCountInfo }) の API シグネチャ・返り値型が維持されることに依存する。破壊的変更があった場合は Revalidation Triggers として custom-metrics/ および custom-resource-attributes/application-resource-attributes.ts を再評価する。
    • 上流: configManager.getConfig('otel:*') の 4 キーが現状の意味で参照可能であることに依存する。
    • 下流: OpenTelemetry Collector およびその先のダッシュボード/アラート群が本 spec の Metric Schema / Resource Attribute 表に整合した参照クエリを保持していること。変更時は PR 説明にて運用者へ通知する。

Requirements

Requirement 1: SDK ライフサイクルと有効化制御

Objective: GROWI 運用者として、OpenTelemetry SDK を環境変数 1 つで有効/無効を切り替えられ、無効時にはランタイムオーバーヘッドや誤った OTLP 接続試行が発生しないことを保証したい。

Acceptance Criteria

  1. The GROWI server shall otel:enabled 設定が false のとき、NodeSDK インスタンスを生成せず Resource Attribute 取得や Custom Metric の登録も行わない。
  2. The GROWI server shall otel:enabled 設定値と OTEL_SDK_DISABLED 環境変数の値が矛盾している場合、OTEL_SDK_DISABLED を上書きして整合性を取り、その旨を warn ログとして出力する。
  3. The GROWI server shall NodeSDK 初期化を「SDK 構築・Resource 静的部分のセット」「DB 初期化後の Resource 追加注入」「start() 呼び出しと Custom Metric 登録」の 3 段階で行う。各段階は otel:enabled を再確認した上で実行する。
  4. The GROWI server shall 同一プロセス内で initInstrumentation() を二重に呼ばれても、SDK インスタンスを重複生成しない(再初期化は警告を出してスキップする)。

Requirement 2: identity 専用 Resource Attribute

Objective: 受信側インフラ管理者として、GROWI が emit する Resource Attribute がテレメトリ発生元エンティティの identity 情報のみであり、測定値や設定値が紛れ込まないことを保証したい。

Acceptance Criteria

  1. The GROWI server shall Resource Attribute として以下のみを emit する: service.name, service.version, service.instance.id(取得できた場合), os.type, os.platform, os.arch, growi.service.type, growi.deployment.type
  2. The GROWI server shall 測定値(メモリ使用量・カウント等)および GROWI のサブシステム設定値(attachment / auth provider 種別等)を Resource Attribute として emit しない。
  3. The GROWI server shall Resource Attribute の取得を 2 段階に分け、1 段階目は DB 非依存(service.name / version / OS info)、2 段階目は DB 初期化後(service.instance.id / growi.service.type / growi.deployment.type)に行う。
  4. If Resource Attribute 取得処理で例外が発生した場合, the GROWI server shall 当該段階の Resource Attribute を空オブジェクトで返し、SDK 起動自体は継続する。

Requirement 3: GROWI 設定情報の info-gauge ラベル統合

Objective: 運用者として、GROWI インスタンスの設定情報(site URL、wiki type、外部認証種別、添付ストレージ種別)を 1 つの info-gauge メトリクスから一覧できることを保証したい。

Acceptance Criteria

  1. The GROWI server shall growi.configs という ObservableGauge を Prometheus info パターン(値は常に 1、情報はラベルに格納)で emit する。
  2. The GROWI server shall growi.configs に以下のラベルを付与する: site_url, site_url_hashed, wiki_type, external_auth_types, attachment_type
  3. If otel:isAppSiteUrlHashedtrue, the GROWI server shall site_url[hashed] リテラルにし、site_url_hashed に SHA-256 ハッシュ値を入れる。false のときは site_url に生 URL を入れ site_url_hashedundefined(emit されない)。
  4. If external_auth_types / attachment_type の値が growiInfoService から取得できない場合, the GROWI server shall 当該ラベルを空文字 '' でフォールバックする。
  5. The GROWI server shall ラベル名を snake_case で統一する。

Requirement 4: 業務カウントメトリクス

Objective: 運用者として、GROWI 上の主要エンティティ(ユーザー、ページ)の総数とアクティビティ指標を継続的に観測したい。

Acceptance Criteria

  1. The GROWI server shall growi.users.total メトリクスを総ユーザー数で観測する(単位 users)。
  2. The GROWI server shall growi.users.active メトリクスをアクティブユーザー数で観測する(単位 users)。
  3. The GROWI server shall growi.pages.total メトリクスを総ページ数で観測する(単位 pages)。
  4. If growiInfoService からのカウント値取得が失敗した場合, the GROWI server shall 0 で観測するか、当該収集サイクルでの観測をスキップし、diag ロガーに error を記録する。

Requirement 5: コンテナ運用に対応したメモリ系メトリクス

Objective: コンテナ環境(Docker / Kubernetes)で GROWI を運用する管理者として、「コンテナに割り当てられたメモリ上限(cgroup limit)」「ホスト物理メモリ総量」「プロセス RSS」「V8 ヒープの使用/確保/外部メモリ」を別々のメトリクスとして観測できることを保証したい。

Acceptance Criteria

  1. The GROWI server shall system.memory.limitprocess.constrainedMemory() の戻り値(>0 のとき)で観測する。値が 0 または falsy のときは当該メトリクスのみ観測をスキップする。
  2. The GROWI server shall system.host.memory.totalos.totalmem() の戻り値で常に観測する。
  3. The GROWI server shall process.memory.usageprocess.memoryUsage().rss で観測する。
  4. The GROWI server shall process.runtime.v8.heap.used / process.runtime.v8.heap.total / process.runtime.v8.heap.externalv8.getHeapStatistics() および process.memoryUsage().external から観測する。
  5. The GROWI server shall 上記すべてのメトリクスを単位 By(bytes)で emit する。

Requirement 6: HTTP リクエストの best-effort anonymization

Objective: プライバシ保護担当者として、otel:anonymizeInBestEfforttrue のとき、ユーザーが入力した検索キーワード・ページパス・ユーザー名がトレース span の http.target に平文で残らないことを保証したい。

Acceptance Criteria

  1. The GROWI server shall otel:anonymizeInBestEfforttrue のとき、HTTP instrumentation の startIncomingSpanHookanonymizationModules を順次評価し、canHandle(url)true を返した module の handle() 結果を span attribute としてマージする。
  2. The GROWI server shall 検索 API (/_api/search, /_search) の q クエリパラメータを [ANONYMIZED] に置換する。
  3. The GROWI server shall page-listing API (/_api/v3/page-listing/{ancestors-children,children,item}) および page API (/_api/v3/pages/list, /_api/v3/pages/subordinated-list, /_api/v3/page/check-page-existence, /_api/v3/page/get-page-paths-with-descendant-count) の path / paths パラメータを匿名化する。
  4. The GROWI server shall ページアクセス(非 API、permalink でない、創出可能なページパス)に対し、ユーザー名およびページパスを SHA-256 prefix(16 文字)でハッシュし [USERNAME_HASHED:...] / [HASHED:...] プレースホルダで置換する。permalink(ObjectId)と users top page(/user)はそのまま残す。
  5. If otel:anonymizeInBestEffortfalse, the GROWI server shall startIncomingSpanHook を渡さず、HTTP instrumentation の標準動作のみを行う。

Requirement 7: Diag Logger と pino の統合

Objective: 開発者として、OpenTelemetry 内部の diag ログが GROWI の通常のアプリケーションログ(pino)と同一フォーマット・同一出力先で観測できることを保証したい。

Acceptance Criteria

  1. When NODE_ENV === 'development' かつ otel:enabledtrue, the GROWI server shall DiagLogger を pino logger にアダプトする実装 (DiagLoggerPinoAdapter) をグローバルに登録する。
  2. The GROWI server shall diag.error/warn/info/debug/verbose で受け取ったメッセージが JSON 文字列の場合に parse して構造化 data に変換し、pino の引数規約(data 第 1 引数・message 第 2 引数)に整合する形で渡す。
  3. The GROWI server shall production 環境では initLogger() を呼ばない(OpenTelemetry の diag 既定動作に委ねる)。

Requirement 8: メトリクスエクスポートと SDK 設定

Objective: 運用者として、OTLP メトリクス/トレースエクスポートが OpenTelemetry SDK 標準の環境変数(OTEL_EXPORTER_OTLP_ENDPOINT 等)で制御でき、内部で勝手な default endpoint が固定されないことを保証したい。

Acceptance Criteria

  1. The GROWI server shall OTLPTraceExporter および OTLPMetricExporter をコンストラクタ引数なしで生成し、エンドポイントなど exporter 設定は OpenTelemetry SDK 標準の環境変数で解決させる。
  2. The GROWI server shall PeriodicExportingMetricReaderexportIntervalMillis を 300000(5 分)で初期化する。
  3. The GROWI server shall auto-instrumentation のうち @opentelemetry/instrumentation-pino および @opentelemetry/instrumentation-fs を明示的に無効化する(pino: log signal を使用しないため、fs: トレース量が膨大すぎるため)。

Requirement 9: SemConv の不安定 attribute のローカルコピー

Objective: 開発者として、@opentelemetry/semantic-conventions の incubating attribute をランタイムコードから直接 import せず、本モジュール内のローカルコピーを参照することで、上流の minor リリースでの破壊的変更からアプリケーションを保護したい。

Acceptance Criteria

  1. The GROWI server shall incubating attribute(service.instance.id, http.target)を semconv.ts 内に文字列定数として定義し、ランタイムコードはこれを import する。
  2. The GROWI server shall @opentelemetry/semantic-conventions/incubating からの import をランタイムコードに含めない。

Requirement 10: 拡張・追加時の境界遵守

Objective: 機能を追加・変更するエンジニアとして、新規 Custom Metric や新規 Anonymization Handler を本 spec の境界に従って実装し、レイヤ責務の汚染を回避したい。

Acceptance Criteria

  1. When 新規 Custom Metric モジュールを追加する, the GROWI server shall custom-metrics/ 配下にファイルを追加し、addXxxMetrics(): void をエクスポートし、custom-metrics/index.tssetupCustomMetrics() から呼び出す。
  2. When 新規 Anonymization Handler を追加する, the GROWI server shall anonymization/handlers/ 配下に AnonymizationModule 実装ファイルを追加し、handlers/index.tsanonymizationModules 配列に登録する。
  3. The GROWI server shall identity 情報を Resource Attribute 経由で、設定値を growi.configs ラベル経由で、観測値を growi.* / system.* / process.* メトリクス経由でそれぞれ emit する責務分離を維持する。