Registro y seguimiento en aplicaciones .NET

Completado

A medida que continúe desarrollando su aplicación y se haga más compleja, querrá aplicarle diagnósticos de depuración adicionales.

El seguimiento es una manera de supervisar la ejecución de la aplicación mientras se está ejecutando. Puede agregar instrumentación de seguimiento y depuración a la aplicación .NET al desarrollarla. Además, puede usar esa instrumentación mientras desarrolla la aplicación y después de implementarla.

Esta técnica sencilla es sorprendentemente eficaz. Se puede usar en situaciones en las que se necesita más de un depurador:

  • Los problemas que se producen durante largos períodos de tiempo pueden ser difíciles de depurar con un depurador tradicional. Los registros permiten una revisión post mortem detallada que abarca largos períodos de tiempo. En cambio, los depuradores están restringidos a análisis en tiempo real.
  • Las aplicaciones multiproceso y las aplicaciones distribuidas a menudo son difíciles de depurar. Adjuntar un depurador tiende a modificar los comportamientos. Puede analizar registros detallados según lo necesite para comprender los sistemas complejos.
  • Los problemas en las aplicaciones distribuidas pueden surgir de una interacción compleja entre muchos componentes. No suele ser recomendable conectar un depurador a todas las partes del sistema.
  • Muchos servicios no deben estar detenidos. Al adjuntar un depurador a menudo se producen errores de tiempo de expiración.
  • Las incidencias no siempre están previstas. El registro y seguimiento están diseñados para una baja sobrecarga, de modo que los programas siempre pueden grabarse en caso de que se produzca una incidencia.

Escritura de información en las ventanas de salida

Hasta este momento, hemos estado usando la consola para mostrar información al usuario de la aplicación. Hay otros tipos de aplicaciones que se compilan con .NET que tienen interfaces de usuario, como aplicaciones móviles, web y de escritorio, y no hay ninguna consola visible. En estas aplicaciones, System.Console registra los mensajes "en segundo plano". Estos mensajes se pueden mostrar en una ventana de salida de Visual Studio o Visual Studio Code. También pueden generarse en un registro del sistema, como logcat de Android. Como resultado, se debe tener mucho cuidado al usar System.Console.WriteLine en una aplicación que no es de consola.

Aquí es donde puede usar System.Diagnostics.Debug y System.Diagnostics.Trace, además de System.Console. Tanto Debug como Trace forman parte de System.Diagnostics y solo escribirán en los registros cuando se asocie un agente de escucha adecuado.

La elección de la API de estilo de impresión que se va a usar depende de usted. Las diferencias clave son:

  • System.Console
    • Siempre está habilitada y siempre escribe en la consola.
    • Resulta útil para la información que el cliente pueda necesitar ver en la versión.
    • Dado que es el enfoque más sencillo, a menudo se usa para la depuración temporal ad hoc. Este código de depuración a veces no se registra nunca en el control de código fuente.
  • System.Diagnostics.Trace
    • Solo se habilita cuando se define TRACE.
    • Escribe en los agentes de escucha asociados, de forma predeterminada, DefaultTraceListener.
    • Use esta API cuando cree registros que se habilitarán en la mayoría de las compilaciones.
  • System.Diagnostics.Debug
    • Solo se habilita cuando se define DEBUG (en modo de depuración).
    • Escribe en un depurador adjuntado.
    • Use esta API cuando cree registros que solo se habilitarán en las compilaciones de depuración.
Console.WriteLine("This message is readable by the end user.");
Trace.WriteLine("This is a trace message when tracing the app.");
Debug.WriteLine("This is a debug message just for developers.");

Al diseñar su estrategia de seguimiento y depuración, piense en cómo quiere que sea la salida. Varias instrucciones Write con información no relacionada producirán un registro difícil de leer. Por otro lado, usar WriteLine para colocar instrucciones relacionadas en líneas independientes puede hacer que sea difícil distinguir la información del mismo tipo. En general, use varias instrucciones Write cuando quiera combinar información de varios orígenes para crear un único mensaje informativo. Por otro lado, use la instrucción WriteLine cuando quiera crear un único mensaje completo.

Debug.Write("Debug - ");
Debug.WriteLine("This is a full line.");
Debug.WriteLine("This is another full line.");

Esta salida proviene del registro anterior con Debug:

Debug - This is a full line.
This is another full line.

Definición de las constantes TRACE y DEBUG

De forma predeterminada, cuando una aplicación se ejecuta durante la depuración, se define la constante DEBUG. Para controlarlo, puede agregar una entrada DefineConstants al archivo de proyecto en un grupo de propiedades. Este es un ejemplo de cómo activar TRACE para las configuraciones de Debug y Release, además de DEBUG para las configuraciones de Debug.

<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|AnyCPU'">
    <DefineConstants>DEBUG;TRACE</DefineConstants>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
    <DefineConstants>TRACE</DefineConstants>
</PropertyGroup>

Si usa Trace cuando no está asociado al depurador, deberá configurar un agente de escucha de seguimiento, como dotnet-trace.

Seguimiento condicional

Además de los métodos simples Write y WriteLine, también existe la funcionalidad para agregar condiciones con WriteIf y WriteLineIf. A modo de ejemplo, la siguiente lógica comprueba si el recuento es cero y, luego, escribe un mensaje de depuración:

if(count == 0)
{
    Debug.WriteLine("The count is 0 and this may cause an exception.");
}

Puede volver a escribirlo en una misma línea de código:

Debug.WriteLineIf(count == 0, "The count is 0 and this may cause an exception.");

También puede usar estas condiciones con Trace y con las marcas que defina en la aplicación:

bool errorFlag = false;  
System.Diagnostics.Trace.WriteIf(errorFlag, "Error in AppendData procedure.");  
System.Diagnostics.Debug.WriteIf(errorFlag, "Transaction abandoned.");  
System.Diagnostics.Trace.Write("Invalid value for data request");

Comprobación de que determinadas condiciones existen

Una aserción, o instrucción Assert, prueba una condición que usted especifica como argumento de la instrucción Assert. Si la condición se evalúa como true, no se produce ninguna acción. Si la condición se evalúa como false, se produce un error en la aserción. Si está ejecutando una compilación de depuración, el programa entra en modo de interrupción.

Puede usar el método Assert de Debug o Trace, que se encuentran en el espacio de nombres System.Diagnostics. Los métodos de la clase Debug no se incluyen en una versión de lanzamiento de su programa, de modo que no aumentan el tamaño ni reducen la velocidad de su código de versión.

Utilice el método System.Diagnostics.Debug.Assert libremente para probar condiciones que deberían ser true si el código es correcto. Por ejemplo, supongamos que ha escrito una función de división de enteros. Según las reglas matemáticas, el divisor nunca puede ser cero. Puede probar esta condición mediante una aserción:

int IntegerDivide(int dividend, int divisor)
{
    Debug.Assert(divisor != 0, $"{nameof(divisor)} is 0 and will cause an exception.");

    return dividend / divisor;
}

Al ejecutar este código en el depurador, se evalúa la instrucción de aserción, Sin embargo, la comparación no se realiza en la versión de lanzamiento, por lo que no hay ninguna sobrecarga adicional.

Nota:

Cuando use System.Diagnostics.Debug.Assert, asegúrese de que cualquier código incluido en Assert no cambie los resultados del programa si se quita Assert. De lo contrario, podría introducir accidentalmente un error que solo se muestra en la versión de lanzamiento del programa. Tenga especial cuidado con las aserciones que contienen llamadas a funciones o procedimientos.

El uso de Debug y Trace del espacio de nombres System.Diagnostics es una excelente manera de proporcionar contexto adicional al ejecutar y depurar la aplicación.