JavaScriptのログのレベリングについて

## やりたいこと

近代的なフレームワークは、だいたいレベル別のロガー機能を持っています。

ログを出力時にエラー/警告/デバッグなどの優先度を指定して出力すると、あとで設定によって出力される種類を変更できて便利です。あるいはAndroid/ADTのように、IDEでフィルタリングがしやすくなったり。

フレームワーク無しのJavaScriptでも同じことをしたかったので、方法を検討してみました。


## 方法1. ちょっと乱暴なやりかた(consoleを使う)

JavaScriptのconsoleには、既にレベル別のロギング機能がビルトインされています。
([参考: consoleオブジェクトが持つlog以外の便利メソッド18（前編）](http://blog.asial.co.jp/1036))

```js
console.debug('log debug');
console.info('log info');
console.log('log normal');
console.warn('log warning');
console.error('log error');
```

ブラウザの機能としてフィルタリングができるため便利なんですが、
そのままだとログがだだ漏れになるため、デバッグ効率がいまいちです。

とはいえログ記述をひとつづつコメントアウトしたり消したりするのも面倒な話。
そこでログメソッドを上書きして無効化することで、一括でログの表示/非表示を切り替えてみます。

```js
// ビルトイン関数を上書きしてしまえば
console.debug = function(){/* NOP */};
console.info = function(){/* NOP */};
console.log = function(){/* NOP */};
console.warn = function(){/* NOP */};
console.error = function(){/* NOP */};

// ログが表示されなくなる
console.debug('log debug');
console.info('log info');
console.log('log normal');
console.warn('log warning');
console.error('log error');
```

console.log()が実行されても、何もしない空の関数が呼ばれるだけなのでログは表示されません。逆に表示させたくなったら上の記述をコメントアウトしてしまえばいいわけです。



## 方法2. ちょっと面倒なやりかた(consoleのラッパー作る)

1の方法で目標は達成できましたが、ビルトイン関数を書き換えるというのは抵抗があります。コメントアウトでON/OFFというのも野蛮なので、Railsのように定数でログの種類を切り替えられるようにラッパーを作ってみます。

```js:logger.js
var logger = function logger(){
};

logger.LEVEL = {
    RUN   : 0,
    ERROR : 1,
    WARN  : 2,
    LOG   : 3,
    INFO  : 4,
    DEBUG : 5,
    FULL  : 5,
};
logger.level = logger.LEVEL.FULL;

logger.debug = function(msg){ (this.level >= this.LEVEL.DEBUG) && console.debug(msg); };
logger.info  = function(msg){ (this.level >= this.LEVEL.INFO) && console.info(msg); };
logger.log   = function(msg){ (this.level >= this.LEVEL.LOG) && console.log(msg); };
logger.warn  = function(msg){ (this.level >= this.LEVEL.WARN) && console.warn(msg); };
logger.error = function(msg){ (this.level >= this.LEVEL.ERROR) && console.error(msg); };
```

以下のようにして使います：

```js

// ログレベルを全開にすると
logger.level = logger.LEVEL.FULL;
// すべてのログが表示される
logger.debug('log debug 1');  // o
logger.info('log info 1');    // o
logger.log('log log 1');      // o
logger.warn('log warn 1');    // o
logger.error('log error 1');  // o

// ログレベルをWARNにすると
logger.level = logger.LEVEL.WARN;
// WARNと、WARNよりレベルの高いERRORだけが表示される
logger.debug('log debug 2');  // x
logger.info('log info 2');    // x
logger.log('log log 2');      // x
logger.warn('log warn 2');    // o
logger.error('log error 2');  // o

```

* * *
以上です。

もともとはデバッグ効率に加えて、リリース時に全ログの無効化ができることを想定して書いていたのですが、考えてみればそもそも閲覧できるソースにログが残っていること自体が問題に思えます。自動化された難読処理の一貫などとしてログを消す対応が別途必要ですね。

* * *

### 補足(2015/12/25)
コメントにて、自前のログ関数をconsoleメソッドのエイリアスとする方法をご教示いただきました。
簡潔で気に入っています。

ラッパーとして多機能化させる方向性についても検討中なのですが、
その場合の問題の一つとして可変長引数が使えない点について、
対応することができそうだったので参考までに追記です:

```js
logger.debug = function(){
    if(this.level < this.LEVEL.DEBUG)
        return;

    // 引数リストをコピー
    var args = [].slice.call(arguments);

    // ここで追加機能への対応として、argsをいじる

    // apply()を使って配列を引数リストとして渡す
    console.debug.apply(console, arguments);
};
```

もう一つの問題、ログ情報に付加されるソース位置が不明確になる問題についてはわかりしだい追記します。一応Chromeの場合はBlackboxという機能によって(一手間ありますが)対応できるそうです:

参考: [console.logのラッパを作った時にコンソール上で呼び出し位置を正しく表示する方法](https://pect.me/thread/565ab700cb176272389f6430)