Wie füge ich einer Bibliothek eine Protokollierung hinzu, damit diese mithilfe der Bibliothek problemlos in das Protokollierungssystem des Programms integriert werden kann?


9

Ich schreibe eine Bibliothek mit vielen Informationen, die in einem Protokoll des Programms, das sie verwendet, von Nutzen sein können, aber ich weiß nicht, wie ich sie am besten so verfügbar machen kann, dass das Programm, das meine Bibliothek verwendet, dies kann Integrieren Sie die Protokolle meiner Bibliothek nahtlos in die eigenen Protokolle (falls gewünscht).

Wenn Sie eine bestimmte Protokollierungsbibliothek für meine Bibliothek auswählen, wird die Liste der Abhängigkeiten für die Verwendung meiner Bibliothek erweitert und das Hauptprogramm an diese Bibliothek gebunden. Wenn dies vom Hauptprogramm mehrere Bibliotheken verwendet haben, könnte jede eine andere Bibliothek ausgewählt haben .

Ich habe darüber nachgedacht, dass das Programm ein C ++ - Stream-Objekt bei der Bibliothek registriert, damit es verwendet werden kann. Das scheint ein relativ allgemeiner Zweck zu sein, aber ich habe auch darüber nachgedacht, dass das Hauptprogramm nur eine Rückruffunktion registriert, die bei der Protokollierung der Daten mit den Inhalten und Metadaten aufgerufen wird. Eine andere Möglichkeit wäre, die Protokolldaten in der Bibliothek in einer Art Liste zu speichern, damit das Hauptprogramm sie abrufen kann, wann immer es mit diesen Daten umgehen möchte, damit das Hauptprogramm entscheiden kann, wann es Zeit hat, mit den Daten umzugehen.

Ich suche nach Vor- und Nachteilen verschiedener Ansätze, damit ich entscheiden kann, was in meiner Situation am besten ist.


1
Keine wirkliche Antwort, aber ich schlage vor, Sie schauen sich an, wie das Qt-Toolkit das macht. Google QtMessageHandler. Und QMessageLogger. Es sieht sehr nach dem aus, was die anderen Antworten vermuten lassen.
Teimpz

Antworten:


8

Sie können mehrere Methoden verfügbar machen, um die Protokollierung von Ihrer Bibliothek zu erhalten, und alle bis auf eine in Adapter für die in der Bibliothek verwendete "echte" umschließen.

Sie entscheiden sich beispielsweise für eine interne std::function<void(std::string)>Sammlung, die jeder Logger-Rückruf darstellt. Sie bieten:

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

und auch

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

und auch

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

und weitere Variationen der Typen "Strings von irgendwoher empfangen", für die Sie Adapter implementieren möchten.


Ich denke, es würde irgendwann auch eine Art "Protokollierungsstufe" benötigen, wie Debug, Warnung, schwerwiegend, ... damit der Benutzer herausfiltern kann, was er braucht. Trotzdem guter Start.
Teimpz

4

Der einfachste Weg, einer Anwendung zu ermöglichen, auf die Protokollierungsfunktion zuzugreifen, besteht darin, eine Klasse / Funktion zu registrieren, um die Protokollnachricht zu empfangen. Was sie mit dieser Nachricht machen, hängt ganz von der Anwendung ab.

Mit C / C ++ können Sie Folgendes in Ihrer Bibliothek verwenden:

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

void registerLogMessageReceiver(LogMessageReceiver receiver,
                                void* user_data);

Eine Anwendung kann eine Funktion durch Aufrufen registrieren registerLogMessageReceiver. mit dem entsprechenden user_data. Im Protokollierungsbereich Ihrer Codebasis müssen Sie sicherstellen, dass Sie diese Funktion mit der entsprechenden Nachricht und der registrierten aufrufen user_data.

Wenn Sie sich nicht um C kümmern müssen, können Sie eine Klasse als Empfänger von Nachrichten verwenden.

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

und fügen Sie eine Funktion in der Bibliothek hinzu, damit eine Anwendung einen Protokollnachrichtenempfänger registrieren kann.

void registerLogMessageReceiver(LogMessageReceiver* receiver);

Eine Anwendung kann a registrieren, LogMessageReceiverindem sie die obige Funktion aufruft. Sie müssen einige politische Entscheidungen bezüglich des Eigentums der registrierten Personen treffen LogMessageReceiver. Wenn die Bibliothek den Empfänger übernimmt, muss sie deleteden Zeiger haben. Wenn die Bibliothek den Empfänger nicht übernimmt, muss sich die Anwendung um deleteden Empfänger kümmern .

Durch die Verwendung einer Klasse als Protokollnachrichtenempfänger kann das user_dataBit weggelassen werden, registerLogMessageReceiverda der Untertyp von LogMessageReceiverfrei ist, Daten zu speichern, die für seine Funktion nützlich sind. Es müssen keine zusätzlichen Benutzerdaten in der receiveFunktion übergeben werden.

Von dort aus kann es komplexer werden, je nachdem, wie ausgefeilt Ihr Protokollierungsmechanismus ist.

Beispielsweise können Sie verschiedene Protokollierungsstufen haben: Concise, Normal, Verbose oder LoggingLevel1, LoggingLevel2, ..., LoggingLevelN.

In diesem Fall müssen Sie der Anwendung erlauben, die Protokollierungsstufe zu steuern, die sie verwenden möchten.

Sobald Sie sich entscheiden, über den simplen Protokollierungsmechanismus hinauszugehen, stehen Ihnen unendlich viele Möglichkeiten zur Verfügung. Es macht keinen Sinn, sich hier mit ihnen zu befassen.


Dies ist eine gute Antwort, wenn Compiler vor C ++ 11 unterstützt werden müssen. Wenn nicht, würde ich Caleths Antwort bevorzugen. Die std :: -Funktion ermöglicht die Erfassung, was eine viel sicherere Alternative zur Leere ist *
Teimpz

1

Ich würde behaupten, dass Sie die Notwendigkeit einer Protokollierung in Verbindung mit Ihrer Bibliothek überdenken sollten . Besonders für C ++, wo es keine Standard-Logger-Schnittstelle gibt.

Unterschiedliche Anwendungen haben unterschiedliche Richtlinien für die Protokollierung. Eine Bibliothek sollte politikunabhängig sein.

Der Zweck einer Bibliothek besteht darin, einen Dienst bereitzustellen und vorzugsweise anzugeben, ob eine Anforderung für diesen Dienst erfolgreich war oder fehlgeschlagen ist. Idealerweise mit einem Hinweis darauf, warum [es fehlgeschlagen ist] über errno, Rückkehrcode, Ausnahme ... Wenn Sie eine Protokollierung wünschen, weil eine bereitgestellte Funktion an mehreren Stellen fehlschlagen kann , versuchen Sie möglicherweise , zu viel in einer Funktion zu tun. Vielleicht nicht , aber erwägen Sie die Möglichkeit.


@xaxxon Ich verstehe nicht, warum dies keine Antwort ist. Laut Wie schreibe ich eine gute Antwort? an [...] answer can be “don’t do that" [...].
doubleYou

1

Das Schreiben einer Bibliothek, die problemlos mit dem Protokollsystem der Hostanwendung verbunden werden kann, ist einfach, sofern Sie wissen, um welches System es sich handelt. Wenn es dafür mehrere Möglichkeiten gibt, ist es schwieriger und Sie werden durch die Tyrannei des kleinsten gemeinsamen Nenners eingeschränkt. Sie könnten eine Kompatibilitätsebene haben, die Ihre Bibliothek an die verschiedenen Protokollsysteme anpasst, aber jetzt können Sie sich nicht mehr auf eine nützliche, einzigartige Funktion verlassen, die nur ein System bietet. Wenn der Endbenutzer das andere System hat, ist diese nützliche einzigartige Funktion nicht vorhanden.

In der .Net-Welt gibt es (mindestens) zwei häufig verwendete Protokollsysteme, log4net und NLog. Beide sind ähnlich, aber nicht identisch. Ich habe bereits log4net verwendet und dann angefangen, NHibernate (eine ORM-Bibliothek) zu verwenden. Glücklicherweise wird log4net intern verwendet, sodass es einfach war, es einem Projekt hinzuzufügen. (Und hier bin ich mit der Antwort von @ Daniel nicht einverstanden: Manchmal ist es für NHibernate äußerst nützlich, seine Aktivitäten in demselben System, das ich an anderer Stelle verwende, ohne zusätzliche Arbeit detailliert zu protokollieren.) Wenn ich bereits in NLog investiert war, bedeutet dies auch Ich wechsle oder habe ein Projekt, das beides verwendet.

Zumindest bei der Protokollierung besteht normalerweise die Möglichkeit, einen benutzerdefinierten Nachrichten-Appender zu erstellen, den Sie so konfigurieren können, dass eine in einem System angemeldete Nachricht an das andere Protokollierungssystem weitergeleitet wird. Wenn ich also bereits NLog verwenden würde und wirklich damit weitermachen wollte, aber auch NHibernate verwenden möchte, könnte ich meine Zähne zusammenbeißen und einen log4net-Appender schreiben, der jede Nachricht an NLog weiterleitet. Es wäre unkompliziert, wenn die APIs ähnlich wären.

Die Alternativen bestehen darin, das beste System für Ihre verfügbaren Anforderungen auszuwählen und vorbehaltlos zu verwenden oder eine Art Adapterschicht zu verwenden, die dem Problem des kleinsten gemeinsamen Nenners unterliegt. Welches ist keine sehr befriedigende Antwort.


Dies und die Tatsache, dass C ++ einfache Dinge schwierig und schwierige Dinge noch schwieriger macht.
Rwong
Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.