Logfile erstellen



  • Hallo,
    Ich würde gern ein Logfile erstellen, das neben den Standard-Ausgaben wie Zeitpunkt und Loglevel folgende Features hat:
    - Es soll die Threadnummer angezeigt werden
    - Bei verschachtelten Funktionsaufrufen, sollen die Logausschriebe eingerückt werden.

    Z.B.
    Es wird Funktion1 aufgerufen. In dieser Funktion1 wird Funktion2 aufgerufen usw..

    Funktion1 --> Funktion2 --> Funktion3

    Im Logfile soll das dann ungefähr so aussehen:

    Zeitpunkt Loglevel Thread -->Funktion1
    Zeitpunkt Loglevel Thread   -->Funktion2
    Zeitpunkt Loglevel Thread      -->Funktion3
    Zeitpunkt Loglevel Thread      <--Funktion3
    Zeitpunkt Loglevel Thread   <--Funktion2
    Zeitpunkt Loglevel Thread <--Funktion1
    

    Hat jemand ne Idee wie man soetwas umsetzen kann?
    Vielleicht irgendwie den CallStack auslesen?

    Danke..



  • Das hatten wir vor vielen Jahren mal in einem Projekt. Mit dem Ergebnis, dass das Programm locker eine Minute zum Starten brauchte und nach dem Start schon ein Logfile von rund 60 Megabytes produziert hatte. Mit sowas nach Fehlern suchen, macht keinen Spaß...

    Wir haben es nicht mit Stack-Analyse gemacht (ich wüsste auch nicht, wie ein einfaches Programm das wohl durchführen sollte), sondern hatten einfach eine Klasse, die in Ctor und Dtor die entsprechenden Log-Einträge schrieb. So musste man lediglich in jeder Funktion ein Objekt dieser Klasse anlegen. Das eigentliche Logfile war eine Art Singleton, das auch den Zeitpunkt und die Thread-Id jedes Eintrags geschrieben hat.

    Wie gesagt, das Konzept war eigentlich nicht praktikabel, ich würde einen solchen Ansatz nicht empfehlen. Was sich als halbwegs nützlich herausgestellt hat, war ein Log, in dem nur Fehler auftauchten, in das also jede Exception geschrieben wurde. Mit solchen Log-Dateien kann man noch ganz gut umgehen. Und in der Regel interessieren ja auch nur Log-Einträge, die mit Fehlern zu tun haben.

    Stefan.



  • Danke für die schnelle Antwort.
    Das bestehende Logfile lässt sich im Moment schon nicht mehr lesen. Geschweige denn der Programmcode. Deswegen will ich da ein bisschen Struktur reinbringen.
    Ich will auch nicht jeden einzelnen Funktionsaufruf loggen, sondern nur die Wichtigsten um den Programmablauf verstehen zu können.

    Also quasi so:
    Funktion1(wichtig) --> Funktion2(unwichtig) --> Funktion3(wichtig)

    Zeitpunkt Loglevel Thread -->Funktion1
    Zeitpunkt Loglevel Thread   -->Funktion3
    Zeitpunkt Loglevel Thread   <--Funktion3
    Zeitpunkt Loglevel Thread <--Funktion1
    

    Ich weiß halt nicht, wie man festellen kann, wo der Logausschrieb eingerückt werden muss..



  • Ich weiß halt nicht, wie man festellen kann, wo der Logausschrieb eingerückt werden muss..

    mit static!? ;o)

    edit:

    struct log_writer
    {
      log_writer(const char* what)
      : what(what)
      {
        std::cout << std::setw(++level * 2) << "-> " << what;
      }
    
      ~log_writer()
      {
        std::cout << std::setw(--level * 2) << "<- " << what;
      }
    private:
      static std::size_t level = 0;
      const char* what;
    };
    

    sollte das machen, was du möchtest, wenn ich dich richtig verstanden habe^^


  • Mod

    @unskilled: Das dürfte aber Problem mit verschiedenen Threads geben.

    Ich bin nicht so der Experte für Threads, aber unskilleds Lösung dürfte sich auch verallgemeinern lassen, indem man die Logtiefe der Threads in einer statischen map speichert.



  • SeppJ schrieb:

    @unskilled: Das dürfte aber Problem mit verschiedenen Threads geben.

    Ich bin nicht so der Experte für Threads, aber unskilleds Lösung dürfte sich auch verallgemeinern lassen, indem man die Logtiefe der Threads in einer statischen map speichert.

    jo, sollte auch nur ne skizze sein
    static map klingt ganz vernünftig...

    bb



  • Ich wollts auch erst mit static machen. Aber wenn ich zwei Logausschriebe in einer Funktion hab, dann hauts schon nicht mehr hin..

    Ich glaub immer noch, dass die Idee ganz gut wäre, die Anzahl der Aufrufe aus dem CallStack auszulesen. Allerdings weiß ich noch nicht genau, wie das geht..



  • qwertz schrieb:

    Ich wollts auch erst mit static machen. Aber wenn ich zwei Logausschriebe in einer Funktion hab, dann hauts schon nicht mehr hin..

    versteh ich nicht... mach ma nen bsp., wie das in der fkt dann aussehen soll...

    bb



  • ein Vorschlag für eine single-thread Anwendung - bei multi-thread käme noch was dazu:

    #include <iostream>
    #include <iomanip> // setw
    #include <cmath> // sin
    
    namespace
    {
        const int TAB_ID = std::ios_base::xalloc();
    }
    template< typename E, typename Traits >
    std::basic_ostream< E, Traits >& tab( std::basic_ostream< E, Traits >& out )
    {
        const int tab_size = int( out.iword( TAB_ID ) );
        if( tab_size > 0 )
            out << std::setw( tab_size ) << out.fill();
        return out;
    }
    
    struct Indent
    {
        Indent( std::ios_base& out )
            : m_out( out )
        {
            m_out.iword( TAB_ID ) += 4;
        }
        ~Indent()
        {
            m_out.iword( TAB_ID ) -= 4;
        }
    private:
        std::ios_base& m_out;
        Indent( const Indent& );
        Indent& operator=( const Indent& );
    };
    
    #define TRC( content ) std::clog << tab << content << std::endl
    #define TRC_FUNC( name ) TRC( #name ); Indent indent( std::clog )
    
    // --   ab hier kommt der Anwender-Code
    void func1( int factor )
    {
        TRC_FUNC( func1 );
        TRC( "Ausgabe " << 3*factor );
    }
    
    void wasAnderes()
    {
        TRC_FUNC( wasAnderes );
        TRC( "Berechnung " << 5*1.7 << "; " << std::sin(0.1) );
        func1( 25 );
        func1( 26 );
    }
    
    int main()
    {
        using namespace std;
        func1( 8 );
        wasAnderes();
    
        return 0;
    }
    

    Ausgabe:

    func1
        Ausgabe 24
    wasAnderes
        Berechnung 8.5; 0.0998334
        func1
            Ausgabe 75
        func1
            Ausgabe 78
    


  • Vielleicht braucht man die Einrückungen gar nicht in die Datei schreiben und kann sich die Thread-spezifischen Daten in der LogFile-Klasse sparen.

    Wenn man in jede Zeile die Thread-Id schreibt, gefolgt von einem eindeutigen Zeichen für die verschiedenen Log-Einträge ("-->", "<--" usw.), und dann erst den eigentlichen Eintrag, kann man das Logfile leicht mit einem weiteren Programm aufbereiten, das dann die Einrückungen besorgt. Allerdings sollte es dann auch die Einträge "Thread started" und "Thread terminated" geben.

    Stefan.


Anmelden zum Antworten