¿Imprimir en consola / stdout es una buena estrategia de depuración?

11

Digamos que tenemos una función como esta:

public void myStart()
{
    for (int i = 0; i<10; i++) myFunction(i); 
}


private int myFunction(int a)
{

    a = foo(a);
    a = bar(a);
    return a; 
}

private int foo(int a)
{
    //do something here

    //something gnarly here

    //etc
    return aValue;
}

private int bar(int a)
{
    // do something here
    //return aValue;
}

Ahora, por alguna razón, nuestro código no funciona. Tal vez está arrojando un error, tal vez está devolviendo el valor incorrecto, tal vez se ha quedado atascado en un bucle infinito.

Lo primero que un programador de primer año es imprimir en consola / std out (habiendo aprendido a imprimir Hello World antes de aprender a usar un depurador).

Por ejemplo, para depurar este código, podrían hacer lo siguiente:

private int myFunction(int a)
{
    print("before foo: a=" + a); 
    a = foo(a);
    print("before bar: a=" + a);
    a = bar(a);

    return a; 
}

private int foo(int a)
{
    //do something here
    print ("foo step1: a=" + a); 

    //something gnarly here
    print ("foo step2: a=" + a + " someOtherValue="+ someOtherValue + " array.length= " + someArray.length()); 
    //etc
    return aValue;
}

private int bar(int a)
{
    // do something here
    //return aValue;
}

Ahora que ejecutan el código, obtienen una gran impresión de la consola, que pueden atravesar para rastrear dónde van las cosas mal.

Una alternativa, por supuesto, es establecer puntos de interrupción y recorrer el código en cada punto.

Una de las principales ventajas de imprimir en la consola es que el desarrollador puede ver el flujo de los valores de una vez, sin tener que hacer clic en los pasos, etc.

Pero la desventaja es que su código está lleno de todas estas declaraciones de impresión que luego deben eliminarse.

(¿Es posible tal vez decirle al depurador que imprima solo ciertos valores en un registro ?, los puntos de interrupción se pueden agregar o eliminar fácilmente sin modificar el código).

Todavía uso la impresión de consola como método principal de depuración, me pregunto qué tan común / efectivo es esto en comparación con algo más.

dwjohnston
fuente
1
Debe aprender a usar un depurador y usar un marco de registro adecuado. Te hará mucho más feliz que imprimir en la consola (eso no siempre existe).
77
Es importante aprender a usar depuradores, sin embargo, hay muchas situaciones en las que puede encontrarse donde la depuración de printf es el único método de depuración disponible.
whatsisname

Respuestas:

26

Las declaraciones impresas y el depurador no son mutuamente excluyentes. Son solo diferentes herramientas disponibles para localizar / identificar errores. Hay quienes afirman que nunca tocan un depurador y hay quienes no tienen una sola declaración de registro / impresión en ninguna parte del código que escriben. Mi consejo es que no quieras estar en ninguno de esos grupos.

En cambio, aprenda a usar el registro y aprenda a usar un depurador. Con experiencia, (casi sin tener que pensarlo) elegirá la herramienta adecuada y hará el trabajo con precisión y eficiencia. Sin experiencia, a veces elegirás uno sobre el otro, y tal vez te lleve un poco más de tiempo analizar las variables o examinar los archivos de registro, pero eso es parte del proceso de aprendizaje.

Entonces, para responder a su pregunta específica. Si. El uso de impresiones para rastrear la ejecución es una estrategia de depuración buena y ampliamente utilizada. Sin embargo...

En lugar de usar declaraciones impresas, considere usar un marco de registro. Los marcos de registro tienen un concepto de niveles de registro para que pueda agregar un montón de mensajes de registro, pero elija un nivel para cada uno. Cuando su aplicación se está ejecutando en condiciones normales, su nivel sería ERROR o ADVERTENCIA para que solo se informen cosas importantes. Sin embargo, cuando rastrea el código y necesita comprender el flujo de ejecución, puede cambiar el registrador a INFO o DEBUG y ahora todas esas declaraciones de "impresión" que ya tiene en el código informarán información adicional.

Usando un marco de registro ...

  1. no necesitará eliminar todas las impresiones una vez que haya terminado.
  2. las impresiones que deje en el código pueden ayudarlo a usted o a cualquier otro desarrollador a depurar ese mismo código en el futuro
  3. podrá depurar este código en el campo sin tener que reconstruirlo cada vez solo para agregar las impresiones que eliminó.
  4. podrá redirigir los mensajes de registro a cualquier lugar que desee, no solo a una consola. Podrían ir a un archivo, syslog, DB, socket ... etc.

Actualización: Acabo de darme cuenta al final de que estaba preguntando: "¿Es posible quizás decirle al depurador que imprima solo ciertos valores en un registro". Dependiendo de qué depurador use. Muchos depuradores modernos le permiten definir una acción para invocar cuando se alcanza un punto de interrupción. En algunos (he hecho esto en VS y WinDbg), es posible especificar "imprimir esto y reanudar". Visual Studio los llama "puntos de rastreo" en lugar de "puntos de interrupción"

DXM
fuente
+1 para el primer párrafo. Los depuradores y los registros resuelven dos problemas muy diferentes.
Blrfl
1
Pero, ¿no está de acuerdo en que dejar las declaraciones de registro en el código lo desagrada?
dwjohnston
1
depende del tipo de declaraciones de registro que deje en su lugar. Acabo de encontrar y eliminar este: "logger.error (" QUÉ ")". De lo contrario, trato el código de registro como el resto de mi código. Formatee, haga que se vea bien y que sea informativo. Sí, rociar una declaración impresa cada dos líneas es demasiado y hubo momentos en que tuve que recurrir a eso. Pero, en general, tener 10-20 declaraciones de registro en todo el archivo (los archivos / clases tienen un tamaño razonable y no son gigantescos) no está nada mal.
DXM
Entiendo que dejar algunas declaraciones de registro puede ser útil, por ejemplo, si tiene un sistema de producción, es posible que desee registrar varias variables importantes a medida que se usan, por lo que si surge algún error , puede ver de inmediato lo que sucedió en su producción medio ambiente. Sin embargo, para el contexto del desarrollo , parece desordenado. Por ejemplo, está escribiendo su función de análisis y no ha entendido bien su expresión regular, por lo que coloca una serie de declaraciones de registro solo para verificar lo que está sucediendo y eventualmente resolverlo. No creo que agregue valor en el futuro dejarlos dentro.
dwjohnston
1
@Izkata: todo eso dependería de los detalles de su error específico y de la cantidad de registro que ya tenga su código. El punto que estoy tratando de transmitir es que siempre hay un valor de tener declaraciones de registro y tenerlas en diferentes niveles: ERR -> WARN ... -> DEBUG. Si necesita más para algo muy específico, seguro comience a agregar declaraciones de impresión. Pero mi respuesta a alguien que usa "imprimir" como una declaración de registro, siempre cambiará a un marco y comenzará a usar eso.
DXM
3

El registro / impresión y los depuradores son técnicas complementarias que tienen diferentes fortalezas y debilidades: es mejor usar ambas. Pero en general, diría que los depuradores son la herramienta superior en la mayoría de los casos y deben usarse primero, con el registro / impresión solo para las cosas en las que es realmente mejor.

Ventajas de los depuradores:

  • Puede inspeccionar todo el estado del programa, no solo los valores que pensó imprimir por adelantado. Esto puede acelerar enormemente el proceso de depuración al reducir el ciclo de retroalimentación a menos de un segundo. Especialmente importante cuando lleva algún tiempo alcanzar el error.
  • Puede ver de dónde provienen las llamadas e incluso inspeccionar los valores en la traza de la pila.
  • Puede (hasta cierto punto, eso depende del idioma / plataforma) depurar código que no puede modificar fácilmente porque solo tiene el código binario o de bytes compilado.

Ventajas de registro / impresión:

  • No requiere una construcción o configuración de depuración especial. Siempre funciona
  • Puede usarse para analizar errores que son difíciles de reproducir y que ocurren raramente
  • Se puede usar para analizar errores dependientes del tiempo donde la pausa causada por un depurador puede hacer que el error desaparezca fácilmente.
  • Le brinda información permanente que puede analizar a su gusto. Puede saltar de un lado a otro entre los resultados en diferentes puntos del flujo del programa.
Michael Borgwardt
fuente
Otra ventaja del inicio de sesión es cuando solo se encuentra con el problema en la enésima pasada a través de su código y realmente podría usar una traza inversa de cómo llegó allí. La mayoría de los depuradores no tienen un botón de retroceso, pero registrar los valores con los que se llamó una función de nivel superior funcionará y, a menudo, al menos lo acercará a un pequeño caso reproducible.
Christopher Creutzig
1

Imprimir en una salida estándar de alguna manera puede ser una buena estrategia para depurar su código, por ejemplo

  • Utilizo muchas declaraciones impresas para ver qué sucede en los diferentes niveles de mi código, especialmente si no entiendo completamente el error

  • o tal vez el error no tiene información detallada que pueda indicarme qué parte del código está causando el problema exactamente.

Sin embargo, debe acostumbrarse a las herramientas de depuración, existen muchas opciones dependiendo del idioma o la plataforma que utilice.

También otro método es el registro, registro los errores todo el tiempo cuando trabajo en aplicaciones de Android, también con el manejo de excepciones, uno puede registrar fácilmente un seguimiento de la pila o un mensaje de error de la excepción que se genera.

Plaix
fuente
3
esta publicación es bastante difícil de leer (muro de texto). ¿Te importaría editarlo en una mejor forma?
mosquito
Lo hice un poco legible. Perdón por lo anterior!
Plaix