🤖

TS の using でプリントデバッグを確率的にサンプリングして出力する

2025/03/11に公開

スコープ単位でログをサンプリングする。

{
  using log = createSampleLog<string>(5);
  log("a");
  log("b");
  // スコープを抜けるときに最大5件サンプリングされて表示される
}

using の使い所

やりたかったこと

Cline が勝手にデバッグログを仕込みまくって膨大なログを食ってトークンを使いまくるので、そもそも出力するログに上限を設定したい。

ロガーは初期化したスコープに依存して、抜ける時に吐き出す。

前提知識: using と Symbol.dispose

スコープから抜けるときの処理を書ける。

function useXXX() {
  return {
    [Symbol.dispose]() {
      console.log("xxx:disposed")
    }
  }
}
// スコープを作る
{
  using xxx = useXXX();
  // スコープから抜ける時に using した参照の Symbol.dispose が発火
}

これをつかって、保持するインメモリのログの上限を設定しながら、スコープを抜けるときにログを表示する。

実装例

自分がコピペしやすくするために、かなりショートコーディングしているのに注意

type SampleLog = (<T = any>(t: T) => void) & Disposable;
export function createSampleLog<T = any>(
  n: number,
  truncateSize = 128,
  print = console.log,
): SampleLog {
  const samples: Array<[T, number]> = [];
  let count = 0;
  return Object.assign((item: T) => {
    count++;
    if (samples.length < n) {
      samples.push([item, count]);
    } else {
      const r = ~~(Math.random() * count);
      if (r < n) samples[r] = [item, count];
    }
  }, {
    [Symbol.dispose]() {
      samples.sort(([, a], [, b]) => a - b).forEach(([item, order]) => {
        print(order, _truncate(item, truncateSize));
      });
    },
  }) as SampleLog;

  function _truncate(input: any, len: number): string {
    let str: string;
    if (input instanceof Object) {
      str = JSON.stringify(input, null, 2);
    } else {
      str = String(input);
    }
    return str.length > len ? str.slice(0, len) + "..." : str;
  }
}

//------ 基本的な使用例-----
function basicExample() {
  using log = createSampleLog<number>(5, 20);
  // 適当なデータを作成(1~41文字のアスキーらへんのコード)
  for (let i = 0; i < 1000; i++) {
    const len = ~~(Math.random() * 40) + 1;
    log(String.fromCharCode(~~(Math.random() * 50 + 30)).repeat(len));
  }
  // スコープを抜けると自動的に結果が表示される
}
basicExample();

やってることは samples にためて、 dispose 時に truncate して出力しているだけ。

実行例

$ deno run ../../docs/practice/using-sampler-example.ts
50 ====================...
598 >>>>>>>>>>>>>>>>>>>>...
643 CCCC
823 FFFFFFFFFFFFF
911 66666666666666666666...

プロンプト

デバッグのためにログを設定する時は `createSampleLog(n, truncateSize)` を使います。
情報が足りない時は、件数や出力する文字数を徐々に大きくしてください。

```ts
{
  // 最大5件, 128文字
  using log = createSampleLog<number>(3, 128);
  log("a");
  log("b");
  log("c");
  //...

  // スコープを抜けると結果が表示される
  // [0] a
  // [2] c
}
```

Discussion