none
TextWriterTraceListener RRS feed

  • Frage

  • Hallo,

    ist das so machbar? Der 'TextWriterTraceListener ' ist für mich ausreichend.

     //for (int i = 0; i < identLevel; i++)
     //    System.Diagnostics.Trace.Indent();
    Das funktioniert nicht. Sieht jemand etwas?


    Kann ich das so für jeden Eintrag machen?
     immer new ?
        myListener = new System.Diagnostics.TextWriterTraceListener();

    Danke für Tipps.

    Grüße Andreas

     

    public bool WriteFileLog(string sContent, int identLevel)
        {
          string sHilf;
          string sFilename;
          DateTime dateTime = DateTime.Now.ToLocalTime();
    
          sHilf = String.Concat(dateTime.ToString("yyyy.MM.dd,HH:mm:ss:ffff, - ,"), sContent);
          sFilename = String.Format("{0:0000000000}-{1}-{2}-{3:00}-{4}{5}", RLogID, RFilenameTag,
                                 dateTime.Year,
                                 dateTime.Month,
                                 dateTime.Day,
                                 RFilenameExtension);
    
    
          if (File.Exists(RFolder + sFilename))
          {
            System.Diagnostics.TextWriterTraceListener myListener = new System.Diagnostics.TextWriterTraceListener();
            System.Diagnostics.Trace.Listeners.Add(myListener);
    
            //for (int i = 0; i < identLevel; i++)
            //  System.Diagnostics.Trace.Indent();
    
            Trace.AutoFlush = true;
    
            Trace.WriteLine(sHilf);
            //System.Diagnostics.Trace.Flush();
            //System.Diagnostics.Trace.Close();
    
            myListener.TraceOutputOptions = TraceOptions.DateTime | TraceOptions.Timestamp;
    
            
            Trace.AutoFlush = true;
            Trace.WriteLine("Test output 1");
            Trace.IndentLevel = 5;
    
          }
          else
          {
            Stream swNew = File.Create(RFolder + "\\" + sFilename);
            System.Diagnostics.TextWriterTraceListener myListener = new System.Diagnostics.TextWriterTraceListener(swNew);
            System.Diagnostics.Trace.Listeners.Add(myListener);
    
            for (int i = 0; i < identLevel; i++)
              System.Diagnostics.Trace.Indent();
    
            System.Diagnostics.Trace.WriteLine(sHilf);
            System.Diagnostics.Trace.Flush();
            System.Diagnostics.Trace.Close();
          }
          return true;
        }
    Dienstag, 25. Januar 2011 18:07

Antworten

  • Hallo Andreas,

    auf keinen Fall immer "new". Und die Listener werden ja eh global in den "Trace.Listeners" gehalten.
    Das Anlegen mit File.Create ist ja ok. Wenn die angegebene Datei nicht vorhanden ist, wird sie erstellt. Wenn sie vorhanden (und nicht schreibgeschützt) ist, wird der Inhalt überschrieben.


            > Indent funktioniert nicht

    hier ein funktionierendes Beispiel, aber nur, um Dir die Funktionalität zu verdeutlichen:

      public Form1()
      {
       InitializeComponent();
    
       TextWriterInitialisieren();
       Trace.WriteLine("Test Ausgabe 1");
       Trace.Indent();
       Trace.WriteLine("Test Ausgabe 2");
    
       // Trace.Listeners[0] ist der DefaultListener.
       int letzterListener = Trace.Listeners.Count;
       TextWriterTraceListener tw = Trace.Listeners[letzterListener-1] as TextWriterTraceListener;
       FileStream sw = (tw.Writer as StreamWriter).BaseStream as FileStream;
       sw.Close();
       MessageBox.Show("Inhalt der Log-Datei: \n" + File.ReadAllText(sw.Name));
      }
    
      public void TextWriterInitialisieren()
      {
       Stream datei = File.Create("Demo.log");
       TextWriterTraceListener dateiListener = new
         TextWriterTraceListener(datei);
       Trace.AutoFlush = true;
       Trace.Listeners.Add(dateiListener);
      }
    

    ciao Frank
    Donnerstag, 27. Januar 2011 09:12

Alle Antworten

  • Hallo,

    hoffe nicht zu sehr zu quängeln 

    Das ist ja static.

    System.Diagnostics.Trace.Listeners.Add(myListener);

    Wurde ja immer dazu getan, besser vermutlich global in der Klasse definieren.

    Oder?

    Ich will halt prüfen, Datei vorhanden? Nein - Erzeugen.

    Grüße Andreas

    Mittwoch, 26. Januar 2011 20:11
  • Hallo Andreas,

    auf keinen Fall immer "new". Und die Listener werden ja eh global in den "Trace.Listeners" gehalten.
    Das Anlegen mit File.Create ist ja ok. Wenn die angegebene Datei nicht vorhanden ist, wird sie erstellt. Wenn sie vorhanden (und nicht schreibgeschützt) ist, wird der Inhalt überschrieben.


            > Indent funktioniert nicht

    hier ein funktionierendes Beispiel, aber nur, um Dir die Funktionalität zu verdeutlichen:

      public Form1()
      {
       InitializeComponent();
    
       TextWriterInitialisieren();
       Trace.WriteLine("Test Ausgabe 1");
       Trace.Indent();
       Trace.WriteLine("Test Ausgabe 2");
    
       // Trace.Listeners[0] ist der DefaultListener.
       int letzterListener = Trace.Listeners.Count;
       TextWriterTraceListener tw = Trace.Listeners[letzterListener-1] as TextWriterTraceListener;
       FileStream sw = (tw.Writer as StreamWriter).BaseStream as FileStream;
       sw.Close();
       MessageBox.Show("Inhalt der Log-Datei: \n" + File.ReadAllText(sw.Name));
      }
    
      public void TextWriterInitialisieren()
      {
       Stream datei = File.Create("Demo.log");
       TextWriterTraceListener dateiListener = new
         TextWriterTraceListener(datei);
       Trace.AutoFlush = true;
       Trace.Listeners.Add(dateiListener);
      }
    

    ciao Frank
    Donnerstag, 27. Januar 2011 09:12
  • Hallo Andreas,

     //for (int i = 0; i < identLevel; i++)
     //    System.Diagnostics.Trace.Indent();
    Das funktioniert nicht. Sieht jemand etwas?

    Trace.Indent setzt den Einzug für ALLE Listener in der Listener-Auflistung, bewirkt per se aber noch keine Einrückung. Und wenn keine TraceListener da sind, gibt es nur einige wenige Seiteneffekte, z.B. DiagnosticsConfiguration.AutoFlush wird auf true gesetzt und es wird DiagnosticsConfiguration.UseGlobalLock gesetzt. Auch ist die Methode Indent mit einem ConditionalAttribut dekoriert, was seine Verwendung (und nicht nur) auf Debug-Szenarien reduziert. Für TextWriterTraceListener kannst Du TraceListener.WriteIndent aufrufen. Noch besser wäre es natürlich wenn Du von TextWriterTraceListener eine eigene Klasse ableiten würdest und die zusätzliche Funktionaliltät, die Du brauchst (wie Einzug, Datei-Erstellung) usw. in diese Klasse kapseln würdest. Du könntest in der abgeleiteten Klasse Write() und WriteLine() überschreiben und sie Deinen Vorstellungen anpassen. Auf welcher Ebene Du den TextWriterTraceListener definierst hängt u.a. davon ab, wie Du ihn später verwenden willst und wie Du die Logs auswertest. Wie Frank bereits schrieb*, macht es aber keinen Sinn für jede Trace-Operation einen neuen Listener zu instanziieren. Wenn Du nur einen einzigen Listener in Deiner Anwendung verwenden möchtest, instanziiere und initialisiere ihn beim Start der Anwendung (statisch, öffentlich, in Eigenschaft gekapselt, etc.) Auch würde ich nicht gleich Trace.WriteLine verwenden (das ruft für jeden Listener in der Auflistung WriteLine auf), sondern die gleichnamige Methode des instanziierten TextWriterTraceListeners.


    Gruss
    Marcel

    (*) Das ist für Frank, er wird's schon verstehen.
     

    Donnerstag, 27. Januar 2011 11:04
    Moderator
  • Hallo Marcel,

    • Du schriebst: > "Auch würde ich nicht gleich Trace.WriteLine verwenden (das ruft für jeden Listener in der Auflistung WriteLine auf),".

    je nach Szenario ist das allerdings andersherum genau der Grund, warum man Trace.WriteLine benutzt und diese Nutzung ist auch später im Code intendiert. Etwa (bspw.) definiert man über EL5 die zu nutzenden Listener in der app.config. Dann will man eigentlich (und nutzt standardmäßig), dass es zum Beispiel in den DefaultTraceListener (also etwa letztlich im Ausgabefenster sichtbar oder innerhalb DebugView sichtbar) und eben "zusätzlich" in eine Datei oder einen Eventlog oder DB (o.ä.) geloggt werden. Das ist eigentlich die Standard-Sichtweise/Nutzung bei den Listenern. Um explizit nur in einen speziellen Listener zu schreiben (was eher ein Ausnahmefall ist), kann man ggf. ungewünschte in der config deaktivieren, oder entsprechende Eigenschaften der Listener über Code setzen.


    ciao Frank
    Donnerstag, 27. Januar 2011 12:11
  • Hallo Frank,

    Marcel schrieb: > Trace.WriteLine [...] ruft für jeden Listener in der Auflistung WriteLine auf.
    Frank schrieb:  > je nach Szenario [...] Das ist eigentlich die Standard-Sichtweise/Nutzung bei den Listenern.

    Ich kann mir einfach nicht vorstellen, wie Du auf diese Idee kommst. Dass es möglich ist, ist selbstverständlich. Aber dass man Trace.WriteLine im ganzen Code streut, statt die Methoden konkreter Listener aufzurufen, halte ich für eine recht bedenkliche Praxis, zumal:

    - jeder Listener Ausnahmen werfen kann und
    - durch die non-deterministische Verwendung beim Kunden Sicherheitslücken entstehen können

    Auch hatte ich Andreas erstes Posting entnommen, dass er nicht andere Listener verwenden möchte.

    Gruss
    Marcel

    Donnerstag, 27. Januar 2011 12:36
    Moderator
  • Hallo Marcel,

    es ist nicht meine Idee, es ist eher die standard practice und intendierte Art, die in den meisten Logging Frameworks und auch in .NET benutzt wird (und auch gemäß Standard-SW-Prinzipien -> loose Kopplung, Information Hiding, Law of Demeter, etc.) anzustreben ist.

    Der Code selber soll (und darf ansich) gar nicht wissen, wie das Logging später (unter der Haube) implementiert ist - wo und wieviel geloggt wird. Das kann man in Konfigurationen später bestimmen (dafür sind sie vorgesehen). Deswegen sollte das auch möglichst flexibel und erweiterbar in Konfigurationsdatei oder anderen Mitteln definierbar und modifizierbar sein. Wenn die Listener sichtbar angesprochen werden würden, würde das gegen "Information Hiding", "Law of Demeter" und einige andere SW-Prinzipien verstossen (CCD). Ganz praktisch unschön würde man auch die von .NET vorgesehenen Standard-Listener verlieren, wenn man die Listener explizit anspricht. Im Code weiss man gar nicht, wann man welche Listener wohl benötigt (man kennt vielleicht [u.a.] den TraceLevel). Das wird erst zur Laufzeit eingestellt. Das ich in meinem Beispiel-Code den Listener direkt anspreche ist übrigens nur zur Erklärung der Funktion für Andreas gedacht - man erkennt sofort auch die Gefahren einer solchen Praxis.

    • > Auch hatte ich Andreas erstes Posting entnommen, dass er nicht andere Listener verwenden möchte.

    ja, ich habe das zwar anders verstanden, aber wenn Du das so interpretiert hast, muss man aus Deiner Sicht sicher explizit den Listener ansprechen. Ich wollte hier nur kurz erwähnen, dass es im Standard ja andersherum (also indirekt über die Listener) gemacht wird.


    ciao Frank
    Donnerstag, 27. Januar 2011 16:14
  • Hallo Frank,

    > es ist nicht meine Idee, es ist eher die standard practice

    Nein, ist es nicht. Man kann im Bereich Ablaufverfolgungen solche Generalisierungen nicht machen. Nicht alle Listener sind thread-safe, XmlWriterTraceListener z.B. ist es nicht, nicht alle Listener sind gleich robust und performant. Wenn eine Ausnahme bei der Verarbeitung durch einen TraceListener geworfen wird, kann das die ganze Anwendung zum Absturz bringen. Je mehr Listener, desto größer die Gefahren. Ich möchte mir auch nicht vorstellen, was z.B. passiert, wenn ich ein verbose-TraceLevel in einer WCF-Anwendung setze, die mehrere TraceListener aktiv hat (wieviele Timeouts würde das verursachen, die wiederum eine Flut von Trace-Meldungen verusachen würden, die wiederum...). Übrigens wird die Ablaufverfolgung gern, und nicht nur in WCF über TraceSource.TraceEvent() bewerkstelligt, das viel feinere Steuerungsmechanismen anbietet. Das Ablaufverfolgungssystem wurde schon seit der Version 2.0 verbessert.

    Aus der Dokumentation: "Ein wichtiger Unterschied in Version 2.0 ist, dass die Ablaufverfolgung mithilfe von Instanzen der TraceSource-Klasse eingeleitet werden kann. TraceSource ist als verbessertes System zur Ablaufverfolgung vorgesehen; die Klasse kann statt den statischen Methoden der älteren Trace-Klasse und der Debug-Klasse verwendet werden. Die vertraute Trace-Klasse und die Debug-Klasse sind immer noch vorhanden, es wird jetzt jedoch empfohlen, die TraceSource-Klasse für die Ablaufverfolgung zu verwenden."

    Gewusst wie: Verwenden von TraceSource und Filtern für Ablaufverfolgungslistener
    http://msdn.microsoft.com/de-de/library/ms228993(v=VS.90).aspx

    Gewusst wie: Erstellen und Initialisieren von Ablaufverfolgungsquellen
    http://msdn.microsoft.com/de-de/library/ms228984(v=VS.90).aspx

    > Wenn die Listener sichtbar angesprochen werden würden, würde das gegen "Information Hiding", "Law of Demeter" und einige andere SW-Prinzipien verstossen (CCD) [...]

    Was soll ich nur dazu sagen? Ich lass es lieber.

    Gruss
    Marcel

    Donnerstag, 27. Januar 2011 17:45
    Moderator
  • Marcel,

    :-) TraceSource kennt er schon durch mich, und auch bei TraceSource ist natürlich der Standard das über Konfigurationsdatei die gewünschten Listenern bestückt werden. Der Code hat keine Ahnung davon, wohin letztlich geloggt wird (sollte es normal nicht). Es ist das gleiche saubere best practice Prinzip. (Tip zu Deinen veralteten Links: klickt auf ".NET Framework 4 (rechts oben), dann wird der aktuelle Artikel angezeigt - es verschwindet dann quasi nur (vs=VS.90) ).

    Die Anwendung kann aber die durch die Konfigurationsdatei festgelegten Eigenschaften dynamisch ändern, um die vom Benutzer angegebenen Einstellungen zu überschreiben. Und das ist sicher ein wichtiges Szenario, wenn die Anwendung unbedingt vielleicht bei kritischen Fehlern in bestimmte eigene Logs schreiben will.

    gut, ich denke, wir sollten dann mal beenden. Es gibt dann keinen Konsens - und das ist auch ok.
    Da ich gerade aktuell ellenlange Recherchen für unsere Firma bzgl. Logging gemacht habe, sage ich mal ganz zurückhaltend: EOT ;-)


    ciao Frank
    Donnerstag, 27. Januar 2011 20:25
  • Frank,

    > TraceSource kennt er schon durch mich [...]

    In diesem Thread hast Du Trace.WriteLine als Standard dargestellt. Das war, ist und bleibt fachlich falsch.

    >  auch bei TraceSource ist natürlich der Standard das über Konfigurationsdatei

    Das Verwenden von eigenen Listenern steht nicht im Konflikt mit der Verwendung der Konfigurationsdatei.

    > Tip zu Deinen veralteten Links [...]

    Auch im "neuen" .NET 4.0-Artikel steht freilich dasselbe drin: "Die vertraute Trace-Klasse und die Debug-Klasse sind immer noch vorhanden, es wird jetzt jedoch empfohlen, die TraceSource-Klasse für die Ablaufverfolgung zu verwenden."

    Gruss
    Marcel

     

    Freitag, 28. Januar 2011 07:21
    Moderator
  • Marcel,

    Da er schon durch mich weiss, dass TraceSource empfohlen ist ...
          Zitat Frank dort:
          "es wird jetzt jedoch empfohlen, die TraceSource-Klasse für die Ablaufverfolgung zu verwenden."
    , hat er Gründe, warum er Trace verwenden möchte, was auch nach wie vor erlaubt ist. Deine Fehlinterpretationen meiner Posting, wie: "Trace wäre besser als TraceSource" ist Deine Sache.

    Hier in diesem Thread benutzt er die Trace-Klasse, die die gleiche Standard best practice (wie bei TraceSource) benutzt, die Listener nicht explizit (sondern nur in Ausnahmefällen) direkt anzusprechen.

    EOT.

    ciao Frank
    • Bearbeitet Frank Dzaebel Freitag, 28. Januar 2011 09:07 EOT. zugefügt
    Freitag, 28. Januar 2011 08:00
  • Frank,

    > deine Fehlinterpretationen meiner Posting [...]

    Hier gibt es nichts was man falsch interpretieren könnte. Du stellts die Verwendung von Trace.WriteLine als standard practice dar. Das ist fachlich falsch. Die Dokumentation sagt unmißverständlich: "Die vertraute Trace-Klasse und die Debug-Klasse sind immer noch vorhanden, es wird jetzt jedoch empfohlen, die TraceSource-Klasse für die Ablaufverfolgung zu verwenden." Deine Argumentation dazu, die ja das Ganze untermauern soll (information hiding, law of demeter usw.), bedarf eigentlich - vor allem auf dem Hintergrund von TraceSource, aber auch ganz allgemein - keines weiteren Kommentars.

    Gruss
    Marcel

     

    Freitag, 28. Januar 2011 08:18
    Moderator