Architecture
全体構成
log_ctrl はログを書き込むプロセスとは独立しています。
┌───────────────────────────┐
│ application / DeviceServer│
│ logger │
└─────────────┬─────────────┘
│ write
▼
log directory
▲
│ read only
│
┌─────────────┴─────────────┐
│ log_ctrl │
│ │
│ LogCtrl │
│ ├─ log_names() │
│ ├─ dates() │
│ ├─ raw_files() │
│ ├─ exists() │
│ └─ tail() │
│ │
│ _LogStream │
│ ├─ read() │
│ └─ read_raw() │
└─────────────┬─────────────┘
│
│ grpc_frame
▼
┌───────────────────────────┐
│ DeviceProxy("log") │
│ │
│ SyncLogGrpcClient │
│ AsyncLogGrpcClient │
└───────────────────────────┘
ログを書き込む側に log_ctrl 固有RPCを追加する必要はありません。
LogCtrl
LogCtrl はログディレクトリをローカルに扱う制御クラスです。
保持する主な状態は、
log_direncoding
です。
生成時に log_dir を Path.resolve()
し、実在するディレクトリであることを確認します。
通常RPCとして公開されるメソッド
log_names()
dates(log_name)
raw_files()
exists(log_name, date=None)
tail(log_name, lines=100, date=None)
これらは grpc_frame
のDescribe/Call機構によってクライアントへbindされます。
LogCtrl は gRPC
固有処理を持たず、ローカルクラスとしてファイル探索・検証・末尾読み込みを担当します。
log_names()
log_dir 直下のファイルについて、
<log_name>.log
<log_name>.log.YYYY-MM-DD
を解析し、log_name の集合を作ります。
現在ログと履歴ログのどちらからでも名前を検出するため、現在の .log
ファイルが存在しない名前も返ることがあります。
dates()
指定された log_name に対して、
<log_name>.log.YYYY-MM-DD
に一致する実在ファイルを調べます。
suffixは datetime.strptime(..., "%Y-%m-%d")
で検証され、新しい日付から順に返されます。
raw_files()
Path.rglob("*") によって log_dir 以下を再帰的に走査します。
ログ命名規則によるフィルタは行わず、全ファイルを log_dir
からの相対パスとして返します。
これは通常のログ探索とは別に、トラブル時に実ファイル構成を確認するための経路です。
_LogStream
ファイル本体は通常RPCの戻り値として一括送信せず、grpc_frame の
StreamCall を使用します。
ストリーム側には、
read(log_name, date=None)
read_raw(relative_path)
があります。
read() は LogCtrl のログパス解決を使用し、read_raw()
はraw相対パス解決を使用します。
ファイルは chunk_size ごとにバイナリで読み、
bytes
bytes
bytes
...
として順次yieldします。
クライアント側
log_ctrl.server_grpc のimport時に、
register_device_proxy(
"log",
async_client_cls=AsyncLogGrpcClient,
sync_client_cls=SyncLogGrpcClient,
...
)
によって DeviceProxy へログclientを登録します。
利用側は、
client = DeviceProxy(
"log",
server_ip="127.0.0.1",
server_port=50060,
async_mode=False,
)
とするだけで、ログ用clientを取得できます。
通常RPC
log_names()、dates()、raw_files()、exists()、tail() は
grpc_frame が LogCtrl のDescribe情報から透過的にbindします。
ファイル本体
read() / read_raw() はデバイス固有client側で実装されています。
同期clientでは stream_call() を反復してチャンクを bytearray
に結合し、最終的に bytes を返します。
そのためアプリケーション側は StreamCall やチャンク境界を意識しません。
パス解決
通常ログのパスは、
log_name
↓
<log_name>.log
または、
log_name + date
↓
<log_name>.log.YYYY-MM-DD
として解決されます。
log_name にディレクトリ成分や .log を含める指定は拒否されます。
read_raw() 用の相対パスは、log_dir と結合して resolve()
した後、解決結果が log_dir 内にあることを確認します。これにより ../
などによるログディレクトリ外へのアクセスを防ぎます。
ライフサイクル
サーバー起動関数 log_grpc_server() が、
LogCtrlを生成ストリーム処理オブジェクトを生成
grpc_frameのgRPC serverを生成指定host/portへbind
serverをstart
自己接続による起動確認
終了まで待機
というライフサイクルを管理します。
クライアント側は DeviceProxy で生成し、使用後に、
client.close()
でchannelを閉じます。