クラス: wxLog, wxLogStderr, wxLogStream, wxLogTextCtrl, wxLogWindow, wxLogGui, wxLogNull, wxLogBuffer, wxLogChain, wxLogInterposer, wxLogInterposerTemp, wxStreamToTextRedirector, wxLogFormatter
目次:
これは wxWidgets の提供するロギングクラス全般の概要です。ここでのロギングという言葉は広い意味を持っており、非対話形式のメッセージに限らず、プログラムの出力すべてを含んでいます。wxWidgets のロギング機能では ログ ターゲットに対する標準的なインタフェースを定義する基底 wxLog クラスや、いくつかのログクラスの標準的な実装とそれを使用する関数群を提供しています。
まず最初に、wxLog クラス郡を使用するのにその知識は必要とされません。そのため、wxLogXXX() 関数 についてのみ、知っておく必要があります。これらの関数はすべて printf() や vprintf() と同じ構文を持ちます。つまり、第一引数にフォーマット文字列を取り、続けて可変引数か可変引数リストのポインタを取ります。以下にすべての関数を示します:
__WXDEBUG__
が定義されている場合) のみ処理を行い、リリースモードの場合 (それ以外の場合) では空文字列に展開されます。Windows でデバッグ出力を確認するためにはデバッガーを使用するか、DbgView (http://www.microsoft.com/technet/sysinternals/Miscellaneous/DebugView.mspx) のようなサードパーティーのプログラムを使用する必要がある点に注意してください。 これらの関数の用途はかなり明確ですが、なぜ C の標準入出力関数や C++ のストリームといった他のロギング機能を使用しないのか疑問に思うかもしれません。簡単に答えれば、それらの関数やストリームは確かに優れた汎用的な仕組みを持っていますが、本当に wxWidgets に適しているとは言えないためです。wxWidgets のログ関数を使用するいくつかの利点は以下の通りです:
wxLog* logger = new wxLogStream(&cout); wxLog::SetActiveTarget(logger);
デフォルトでは大半のログメッセージが有効化されています。特に、wxWidgets 自身の記録するエラー (何らかの処理の失敗時、例えば、wxFile::Open() はファイルのオープンに失敗した場合にエラーを記録します) がユーザに通知されることを意味します。完全にロギングを無効化するためには wxLog::EnableLogging() か、普通は一時的にロギングを無効化し、オブジェクトが破棄される時に元の設定を復元する wxLogNull クラスを使用します。
重要なメッセージのみを記録するためには wxLog::SetLogLevel() に wxLOG_Warning などを指定します。こうすることで、重要度が警告未満であるすべてのメッセージのロギングが無効になり、wxLogMessage() の出力はもはやユーザに通知されなくなります。
さらに、異なるログコンポーネント間で別々にログレベルを設定することも可能です。これがどれほど役立つか説明する前に、ログコンポーネントについて説明しましょう: ログコンポーネントとは単純に、メッセージを生成するコンポーネントやモジュールを識別する任意の文字列のことです。ログコンポーネントは "foo/bar/baz" コンポーネントが "foo" コンポーネントの子として扱われるという意味では階層的です。そして、すべてのコンポーネントは無名のルートコンポーネントの子になります。
デフォルトでは wxWidgets によって記録されるすべてのメッセージは "wx" コンポーネントかそのサブコンポーネント ("wx/net/ftp" など) に割り当てられており、あなた自身のコードによって記録されるメッセージは空のログコンポーネントに割り当てられています。これを変更するためには、 wxLOG_COMPONENT
として各コンポーネントを一意に識別する文字列を定義する必要があります。この文字列は例えばデフォルトで "MyProgram" を指定し、データベースを利用するモジュールでは "MyProgram/DB" に、さらにトランザクションを扱う部分では "MyProgram/DB/Trans" に再定義する、というようにします。そして、以下のように wxLog::SetComponentLevel() を使用します:
// 絶対にデータベースが失敗しないことを誰もが知っているため、 // データベースのすべてのエラーメッセージを無効化する wxLog::SetComponentLevel("MyProgram/DB", wxLOG_FatalError); // しかし、どういうわけか時たま変更のコミットに失敗するため、 // トランザクションのトレースは有効化する wxLog::SetComponentLevel("MyProgram/DB/Trans", wxLOG_Trace); // また、wxWidgets の動的モジュールロード機能の // トレースメッセージも有効化する wxLog::SetComponentLevel("wx/base/module", wxLOG_Trace);
トランザクションコードで明示的に設定されたログレベルは親コンポーネントのログレベルを上書きしますが、データベースコードの他のサブコンポーネントはすべて親コンポーネントの値を継承するため、ログメッセージはまったく生成されません。
メッセージのロギングに通常使用するすべての関数と、それらを使用する理由について列挙したので、これらの動作方法について説明します。
wxWidgets では ログターゲット という考え方があります: これは単純に wxLog を継承したクラスです。そのため、メッセージのロギング時に呼ばれる基底クラスの仮想関数を実装ています。いつでも 有効な ログターゲットはひとつだけで、これが wxLogXXX() 関数によって使用されます。ログオブジェクト (つまり、wxLog を継承したクラスのオブジェクト) の通常の使用方法は SetActiveTarget() を使用して有効なターゲットとして設定することで、それ以降に呼び出された wxLogXXX() 関数で自動的に使用されるようになります。
新しいログターゲットクラスを作成する場合、wxLog を継承し、wxLog::DoLogRecord()、wxLog::DoLogTextAtLevel()、wxLog::DoLogText() のいずれか (または複数) をオーバーライドするだけです。最初の関数はもっとも柔軟で、メッセージ形式の変更、メッセージの動的なフィルタリングやリダイレクトなどを行なうことができます。wxLogFatalError() で生成されたメッセージを除いて、すべてのメッセージがこの関数に引き渡されます。メッセージの形式を変更することなく、ログメッセージを単純に他の場所へリダイレクトしたいだけの場合、wxLog::DoLogTextAtLevel() をオーバーライドします。最後に、メッセージのログレベルに関係なく、ログメッセージをリダイレクトしたいだけの場合、wxLog::DoLogText() をオーバーライドすれば充分です。
wxLog を継承したクラスがいくつか事前に定義されており、新しいログターゲットクラスを作成する際の参考になると思います。もちろん、そのまま使用することも可能です。以下にその一覧を示します:
FILE *
へ出力します。その名の通り、デフォルトでは stderr へ出力します。 FILE *
と stderr の代わりに ostream と cerr を使用します。 wxFile file; // 通常はファイルを開けなかった場合に wxFile.Open() がエラーメッセージを出力するが、そうさせたくない { wxLogNull logNo; if ( !file.Open("bar") ) { // ... 自分自身でエラー処理を行なう ... } } // ~wxLogNull が呼ばれ、古いログ出力先が元に戻される wxLogMessage("..."); // ok
ログターゲットは組み合わせて使用することもできます: 例えば、メッセージを他の場所 (例えばログファイルなど) にリダイレクトしつつ、通常通りの方法でも処理したいとします。このために wxLogChain、wxLogInterposer、wxLogInterposerTemp を使用できます。
wxWidgets 2.9.1 から、どのスレッドからでも安全にロギング関数を呼べるようになりました。メインスレッド以外のスレッドで記録されたメッセージはメインスレッドで wxLog::Flush() が呼ばれるまで (通常はアイドル時、つまり、未処理のイベントがすべて処理された後に呼ばれます) バッファリングされ、wxLog::Flush() が呼ばれたときに初めて実際に出力されます。デフォルトの GUI ロガーはフラッシュされるときに初めてメッセージを出力するようにすでになっているため、他スレッドのメッセージは通常どおり、ほぼ同時に表示されます。しかし、カスタムログターゲットを定義している場合、メッセージの記録される順がバラバラになる可能性があります。例えば、より後のタイムスタンプを持つメインスレッドのメッセージがより早いタイムスタンプを持つ他のスレッドのメッセージの前に表示されることがあります。ただし、wxLog は各スレッドで記録されたメッセージは記録された順に表示されることを保証しています。
また、wxLog::EnableLogging() と wxLogNull クラスは現在のスレッドにのみ影響することに注意してください。つまり、EnableLogging(false)
を呼んだ後でも他のスレッドでは依然としてログメッセージが生成されます。
ロギングの振る舞いを完全に変更するためにはカスタムログターゲットを定義します。例えば、モーダルメッセージボックスでユーザの操作を妨げないようにするため、メインウィンドウの一部にすべてのログメッセージを表示するように wxLog を継承したクラスを定義することができます。
作成したカスタムログターゲットを使用するためには、カスタムログターゲットのオブジェクトを指定して wxLog::SetActiveTarget() を呼ぶか、wxAppTraits を継承したクラスを作成して wxAppTraits::CreateLogTarget() 仮想関数をオーバーライドし、自作の特性オブジェクトのインスタンスを返却するように wxApp::CreateTraits() をオーバーライドします。後者の場合、例えばプログラムの起動時に早くメッセージのロギングの準備をする必要があり、プログラムの終了時にはメインウィンドウの存在に依存してはいけない点に注意してください。しかし、ログターゲットを使用するときに (すでに/まだ) GUI が存在すると安全に仮定することができます。なぜなら、 GUI が存在しない場合は wxLogStderr を使用するように wxWidgets がログターゲットを自動的に切り替えるためです。
ログメッセージの処理方法をカスタマイズするためには継承クラスでいくつかの関数をオーバーライドします: wxLog::DoLogRecord()、 wxLog::DoLogTextAtLevel()、wxLog::DoLogText()。
最後の関数が一番単純です: メッセージのログレベルを考慮せず、単純にログ出力を他の場所へリダイレクトしたい場合はこの関数をオーバーライドします。異なるレベルのメッセージを別々に処理したい場合、wxLog::DoLogTextAtLevel() をオーバーライドしてください。
加えて、ログメッセージを (タイムスタンプ、ソースファイルの情報、スレッドID などの) 部品から構築する方法を完全にカスタマイズすることもできます。これは wxLogFormatter クラスによって行われるため、このクラスを継承し、望む方法でログメッセージを構築するように Fortmat() 関数をオーバーライドする必要があります。ただし、タイムスタンプの形式を変更 (または出力を抑制) したいだけの場合、FormatTime() をオーバーライドするだけで充分です。
最後に、出力形式をさらに制御する必要がある場合、DoLogRecord() をオーバーライドすることでログレベルに応じてカスタムメッセージを構築したり、メッセージの重要度に応じてまったく別のことを行なうことさえできます。(例えば警告とエラー以外のメッセージを破棄し、警告は画面に表示して、エラーメッセージはユーザ (もしくはプログラマ) の携帯電話へ転送するなどです。ただ、これは現在のタイムゾーンにおいて昼か夜かがタイムスタンプで分かるかどうかによりますが)
ダイアログ サンプルでは wxLogGui でダイアログを使用するようにカスタマイズしたカスタムログターゲットを定義する例を解説しています。
現在の wxWidgets では、もはやトレースマスクを使用する必要性がほとんどないことに注意してください。なぜなら、異なるログ文に対して異なるログコンポーネントを使用することで同じ事を実現できるためです。ログコンポーネントについての詳細は ログメッセージの選択 を参照してください。
以下の関数は新しいログターゲットを作成することなく、wxLog の振る舞いを限定的に変更することができます。詳細メッセージとはリリースモードでも無効化されないトレースメッセージのことで、wxLogVerbose() によって生成されます。ユーザがこれらのメッセージに関心を示すことがないため、通常はユーザに通知されません。しかし、ユーザがプログラムの問題を見つける際の手助けとして有効化されます。
(本当の) トレースメッセージについては、現在有効になっているトレースマスクに応じて処理が行われます: 指定されたメッセージのマスクに対して wxLog::AddTraceMask() が呼ばれている場合はメッセージが記録されますが、そうでない場合は何も起こりません。
以下に例を示します。
wxLogTrace( wxTRACE_OleCalls, "IFoo::Bar() called" );
このメッセージは先に以下の処理を行なっている場合に記録されます:
wxLog::AddTraceMask( wxTRACE_OleCalls );
標準のトレースマスクは wxLogTrace() のドキュメントに記載しています。