none
Accodare testo in diversi file di log RRS feed

  • Domanda

  • Buonasera a tutti,

    vi scrivo nuovamente per un aiuto.

    Sto lavorando ad un software che lavora con 10 form in maniera indipendente l'uno dall'altro e per ogni form ho la necessità di accodare dei log in diversi file, uno per ogni form.

    Tutto il codice è scritto in maniera asincrona (almeno credo), ma l'append nei vari file non avviene simultaneamente ma un file alla volta.

    L'ho constatato verificando l'aumentare delle dimensioni dei file di log tramite la vista Dettagli di Esplora Risorse.

    Inoltre ho notato che quando un form scrive diversi Kb di testo, anche tutta l'UI di blocca, compreso una label pilotata da un timer che tiene conto del tempo trascorso.

    In sostanza, ogni form utilizza un'istanzia la classe che gestisce i log

    public partial class FormBrowser : Form { public LogDedicato LogDedicato; public FormBrowser(string titoloForm, LogDedicato sessioneLog) {

    var nomeFile = "log_" + this.Text + ".log"; LogDedicato = new LogDedicato(@"C:\", nomeFile);

    // esempio di scrittura log LogDedicato.ScriviLogAsync("Istanziato FormBrowser: " + titoloForm); } }

    Mentre la classe Log è così scritta

    public class LogDedicato { private string Folder { get; set; } private string FileName { get; set; } private string filePath;

    public LogDedicato(string folder, string fileName) { Folder = folder; FileName = fileName; filePath = folder + fileName; } public async Task ScriviLogAsync(string text) { using (StreamWriter w = File.AppendText(filePath)) { Task task = w.WriteLineAsync(DateTime.Now.ToLongTimeString() + " - " + testo); } } }


    Dove sto sbagliando?

    Grazie in anticipo



    • Modificato Thomas_nix martedì 20 dicembre 2016 00:26
    martedì 20 dicembre 2016 00:24

Risposte

  • La cosa che mi sembra strana a colpo d'occhio è lo streamwriter generato con AppendText... ma se funziona Ok quello che posso fare per te è mostrarti un sistema di log centralizzato funzionante, in modo tale che tu possa prenderne spunto per il tuo caso:

    http://blogs.dotnetwork.it/sabrina/blog/implemetare-un-sistema-di-log-generico-per-le-nostre-applicazioni/

    http://blogs.dotnetwork.it/sabrina/blog/testare-il-sistema-di-log-degli-eventi-in-una-applicazione-wpf/

    Nel primo articolo viene implementato quanto necessario ad avere un sistema di log pervasivo della tua applicazione che possa essere usato in qualsiasi punto della stessa.

    Nel secondo viene implementata la parte di codice applicativo che specificamente effettua il log.

    Probabilmente la porzione che contiene il codice eseguito all'evento LogNewEntry nel secondo articolo è quella che ti può aiutare.

    Non è asincrona, ma sarebbe semplice renderla tale, non ha diversi tipi di log, ma questo lo potresti realizzare semplicemente aggiungendo un parametro che indichi all'evento su quale dei log dovrà scrivere.

    Il fatto di usare una routine singola e centralizzata per loggare da ogni punto del programma, ha diversi vantaggi, il primo, che hai un punto solo del codice da dover verificare, il secondo che puoi aggiungere il log da ulteriori oggetti senza aver bisogno di modificare il codice. Inoltre, rendere asincrona la scrittura del file non credo sia complicato, visto che la routine è una sola.

    Fai solo attenzione che Asincrono e con accesso multiplo non sono la stessa cosa e ovviamente devi proteggere la parte di codice che scrive fisicamente su file in modo tale che non accada che 2 diverse chiamate scrivano in contemporanea sullo stesso file, altrimenti ti troverai ad avere dei problemi.

    saluti


    Sabrina C. - http://www.dotnetwork.it

    martedì 20 dicembre 2016 11:01
  • Scusa il ritardo nella risposta, ma ogni tanto il lavoro mi raggiunge anche se cerco di correre più veloce di lui.

    Ho fatto un piccolo test sul problema dei log in multithreading e ti ho postato il codice su questo post:

    http://blogs.dotnetwork.it/sabrina/blog/simulare-un-log-su-file-multipli/

    Se lo guarderai, vedrai come ho implementato un unico metodo che scrive i log anche se scrivo su log diversi da thread diversi.

    Ti do solo un indizio sul problema che ho riscontrato in questo punto del codice:

    public void Run()
    {
    	ExitCycles = false;
    	int ndx = 1;
    	for (int i = 0; i < Program.LogNames.Count; i++)
    	{
    		Thread t = new Thread(() => LogEachSecond(ndx));
    		Console.Write("Starting thread: ");
    		Console.WriteLine(ndx.ToString());
    		t.Start();
    		Thread.Sleep(200);
    		ndx++;
    	}
    
    	Console.WriteLine("Press enter to end the application");
    	Console.ReadLine();
    	ExitCycles = true;
    	Console.ReadLine();
    
    }

    Se inserivo la riga ndx++; prima del Thread.Sleep(200) che mi serviva semplicemente per non avere tutti i thread troppo appiccicati, visto che la scrittura viene fatta una volta ogni 2 secondi, mi venivano un numero di file di log sbagliati, guardando in profondità, mi sono accorta che l'ndx++; veniva eseguito prima che il Thread partisse, pertanto il valore che arrivava nel parametro di apertura del thread era 23456 invece di 12345 come sarebbe stato giusto.

    messo a punto questo, spostando l'incremento dopo la pausa, I file vengono generati ed incrementati correttamente e in sinc.

    Ti consiglio di verificare il tuo codice per capire se hai qualche problema simile, inoltre se osserverai la parte del metodo che scrive fisicamente sul file, ho implementato un thread lock per sicurezza. Potrebbe non essere necessario, ma a scanso di equivoci, essendo un metodo statico.


    Sabrina C. - http://www.dotnetwork.it

    giovedì 22 dicembre 2016 17:17

Tutte le risposte

  • La cosa che mi sembra strana a colpo d'occhio è lo streamwriter generato con AppendText... ma se funziona Ok quello che posso fare per te è mostrarti un sistema di log centralizzato funzionante, in modo tale che tu possa prenderne spunto per il tuo caso:

    http://blogs.dotnetwork.it/sabrina/blog/implemetare-un-sistema-di-log-generico-per-le-nostre-applicazioni/

    http://blogs.dotnetwork.it/sabrina/blog/testare-il-sistema-di-log-degli-eventi-in-una-applicazione-wpf/

    Nel primo articolo viene implementato quanto necessario ad avere un sistema di log pervasivo della tua applicazione che possa essere usato in qualsiasi punto della stessa.

    Nel secondo viene implementata la parte di codice applicativo che specificamente effettua il log.

    Probabilmente la porzione che contiene il codice eseguito all'evento LogNewEntry nel secondo articolo è quella che ti può aiutare.

    Non è asincrona, ma sarebbe semplice renderla tale, non ha diversi tipi di log, ma questo lo potresti realizzare semplicemente aggiungendo un parametro che indichi all'evento su quale dei log dovrà scrivere.

    Il fatto di usare una routine singola e centralizzata per loggare da ogni punto del programma, ha diversi vantaggi, il primo, che hai un punto solo del codice da dover verificare, il secondo che puoi aggiungere il log da ulteriori oggetti senza aver bisogno di modificare il codice. Inoltre, rendere asincrona la scrittura del file non credo sia complicato, visto che la routine è una sola.

    Fai solo attenzione che Asincrono e con accesso multiplo non sono la stessa cosa e ovviamente devi proteggere la parte di codice che scrive fisicamente su file in modo tale che non accada che 2 diverse chiamate scrivano in contemporanea sullo stesso file, altrimenti ti troverai ad avere dei problemi.

    saluti


    Sabrina C. - http://www.dotnetwork.it

    martedì 20 dicembre 2016 11:01
  • Ciao,

    intanto grazie per la risposta; sto ancora studiando parecchio del C# e apprezzo molto questi tutorial in italiano che mi permettono di imparare come strutturare correttamente una programma :)

    Ho dato uno sguardo veloce alla libreria e all'esempio. Anche se al momento non comprendo al 100% tutto il processo ho visto che, indipendentemente da tutta a struttura, l'evento che gestisce fisicamente la scrittura dei log nel file è (ho commentato le parti ininfluenti allo scopo):

    /// <summary>
    /// Handles the LogNewEntry event of the EventLogger control.
    /// </summary>
    /// <param name="sender">The source of the event.</param>
    /// <param name="e">The <see cref="SendMessageEventArgs"/> instance containing the event data.</param>
    void EventLogger_LogNewEntry(object sender, SendMessageEventArgs e)
    {
    	try
    	{
    		if (EventLogger.LogType != LogType.None)
    		{
    			StringBuilder message = new StringBuilder();
    			/*bool doLog = false;
    			switch (e.MessageType)
    			{
    				case MessageType.Error:
    					{
    						doLog = true;
    						break;
    					}
    				case MessageType.Warning:
    					{
    						if (EventLogger.LogType == LogType.WarningsAndExceptions || EventLogger.LogType == LogType.AllMessages)
    						{
    							doLog = true;
    						}
    						break;
    					}
    				case MessageType.Info:
    					{
    						if (EventLogger.LogType == LogType.AllMessages)
    						{
    							doLog = true;
    						}
    						break;
    					}
    			}*/
    			if (true) //originale 'doLog'
    			{
    				message.AppendLine(new string('*', 80));
    				message.AppendFormat("[{0} {1}]", DateTime.Now.ToShortDateString(), DateTime.Now.ToLongTimeString());
    				message.AppendLine();
    				message.Append(e.MessageToLog);
    				message.AppendLine();
    				message.AppendLine(new string('*', 80));
    				message.AppendLine();
    				using (StreamWriter sw = new StreamWriter(mLogFileName, true, Encoding.UTF8))
    				{
    					sw.Write(message.ToString());
    					sw.Flush();
    					sw.Close();
    				}
    			}
    		}
    	}
    	catch (Exception)
    	{
    		//Se il logger da un eccezione c'è qualcosa che non va ma non possiamo
    		//gestirla
    	}
    }	}

    che, se non sbaglio, è molto simile alla procedura utilizzata da me.

    Ripeto, non sono per niente esperto di C# quindi non mi stupirei se sbagliassi.

    Le differenze che vedo sono:

    - viene utilizzato un evento per scrivere il log anziché invocare un metodo della classe.

    - viene utilizzato un StringBuilder per creare una stringa invece di scrivere direttamente nello stream.

    - chiude forzatamente il flusso anche se è all'interno di un costrutto "using".

    - viene utilizzata una classe statica anziché istanziarne una (nel mio caso è una per ogni form).

    Una o tutte queste differenze possono dare il problema che ho riscontrato?

    Grazie

    martedì 20 dicembre 2016 15:19
  • Scusa il ritardo nella risposta, ma ogni tanto il lavoro mi raggiunge anche se cerco di correre più veloce di lui.

    Ho fatto un piccolo test sul problema dei log in multithreading e ti ho postato il codice su questo post:

    http://blogs.dotnetwork.it/sabrina/blog/simulare-un-log-su-file-multipli/

    Se lo guarderai, vedrai come ho implementato un unico metodo che scrive i log anche se scrivo su log diversi da thread diversi.

    Ti do solo un indizio sul problema che ho riscontrato in questo punto del codice:

    public void Run()
    {
    	ExitCycles = false;
    	int ndx = 1;
    	for (int i = 0; i < Program.LogNames.Count; i++)
    	{
    		Thread t = new Thread(() => LogEachSecond(ndx));
    		Console.Write("Starting thread: ");
    		Console.WriteLine(ndx.ToString());
    		t.Start();
    		Thread.Sleep(200);
    		ndx++;
    	}
    
    	Console.WriteLine("Press enter to end the application");
    	Console.ReadLine();
    	ExitCycles = true;
    	Console.ReadLine();
    
    }

    Se inserivo la riga ndx++; prima del Thread.Sleep(200) che mi serviva semplicemente per non avere tutti i thread troppo appiccicati, visto che la scrittura viene fatta una volta ogni 2 secondi, mi venivano un numero di file di log sbagliati, guardando in profondità, mi sono accorta che l'ndx++; veniva eseguito prima che il Thread partisse, pertanto il valore che arrivava nel parametro di apertura del thread era 23456 invece di 12345 come sarebbe stato giusto.

    messo a punto questo, spostando l'incremento dopo la pausa, I file vengono generati ed incrementati correttamente e in sinc.

    Ti consiglio di verificare il tuo codice per capire se hai qualche problema simile, inoltre se osserverai la parte del metodo che scrive fisicamente sul file, ho implementato un thread lock per sicurezza. Potrebbe non essere necessario, ma a scanso di equivoci, essendo un metodo statico.


    Sabrina C. - http://www.dotnetwork.it

    giovedì 22 dicembre 2016 17:17