# Guión charla Debugging & Profiling TODO definir lo que es texto del guión y lo que quiero poner en el slide ## Presentación Somos Alberto Menéndez y, quien les habla, Fernando Canizo y hoy les venimos a hablar de debugging y profiling en Nodejs. ## Debugging? El debugging, o despioje o depuración de programas es el proceso de identificar y corregir errores de programación. Se dice que el nombre nació debido a que en 1947 encontraron una polilla en la computadora Mark II, y ésta estaba ocasionando errores en los cálculos. Una polilla es un bicho, en inglés bicho es "bug", por lo tanto quitar bugs es _debugging_. ## Estadísticas [Statista](https://www.statista.com/) es una empresa dedicada a las estadísticas en Estados Unidos, te hacen estadísticas de lo que se te ocurra. Una empresa muy respetada por allá. Encontramos este gráfico en su sitio, que muestra que el debugging y el testeo manual es la segunda acción más ejecutada en el software después de escribirlo, que es lo que toma el mayor tiempo. Dejemos que cale un poco esta información: primero escribimos el software, y eso nos toma el 30% del tiempo, y luego buscamos y resolvemos errores, usando un 17% del tiempo, poco más de la mitad. Y después vienen el resto de las tareas relacionadas con la producción, despliegue y mantenimiento del software. No es una tarea menor y por lo tanto debemos aprender a hacerla bien. ## Rubberduck La primer forma de debugging es el debugging mental. Esclarecer las ideas, las preguntas que nos queremos hacer primero en nuestra mente. Para eso se usa la técnica del _rubberducking_. Les voy a contar una anécdota: en mi laburo anterior el equipo era chico y trabajábamos todos en una misma sala. Entonces no costaba nada darse vuelta y molestar a un compañero para pedirle una opinión. La necesidad de explicar tu problema en voz alta hace que necesariamente ordenes tus ideas. Y al ordenarlas, muchas veces aclarás tanto el panorama que terminás respondiendo tu pregunta inicial vos mismo. ### `console.log()` y amigos Cuando hablamos de debugging quizá lo primero que se nos viene a la cabeza es usar el debugger de Nodejs, del que seguramente hemos escuchado en algún momento de nuestras carreras, e incluso tal vez hasta lo hayamos usado ya, sin embargo lo primero que efectivamente la mayoría hace, es llenar de `console.log` el código. Y no está mal. Nosotros les vamos a mostrar en un rato cómo usar el debugger de Nodejs, pero también es cierto que un desarrollador debe tener criterio, y si no lo tiene aún, lo debe ir educando mediante la experiencia. Si tenés que ver el estado de muchas variables complejas (un JSON gigante), si acaso tenés un flujo de llamadas entre funciones relativamente elaborado, también complejo, entonces quizá usar `console.log` no sea lo más recomendable. Sin embargo si, por ejemplo, estás debuggeando una REST API livianita, ordenada, donde conocés el flujo al dedillo y sabés aproximadamente donde tenés que buscar un problema en particular, entonces tirar un par de `console.log` puede resultarte mucho más sencillo que levantar el debugger, setear breakpoints y watchers para llegar adonde querés y observar el estado de las variables que te interesan. No hay una medida, esto lo define el criterio de cada uno. - .clear -> limpia la pantalla - .log = .debug = .info -> alias cuya utilidad podría ser que sean fáciles de encontrar para eliminar al terminar de debuggear - .error = .warn -> imprimen a standard error, que en general lo tenemos apuntando a stdout también, así que podría decirse que su utilidad es la misma que los anteriores Personalmente uso uno solo: .log si ya tengo un logger distinto. O .log y .debug si no estoy usando otro logger. - .dir -> permite imprimir objetos complejos con la profundidad que deseemos - .table -> imprime objetos complejos en tablita bonita - .trace -> imprime stack trace como el de una exepción ### Otros loggers En todo proyecto se suelen usar bibliotecas especializadas para hacer logging debido a diferentes razones: performance, colorcitos u otros formateos bonitos, manejo de la permanencia y en particular quiero hablarles de los niveles, la mayoría implementa más o menos los mismos niveles, que son los de `npm`: ``` { error: 0, warn: 1, info: 2, http: 3, verbose: 4, debug: 5, silly: 6 } ``` Dado que estos niveles son configurables, nosotros podemos agregar otros que nos interesen si lo necesitamos. Una cosa que suelo hacer en los proyectos que me lo permiten, es loggear el flujo y las queries. Pero lo configuro en niveles especiales y de baja prioridad, de manera que no molesten en general. Por ejemplo, podría modificar los niveles anteriores de la siguiente manera: ``` { // ... todo igual hasta 'debug' flux: 6, query: 7, silly: 8, } ``` De esta manera yo puedo usar la convención de colocar un log al inicio de cada función: ``` const foo = () => { log.flux('foo'); // ... the rest of the function code }; ``` Y luego si deseo ver por donde se movió un request de principio a fin, puedo activar ese nivel llamando a mi programa con `LOG_LEVEL=flux node app.js`. Idem para queries. Y a veces uso el nivel `silly` cuando tengo que tirar a consola una variable gigante que sé que va a escupir varias páginas de información. De esta manera podemos dejar estas sentencias de logueo en el código y activarlas cuando necesitamos debuguear. ### `at()` Un problema que me suelo encontrar cuando uso estas técnicas manuales es que uno pone varias sentencias de log y en un momento, tenés que comenzar a identificarlas. Entonces escribís cosas como: ``` log.debug('antes del if'); log.debug('en if'); log.debug('en else'); log.debug('antes de llamar a foo'); log.debug('>>>>>>>>>>>>>>>>>>>', someVariableWeWantToInspect); ``` Entonces, y un poco también de la mano de esta técnica de tener todo pre-logueado y activarlo cuando se requiere, suelo usar un modulito de mi propia cosecha que llamé `at`. Lo que hace `at` es generar una excepción y parsearla para extraer de la misma el archivo y línea donde se llamó a `at`. De esa manera podemos configurar nuestro logger o al mismo `console.log` para que llamen a `at` y de esta manera nos evitamos tener que agregar comentarios de texto para poder identificar las distintas llamadas. Luego, mi código solo hace: ``` log(); log(); log({ someVariableWeWantToInspect }); ``` ### Paquete `debug` Un paquete especialmente interesante para imprimir en consola es `debug`. La diferencia que tiene este paquete respecto de los loggers con niveles, es que `debug` permite definir cadenas de texto en tu código, de manera que la impresión de las sentencias de debugging de dicho módulo solo se ejecuten de acuerdo a la configuración de la variable de ambiente `DEBUG`. Por ejemplo, ustedes podrían tener el módulo A y el B, cada uno con una cadena configurada y llamadas a debug, luego pueden iniciar su aplicación de varias maneras para ver todo el debugging, o solo el que les interese, ejemplos: ``` DEBUG='myapp:*' # muestra todo DEBUG='myapp:a' # solo mostrará las sentencias del módulo 'a' DEBUG='myapp:b' # solo mostrará las sentencias de debugging del módulo 'b' ``` Lo interesante del paquete `debug` es que muchas librerías lo usan. Entonces cuando necesitan ir a ver más allá de su código, qué está ocurriendo con la aplicación, eso suele estar disponible en muchos paquetes. ### Node inspector - Muestro el código - Muestro el código corriendo hasta que explote. Pero no sabemos qué se rompió. - Primera opción: lo que ya aprendimos antes: log, log, log, antes de cada llamada para saber cuál explota. Cada log debe tener un identificador si no estamos usando `at()`, y si no hay que escribir los distintos logs con mensajes que nos indiquen donde estamos. -> Mostrar una versión del código con logs con distintos mensajitos. Pero solo mostrarla y señalar que podemos usar el debugger de Nodejs. Debugger legacy para CLI - help para ver comandos - step o next - repl para revisar variables, CTRL-C para salir del REPL Muestro esta version y muestro como examinar variables entrando al `repl`. Cortita. Hacer notar que este debugger hace un break automáticamente en la primera línea de código. También podemos colocar la palabra reservada `debugger` en el código y el debugger se detendrá en esa línea. Es una manera de poner un hard-breakpoint. - Entonces corro node --inspect -> señalar como el programa termina y no pude revisar nada => entonces necesitamos `--inspect-brk` Mencionar que puede ser que les pida permiso para acceder los archivos del disco la primera vez. - Correr con `--inspect-brk` - Recorrer el programa y mostrar y comentar las distintas funciones y hotkeys: - Step: F9 - Step over: F10 - Step out: Shift F11 (para cuando nos metemos en código que no nos interesa revisar: bibliotecas y funciones internas de Nodejs) - Continuar: F8 Esto puede ser útil para dejar correr el resto del script en busca de algún resultado final, o bien, para parar en un siguiente breakpoint que hayamos definido en el debugger. -> Mostrar cómo se ponen breakpoints y watchers. #### Debugging remoto (mención) Es posible debuggear remotamente una aplicación, incluso podría ser una aplicación en pro. https://nodejs.org/en/docs/guides/debugging-getting-started/#enabling-remote-debugging-scenarios ### `nodemon` ### Pasada a Alberto haciendo lo mismo en VSCode