2010-04-12 13 views
5

Estoy buscando en C# tala y yo no quiero que mis mensajes de registro que pasar ningún tiempo de procesamiento si el mensaje está por debajo del umbral de la tala. Lo mejor que veo que log4net hace es una comprobación de umbral DESPUÉS de evaluar los parámetros de registro.trucos de rendimiento para C# Registro

Ejemplo:

_logger.Debug("My complicated log message " + thisFunctionTakesALongTime() + " will take a long time") 

Incluso si el umbral está por encima de Depuración, thisFunctionTakesALongTime todavía serán evaluados.

En log4net que se supone que usar _logger.isDebugEnabled por lo que terminan con

if(_logger.isDebugEnabled) 
    _logger.Debug("Much faster") 

quiero saber si hay una mejor solución para el registro de .NET que no implique un cheque cada vez que quiero iniciar sesión.

En C++ se me permite hacer

LOG_DEBUG("My complicated log message " + thisFunctionTakesALongTime() + " will take no time") 

desde mi macro LOG_DEBUG le examina el nivel de registro en sí. Esto me libera para tener un mensaje de registro de 1 línea a lo largo de mi aplicación que prefiero mucho. ¿Alguien sabe de una manera de replicar este comportamiento en C#?

Respuesta

8

Si puede orientar .NET 3.5 (C# 3.0) puede usar extension methods para envolver las declaraciones if.

para que pueda hacer el "macro" equivalente:

logger.Log_Debug("Much faster"); 

logger.Log_Debug(() => { "My complicated log message " + thisFunctionTakesALongTime() + " will take no time" }); 

envolviendo la verificación en este método:

public class Log4NetExtensionMethods { 
    // simple string wrapper 
    public void Log_Debug(this log4net.ILog logger, string logMessage) { 
     if(logger.isDebugEnabled) { 
      logger.Debug(logMessage); 
     } 
    } 

    // this takes a delegate so you can delay execution 
    // of a function call until you've determined it's necessary 
    public void Log_Debug(this log4net.ILog logger, Func<string> logMessageDelegate) { 
     if(logger.isDebugEnabled) { 
      logger.Debug(logMessageDelegate()); 
     } 
    } 
} 
+0

Pero si estoy evaluando funciones para obtener el mensaje de registro, ¿no sería tan lento? Quiero evitar todas las evaluaciones de funciones si el nivel de registro está por debajo de mi umbral. – Charles

+1

Tiene razón en que necesitará retrasar la evaluación de la función.Ver mi actualización que usa un delegado anónimo. –

+1

D'oh, esa fue mi respuesta también. Debo advertirle que si está buscando velocidad, no quiere usar delegados. Debería tomar el golpe y usar si 'if (logger.isDebugEnabled)' alrededor de cualquier cosa costosa. No hay una solución que sea tan eficiente como elegante. – bobbymcr

-2

Sin un preprocesador que estás SOL. Por supuesto, no hay nada que le impida usar uno antes de alimentar su código al compilador de C#.

+0

delegados anónimos y clases extra - wow eso es mucha escritura. – blammo

1

El problema aquí es que todos los parámetros del método deben ser evaluados antes de invocar el método. No hay forma de evitar esto, dada la sintaxis que está usando. Como C# no tiene un preprocesador real o macros, no puede hacer nada como "LOG_DEBUG". Lo mejor que puede hacer es usar if (logger.isDebugEnable) según lo sugerido.

La única cosa que puedo pensar es tal vez el uso de algo como una expresión lambda para retrasar la evaluación. Pero te advertiría que esto es casi seguro que tienen más de un impacto en el rendimiento en el extremo.

internal class Sample 
{ 
    private static void Main(string[] args) 
    { 
     DelayedEvaluationLogger.Debug(logger,() => "This is " + Expensive() + " to log."); 
    } 

    private static string Expensive() 
    { 
     // ... 
    } 
} 

internal static class DelayedEvaluationLogger 
{ 
    public static void Debug(ILog logger, Func<string> logString) 
    { 
     if (logger.isDebugEnabled) 
     { 
      logger.Debug(logString()); 
     } 
    } 
} 
+0

¿Las expresiones lambda son caras? – Charles

+2

@Charles: las lambdas son relativamente económicas en lo que respecta a la vinculación tardía, pero son mucho más costosas que una verificación booleana y una llamada a un método. En mi prueba simple de más de mil millones de iteraciones (código aquí: http://pastebin.com/aRtMtsxz), medí alrededor del 25% de sobrecarga para lambda frente a la verificación booleana cuando el registro no estaba habilitado. Con una lambda en caché (guarde la lambda en un campo para que no se vuelva a generar cada vez), hay un poco menos de sobrecarga (una camada por debajo del 20%). – bobbymcr

2

17.4.2 El atributo condicional

El atributo condicional permite la definición de métodos condicionales. El atributo Condicional indica una condición al probar un símbolo de compilación condicional. Las llamadas a un método condicional se incluyen u omiten dependiendo de si este símbolo está definido en el punto de la llamada. Si el símbolo está definido, la llamada está incluida; de lo contrario, la llamada (incluida la evaluación de los parámetros de la llamada) se omite.

[ Conditional("DEBUG") ] 
public static void LogLine(string msg,string detail) 
{ 
    Console.WriteLine("Log: {0} = {1}",msg,detail); 
} 

public static void Main(string[] args) 
{ 
    int Total = 0; 
    for(int Lp = 1; Lp < 10; Lp++) 
    { 
     LogLine("Total",Total.ToString()); 
     Total = Total + Lp; 
    } 
} 
+0

Esto es interesante, haré más investigación al respecto. MSDN: http://msdn.microsoft.com/en-us/library/aa664622%28VS.71%29.aspx – Charles

+1

Supongo que las mejores bibliotecas de registro ya usan esta técnica. – blammo

+1

solo porque está llamando a un método "logger.debug" no significa que realmente se está ejecutando en modo de depuración. El nombre "depuración" es realmente solo el nombre del método para un umbral arbitrario según lo definido por el desarrollador de la aplicación. Me sorprendería que Log4Net, EnterpriseLibrary, etc ... realmente usen atributos condicionales ya que el atributo depende de la presencia de un símbolo de compilación. –

Cuestiones relacionadas