diff --git a/manuals/1.0/en/10-semantic-logging.md b/manuals/1.0/en/10-semantic-logging.md index 1801c49..f8f9993 100644 --- a/manuals/1.0/en/10-semantic-logging.md +++ b/manuals/1.0/en/10-semantic-logging.md @@ -1,7 +1,7 @@ --- layout: docs-en title: "Semantic Logging" -category: Draft +category: Manual permalink: /manuals/1.0/en/10-semantic-logging.html --- @@ -9,54 +9,187 @@ permalink: /manuals/1.0/en/10-semantic-logging.html > "What we record becomes memory; what we remember becomes truth" > -> —Adaptation of Orwell's concept from '1984' (1949) +> —Adapted from Orwell's concept in *1984* (1949) ## Overview -Be Framework implements **semantic logging** functionality that automatically records object metamorphosis processes as structured logs. +Traditional logs: -### Basic Concept +``` +[INFO] User registered: alice@example.com +[INFO] Verification passed +[INFO] Insert into users table +``` + +Semantic logs: + +```json +{ + "open": { "from": "UnverifiedEmail", "to": "RegisteredUser" }, + "events": [ + { "type": "email_format_asserted", "context": { "email": "alice@example.com" } }, + { "type": "user_inserted", "context": { "userId": 42, "email": "alice@example.com" } } + ], + "close": { "properties": { "userId": 42, "value": "alice@example.com" } } +} +``` + +Traditional logs are just lines of text arranged chronologically — the reader is left to guess which lines belong to the same operation. + +With semantic logging, one metamorphosis fits in one JSON. Source and destination, intermediate events, final properties — "what became what, and why" is recorded as typed, structured data and can be validated with JSON Schema. + +Be Framework provides two semantic recording mechanisms: -**Traditional Logs**: Fragmented event records -**Semantic Logs**: Complete metamorphosis story records of objects +- **`$been`** — Proof that the Final object carries its own history (proof) +- **`SemanticLoggerInterface`** — A log that records hierarchical operations (log) + +| | log | `$been` | +|--------------|------------------------|-----------------------------| +| Nature | descriptive | constitutive | +| Perspective | third-person (observer)| first-person | +| Question | What happened? | Why am I what I am now? | +| Grammar | doing | being | +| Role | record | proof | + +## `$been` — Proof of Existence + +Inject `Been` into the Final object's constructor and record events with `with()`. This becomes proof of why the object is in its current state. ```php -// Object metamorphosis... -#[Be(RegisteredUser::class)] -final readonly class UserInput { /* ... */ } +final class RegisteredUser +{ + public readonly int $userId; + public readonly Been $been; + + public function __construct( + #[Input] string $value, + #[Inject] EmailVerifier $verifier, + #[Inject] UserRepository $users, + #[Inject] Been $been, + ) { + if (! $verifier->check($value)) { + throw new UnbecomingException('email format failed'); + } + + $this->userId = $users->insert(['email' => $value]); + $this->been = $been + ->with(new EmailFormatAssertedContext( + email: $value, + )) + ->with(new UserInsertedContext( + userId: $this->userId, + email: $value, + )); + } +} +``` + +`Been` is received from the DI container via `#[Inject]`. The received `Been` already contains the source and destination information recorded by the framework at the start of metamorphosis. The developer appends events that only the Final object's internals can know — email was verified, user was inserted — using `with()`. + +## Event Context -final readonly class RegisteredUser { /* ... */ } +Events passed to `Been` are subclasses of `AbstractContext`. -// Automatically recorded as structured logs +```php +final class EmailFormatAssertedContext extends AbstractContext { - "metamorphosis": { - "from": "UserInput", - "to": "RegisteredUser", - // Complete metamorphosis information... - } + public const string TYPE = 'email_format_asserted'; + public const string SCHEMA_URL = 'https://myvendor.example.com/schemas/email-format-asserted.json'; + + public function __construct( + public readonly string $email, + ) {} } ``` -## Technical Foundation +`TYPE` is the event type in the log, and `SCHEMA_URL` points to the JSON Schema for that event. Constructor properties become the JSON `context` field as-is. + +These are domain events in DDD terms — objects representing "what happened" in business terms. Define the facts that the Final object experienced on its way to completion as application-specific event contexts. + +## SemanticLogger — Hierarchical Operation Recording -Integrated with [Koriym.SemanticLogger](https://github.com/koriym/Koriym.SemanticLogger): +When hierarchical operation recording is needed, inject `SemanticLoggerInterface` directly. + +```php +final class RegisteredUser +{ + public function __construct( + #[Input] string $value, + #[Inject] UserRepository $users, + #[Inject] SemanticLoggerInterface $logger, + ) { + // Declare intent (open) + $id = $logger->open(new DbTransactionContext(table: 'users')); + + // Record intermediate events (event) + $this->userId = $users->insert(['email' => $value]); + $logger->event(new RowInsertedContext(userId: $this->userId)); + + // Record result (close) + $logger->close(new TransactionResultContext(committed: true), $id); + } +} +``` -- **Type-safe structured logging** -- **Open/Event/Close pattern** -- **JSON schema validation** -- **Hierarchical operation tracking** +open/event/close uses the hierarchical structure of [Koriym.SemanticLogger](https://github.com/koriym/Koriym.SemanticLogger) directly. Intent → occurrences → result — these three layers record a cohesive operation. -## Value Provided +`$been` is proof of what the Final object is. `SemanticLoggerInterface` is a detailed record of intermediate steps, closer to traditional logging. Usually `$been` is sufficient. -### Development & Debugging -Complete tracking of object metamorphosis makes it easy to understand complex processing flows and identify problems. +## Automatic Metamorphosis Recording -### Audit & Compliance -Since all metamorphoses are recorded as structured data, complete audit trails can be provided. +Separate from `$been` and `SemanticLoggerInterface`, the framework automatically records the metamorphosis itself with open/close. Developers do not need to write this recording code. -### System Analysis -Analysis of object growth patterns and processing efficiency becomes possible. +Here is the full JSON output: + +```json +{ + "open": { + "type": "metamorphosis_open", + "context": { + "fromClass": "MyVendor\\MyApp\\UnverifiedEmail", + "beAttribute": "#[Be(RegisteredUser::class)]", + "immanentSources": { + "value": "MyVendor\\MyApp\\UnverifiedEmail::value" + }, + "transcendentSources": { + "verifier": "MyVendor\\MyApp\\EmailVerifier", + "users": "MyVendor\\MyApp\\UserRepository", + "been": "Be\\Framework\\SemanticLog\\Been" + } + } + }, + "events": [ + { + "type": "email_format_asserted", + "context": { "email": "alice@example.com" } + }, + { + "type": "user_inserted", + "context": { "userId": 42, "email": "alice@example.com" } + } + ], + "close": { + "type": "metamorphosis_close", + "context": { + "properties": { "userId": 42, "value": "alice@example.com" }, + "be": { "finalClass": "MyVendor\\MyApp\\RegisteredUser" } + } + } +} +``` + +open records the intent of metamorphosis (what to what, with which materials), events record the occurrences from `$been->with()`, and close records the result (final properties and destination). + +## From Logs to DSL + +Traditional logs are records of execution. They are born after code runs, used for debugging, and eventually discarded. + +This JSON is different. It is simultaneously a record of execution, a specification of metamorphosis, and a proof of existence. Where it came from, what it became, what it is. "`UnverifiedEmail` becomes `RegisteredUser` through `email_format_asserted` and `user_inserted`" — this reads as both a description of past fact and a declaration of future expectation. Moreover, as typed structured data, it functions as a DSL that AI can read and write. + +Record, specification, proof, DSL. When these four converge in the same JSON, rigorous validation on par with tests through JSON Schema, and a cycle of generating code from logs and logs from code, become possible. --- -**Detailed usage methods, configuration examples, and practical samples will be documented at a later date.** \ No newline at end of file +Technical foundation: [Koriym.SemanticLogger](https://github.com/koriym/Koriym.SemanticLogger) + +For the full framework overview, see [Reference](./11-reference-resources.html) ➡️ diff --git a/manuals/1.0/ja/10-semantic-logging.md b/manuals/1.0/ja/10-semantic-logging.md index e4a8508..3f6a826 100644 --- a/manuals/1.0/ja/10-semantic-logging.md +++ b/manuals/1.0/ja/10-semantic-logging.md @@ -1,7 +1,7 @@ --- layout: docs-ja title: "意味的ログ" -category: Draft +category: Manual permalink: /manuals/1.0/ja/10-semantic-logging.html --- @@ -13,51 +13,183 @@ permalink: /manuals/1.0/ja/10-semantic-logging.html ## 概要 -Be Frameworkは、オブジェクトの変容プロセスを構造化されたログとして自動記録する**意味的ログ**機能を実装しています。 +従来のログ: -### 基本コンセプト +``` +[INFO] User registered: alice@example.com +[INFO] Verification passed +[INFO] Insert into users table +``` + +意味的ログ: + +```json +{ + "open": { "from": "UnverifiedEmail", "to": "RegisteredUser" }, + "events": [ + { "type": "email_format_asserted", "context": { "email": "alice@example.com" } }, + { "type": "user_inserted", "context": { "userId": 42, "email": "alice@example.com" } } + ], + "close": { "properties": { "userId": 42, "value": "alice@example.com" } } +} +``` + +従来のログは行単位のテキストが時系列に並ぶだけで、どの行が同じ操作に属するかは読み手の推測に委ねられます。 + +意味的ログでは、ひとつの変容がひとつのJSONに収まります。変容元と変容先、途中の出来事、最終プロパティ — 「何が何になり、なぜそうなったか」が型付きの構造化データとして記録され、JSONスキーマで検証できます。 + +Be Frameworkには二つの意味的記録の仕組みがあります。 + +- **`$been`** — Finalオブジェクトが自分の来歴を保持する証明(proof) +- **`SemanticLoggerInterface`** — 階層的な操作を記録するログ(log) + +| | log | `$been` | +|----------|------------------------------|----------------------------------| +| 性質 | descriptive(記述) | constitutive(構成) | +| 視点 | 第三者(観測者) | 一人称 | +| 問い | 何が起きたか | なぜ今の私なのか | +| 文法 | doing | being | +| 役割 | 記録 | 証明 | + +## `$been` — 存在証明 + +Finalオブジェクトのコンストラクタで`Been`をインジェクトし、`with()`で出来事を記録していくと、そのオブジェクトがなぜ今の状態にあるかの証明になります。 + +```php +final class RegisteredUser +{ + public readonly int $userId; + public readonly Been $been; + + public function __construct( + #[Input] string $value, + #[Inject] EmailVerifier $verifier, + #[Inject] UserRepository $users, + #[Inject] Been $been, + ) { + if (! $verifier->check($value)) { + throw new UnbecomingException('email format failed'); + } + + $this->userId = $users->insert(['email' => $value]); + $this->been = $been + ->with(new EmailFormatAssertedContext( + email: $value, + )) + ->with(new UserInsertedContext( + userId: $this->userId, + email: $value, + )); + } +} +``` -**従来のログ**:イベントの断片的な記録 -**意味的ログ**:オブジェクトの完全な変容ストーリーの記録 +`Been`は`#[Inject]`でDIコンテナから受け取ります。受け取った`Been`にはフレームワークが変容開始時に記録した変容元・変容先の情報がすでに含まれています。開発者は`with()`で、Finalオブジェクトの内部でしか知り得ない出来事 — メールを検証した、ユーザーを挿入した — を追記します。 + +## イベントコンテキスト + +`Been`に渡すイベントは`AbstractContext`のサブクラスです。 ```php -// オブジェクトの変容が... -#[Be(RegisteredUser::class)] -final readonly class UserInput { /* ... */ } +final class EmailFormatAssertedContext extends AbstractContext +{ + public const string TYPE = 'email_format_asserted'; + public const string SCHEMA_URL = 'https://myvendor.example.com/schemas/email-format-asserted.json'; + + public function __construct( + public readonly string $email, + ) {} +} +``` + +`TYPE`はログ上のイベント種別、`SCHEMA_URL`はそのイベントのJSONスキーマを指します。コンストラクタのプロパティがそのままJSONの`context`フィールドになります。 + +DDDでいうドメインイベント — ビジネス上「起きたこと」を表すオブジェクトです。Finalオブジェクトが完了までに経験した事実を、アプリケーション固有のイベントコンテキストとして定義します。 -final readonly class RegisteredUser { /* ... */ } +## SemanticLoggerInterface — 階層的な操作記録 -// 自動的に構造化ログとして記録される +階層的な操作記録が必要な場合は、`SemanticLoggerInterface`を直接インジェクトします。 + +```php +final class RegisteredUser { - "metamorphosis": { - "from": "UserInput", - "to": "RegisteredUser", - // 完全な変容情報... - } + public function __construct( + #[Input] string $value, + #[Inject] UserRepository $users, + #[Inject] SemanticLoggerInterface $logger, + ) { + // 意図を宣言(open) + $id = $logger->open(new DbTransactionContext(table: 'users')); + + // 途中の出来事を記録(event) + $this->userId = $users->insert(['email' => $value]); + $logger->event(new RowInsertedContext(userId: $this->userId)); + + // 結果を記録(close) + $logger->close(new TransactionResultContext(committed: true), $id); + } } ``` -## 技術的基盤 +open/event/closeは[Koriym.SemanticLogger](https://github.com/koriym/Koriym.SemanticLogger)の階層構造をそのまま使います。意図(intent)→ 出来事(occurrences)→ 結果(result)の三層で、ひとまとまりの操作を記録できます。 + +`$been`はFinalオブジェクトが何であるかの証明です。`SemanticLoggerInterface`は従来のログに近い、途中経過の詳細な記録です。通常は`$been`で足ります。 + +## 変容の自動記録 + +`$been`や`SemanticLoggerInterface`とは別に、変容そのものもフレームワークがopen/closeで自動記録します。開発者がこの記録コードを書く必要はありません。 + +出力されるJSONの全体像です。 -[Koriym.SemanticLogger](https://github.com/koriym/Koriym.SemanticLogger)と統合: +```json +{ + "open": { + "type": "metamorphosis_open", + "context": { + "fromClass": "MyVendor\\MyApp\\UnverifiedEmail", + "beAttribute": "#[Be(RegisteredUser::class)]", + "immanentSources": { + "value": "MyVendor\\MyApp\\UnverifiedEmail::value" + }, + "transcendentSources": { + "verifier": "MyVendor\\MyApp\\EmailVerifier", + "users": "MyVendor\\MyApp\\UserRepository", + "been": "Be\\Framework\\SemanticLog\\Been" + } + } + }, + "events": [ + { + "type": "email_format_asserted", + "context": { "email": "alice@example.com" } + }, + { + "type": "user_inserted", + "context": { "userId": 42, "email": "alice@example.com" } + } + ], + "close": { + "type": "metamorphosis_close", + "context": { + "properties": { "userId": 42, "value": "alice@example.com" }, + "be": { "finalClass": "MyVendor\\MyApp\\RegisteredUser" } + } + } +} +``` -- **型安全な構造化ログ** -- **Open/Event/Close パターン** -- **JSONスキーマ検証** -- **階層的操作追跡** +openが変容の意図(何から何へ、どの材料で)、eventsが`$been->with()`で記録された出来事、closeが結果(最終プロパティと変容先)です。 -## 提供価値 +## ログからDSLへ -### 開発・デバッグ -オブジェクト変容の完全な追跡により、複雑な処理フローの理解と問題特定が容易になります。 +従来のログは実行の記録です。コードが走った後に生まれ、デバッグに使われ、やがて消えます。 -### 監査・コンプライアンス -すべての変容が構造化データとして記録されるため、完全な監査証跡を提供できます。 +このJSONは違います。実行の記録であると同時に、変容の仕様でもあり、存在の証明でもあります。どこから来て、どう成ったのか、何であるのかの記録です。「`UnverifiedEmail`が`RegisteredUser`になる過程で`email_format_asserted`と`user_inserted`が起きる」— これは過去の事実の記述としても、未来の期待の宣言としても読めます。しかも型付きの構造化データなので、AIが読み書きできるDSLとしても機能します。 -### システム分析 -オブジェクトの成長パターンと処理効率の分析が可能になります。 +記録、仕様、証明、DSL。この四つが同じJSONに重なるとき、JSONスキーマによるテスト並みの厳密な検証と、ログからコードを生成しコードからログを生成する循環が可能になりえます。 --- -**詳細な使用方法、設定例、実践的なサンプルについては、ドキュメントを後日整備します。** +技術的基盤: [Koriym.SemanticLogger](https://github.com/koriym/Koriym.SemanticLogger) +フレームワークの全体像は[リファレンス](./11-reference-resources.html)へ ➡️