2012-03-18 13 views
6

Estoy haciendo un programa que controla un servidor de juegos. Una de las funciones que estoy haciendo es un monitor de archivo de registro en vivo.Cómo supervisar el archivo de texto y salir continuamente del contenido en un cuadro de texto

Hay un archivo de registro (un archivo de texto simple) que se actualiza por el servidor mientras se ejecuta.

¿Cómo reviso continuamente el archivo de registro y lo visualizo en un RichTextBox?

Hice esta simple función simplemente intente obtener el contenido del registro. Obtendrá, por supuesto, el texto fila por fila y lo enviará a mi cuadro de texto. También bloqueará el programa mientras se ejecute el bucle, así que sé que es inútil.

public void ReadLog() 
{ 
    using (StreamReader reader = new StreamReader("server.log")) 
    { 
    String line; 

    // Read and display lines from the file until the end of the file is reached. 
    while ((line = reader.ReadLine()) != null) 
    { 
     monitorTextBox.AppendText(line + "\n"); 
     CursorDown(); 
    } 
    } 
} 

Pero, ¿cómo resolver el monitoreo en vivo lo más simple posible?

* * EDITAR

estoy usando solución Prescots. Buena cosa.

Por el momento estoy usando un sstreamreader para poner el texto del archivo en mi cuadro de texto. Me encontré con el problema es que, cuando intenté acceder a cualquiera de los controles gui en mi controlador de eventos, el programa simplemente se detuvo sin errores ni advertencias.

Descubrí que tiene que ver con el enhebrado. He resuelto que de esta manera:

private void OnChanged(object source, FileSystemEventArgs e) 
{ 
    if (monitorTextField.InvokeRequired) 
    { 
     monitorTextField.Invoke((MethodInvoker)delegate { OnChanged(source, e); }); 
    } 
    else 
    { 
     StreamReader reader = new StreamReader("file.txt"); 

     monitorTextField.Text = ""; 
     monitorTextField.Text = reader.ReadToEnd(); 
     reader.Close(); 
     CursorDown(); 
    } 
} 

Ahora mi único problema es que el archivo.txt es utilizado por el servidor así que no puedo acceder a ella, ya que está "siendo utilizado por otro proceso". No puedo controlar ese proceso ... así que tal vez no tenga suerte.

Pero el archivo se puede abrir en el bloc de notas mientras se está ejecutando el serevr, por lo que de alguna manera debe ser posible. Tal vez pueda hacer una copia temporal del archivo cuando se actualice y lea la copia. No ...

+0

usa el temporizador ... y revísalo cada 10 segundos o 5 segundos ... – SolidSnake

Respuesta

13

Mira la clase System.IO.FileSystemWatcher:

public static Watch() 
{ 
    var watch = new FileSystemWatcher(); 
    watch.Path = @"D:\tmp"; 
    watch.Filter = "file.txt"; 
    watch.NotifyFilter = NotifyFilters.LastAccess | NotifyFilters.LastWrite; //more options 
    watch.Changed += new FileSystemEventHandler(OnChanged); 
    watch.EnableRaisingEvents = true; 
} 

/// Functions: 
private static void OnChanged(object source, FileSystemEventArgs e) 
{ 
    if(e.FullPath == @"D:\tmp\file.txt") 
    { 
     // do stuff 
    } 
} 

Editar: si usted sabe algunos detalles sobre el archivo, se puede manejar de la manera más eficiente para obtener la última línea. Por ejemplo, tal vez cuando lea el archivo, puede borrar lo que ha leído, de modo que la próxima vez que lo actualice, solo tome lo que esté allí y lo muestre. Quizás sepa que se agrega una línea a la vez, luego su código puede saltar inmediatamente a la última línea del archivo. Etc.

+0

¡Gracias! Eso ayudara mucho. Soy muy nuevo en esto ... En este momento me meto en el problema novato de obtener "El método debe tener un tipo de devolución", eso no ocurre si hago que la clase Watch() como esta "public void Watch () ". Probablemente lo estoy creando en el lugar o camino equivocado. Lo estoy poniendo en mi "clase parcial pública MYPROGRAM: Formulario" – Christoffer

+0

ah sí lo siento, escribí ese código que rodea muy rápido. Puedes extraer el código del observador y ponerlo siempre que sea apropiado en tu código, no tiene que estar en una función Watch() que tengo arriba. – Prescott

+0

¡Gracias! Acabo de agregar algo de información a la primera pregunta (nueva aquí ... ¿Estoy violando alguna regla al hacerlo?) – Christoffer

0

Intente agregar un temporizador y configure Timer.Tick en un intervalo de 1 segundo. En Timer.Tick ejecuta la función.

private void myTimer_Tick(object sender, EventArgs e) 
{ 
    ReadLog(); 
} 
1

Como @Prescott sugirió, use un FileSystemWatcher. Y asegúrese de abrir el archivo con el modo apropiado FileShare (FileShare.ReadWrite parece ser apropiado), ya que el servidor aún puede abrir el archivo. Si intenta abrir el archivo exclusivamente mientras otro proceso lo utiliza, la operación de apertura fallará.

También para obtener un poco de rendimiento, puede recordar la última posición hasta la que ya haya leído el archivo y solo lea las partes nuevas.

+0

¿Puedo hacer eso si uso StreamReader? – Christoffer

+0

Creo que para buscar una posición específica, tendrá que acceder directamente al 'FileStream' subyacente (a través de la propiedad' Posición'). Por lo tanto, crea primero el 'FileStream', vaya a la posición correcta, construya un' StreamReader' en la parte superior de la secuencia, lea los datos y luego obtenga la posición final nuevamente desde 'FileStream'. (No lo he probado, espero que funcione como se describe ...) – MartinStettner

3

Aunque el FileSystemWatcher es la solución más simple, en realidad no es confiable. A menudo, un archivo se puede actualizar con nuevos contenidos, pero el FileSystemWatcher no activa un evento hasta segundos después y con frecuencia nunca.

La única forma confiable que he encontrado para abordar esto es verificar los cambios en el archivo de forma regular utilizando un objeto System.Timers.Timer y verificar el tamaño del archivo.

He escrito una pequeña clase que demuestra esta disponible aquí:

https://gist.github.com/ant-fx/989dd86a1ace38a9ac58

Ejemplo de Uso

var monitor = new LogFileMonitor("c:\temp\app.log", "\r\n"); 

monitor.OnLine += (s, e) => 
{ 
    // WARNING.. this will be a different thread... 
    Console.WriteLine(e.Line); 
}; 

monitor.Start(); 

La única desventaja real aquí (aparte de un retraso de rendimiento leve causada por verificación de tamaño de archivo) es que porque utiliza un System.Timers.Timer la devolución de llamada proviene de un hilo diferente.

Si está utilizando una aplicación de Windows Forms o WPF, puede modificar fácilmente la clase para aceptar un SynchronizingObject que garantizaría que los eventos del controlador de eventos se invoquen desde el mismo hilo.

0

Usa esta respuesta en otra publicación c# continuously read file.

Este es bastante eficiente y comprueba una vez por segundo si el tamaño del archivo ha cambiado.

Puede ejecutarlo en otro hilo (o convertirlo en código asíncrono), pero en cualquier caso deberá volver a colocar el texto en el hilo principal para anexarlo al cuadro de texto.

Cuestiones relacionadas