ライブラリにログを追加して、ライブラリを使用するプログラムのログシステムと簡単に統合できるようにする方法を教えてください。


9

私はそれを使用するプログラムのログで役立つ可能性のある多くの情報を含むライブラリを書いていますが、私のライブラリを使用するプログラムができるような方法でそれを公開する最良の方法がわかりませんライブラリのログを独自のログとシームレスに統合します(必要な場合)。

ライブラリに特定のロギングライブラリを選択すると、ライブラリを使用するための依存関係のリストに追加され、メインプログラムがそのライブラリに関連付けられます。メインプログラムで使用される複数のライブラリがこれを行った場合、それぞれが異なるライブラリを選択した可能性があります。

私は、プログラムでC ++ストリームオブジェクトをライブラリに登録して使用できるようにすることについて考えました。それは比較的一般的な目的のようですが、データがログに記録されるときにコンテンツとメタデータで呼び出されるコールバック関数をメインプログラムに登録することも考えました。別のオプションは、データを処理する必要があるときはいつでもメインプログラムが取得できるように、ログデータをライブラリ内のある種のリストに格納し、メインプログラムがデータを処理する時間を決定できるようにすることです。

自分の状況に最適なものを決定できるように、さまざまなアプローチの提案と長所/短所を探しています。


1
実際には答えではありませんが、Qtツールキットがどのように実行するかを確認することをお勧めします。Google QtMessageHandler。そしてQMessageLogger。それは、他の答えが示唆しているものとよく似ています。
Teimpz 2017年

回答:


8

複数のメソッドを公開してライブラリからログを受け取り、1つを除くすべてをアダプタでラップして、ライブラリで使用される「実際の」メソッドにラップできます。

たとえば、std::function<void(std::string)>各ロガーコールバックであるコレクションを内部的に持つことにしました。あなたが提供するもの:

void registerLogCallback(std::function<void(std::string)> callback); 
// Main logging implemention

そしてまた

registerLogStream(std::ostream stream) { 
    registerLogCallback([stream](std::string message){ stream << message; }); 
}

そしてまた

template<typename OutputIterator>
registerLogOutputIterator(OutputIterator iter) { 
    registerLogCallback([iter](std::string message){ *iter++ = message; }); 
}

アダプターを実装したい「どこかから文字列を受け取る」タイプのバリエーション。


最終的には、デバッグ、警告、致命的など、何らかの「ログレベル」も必要になると思います。ユーザーが必要なものを除外できるようにするためです。それでも良いスタートです。
Teimpz 2017年

4

アプリケーションがログ機能を利用できるようにする最も簡単な方法は、ログメッセージを受信するクラス/関数を登録することです。彼らがそのメッセージで何をするかは完全にアプリケーション次第です。

C / C ++を使用すると、ライブラリで以下を使用できます。

typedef void (*LogMessageReceiver)(char const* message,
                                   void* user_data);

void registerLogMessageReceiver(LogMessageReceiver receiver,
                                void* user_data);

アプリケーションはを呼び出して関数を登録できますregisterLogMessageReceiver。適切なuser_data。コードベースのロギングセクションで、適切なメッセージと登録済みのを使用してその関数を必ず呼び出す必要がありますuser_data

Cについて心配する必要がない場合は、メッセージの受信者としてクラスを使用できます。

struct LogMessageReceiver
{
   virtual ~LogMessageReceiver() {}
   virtual void receive(std::string const& message) = 0;
};

ライブラリに関数を追加して、アプリケーションがログメッセージレシーバーを登録できるようにします。

void registerLogMessageReceiver(LogMessageReceiver* receiver);

アプリケーションはLogMessageReceiver、上記の関数を呼び出すことによってを登録できます。登録済みのの所有権に関して、いくつかのポリシー決定を行う必要がありますLogMessageReceiver。ライブラリーがレシーバーの所有権を取得する場合、それdeleteはポインターでなければなりません。ライブラリがレシーバーの所有権を取得しない場合は、アプリケーションdeleteがレシーバーを処理する必要があります。

クラスをログメッセージレシーバーとして使用するuser_dataregisterLogMessageReceiver、のサブタイプがLogMessageReceiver機能に役立つデータを自由に保持できるため、でビットを省略できます。receive関数で追加のユーザーデータを渡す必要はありません。

そこからは、ロギングメカニズムがどの程度高度であるかに応じて、より複雑になる可能性があります。

たとえば、さまざまなログレベルを設定できます:Concise、Normal、Verbose、またはLoggingLevel1、LoggingLevel2、...、LoggingLevelN。

その場合、アプリケーションが使用したいロギングのレベルを制御できるようにする必要があります。

単純化したロギングメカニズムを超えて決定すると、選択肢は無限に広がります。ここでそれらを掘り下げることは意味がありません。


これは、C ++ 11以前のコンパイラをサポートする必要がある場合の良い答えです。そうでなければ、私はカレスの答えを支持します。std :: functionは、void *よりもはるかに安全な代替手段であるキャプチャを許可します
Teimpz

1

ロギングをライブラリと組み合わせる必要性を再考する必要があると私は断言します。特に、標準のロガーインターフェイスがないC ++の場合。

アプリケーションごとに、ロギングに関するポリシーが異なります。ライブラリはポリシーにとらわれないものである必要があります。

ライブラリの目的は、サービスを提供することであり、できれば、そのサービスの要求が成功したか失敗したかを示すことです。理想的には、errno、戻りコード、例外を介して[失敗した理由]を示します。ロギングが必要なのは、提供された関数が複数の場所で失敗する可能性がある場合、1つの関数でやりすぎている可能性があります。ないかもしれませんが、可能性を考慮してください。


@xaxxonこれが答えではない理由がわかりません。どうすれ良い答えを書くことができますか?[...] answer can be “don’t do that" [...]
doubleYou

1

ホストアプリケーションのログシステムと簡単に連動するライブラリを作成するのは、そのシステムが何であるかを知っていれば簡単です。そのための複数の可能性がある場合、それはより困難であり、最も一般的な分母の専制政治によって制約されます。ライブラリをさまざまなログシステムに適合させる互換性レイヤーを用意することもできますが、今では1つのシステムのみが持ついくつかの便利でユニークな機能に依存することはできません。エンドユーザーが他のシステムを使用している場合、その便利な独自の機能はありません。

.Netの世界では、(少なくとも)2つの一般的に使用されるログシステム、log4netとNLogがあります。どちらも似ていますが、同一ではありません。私はすでにlog4netを使用しており、NHibernate(ORMライブラリ)の使用を開始しました。幸い、内部でlog4netを使用しているため、プロジェクトに簡単に追加できました。(そして、ここで私は@Danielの回答に同意しません。NHibernateが他の場所で使用しているのと同じシステムでアクティビティを詳細にログに記録することは、非常に役立つ場合があります。追加の作業は必要ありません。)すでにNLogに投資していた場合、それはどちらかを意味します。私は切り替えるか、両方を使用するプロジェクトを持っています。

少なくともロギングに関しては、通常、1つのシステムでログに記録されたメッセージを他のログシステムに転送するように構成できるカスタムメッセージアペンダーを作成するオプションがあります。したがって、すでにNLogを使用していて、本当にそれを続行したいが、NHibernateも使用したい場合は、各メッセージをNLogに転送するlog4netアペンダーを書くことができます。APIが類似している場合は、簡単です。

代替策は、実際に利用可能なニーズに最適なシステムを選択し、それを無条件に使用するか、または最も一般的な共通点の問題の影響を受けるある種のアダプターレイヤーを使用することです。これは非常に満足のいく答えではありません。


これに加えて、C ++は簡単なことを困難にし、困難なことをさらに困難にするという事実があります。
rwong
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.