Tracy の拡張を作る

Tracy はアプリケーションをデバッグする素晴らしい道具です。とはいえ、追加の情報をすぐ見られるようにしたいこともあります。ここでは Tracy Bar の独自の拡張を書いて、開発をいっそう心地よくする方法をお見せします。

  • Tracy Bar の独自のパネルを作る
  • BlueScreen の独自の拡張を作る

できあいの Tracy の拡張は Componette にそろっています。

Tracy Bar の拡張

Tracy Bar の新しい拡張を作るのは簡単です。Tracy\IBarPanel インターフェースを実装するオブジェクトを作ります。このインターフェースには getTab()getPanel() の 2 つのメソッドがあります。これらのメソッドは、タブ(Bar の上に直接表示される小さなラベル)とパネル(タブをクリックすると出てくるポップアップ)の HTML のコードを返さなければなりません。getPanel() が何も返さなければ、タブだけが表示されます。getTab() が何も返さなければ何も表示されず、getPanel() も呼ばれません。

class ExamplePanel implements Tracy\IBarPanel
{
	public function getTab()
	{
		return /* ... */;
	}

	public function getPanel()
	{
		return /* ... */;
	}
}

登録

登録は Tracy\Debugger::getBar()->addPanel() を呼んで行います。

Tracy\Debugger::getBar()->addPanel(new ExamplePanel);

あるいはアプリケーションの設定でパネルを直接登録できます。

tracy:
	bar:
		- ExamplePanel

タブの HTML のコード

だいたい次のようになります。

<span title="Explanatory tooltip">
	<svg>...</svg>
	<span class="tracy-label">Title</span>
</span>

画像は SVG の形式にすべきです。説明のツールチップが要らなければ、外側の <span> は省けます。

パネルの HTML のコード

だいたい次のようになります。

<h1>Title</h1>

<div class="tracy-inner">
<div class="tracy-inner-container">
	... content ...
</div>
</div>

見出しはタブの見出しと同じにするか、追加の情報を含めるとよいでしょう。

ひとつの拡張が、違う設定で何度も登録されることがあるのを忘れないでください。ですからスタイルには CSS の ID は使えず、クラスだけを、できれば tracy-addons-<ClassName>[-<optional>] の形で使います。このクラスを tracy-inner クラスと一緒に div に足します。CSS を書くときは、セレクタに #tracy-debug .your-class の接頭辞を付けると便利です。そうすればリセットのスタイルより詳細度が高くなります。

既定のスタイル

パネルの中では <a><table><pre><code> の要素にあらかじめスタイルが付いています。ほかの要素を隠したり見せたりするリンクを作りたいなら、hrefid の属性、そして tracy-toggle クラスで結び付けます。

<a href="#tracy-addons-ClassName-{$counter}" class="tracy-toggle">Details</a>

<div id="tracy-addons-ClassName-{$counter}">...</div>

既定で畳んだ状態にしたいなら、両方の要素に tracy-collapsed クラスを足します。

1 ページの中で ID が重ならないように、静的なカウンタを使ってください。

独自のアセット

パネルに独自のスタイルシートやスクリプトが要るなら、Tracy に自分のアセットと一緒に追加のファイルを読み込ませられます。

Tracy\Debugger::$customCssFiles[] = __DIR__ . '/panel.css';
Tracy\Debugger::$customJsFiles[] = __DIR__ . '/panel.js';

AI エージェントへの対応

AI エージェントがブラウザを動かしているとき、Tracy は Tracy Bar の markdown の要約を JS のコンソールへ送ります。独自のパネルは、IBarPanel の実装に getAgentInfo(): ?string メソッドを足せば自分の markdown を出せます。

class DatabasePanel implements Tracy\IBarPanel
{
	public function getTab(): string { /* ... */ }
	public function getPanel(): string { /* ... */ }

	public function getAgentInfo(): ?string
	{
		return "## Database\n\n- Queries: {$this->count}\n- Total time: {$this->time} ms\n";
	}
}

返された markdown はバーの markdown の要約に含まれます。このメソッドがないか null を返す場合、そのパネルは要約から外されます。

全体像は Tracy の AI エージェントとの統合をご覧ください。

BlueScreen の拡張

このやり方で、青い画面に現れる独自の例外の見せ方やパネルを足せます。

拡張は次のように作ります。

Tracy\Debugger::getBlueScreen()->addPanel(function (?Throwable $e) { // 捕まえた例外
	return [
		'tab' => '...Title...',
		'panel' => '...HTML panel content...',
	];
});

この関数は 2 回呼ばれます。まず(例外が起きていれば)例外そのものが $e パラメータに渡され、返されたパネルはページの先頭に描かれます。null か空の配列を返すと、そのパネルは描かれません。次に $e = null で呼ばれ、返されたパネルは呼び出しのスタックの下に描かれます。関数が配列で 'bottom' => true を返すと、そのパネルはいちばん下に描かれます。

パネルのほかに、addAction()アクションも足せます。これはエラーのページの見出しの部分に、組み込みのもの(search など)と並んで現れる、クリックできるリンクやボタンです。

Tracy\Debugger::getBlueScreen()->addAction(function (Throwable $e): ?array {
	if ($e instanceof MyException) {
		return [
			'link' => 'https://example.com/help?code=' . $e->getCode(),
			'label' => 'view help',
		];
	}
	return null;
});

このコールバックは捕まえた例外を受け取り、linklabel のキーを持つ配列を返します。その例外にアクションを足したくなければ null を返します。

ファイルを作るアクション

エラーのページでまだ存在しないファイルをクリックすると、Tracy はそれを作ることを申し出ます(*create file* の操作)。そのファイルの最初の中身は、生成器を登録すれば決められます。

Tracy\Debugger::getBlueScreen()->addFileGenerator(function (string $file, ?string $class): ?string {
	if (str_ends_with($file, 'Test.php')) {
		return "<?php\n\nclass $class extends Tester\\TestCase\n{\n\t\$END\$\n}\n";
	}
	return null;
});

このコールバックは対象のファイルのパスと、分かっていればそこで定義されるべきクラスの名前を受け取ります。そして最初の中身を返します($END$ のトークンはカーソルが置かれる場所を示し、出力からは取り除かれます)。判断をほかの生成器に任せたいなら null を返します。生成器は最後に登録されたものから順に試されます。組み込みの生成器はごく素朴な PHP の骨組みを作ります。

ファイバーとジェネレータ

ファイバーやジェネレータが止まっているあいだに例外が投げられると、そのスタックはふつうの呼び出しのスタックの一部にはなりません。Tracy は例外からたどれるファイバーとジェネレータのスタックを自動的に見せますが、独立して動いているものは見落とされます。それは手で BlueScreen に足せます。

Tracy\Debugger::getBlueScreen()->addFiber($fiber);