ログ出力の小技

昨今のソフトウェア開発では
デバッグに便利なデバッガやログ出力の仕組みが多数存在しますが、
それらの便利なツールを単に使うだけではどうにもバグの原因特定が難しいことが多々あるのは、今も昔もそれほど変わりありません。

開発現場によってはそのようなツール自体が存在しないことも珍しくありません。
今回は私の業務経験から、知っていると少しは役立つことがあるかもしれない
ちょっとしたログ出力の小技を紹介したいと思います。
まず紹介するケースですが、
「頻繁に実行される関数で、特定の条件に限りログ出力したい時」を想定します。
これは私が過去いくつかのプロジェクトで実際に遭遇したケースです。
今回のサンプルとして、以下のような構成を取り上げたいと思います。
void functionA() {
// 処理A
commonFunction(sampleParam);
}
void functionB() {
// 処理B
commonFunction(sampleParam);
}
~中略~
void functionZ() {
// 処理Z
commonFunction(sampleParam);
}
void commonFunction(int param) {
// 頻繁に実行される共通関数
// この関数で特定の条件に限りログ出力したい
~何らかの処理~
}
上記サンプルにある
commonFunction()
に素直にログを埋め込むと大量のログが出力されるばかりか、
実行環境によってはログを格納する領域が溢れてしまい古いログが消えてしまうことになってしまいます。
また、呼び出し元が大量にあり、どの関数から呼び出された時のログなのかを大量のログから
解析するという作業を余儀なくされ、最悪の場合原因の特定に失敗し時間を浪費することになり兼ねません。
共通関数のような関数をデバッグしたい場合は、
引数を追加したり、関数内の処理に影響が出そうな条件文をなるべく追加せずに特定の条件でのみログ出力をしたくなります。
そういった場合には、私はよく以下のような方法でログを採取していました。
例として、functionB()から呼び出された時にだけログ出力したいと仮定します。
// このフラグがtrueの時だけログ出力する
bool printLog = false;
void functionB() {
// 処理B
printLog = true;
commonFunction(sampleParam);
printLog = false;
}
void commonFunction(int param) {
~何らかの処理~
if (printLog) { log("%d\n", param); } // フラグが立っている時だけログ出力
~何らかの処理~
}
単純にグローバル変数にフラグを1個用意し、そのフラグが立っている場合だけログ出力するという方法です。
突貫でログを追加でき、簡単にログ出力したいケースを限定できることから、この手法が有効な時は
よく使用していました。
最先端のデバッガが使えるような現場では上記のような小技は全く必要ありませんが、
残念ながらそのような恵まれた環境で開発できないことも多々ありますので、
こういった些細なノウハウが個々の開発業務で助けになっていることが現実です。
機会がありましたら、また別の小技を紹介したいと思います。
