Laravel 3のDBのクエリログを粛々と流す

#SQLのクエリをプロファイラーではなくログファイルで確認したい
[Laravel 4のDBのクエリログを淡々と流す](http://qiita.com/takaaki-mizuno/items/6304cafe496834172a92)
前からしたかったことがちょうど投稿されていたので3用に編集してメモる。

同じく参照元[Laravel 4 - logging SQL queries](http://stackoverflow.com/questions/19131731/laravel-4-logging-sql-queries)

```php:クエリログの出力
Event::listen('laravel.query', function($query, $bindings, $time)
{
    $data = compact('bindings', 'time', 'name');

    // Format binding data for sql insertion
    foreach ($bindings as $i => $binding)
    {
        if ($binding instanceof \DateTime)
        {
            $bindings[$i] = $binding->format('\'Y-m-d H:i:s\'');
        }
        else if (is_string($binding))
        {
            $bindings[$i] = "'$binding'";
        }
    }

    // Insert bindings into query
    $query = str_replace(array('%', '?'), array('%%', '%s'), $query);
    $query = vsprintf($query, $bindings);

    Log::info($query . PHP_EOL . print_r($data, true));
});
```

変更箇所はイベント名の変更と引数の`$name`が着火時になかったので削除。
あとログの出力が4では第2引数に配列を入れることができるが、3では無理なので`print_r()`。ただし出力が縦長になるのでちょっと見難いか。

あとは`application/config/database.php`を`'profile' => true,`にしてやればいい。

Stack Overflow では丁寧にログ出力前に`database.profile`が`true`か確認しているが、`laravel.query`イベント自体が`profile`が`true`じゃないと発生しないので省略。

##クエリログだけ別ファイルに書き込む

ここまで通常のログファイルに書き込んだが、ここまでくればクエリログだけ別ファイルにしたい。

あった。
[Laravel4、特定のログを別ファイルにする](http://kore1server.com/271/)
いつもお世話になっています。

`useFiles()`メソッドで変えるのかー
ま、Laravel3のLogクラスには無いのですが

### Laravel3のログ書き込み先

```php:/laravel/log.php
public static function write($type, $message, $pretty_print = false)
{
	$message = ($pretty_print) ? print_r($message, true) : $message;

	// If there is a listener for the log event, we'll delegate the logging
	// to the event and not write to the log files. This allows for quick
	// swapping of log implementations for debugging.
	if (Event::listeners('laravel.log'))
	{
		Event::fire('laravel.log', array($type, $message));
	}

	$message = static::format($type, $message);

		File::append(path('storage').'logs/'.date('Y-m-d').'.log', $message);
	}
```

`/stroge/logs/日付.log`に書きこんでいる。
1日毎にローテーションしてくれるのは嬉しいけどlogsディレクトリまで指定されている。

`Log`クラスを継承して`write()`をオーバライドしてパスだけ変えれば良さそうな感じだが、妥協して`path('storage')`を変更して`/storage/sql/logs/日付.log`に吐き出す。

### 出力先を変更したクエリログ

```php:出力先を変更したクエリログ
Event::listen('laravel.query', function($query, $bindings, $time)
{
    $data = compact('bindings', 'time', 'name');

    // Format binding data for sql insertion
    foreach ($bindings as $i => $binding)
    {
        if ($binding instanceof \DateTime)
        {
            $bindings[$i] = $binding->format('\'Y-m-d H:i:s\'');
        }
        else if (is_string($binding))
        {
            $bindings[$i] = "'$binding'";
        }
    }

    // Insert bindings into query
    $query = str_replace(array('%', '?'), array('%%', '%s'), $query);
    $query = vsprintf($query, $bindings);

    $path = path('storage');
    set_path('storage', $path . 'sql' . DS);
    Log::info($query . PHP_EOL . print_r($data, true));
    set_path('storage', $path);
});
```

`path()`,`set_path()`は`/paths.php`にある。
`$GLOBALS['laravel_paths']['storage']`を書き換えしているだけ。

あとは`/storage/sql`と`/storage/sql/logs`ディレクトリを用意してやるだけだけど、存在チェックや無いなら作成などを追加するかいつも悩んでいる。
今回は手動で作成しておく。
消したりしないし元の`write()`もチェックしてないし。

後はブラウザからアクセスしてログが作成されているか確認する。

### profilerのログには表示したくない
ブラウザで確認するとデバッグ用のプロファイラー
![キャプチャ.PNG](https://qiita-image-store.s3.amazonaws.com/0/62354/65416a97-a324-1c60-571b-c636d1174870.png)
にLOGとSQLタブでほぼ同じ内容が表示されている。
(そもそもプロファイラー上で確認できるSQLをログに残したかったのがスタートだから当然。)

SQLのログは多いし他のログが埋まるので、SQLのログ出力はプロファイラーに登録しない様に変更してみる。

```php:プロファイラーのログ登録箇所/laravel/profiling/profiler.php

public static function attach()
{
	// First we'll attach to the query and log events. These allow us to catch
	// all of the SQL queries and log messages that come through Laravel,
	// and we will pass them onto the Profiler for simple storage.
	Event::listen('laravel.log', function($type, $message)
	{
		Profiler::log($type, $message);
	});

	Event::listen('laravel.query', function($sql, $bindings, $time)
	{
		Profiler::query($sql, $bindings, $time);
	});

	// We'll attach the profiler to the "done" event so that we can easily
	// attach the profiler output to the end of the output sent to the
	// browser. This will display the profiler's nice toolbar.
	Event::listen('laravel.done', function($response)
	{
		echo Profiler::render($response);
	});
}

```

`Log::write()`の`Event::fire('laravel.log')`をここで受け取ってる。
幸いにも`Event::fire('laravel.log')`するには`if (Event::listeners('laravel.log'))`とイベントの登録確認があるので、一時的に消してしまえばいい。

ただし、イベントの削除はあるが取得が無いのでプロパティから直接取得する。

```php:プロファイラーには表示せずにログを書き出す
Event::listen('laravel.query', function($query, $bindings, $time)
{
    $data = compact('bindings', 'time', 'name');

    // Format binding data for sql insertion
    foreach ($bindings as $i => $binding)
    {
        if ($binding instanceof \DateTime)
        {
            $bindings[$i] = $binding->format('\'Y-m-d H:i:s\'');
        }
        else if (is_string($binding))
        {
            $bindings[$i] = "'$binding'";
        }
    }

    // Insert bindings into query
    $query = str_replace(array('%', '?'), array('%%', '%s'), $query);
    $query = vsprintf($query, $bindings);

    $event = Event::$events['laravel.log'];
    Event::clear('laravel.log');

    $path = path('storage');
    set_path('storage', $path . 'sql' . DS);
    Log::info($query . PHP_EOL . print_r($data, true));
    set_path('storage', $path);
    Event::$events['laravel.log'] = $event;
});
```