uso - thread invoke c#
Implementación segura de la clase de registro de subprocesos (5)
¿Sería la siguiente la forma correcta de implementar una clase de registro de seguridad de subprocesos bastante sencilla?
Sé que nunca cierro explícitamente el TextWriter
, ¿sería un problema?
Cuando utilicé inicialmente el método TextWriter.Synchronized
, no pareció funcionar hasta que lo inicialicé en un constructor estático y lo hice de esta forma:
public static class Logger
{
static readonly TextWriter tw;
static Logger()
{
tw = TextWriter.Synchronized(File.AppendText(SPath() + "//Log.txt"));
}
public static string SPath()
{
return ConfigManager.GetAppSetting("logPath");
}
public static void Write(string logMessage)
{
try
{
Log(logMessage, tw);
}
catch (IOException e)
{
tw.Close();
}
}
public static void Log(string logMessage, TextWriter w)
{
w.WriteLine("{0} {1}", DateTime.Now.ToLongTimeString(),
DateTime.Now.ToLongDateString());
w.WriteLine(" :");
w.WriteLine(" :{0}", logMessage);
w.WriteLine("-------------------------------");
// Update the underlying file.
w.Flush();
}
}
Al llamar a TextWriter.Synchronized protegerá esa única instancia de TextWriter
, no sincronizará sus escrituras para que una llamada de "Log" permanezca unida dentro del archivo.
Si llama a Write
(o Log
utilizando la instancia interna de TextWriter
) desde varios hilos, las llamadas individuales de WriteLine
pueden entrelazarse, haciendo que sus sellos de fecha y hora no se puedan utilizar.
Yo personalmente usaría una solución de registro de terceros que ya existe para esto. Si eso no es una opción, sincronizar esto usted mismo (incluso con un simple bloqueo) probablemente sea más útil que usar el contenedor TextWriter.Synchronized
del marco.
Alguien me señaló este post mientras discutía algunos problemas de registro hoy. Ya tenemos buenas respuestas aquí, pero estoy agregando mi respuesta solo para mostrar una versión más simple de la clase Logger
que hace exactamente lo mismo, en una forma completamente Threadsafe
para Threadsafe
.
Una cosa importante a tener en cuenta aquí es que no se requiere TextWriter.Synchronized
para la seguridad de subprocesos, ya que estamos escribiendo el archivo dentro de un lock
adecuado.
Nota: Esto ya se ha discutido en la sección de comentarios de la respuesta de x0n.
public static class Logger
{
static readonly object _locker = new object();
public static void Log(string logMessage)
{
try
{
var logFilePath = Path.Combine(@"C:/YourLogDirectoryHere", "Log.txt");
//Use this for daily log files : "Log" + DateTime.Now.ToString("yyyy-MM-dd") + ".txt";
WriteToLog(logMessage, logFilePath);
}
catch (Exception e)
{
//log log-exception somewhere else if required!
}
}
static void WriteToLog(string logMessage, string logFilePath)
{
lock (_locker)
{
File.AppendAllText(logFilePath,
string.Format("Logged on: {1} at: {2}{0}Message: {3}{0}--------------------{0}",
Environment.NewLine, DateTime.Now.ToLongDateString(),
DateTime.Now.ToLongTimeString(), logMessage));
}
}
}
Para registrar algo, simplemente llame al
Logger.Log("Some important event has occurred!");
Y hará una entrada de registro como esta
Conectado: 07 de octubre de 2015 a las: 02:11:23
Mensaje: ¡Ha ocurrido algún evento importante!
--------------------
Debería ver esta clase (parte de .NET 2.0), no es necesario "crear" su propio registrador. le permite iniciar sesión en un archivo de texto, vista de evento, etc.
http://msdn.microsoft.com/en-us/library/system.diagnostics.tracesource.aspx
Su método "Log" puede verse más o menos así (suponiendo que exista una variable interna llamada ''traceSource''):
public void Log(TraceEventType eventType, string message)
{
this.traceSource.TraceEvent(eventType, 0, message);
this.traceSource.Flush();
}
Esto es una sección de configuración que nombra TraceSource y tiene algunas configuraciones. Se supone que cuando construyes un TraceSource en tu registrador estás instanciando con una de las fuentes de rastreo nombradas en la configuración.
<system.diagnostics>
<sources>
<source name="Sample" switchValue="Information,ActivityTracing">
<listeners>
<add name="file"
initializeData="C:/temp/Sample-trace.log"
traceOutputOptions="DateTime"
type="System.Diagnostics.TextWriterTraceListener, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
</listeners>
</source>
</sources>
Además, no pongas tu registrador estático. En su lugar, use Enterprise Library 5.0 Unity para Dependency Injection / IOC.
¡Espero que esto ayude!
Si está buscando un método simple para instrumentar su código, la instalación ya existe dentro de .NET:
http://msdn.microsoft.com/en-us/library/system.diagnostics.trace.aspx
Además, las herramientas de terceros le brindarán soluciones robustas para el registro; Los ejemplos incluyen log4net , nLog y Enterprise Library .
Realmente recomiendo no reinventar la rueda en esto :)
Voy a tomar un enfoque completamente diferente aquí que las otras respuestas y supongo que realmente quieres aprender a escribir un mejor código de detección de subprocesos, y no estamos buscando sugerencias de terceros de parte nuestra (aunque en realidad puedas terminar usando uno.)
Como han dicho otros, está creando un TextWriter
seguro para TextWriter
que significa que las llamadas a WriteLine son seguras para subprocesos, eso no significa que se realizarán WriteLine
llamadas a WriteLine
como una operación atómica. Con eso quiero decir que no hay garantía de que las cuatro llamadas WriteLine vayan a suceder en secuencia. Puede tener un TextWriter
seguro para TextWriter
, pero no tiene un método Logger.Log
seguro para Logger.Log
;) ¿Por qué? Porque en cualquier momento durante esas cuatro llamadas, otro hilo podría decidir llamar Log
también. Esto significa que sus llamadas WriteLine
no estarán sincronizadas. La forma de solucionar esto es usar una declaración de lock
como esta:
private static readonly object _syncObject = new object();
public static void Log(string logMessage, TextWriter w) {
// only one thread can own this lock, so other threads
// entering this method will wait here until lock is
// available.
lock(_syncObject) {
w.WriteLine("{0} {1}", DateTime.Now.ToLongTimeString(),
DateTime.Now.ToLongDateString());
w.WriteLine(" :");
w.WriteLine(" :{0}", logMessage);
w.WriteLine("-------------------------------");
// Update the underlying file.
w.Flush();
}
}
Entonces, ahora tienes un TextWriter
seguro para TextWriter
Y un TextWriter
seguro TextWriter
subprocesos.
¿Tener sentido?