最近、VSCode + Claude Code でドキュメントのPDF化作業を任せることが増えました。
長時間の作業を放置できて本当に便利なのですが、ある日ふと思ったんです・・・

これ、AIに何をさせたか、説明できる状態になっているのかな?
ファイル操作もコマンド実行もWebアクセスも、Claude Codeが自動でこなしてくれる。便利な反面、後から「あのとき何をしたか」を追えるようにしておかないと、業務利用としては心許ない。
そこで今回は、EasyBlocks Syslogを使ってClaude Codeの作業証跡を自動的に集約する仕組みを実際に組んでみました。
本記事ではその実装過程と、ハマったポイントについて共有します。

なぜ証跡が必要か
Claude Codeのようなコーディングエージェントは、自律的に以下を行います。
- ファイルの作成・編集・削除
- シェルコマンド(PowerShell等)の実行
- Webからの情報取得
- 外部APIとの連携
これらが自動で進むのは効率的ですが、業務で利用する場合「何を読んで」「何を作って」「何を削除したか」が後から検証できる状態にしておく必要があります。
社内ガバナンス、コンプライアンス、トラブル時の原因究明、どれをとっても証跡は必須です。
ところが、Claude Code単体ではこの「行動証跡」を外部に出す仕組みがありません。ここで EasyBlocks Syslog の出番です。
構成
構成自体はシンプルです。
[Windows端末: VSCode + Claude Code]
(1) hooks 機能で発火
▼
[PowerShellスクリプト]
(2) syslog形式(RFC3164)に変換してUDP/514で送信
▼
[EasyBlocks Syslog]
(3) 受信・蓄積・Web UIで可視化
▼
[運用者がブラウザで確認]
重要な気付き:Claude Codeは直接Syslogを投げない
Claude Codeにはsyslog送信機能はありません。あくまで「特定タイミングで任意のコマンドを実行できる」hooks機能があるだけ。
つまり「hooksでPowerShellスクリプトを呼び出し、そのスクリプトがsyslog形式に変換して送信する」という1ステップを挟む必要があります。
環境
- クライアント:Windows 11 + VSCode + Claude Code 拡張
- シェル:Windows PowerShell 5.1
- Syslogサーバー:EasyBlocks Syslog
- ネットワーク:クライアントから Syslog サーバへ UDP/514 が疎通可能
実行手順
STEP①:PowerShellスクリプトの作成
まず C:\claude-hooks\ フォルダを作って、その中にsyslog送信用スクリプトを置きます。
PowerShellで以下を実行してフォルダを作成。
New-Item -Path "C:\claude-hooks" -ItemType Directory
次に、メモ帳で C:\claude-hooks\Send-ClaudeSyslog.ps1 を作成し、以下の内容を貼り付けます。
# Send-ClaudeSyslog.ps1
# Claude CodeのhooksからJSON入力を受け取り、EasyBlocks Syslog (RFC3164) へUDP送信する
# ===== 設定 =====
$SyslogServer = "192.168.X.X" # 自社のEasyBlocks SyslogのIPに置き換え
$SyslogPort = 514
$Facility = 21 # local5
$DefaultSeverity = 5 # notice
# ================
try {
# stdinからJSONを受け取る
$jsonInput = [Console]::In.ReadToEnd()
$hookData = $jsonInput | ConvertFrom-Json
$eventName = $hookData.hook_event_name
$toolName = $hookData.tool_name
$sessionId = $hookData.session_id
# tool_inputから主要パラメータを抜き出す
$detail = switch ($toolName) {
"Bash" { "cmd=`"$($hookData.tool_input.command)`"" }
"PowerShell" { "cmd=`"$($hookData.tool_input.command)`"" }
"Write" { "file=`"$($hookData.tool_input.file_path)`"" }
"Edit" { "file=`"$($hookData.tool_input.file_path)`"" }
"Read" { "file=`"$($hookData.tool_input.file_path)`"" }
"WebFetch" { "url=`"$($hookData.tool_input.url)`"" }
"WebSearch" { "query=`"$($hookData.tool_input.query)`"" }
default { "" }
}
# RFC3164フォーマット組み立て
$priority = ($Facility * 8) + $DefaultSeverity
# TIMESTAMP: "Mmm dd hh:mm:ss" (英語ロケール固定、日が1桁ならスペース埋め)
$now = Get-Date
$invariant = [System.Globalization.CultureInfo]::InvariantCulture
$month = $now.ToString("MMM", $invariant)
$day = $now.Day.ToString().PadLeft(2, ' ')
$time = $now.ToString("HH:mm:ss")
$timestamp = "$month $day $time"
$hostname = $env:COMPUTERNAME
$user = $env:USERNAME
$tag = "claude-code"
$sessionShort = if ($sessionId) { $sessionId.Substring(0, [Math]::Min(8, $sessionId.Length)) } else { "-" }
$message = "<$priority>$timestamp $hostname ${tag}: user=$user session=$sessionShort event=$eventName tool=$toolName $detail"
# UDP送信
$udp = New-Object System.Net.Sockets.UdpClient
$bytes = [System.Text.Encoding]::UTF8.GetBytes($message)
[void]$udp.Send($bytes, $bytes.Length, $SyslogServer, $SyslogPort)
$udp.Close()
exit 0
}
catch {
# hook失敗でClaude Codeを止めないため、stderrに出して0で抜ける
[Console]::Error.WriteLine("Syslog send failed: $_")
exit 0
}
ポイントを少し解説します。
stdinからJSON受信:Claude CodeのhooksはJSON形式で情報を渡してきます。tool_name でどのツールが使われたか、tool_input でその引数が分かります
RFC3164フォーマット:EasyBlocks SyslogはBSD-syslog(RFC3164)とIETF-syslog(RFC5424)の両方に対応していますが、今回はシンプルな前者を採用。
タイムスタンプは英語ロケール固定:日本語ロケールのWindowsでは Get-Date -Format “MMM” が「11月」と返してしまうので、InvariantCulture を明示して英語表記を強制しています。ここ地味だけど重要です。
失敗してもexit 0:syslog送信失敗でClaude Codeの動作を止めないため
STEP②:PowerShellの実行ポリシー確認
PowerShellは初期状態だとスクリプト実行が禁止されています。確認してみましょう。
Get-ExecutionPolicy
Restricted だった場合は以下で許可します。
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
RemoteSigned Unrestricted Bypass のいずれかなら、既に実行可能なのでスキップでOK。
STEP③:スクリプト単体テスト
Claude Codeに組み込む前に、スクリプト単体で動作確認します。PowerShellで以下を実行します。
'{"hook_event_name":"PostToolUse","tool_name":"Bash","session_id":"test1234","tool_input":{"command":"dir"}}' | powershell -File C:\claude-hooks\Send-ClaudeSyslog.ps1
擬似的にClaude Codeから来るJSONをスクリプトに流し込んで、Syslogサーバーへ送信させるテストです。
EasyBlocks SyslogのWeb UIを開いて、テストログが届いていることを確認します。

このときFacilityが [ local5 ]、Priorityが [ notice ]、プログラム名が [ claude-code ] として正しく分類されていることもポイントです。EasyBlocks Syslog側でフィルタや色分けが効きます。
STEP④:Claude Codeの設定ファイル(settings.json)を編集
Claude Codeに「ツールを使ったら自動でこのスクリプトを呼ぶ」と教える設定を書きます。
ファイルパス:C:\Users\<ユーザー名>\.claude\settings.json
すでに既存の設定がある場合は、その内容を活かしつつ “hooks” ブロックを追加します。ゼロから書くなら以下のような内容になります。
{
"hooks": {
"PostToolUse": [
{
"matcher": "*",
"hooks": [
{
"type": "command",
"shell": "powershell",
"command": "C:\\claude-hooks\\Send-ClaudeSyslog.ps1"
}
]
}
],
"UserPromptSubmit": [
{
"hooks": [
{
"type": "command",
"shell": "powershell",
"command": "C:\\claude-hooks\\Send-ClaudeSyslog.ps1"
}
]
}
],
"SessionStart": [
{
"hooks": [
{
"type": "command",
"shell": "powershell",
"command": "C:\\claude-hooks\\Send-ClaudeSyslog.ps1"
}
]
}
]
}
}
各イベントの意味は以下のとおりです。
| イベント | 発火タイミング |
|---|---|
| SessionStart | Claude Codeの起動・再開時 |
| UserPromptSubmit | ユーザーがチャットでプロンプトを送信したとき |
| PostToolUse | ツール(ファイル操作、コマンド実行など)が完了したとき |
PostToolUse の matcher は最初 “Bash|Write|Edit|…” のように個別指定にしていたのですが、後述する理由で最終的に “*”(全マッチ)にしました。
重要:保存時の文字コードはUTF-8(BOMなし)にする
メモ帳で編集する場合、必ず「名前を付けて保存」から文字コードを 「UTF-8」 に明示的に指定してください。普通の Ctrl+S だと環境によって ShiftJIS で保存されてしまい、Claude Codeが「Invalid or malformed JSON」エラーを出すことがあります。
動作確認
VSCodeを完全に終了してから再起動し、Claude Codeを開きます(設定は起動時に読み込まれます)。
ログの見え方
Claude Codeを起動すると SessionStart のログが、チャットに何か送信すると UserPromptSubmit のログが、ツールが実行されると PostToolUse のログが、それぞれEasyBlocks Syslogに自動で送られます。

実際に「テストファイルを作って削除して」と指示してみたときのログがこちら。

作成→削除の流れが完全に追跡できています。しかもどのファイルを、どんなコマンドで削除したかまで証拠として残ります。
session= でフィルタすれば、1つの作業セッション全体の足跡を一覧できます。
ハマったポイント
検証中にいくつかつまずいたので、同じ道を歩む方の参考になればと思い、共有しておきます。
ハマりポイント①:WindowsのClaude CodeはBashではなくPowerShellツールを使う
公式ドキュメントを見ると、シェル実行ツールは Bash という名前で紹介されています。なので最初は matcher を “Bash|Write|Edit|…” と書いていました。
ところが実際にWindowsで使ってみると、シェルコマンドのログがほとんど飛んでこない。デバッグ用に matcher を “*”(全マッチ)に変えて流してみたところ判明したのが、Windows版のClaude Codeは Bash ではなく PowerShell というツールを使うということ。
これは公式ドキュメントには明示的に書かれておらず、実機で動かさないと気付けない部分でした。matcher で個別指定するなら “Bash|PowerShell|Write|Edit|…” のようにPowerShellも含める必要があります。
ただ実用上は、何が来るか分からないのでむしろ “*” で全マッチさせて全部記録するのがおすすめです。証跡として網羅性が高いほうがいいですから。
ハマりポイント②:settings.jsonのエンコーディング問題
すでに permissions が設定された状態の settings.json に hooks を追記したところ、メモ帳の保存時にShiftJISで保存されてしまい、Claude Codeが「Invalid or malformed JSON」エラーで全部の設定を読まなくなりました。

解決策は「名前を付けて保存」で文字コードを「UTF-8」(BOMなし)に明示すること。
普段あまり意識しないところですが、メモ帳の保存ダイアログには文字コード選択欄があります。日本語を含むJSONを扱うときは必ず確認しましょう。
業務効率と監査証跡の両立
本記事のテーマには、もうひとつ大事な側面があります。
settings.json の permissions ブロックで「特定のコマンドは確認なしで自動許可」を設定すると、PDF化のような長時間作業を放置で流せます。具体的には以下の通りです。
{
"permissions": {
"deny": [
"Write(C:/Windows/**)",
"PowerShell(*Format-Volume*)",
"PowerShell(*diskpart*)"
],
"allow": [
"Read",
"Edit",
"Write",
"PowerShell(pip install python-docx -q)",
"PowerShell(python -m pip install python-docx -q)"
]
},
"hooks": {
"...": "..."
}
}
このように permissions と hooks は共存できます。
つまり、
- deny ルールでシステム破壊系コマンドは確実にブロック
- allow ルールで日常作業の確認プロンプトをスキップ(効率化)
- hooksで全行動を Syslog に自動記録(証跡)
「効率」と「ガバナンス」を同時に確保できる、というのがこの構成の本質的な価値です。
今後の展望
EasyBlocks Syslog の活用範囲は、まだまだ広がります。
EasyBlocks Syslogでは複数人のClaude Code利用ログを集約・保存でき、これからAIエージェントを業務で本格利用する組織にとって、AIの行動証跡を専用のSyslog基盤に集約するという発想は、シンプルだけど効果的です。
まとめ
VSCode + Claude Code で生産性は確実に上がっています。
だからこそ、その活動を見える化しておくことが、これからのAI時代における働き方のスタンダードになるのではないでしょうか。
AIの行動証跡を手軽に集約・管理したいとお考えの方は、ぜひEasyBlocks Syslogをご検討ください。
(最後に、本記事のソースコードおよび設定ファイルはサンプルであり、動作を保証するものではありません。ご自身の環境に合わせてご確認のうえご利用ください。)
