2
3

JavaScriptの関数呼び出し前後にログ出力処理を追加する方法(Proxy)

Last updated at Posted at 2024-05-28

はじめに

関数の「呼び出し直前」と「処理完了後」にログ出力がしたい場合、何かいい方法がないか?と調べたところ、
組み込み機能のProxyでできることがわかりました

Proxyとは

あるプロパティが参照されたとき及び代入されたときや、関数が呼び出されたときの動作をカスタマイズすることができる(JavaScriptの標準)機能です

Proxyの使い方使い方

関数の呼び出しだけに絞った簡単なサンプルです

func()をProxyでラップすることで、関数呼び出し時にapply()が割り込みます

/**
 * プロキシ対象の関数
 */
function func(val1, val2) {
  return val1 + val2;
}

const handler = {
  apply(target, thisValue, args) { // 関数呼び出し時にコールバックされる処理
    console.log(`call ${target.name}(${args})`); // 呼び出し前ログ
    return target.apply(thisValue, args); // func()自体を呼び出して戻り値にする
  },
};

const proxy = new Proxy(func, handler); // funcのプロキシを作成

console.log(proxy(1, 2));

実行結果(関数呼び出し前にログが出力されました)

call func(1,2)
3

呼び出し前後でログ+引数や戻り値を変更する場合

  • 関数呼び出し時の引数や、戻り値を変更することもできます
const proxy = new Proxy(func, {
  apply(target, thisValue, args) {
    console.log(`call ${target.name}(${args})`); // ①呼び出し前ログ
    console.log(`${args} =>`, (args = args.map((value) => value + 1))); // ②引数を変更
    const result = target.apply(thisValue, args); // func()の呼び出し
    console.log(`result: ${result}`); // ③呼び出し後ログ
    return String(result).padStart(3, '0'); // ④戻り値を書き換え
  },
});

実行結果

  • ①呼び出し前ログ(関数名と引数)
  • ②引数を変更 (引数に+1)
  • ③呼び出し後ログ ((2,3)を渡したので結果は5)
  • ④戻り値を書き換え (0パディングして返す)
call add(1,2)
1,2 => [ 2, 3 ]
result: 5
005

別モジュールに定義された関数にログを追加するProxy

別モジュールで定義された全ての関数に対して、ログ機能を追加する関数log_proxyを作ります

まず、import対象のモジュールを用意します

api.js
export function add(val1, val2) {
  return val1 + val2;
}
export const strcat = (s1, s2) => s1 + s2;

api.jsをimportして、その関数に対してログを追加します

  • moduleの関数を列挙して、それぞれの関数に対してProxyを作る

プログラムソース

import * as api from './api.js';
/**
 * モジュールに定義された関数の引数と戻り値をログ出力するProxy
 */
const log_proxy = (module) => {
  const proxy_module = {};
  // モジュールに定義された関数、変数を列挙
  for (const key of Object.keys(module)) {
    if (typeof module[key] == 'function') {
      // 関数毎にログ用Proxyを生成する
      const proxy = new Proxy(module[key], {
        apply(target, thisValue, args) {
          console.log(`call ${target.name}(${args})`); // 呼び出し前ログ
          const result = target.apply(thisValue, args);
          console.log(`result: ${result}`); // 呼び出し後ログ
          return result;
        },
      });
      // proxyをまとめたオブジェクトを作成して返す(apiと同じ関数を持つ)
      proxy_module[key] = proxy;
    }
  }
  return proxy_module;
};

// proxyを作成して、呼び出す
const proxy = log_proxy(api);
console.log(proxy.add(1, 2));
console.log(proxy.strcat('val1', 'val2'));

実行結果

  • 別モジュールで定義された関数に、ログ機能が追加されました
call add(1,2)
result: 3
3
call strcat(val1,val2)
result: val1val2
val1val2
2
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3