Mostrando entradas con la etiqueta Ingeniería de Software. Mostrar todas las entradas
Mostrando entradas con la etiqueta Ingeniería de Software. Mostrar todas las entradas

lunes, 29 de noviembre de 2021

Log4j ofrece control sobre el logging

La API log4j de código abierto para Java ofrece servicios de logs rápidos y eficientes

¡Advertencia! Log4j 1.x ha sido deprecada y reemplazada por Log4j 2.x. Sin embargo es buena practica iniciarnos con la versión predecesora.

Casi todas las aplicaciones grandes incluyen su propia API de logging. La experiencia indica que el logging representa un componente importante del ciclo de desarrollo. Como tal, el logging ofrece varias ventajas. Primero, puede proporcionar un contexto preciso sobre una ejecución de la aplicación. Una vez insertado en el código, la generación de la salida del log no requiere intervención humana. En segundo lugar, la salida del log se puede guardar en un medio persistente para estudiarla más adelante. Finalmente, además de su uso en el ciclo de desarrollo, también se puede emplear un paquete de logs suficientemente rico como herramienta de auditoría.

De conformidad con esa regla, a principios de 1996, el proyecto EU SEMPER (Mercado electrónico seguro para Europa) decidió escribir su propia API de logging. Después de innumerables mejoras, varias encarnaciones y mucho trabajo, esa API se ha convertido en log4j, un paquete de logging popular para Java. El paquete se distribuye bajo la Licencia Pública de IBM, certificada por la iniciativa de código abierto.

El logging tiene sus inconvenientes. Puede ralentizar una aplicación. Si es demasiado detallado, puede causar ceguera de desplazamiento. Para aliviar esas preocupaciones, log4j está diseñado para ser rápido y flexible. Dado que el log rara vez es el enfoque principal de una aplicación, la API de log4j se esfuerza por ser simple de entender y usar.

Este artículo comienza describiendo los componentes principales de la arquitectura log4j. Continúa con un ejemplo simple que describe el uso y la configuración básica. Concluye tocando los problemas de rendimiento y la próxima API de logging de Sun.

Categorías, Appenders y Layouts

Log4j tiene tres componentes principales:

  • Categorías
  • Appenders
  • Layouts

Los tres componentes trabajan juntos para permitir que los desarrolladores registren los eventos según el tipo de evento y la prioridad, y para controlar en tiempo de ejecución cómo se formatean estos mensajes y dónde se informan. Echemos un vistazo a cada uno de ellos.

Jerarquía de categorías

La primera y principal ventaja de cualquier API de logs sobre System.out.println simple reside en su capacidad para deshabilitar ciertas declaraciones de registro mientras permite que otras impriman sin obstáculos. Esa capacidad asume que el espacio del log, es decir, el espacio de todas las declaraciones de logs posibles, se categoriza de acuerdo con algunos criterios elegidos por el desarrollador.

De conformidad con esa observación, la clase org.log4j.Category figura en el núcleo del paquete. Las categorías son entidades con nombre. En un esquema de nomenclatura familiar para los desarrolladores de Java, se dice que una categoría es padre de otra categoría si su nombre, seguido de un punto, es un prefijo del nombre de la categoría secundaria. Por ejemplo, la categoría denominada com.foo es un padre de la categoría denominada com.foo.Bar. De manera similar, java es un padre de java.util y un antepasado de java.util.Vector.

La categoría raíz, que reside en la parte superior de la jerarquía de categorías, es excepcional de dos maneras:

  1. Siempre existe
  2. No se puede recuperar por nombre

En la clase Category, al invocar el método getRoot () estático se recupera la categoría raíz. El método estático getInstance () crea una instancia de todas las demás categorías. getInstance () toma el nombre de la categoría deseada como parámetro. Algunos de los métodos básicos de la clase Categoría se enumeran a continuación:

A las categorías se les pueden asignar prioridades del conjunto definido por la clase org.log4j.Priority. Aunque el conjunto de prioridades coincide con el del sistema Unix Syslog, log4j fomenta el uso de solo cuatro prioridades: ERROR, WARN, INFO y DEBUG, enumeradas en orden decreciente de prioridad. La lógica detrás de ese conjunto aparentemente restringido es promover una jerarquía de categorías más flexible en lugar de un conjunto de prioridades estático (aunque sea grande). Sin embargo, puede definir sus propias prioridades subclasificando la clase Prioridad. Si una categoría determinada no tiene una prioridad asignada, hereda una de su antepasado más cercano con una prioridad asignada. Como tal, para garantizar que todas las categorías puedan eventualmente heredar una prioridad, la categoría raíz siempre tiene una prioridad asignada.

Si una categoría no tiene una prioridad asignada va a heredar la prioridad de su padre.

Para registrar un evento de la aplicación, invoque uno de los métodos de impresión de una instancia de categoría. Esos métodos de impresión son:

  • error()
  • warn()
  • info()
  • debug()
  • log()

Por definición, el método de impresión determina la prioridad del registro del evento. Por ejemplo, si c es una instancia de categoría, entonces la instrucción c.info ("..") es una solicitud de logging de prioridad INFO.

Se dice que una solicitud de registro está habilitada si su prioridad es mayor o igual que la prioridad de su categoría. De lo contrario, se dice que la solicitud está deshabilitada. Una categoría sin una prioridad asignada heredará una de la jerarquía.

A continuación, encontrará un ejemplo de esa regla:

Llamar al método getInstance () con el mismo nombre siempre devolverá una referencia al mismo objeto de categoría. Por lo tanto, es posible configurar una categoría y luego recuperar la misma instancia en algún otro lugar del código sin pasar referencias. Las categorías se pueden crear y configurar en cualquier orden. En particular, una categoría padre buscará y vinculará a sus hijos incluso si se crea una instancia después de ellos. El entorno log4j generalmente se configura en la inicialización de la aplicación, preferiblemente leyendo un archivo de configuración, un enfoque que discutiremos en breve.

Log4j facilita la tarea de nombrar categorías por componente de software. Eso se puede lograr instanciando estáticamente una categoría en cada clase, con el nombre de la categoría igual al nombre completo de la clase, un método útil y sencillo para definir categorías. Como la salida del registro lleva el nombre de la categoría generadora, dicha estrategia de denominación facilita la identificación del origen de un mensaje del registro. Sin embargo, esa es solo una estrategia posible, aunque común, para nombrar categorías. Log4j no restringe el posible conjunto de categorías. De hecho, el desarrollador es libre de nombrar las categorías como desee.

Appenders y Layouts

La capacidad de habilitar o deshabilitar selectivamente las solicitudes de registro según su categoría es solo una parte de la imagen. Log4j también permite que las solicitudes de registro se impriman en múltiples destinos de salida llamados appenders en log4j. Actualmente, existen appenders para la consola, archivos, componentes GUI, servidores de socket remotos, NT Event Loggers y demonios remotos de UNIX Syslog.

Una categoría puede referirse a múltiples appenders. Cada solicitud de registro habilitada para una categoría determinada se reenviará a todos los appenders de esa categoría, así como a los que se agregan más arriba en la jerarquía. En otras palabras, los appenders se heredan de forma aditiva de la jerarquía de categorías. Por ejemplo, si agrega un appender de consola a la categoría raíz, todas las solicitudes de registro habilitadas se imprimirán al menos en la consola. Si, además, se agrega un appender de archivos a una categoría, digamos C, las solicitudes de registro habilitadas para C y los hijos de C se imprimirán en un archivo y en la consola. Tenga en cuenta que puede anular ese comportamiento predeterminado para que la acumulación de appenders ya no sea aditiva.

Las reglas que gobiernan la aditividad del appender se resumen a continuación.

Aditividad del appender

La salida de una declaración de registro del registrador C irá a todos los appenders en C y sus ancestros. Este es el significado del término "aditividad del appender".

Sin embargo, si un ancestro del registrador C, digamos P, tiene el indicador de aditividad establecido en falso, entonces la salida de C se dirigirá a todos los anexos en C y sus ancestros hasta e incluyendo P, pero no a los agregadores en ninguno de los ancestros de P .

Los registradores tienen su indicador de aditividad establecido en verdadero de forma predeterminada.

La siguiente tabla muestra un ejemplo:

Logger
Name
Added
Appenders
Additivity
Flag
Output TargetsComment
rootA1not applicableA1The root logger is anonymous but can be accessed with the Logger.getRootLogger() method. There is no default appender attached to root.
xA-x1, A-x2trueA1, A-x1, A-x2Appenders of "x" and root.
x.ynonetrueA1, A-x1, A-x2Appenders of "x" and root.
x.y.zA-xyz1trueA1, A-x1, A-x2, A-xyz1Appenders in "x.y.z", "x" and root.
securityA-secfalseA-secNo appender accumulation since the additivity flag is set to false.
security.accessnonetrueA-secOnly appenders of "security" because the additivity flag in "security" is set to false.

Para configurar el flag agregar la siguiente linea al archivo de configuración.

log4j.additivity.nombre.logger=false

La mayoría de las veces, los usuarios desean personalizar no solo el destino de salida, sino también el formato de salida, una hazaña que se logra al asociar un layout con un appender. El layout formatea el mensaje del registro de acuerdo con los deseos del usuario, mientras que un appender se encarga de enviar la salida formateada a su destino. PatternLayout, parte de la distribución estándar de log4j, permite al usuario especificar el formato de salida de acuerdo con patrones de conversión similares a la función printf del lenguaje C.

Por ejemplo, PatternLayout con el patrón de conversión % r [% t]% - 5p% c -% m% n generará algo similar a:

En la salida de arriba:

  • El primer campo es igual al número de milisegundos transcurridos desde el inicio del programa.
  • El segundo campo indica el hilo que realiza la solicitud de registro
  • El tercer campo representa la prioridad de la declaración de registro.
  • El cuarto campo es igual al nombre de la categoría asociada con la solicitud de registro.
  • El texto después de - indica el mensaje de la declaración.
Mas sobre los Layouts
https://logging.apache.org/log4j/2.x/manual/layouts.html

Mas sobre los Appenders
https://logging.apache.org/log4j/2.x/manual/appenders.html

Mas sobre Filtros (disponible en la versión 2.x)
https://logging.apache.org/log4j/2.x/manual/filters.html

Configuración

Insertar solicitudes de registro en el código de la aplicación requiere una gran cantidad de planificación y esfuerzo. La observación muestra que el código dedicado al registro representa aproximadamente el cuatro por ciento del total de la aplicación. En consecuencia, incluso las aplicaciones de tamaño moderado tendrán miles de declaraciones de registro incrustadas en su código. Dado su número, es imperativo administrar esas declaraciones de registro sin la necesidad de modificarlas manualmente.

El entorno log4j se puede configurar completamente mediante programación. Sin embargo, es mucho más flexible configurar log4j utilizando archivos de configuración. Actualmente, los archivos de configuración se pueden escribir en XML o en formato de propiedades Java (clave = valor).

Démosle una idea de cómo se hace con la ayuda de una aplicación imaginaria, MyApp, que usa log4j:

Como se ve en el código anterior, MyApp comienza importando clases relacionadas con log4j. Luego define una variable de categoría estática con el nombre MyApp, que resulta ser el nombre completo de la clase.

MyApp usa la clase Bar definida en el paquete com.foo:

En MyApp, la invocación del método BasicConfigurator.configure () crea una configuración log4j bastante simple. Ese método está programado para agregar a la categoría raíz una impresión FileAppender en la consola. La salida se formateará utilizando un PatternLayout establecido en el patrón% -4r [% t]% -5p% c% x -% m% n.

Tenga en cuenta que, de forma predeterminada, la categoría raíz se asigna a Priority.DEBUG.

La salida de MyApp es:

La Figura 1 muestra el diagrama de objetos de MyApp inmediatamente después de que llama al método BasicConfigurator.configure ().

//agregar figura

La Figura 1 muestra el diagrama de objetos de MyApp inmediatamente después de que llama al método BasicConfigurator.configure ().

La clase MyApp configura log4j invocando el método BasicConfigurator.configure (). Otras clases solo necesitan importar la clase org.log4j.Category, recuperar las categorías que desean usar y cerrar sesión.

El ejemplo anterior siempre genera la misma información de registro. Afortunadamente, es fácil modificar MyApp para que la salida del registro se pueda controlar en tiempo de ejecución. A continuación, verá una versión ligeramente modificada:

Esta versión de MyApp indica a PropertyConfigurator que analice un archivo de configuración y configure el registro en consecuencia.

Veamos un archivo de configuración de muestra que da como resultado exactamente el mismo resultado que el ejemplo anterior basado en BasicConfigurator:

Suponga que ya no deseamos ver la salida de ningún componente que pertenezca al paquete com.foo. El siguiente archivo de configuración muestra una posible forma de lograrlo:

La salida de MyApp configurada con este archivo se muestra a continuación:

Como la categoría com.foo.Bar no tiene una prioridad asignada, hereda su prioridad de com.foo, que se estableció en WARN en el archivo de configuración. La declaración de registro del método Bar.doIt () tiene la prioridad DEBUG, menor que la prioridad de categoría WARN. En consecuencia, se suprime la solicitud de registro de doIt ().

A continuación, vemos otro archivo de configuración que usa múltiples appenders:

Llamar a la MyApp mejorada con ese archivo de configuración generará lo siguiente en la consola:

Además, como a la categoría raíz se le ha asignado un segundo appender, la salida también se dirigirá al archivo example.log. Ese archivo se renovará cuando alcance los 100 KB. Cuando se produce una renovación, la versión anterior de example.log se mueve automáticamente a example.log.1.

Tenga en cuenta que para obtener esos diferentes comportamientos de registro, no es necesario volver a compilar el código. Con la misma facilidad podríamos haber iniciado sesión en un demonio de Unix Syslog y redirigir toda la salida de com.foo a un registrador de eventos de NT. De manera similar, podríamos haber reenviado los eventos de registro a un servidor log4j remoto, que registraría de acuerdo con la política del servidor local, por ejemplo, iniciando sesión en un archivo local y reenviando el evento de registro a un segundo servidor log4j.

Contextos de diagnóstico anidados

La mayoría de los sistemas del mundo real deben tratar con varios clientes simultáneamente. En una implementación multiproceso típica de un sistema de este tipo, diferentes subprocesos manejarán diferentes clientes. Teniendo en cuenta eso, el registro es especialmente adecuado para rastrear y depurar aplicaciones distribuidas complejas. Un enfoque común para diferenciar la salida de registro de un cliente de otro es crear una instancia de una nueva categoría separada para cada cliente. Sin embargo, ese enfoque promueve la proliferación de categorías y aumenta la sobrecarga administrativa del registro.

En una técnica más ligera, puede sellar de forma única cada solicitud de registro iniciada desde la misma interacción con el cliente, un enfoque descrito por Neil Harrison (ver Recursos). Para sellar de forma única cada solicitud, el usuario inserta información contextual en el contexto de diagnóstico anidado (NDC). La clase NDC se muestra a continuación:

El NDC se gestiona por hilo como una pila de información contextual. Tenga en cuenta que todos los métodos de org.log4j.NDC son estáticos. Suponiendo que la impresión NDC está activada, cada vez que se realiza una solicitud de registro, el componente log4j apropiado incluirá la pila NDC completa para el hilo actual en la salida del registro. Eso se hace sin la intervención del usuario, que es responsable únicamente de colocar la información correcta en el NDC mediante el uso de los métodos push () y pop () en algunos puntos bien definidos del código. En contraste, el enfoque de categoría por cliente exige cambios extensos en el código.

Para ilustrar ese punto, tomemos el ejemplo de un servlet que entrega contenido a numerosos clientes. El servlet puede construir el NDC al comienzo de la solicitud antes de ejecutar otro código. La información contextual puede ser el nombre de host del cliente y otra información inherente a la solicitud, generalmente información contenida en cookies. Por lo tanto, incluso si el servlet está sirviendo a varios clientes simultáneamente, los registros iniciados por el mismo código (es decir, pertenecientes a la misma categoría) aún se pueden distinguir porque cada solicitud de cliente tendrá una pila de NDC diferente. Compare eso con la complejidad de pasar una categoría recién instanciada a todo el código ejercido durante la solicitud del cliente.

No obstante, algunas aplicaciones sofisticadas, como los servidores web de alojamiento virtual, deben registrarse de manera diferente, según el contexto del host virtual y el componente de software que emite la solicitud. La versión más reciente de log4j admite múltiples árboles de jerarquía, una mejora que permite que cada host virtual posea su propia copia de la jerarquía de categorías.

Rendimiento

Uno de los argumentos más citados en contra del logging proviene de su costo computacional. Esa es una preocupación legítima, ya que incluso las aplicaciones de tamaño moderado pueden generar miles de solicitudes de registro. Se dedicó mucho esfuerzo a medir y ajustar el rendimiento del registro. Log4j afirma ser rápido y flexible: primero la velocidad, luego la flexibilidad.

No obstante, el usuario debe conocer los siguientes problemas de rendimiento:

1.- Rendimiento del registro cuando el registro está desactivado. Cuando el registro está desactivado por completo o para un conjunto de prioridades, el costo de una solicitud de registro es una invocación de método más una comparación de enteros. La invocación del método implica el costo oculto de la construcción de parámetros. Para algunas categorías cat, escribir

incurre en el costo de construir el parámetro de mensaje que convierte tanto el entero i como la entrada [i] en una cadena y concatena cadenas intermedias, independientemente de si el mensaje se registrará o no. Si a uno le preocupa la velocidad, escriba:

Eso no incurrirá en el costo de la construcción de parámetros si la depuración está deshabilitada. Por otro lado, si la categoría está habilitada para la depuración, incurrirá dos veces en el costo de evaluar si la categoría está habilitada o no: una vez en debugEnabled () y una vez en debug (). Eso representa una sobrecarga insignificante porque la evaluación de una categoría lleva aproximadamente el 1 por ciento del tiempo que lleva registrar realmente. En log4j, las solicitudes de registro se realizan a instancias de la clase Categoría. Debido a que Categoría es una clase y no una interfaz, el costo de la invocación del método es posiblemente menor, pero a costa de cierta flexibilidad. En una máquina Pentium II de 233 MHz, el costo típico de registro cuando el registro está desactivado está en el rango de 5 a 50 nanosegundos.

2.- El rendimiento de decidir si registrar o no registrar un registro cuando el logging está activado. En ese escenario, la pregunta se centra en el costo de rendimiento de recorrer la jerarquía de categorías. Cuando el registro está activado, log4j aún necesita comparar la prioridad de la solicitud de registro con la prioridad de la categoría de solicitud. Sin embargo, es posible que las categorías no tengan una prioridad asignada; pueden heredarlos de la jerarquía de categorías. Por lo tanto, para descubrir su prioridad, la categoría puede necesitar buscar a sus antepasados. Se ha hecho un esfuerzo serio para que esa jerarquía camine lo más rápido posible. Por ejemplo, las categorías secundarias se vinculan solo a sus antepasados ​​existentes. En el ejemplo de BasicConfigurator mostrado anteriormente, la categoría denominada com.foo.Bar enlaza directamente con la categoría raíz, evitando así las categorías com o com.foo inexistentes, mejorando significativamente la velocidad de la caminata, especialmente en jerarquías dispersas. caminar por la jerarquía está en el rango de 5 a 15 microsegundos, nuevamente en una máquina Pentium II de 233 MHz.

3.- El rendimiento real del Loggin. Otro problema de rendimiento se deriva del costo de formatear la salida del registro y enviarla a su destino. Aquí nuevamente, se hizo un gran esfuerzo para que los Layouts (formateadores) funcionen lo más rápido posible. Lo mismo ocurre con los Appenders. El costo típico de registrar realmente varía entre 100 y 300 microsegundos.

Aunque log4j tiene muchas características, su primer objetivo de diseño fue la velocidad. Algunos componentes de log4j se han reescrito muchas veces para mejorar el rendimiento. Sin embargo, los contribuyentes suelen presentar nuevas optimizaciones.

La empresa SUN está construyendo su API de Logging

**Actualización: La API ya se encuentra disponible, se le conoce como JUL Java Util Logging**

Sun ha iniciado la petición JSR47 en Java Community Process (JCP) para definir una API de Logging para Java. La API resultante requerirá la versión 1.4 de JDK y pronto estará lista para su revisión pública.

La API JSR47 y Log4j son bastante similares a nivel arquitectónico. La API JSR47 admitirá un espacio de nombres jerárquico, una de las características centrales de log4j. Por otro lado, log4j posee muchas características útiles que faltan en JSR47. Dado el impulso detrás de log4j, en mi opinión partidista, es probable que JSR47 esté obsoleto para cuando se lance. Log4j está escrito por las personas que lo utilizan, no por un comité cerrado.

Por cierto, log4j ya proporciona soporte para la conexión en la API JSR47 cuando esta última esté disponible.

Permítanme también mencionar JLog, otra API de registro disponible en alphaWorks de IBM. JLog se llamaba anteriormente RAS Toolkit hasta que IBM lo renombró. En el sitio alphaWorks, JLog tiene la etiqueta "Logging Toolkit for Java", casi la misma etiqueta que tenía log4j en alphaWorks antes de migrar a http://log4j.org. JLog, aunque es un buen paquete de registro, no debe confundirse con log4j.

Conclusión

Log4j es un paquete de registro popular escrito en Java. Uno de sus rasgos distintivos incluye la noción de herencia en categorías. Usando una jerarquía de categorías, es posible controlar qué declaraciones de registro se generan con granularidad arbitraria, reduciendo así el volumen de salida registrada y minimizando el costo de registro.


Una de las ventajas de la API de log4j es su capacidad de administración. Una vez que las declaraciones de registro se han insertado en el código, se pueden controlar con archivos de configuración. Además, pueden habilitarse o deshabilitarse selectivamente y enviarse a diferentes y múltiples destinos de salida en formatos elegidos por el usuario. Además, el paquete log4j está diseñado para que las declaraciones de registro puedan permanecer en el código enviado sin incurrir en un alto costo de rendimiento.


Log4j es el resultado de un esfuerzo colectivo. Mi especial agradecimiento a todos los autores que han contribuido al proyecto. Sin excepción, las mejores características del paquete se han originado en la comunidad de usuarios.

Referencia: https://logging.apache.org/log4j/1.2/manual.html

Más información sobre este tema

"Patterns for Logging Diagnostic Messages," Neil Harrison in Pattern Languages of Program Design 3, edited by R. Martin, D. Riehle, and F. Buschmann (Addison-Wesley, 1997)
http://www.amazon.com/exec/obidos/ASIN/0201310112/qid%3D973215403/002-8420944-7444837/javaworld

Log4j project homepage
http://jakarta.apache.org/log4j/docs/

Sun's Java Logging API Specification
http://java.sun.com/aboutJava/communityprocess/jsr/jsr_047_log.html

Igor Poteryaev, an independent author, has ported log4j to the Python language, called log4p
http://log4p.sourceforge.net

IBM's JLog API
http://www.alphaworks.ibm.com/tech/loggingtoolkit4j
The EU's Secure Electronic Marketplace for Europe (SEMPER) project homepage

http://www.semper.org

OpenSource.org
http://www.opensource.org

Level
https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/Level.html

Category
https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/Category.html

PatternLayout
https://logging.apache.org/log4j/1.2/apidocs/org/apache/log4j/PatternLayout.html

miércoles, 24 de noviembre de 2021

Logging de una aplicación en Java

El logging de una aplicación consiste en registrar (logs) información relevante del comportamiento de la ejecución de nuestra aplicación. Contiene datos de variables, entidades, cambios de estado,  componentes de software involucradas en dicha ejecución y la llamada de los métodos. Su principal funcionalidad es facilitar el seguimiento o análisis de la ejecución de la aplicación:

  • Analizar el comportamiento de la aplicación durante la fase de desarrollo y depuración (pruebas de caja blanca)
  • Analizar los bugs o errores de ejecución detectados, sus causas y consecuencias
  • Servir de registro de auditoría cuando la información contenida y el modo en que se ha procesado cumpla los criterios requeridos
  • Medir el rendimiento o carga de los sistemas o aplicaciones
  • Revertir el estado del aplicativo siguiendo en orden inverso el log

Aunque depende de las circunstancias en las que nos encontremos, el segundo de los usos suele ser el más relevante. Un buen log desarrollado correctamente en el código y mantenido o configurado en explotación es una garantía de respuesta rápida para análisis de errores; que incluso podrían hacerse sin necesidad de parar el aplicativo, reconfigurarlo o aplicarle ningún cambio.

El log o registro de la aplicación suele formarse por un conjunto de eventos que se almacenan secuencialmente, por lo general en el orden en que suceden, de manera persistente o recuperable. Se pueden almacenar en ficheros, en BBDD, en componentes distribuidos a tal efecto. Se pueden habilitar mecanismos de rotación o históricos de estos logs, se pueden utilizar por monitores para lanzar alertas, se pueden integrar y fusionar para hacer análisis más exhaustivos. Lo relevante es que la información registrada y la forma en que se gestiona sea útil.

Existen numerosas soluciones y propuestas de software, tanto libres como propietarias, más o menos estandarizadas, más sencillas o más completas, de mil tipos y formas. Lo importante es buscar aquella se ajusta a nuestras necesidades y entornos, para olvidarnos de la implementación del mecanismo por completo; y ceñirnos a los dos aspectos más importantes:

  • El contenido de cada registro o evento, principal preocupación del desarrollador
  • El modo en que se procesa, persiste y gestiona, principal preocupación de la explotación del sistema o aplicativo

El coste de implementación del logging se encuentra en el ir, mientras se desarrolla, dejando registros (logs) en los diferentes puntos del código. Esta actividad debe hacerse durante el desarrollo, siguiendo patrones, criterios y procedimientos preestablecidos. De esta manera los desarrolladores tendrán criterios comunes y los logs serán coherentes entre las diferentes partes del código.

La información registrada en el log debe ser relevante y completa. Se pueden considerar los siguientes aspectos a la hora de decidir qué información incluir:

Qué:

Qué evento o acción ha ocurrido.

Qué entidades han estado involucradas.

Si hay un cambio de estado, ¿Cuál era el anterior? ¿Cuál es el nuevo estado?.

Dónde:

En qué punto del código ha ocurrido: componente, clase, fichero de código, método o bloque de ejecución, línea de código… Cuanto más detallada sea esta información mejor para localizar el lugar del posible error o por donde ha pasado la ejecución, por un lado; pero más puede afectar el logging al rendimiento.

Cuándo:

Registrando el momento temporal, bien absoluto o bien relativo al comienzo de ejecución o cualquier otro evento.

Generando los logs secuencial o causal, en la que los eventos que ocurren antes en el tiempo o que ocasionan otros, aparezcan antes.

En qué contexto:

Registrando estados o variables: propios de la ejecución (parámetros), de personalización o específicos de usuario, referentes a la sesión o transacción en ejecución…

Indicando hilos, transacciones o peticiones relacionadas cuando estemos en entornos concurrentes.

Para que la información de los logs sea más detallada en momento de análisis y más manejable durante la explotación de la misma, se establecen niveles de filtrado. De tal manera que solo se muestra o almacenan aquellos eventos con un nivel mayor o igual al del nivel de log establecido. Las librerías de logging permiten filtrar los eventos por otros criterios como son la clase o contexto del evento, también.

Los niveles más comunes son DEBUG, INFO, WARNING y ERROR. La clasificación de los diferentes eventos en cada nivel es parte del ejercicio de análisis, y deben orientarse a que la traza sea legible y útil en los diferentes contextos del aplicativo, desde el desarrollo hasta la explotación.

Este puede ser un ejemplo de semántica de niveles de logging:

DEBUG: para información de muy bajo nivel solo útil para el debug de la aplicación, tanto en el desarrollo como en el análisis de incidencias

Llamadas a funciones y procedimientos y otros componentes, con parámetros y respuestas

Flujos de ejecución

Desarrollo de algoritmos y procedimientos que permitan identificar y seguir su ejecución en desarrollo

INFO: información de más alto nivel que permita hacer un seguimiento de la ejecución normal

Paradas y arranques de servicios y sistemas

Parámetros críticos o relevantes de configuración

Comienzo y fin de transacciones y operaciones completas

Cambios de estado de operaciones

WARN: información de situaciones, que aún sin ser de error, si son anómalas o no previstas, aunque el aplicativo tiene alternativas para solventarlas

Parámetros no definidos, y cuyo valor se toma por defecto

Situaciones anómalas, pero que son resueltas por el aplicativo, dejando la operación en un estado correcto

Funcionalidades no primordiales o imprescindibles, que no pueden resolverse, pero que dejan la operación en un estado correcto

ERROR: información de situaciones que son de error y que impiden la ejecución correcta de una operación o transacción, pero sin afectar a otras operaciones o transacciones (error aislado o contenido)

No se pudo realizar una operación o transacción, pero no afecta a otras

Peticiones o consultas erróneas (almacenando los parámetros de entrada)

Funcionalidades generales del aplicativo, que aún afectando al funcionamiento general del aplicativo, no se consideran primordiales o imprescindibles

FATAL: información de situaciones de error que afectan al funcionamiento general del aplicativo (errores no aislados o contenidos en alcance)

Parámetros no definidos o configuraciones erróneas

Falta de conexión o comunicación con otros componentes

Errores de ejecución que pueden afectar a operaciones o transacciones independientes, o que afectan al funcionamiento general de la aplicación

Tanto el contenido y forma de cada evento de log, como la semántica de los niveles son parte del diseño del aplicativo. Estos criterios deben definirse y ser comunicados al equipo de desarrollo para que se apliquen de manera homogénea y coherente en todo el desarrollo. Si estos criterios son consensuados con el equipo se puede sacar mucho provecho de la experiencia de los desarrolladores.

Otras recomendaciones a tener en cuenta son:

Registrar los eventos de manera atómica, que toda la información referente a un evento se almacene en un registro o línea.

Orientar el formato de la información mostrada a poder ser usado de manera informatizada o automática con herramientas específicas.

En lo referente a excepciones:

Mostrar siempre la traza (trace o llamada secuencial de los métodos) completa de la excepción con su mensaje y todo el stacktrace.

Si la excepción es capturada, tratada y luego lanzada de nuevo (bien la misma u otra) no dejar traza de la excepción hasta que se capture y se trate en última instancia. De esta manera cada excepción solo se mostrará una vez. Si no lo hacemos así y registramos cada captura y relanzamiento aparecerá en la traza varias veces y no sabremos si es la misma o son varias. (Antipatrón catch-log-throw).

Hacer uso de la propiedad de excepción causante o interna cuando capturemos y relancemos la excepción. En Java es el método getCause(), en .NET la propiedad InnerException.

Implementar el método toString() de todas las clases de negocio o POJOs para facilitar su traceado.

Hay que tener en cuenta las diferencias y zonas horarias a la hora de trabajar con logs de varias máquinas o componentes.

El logging no puede tener efectos laterales, no puede modificar el estado de ningún parámetro, variable o procedimiento. Solo muestra información. Debe ser ligero, el propio logging no puede requerir de procesamientos largos o costosos.

Las llamadas a métodos, componentes y componentes distribuidos externos, deberían registrarse tras la llamada incluyendo los parámetros de entrada y la respuesta, así como clase/componente y método, a nivel de DEBUG. Y a nivel de ERROR y FATAL cuando ocurra un error en la llamada (si fuese una excepción se debe tracear parámetros de entrada y excepción)

En Java tenemos varias API's o librerías para llevar a cabo el logging de nuestra aplicación, siendo las mas comunes:

  • Log4j2 antes Log4j
  • Loogback
  • JUL (Java Util Logging)
Tenemos dos fachadas las cuales permitirán que nuestra aplicación funcione con cualquiera las tres API's mencionadas, estas fachadas son:
  • Commons Logging
  • SLF4J
Continua con una guía practica para la API Log4j  Log4j ofrece control sobre el logging

domingo, 19 de septiembre de 2021

6 Herramientas para mejorar la productividad del desarrollador Java

Los desarrolladores de Java tienen perspectivas laborales muy altas en la industria del desarrollo de software. Con razón, Java impulsa muchas de las aplicaciones y servidores de escritorio, web y móviles del mundo. La mayoría de las empresas que reclutan desarrolladores de Java pertenecen a las industrias más maduras, como la atención médica y el Retail.

En consecuencia, es importante que los desarrolladores de Java sean eficaces y eficientes en sus trabajos.

A continuación se muestran seis de las herramientas más útiles que los desarrolladores de Java deberían considerar usar.

Netbeans

Son pocos los desarrolladores de Java que no conocen Netbeans. Es un entorno de desarrollo integrado (IDE) de código abierto en varios idiomas.

Lejos de ser un simple editor de texto, proporciona algunas características interesantes que ayudan a los desarrolladores a ser productivos.

Las características clave incluyen su resaltado de sintaxis que facilita la refactorización del código, ya que no solo le brinda información de sintaxis sino también de semántica. También proporciona excelentes plantillas y asistentes para una amplia gama de lenguajes de programación.

También es una plataforma de aprendizaje: tiene un tutorial completo de Java (y también PHP). Otra cosa que se destaca de Netbeans es que facilita a los desarrolladores la gestión de sus proyectos mediante el uso de favoritos y el control de versiones. También se sincroniza bien con Github.

La comunidad crea continuamente una gran cantidad de herramientas poderosas para hacer que el uso de Netbeans sea más placentero.

¿Cuál es tu nivel de conocimiento en NetBeans?, ¿Básico, medio o experto?. 

Rookout

Como desarrollador de Java, al igual que con todos los desarrolladores, encontrar y solucionar errores son dos de las actividades que requieren más tiempo. Estos no solo consumen mucho tiempo, sino que causan mucha angustia innecesaria cada vez que parece que un error da origen a otros errores en el futuro.

El problema se agrava cuando el error se experimenta en producción y el equipo de operaciones querría que corrigiera los errores en el código de producción.

Si bien es importante contar con una buena estrategia de depuración, es aún más esencial contar con las herramientas adecuadas para ayudarlo a hacerlo. Después de todo, nuestros ojos humanos están limitados en lo que pueden sentir.

Aquí es donde una herramienta de depuración como Rookout puede ayudar. Te permite depurar en cualquier entorno. El equipo de operaciones quiere que vayas y averigües qué está mal con el código enviado a producción. Puede realizar una depuración remota en vivo allí. También puede implementar la depuración de producción, lo que permite una experiencia perfecta.

También mejora la colaboración al proporcionar excelentes datos e información sobre los errores encontrados y también comparte métricas comerciales a pedido.

Y tu, ¿ya has usado Rookout?  ¿Te atreverías a probarlo?

JUnit

Corregir errores es una gran cosa. Pero, a menudo, el código que no presenta errores no se comporta como se esperaba durante la ejecución. Por ejemplo, declarar una calculo matemático incorrecto, tu programa puede no presentar errores de compilación, pero como garantizas que el resultado es el correcto?. O cuando realizas modificación en una clase, ¿Cómo garantizas que el cambio no impacta en toda la funcionalidad?

Este error se puede detectar mediante pruebas automatizadas.

JUnit es una herramienta de prueba elegida por los desarrolladores de Java. Es un Framework de código abierto. Por lo general, usa anotaciones para que las pruebas tengan accesorios antes de ejecutarlas. Algunas de sus ventajas incluyen la simplicidad de uso, proporciona informes de inmediato, no solo te permite escribir pruebas sino también todo un conjunto de pruebas.

La mayoría de los IDE son compatibles con JUnit, por lo que se ha convertido en uno de los Framework de prueba más populares, si no el más popular, para Java.

Y tu, ¿Haces pruebas unitarias de tus funcionalidades?

Apache Maven

Apache Maven es simplemente un software de gestión de proyectos de software. Si bien probablemente pueda usar cualquier otra herramienta para este propósito, Maven también administra compilaciones para sus proyectos Java.

Su principio de funcionamiento se basa en el concepto de modelo de objetos de proyecto (POM). La creación de un proyecto con Maven le facilitará automáticamente la creación de los siguientes. Una de las cosas que ayuda a aumentar la productividad de los desarrolladores de Java es su actualización automática de dependencias.

Dado que es de código abierto, la comunidad siempre agrega nuevas herramientas a su ya enorme biblioteca. No necesitará muchas configuraciones cuando lleguen nuevas herramientas útiles o se necesiten actualizaciones.

En este blog puedes encontrar muchos artículos útiles sobre Maven.

Git

Git es un software de control de versiones diseñado por Linus Torvalds, pensando en la eficiencia, la confiabilidad y compatibilidad del mantenimiento de versiones de aplicaciones cuando estas tienen un gran número de archivos de código fuente. Contar con un sistema de versionamiento te permitirá gestionar tu proyecto de forma flexible, revertir cambios, comparar versiones, aprobar cambios y restaurar cambios.

En este blog puedes encontrar una guía básica pero muy útil de GitHub

Apache Spark

Apache Spark permite a los desarrolladores de Java crear aplicaciones de procesamiento de datos intensivo. Permite procesar grandes cantidades de datos de forma rápida y escalable.

Es un marco basado en el modelo de programación MapReduce de Hadoop. Incluso amplía este modelo para incluir cálculos en una amplia gama de tipos de datos y tipos de consultas (incluida la transmisión).

A los desarrolladores de Java interesados en la ciencia de datos les encantará.

Conclusión

La productividad es más que usar herramientas. Es importante que un desarrollador también sepa jugar bien con todo el equipo. Sin embargo, estas herramientas, ya sea que se utilicen individualmente o juntas, de hecho pueden aumentar la cantidad de trabajo de calidad realizado por los desarrolladores.

domingo, 16 de mayo de 2021

El requerimiento de Atrapar o Especificar

Para que el codigo de un programa Java sea valido, debe hacer honor al requerimiento Atrapar o Especificar, en ingles Catch or Specify Requirement. Esto significa que el código que puede lanzar ciertas excepciones debería estar encerrado por uno de los siguientes:

Una sentencia try que atrape la excepción. La sentencia try debería proporcionar un manejador para la excepción, tal como se describe en Atrapando y Manejando Excepciones.

El metodo debería especificar las excepciones que puede lanzar. El metodo debería proporcionar una clausula throws con la lista de excepciones que pudiera lanzar, tal como se describe en Especificando las Excepciones que puede lanzar un metodo.

El codigo que no cumple con el principio de Catch or Specify Requirement ni siquiera compilara.

No todas las excepciones está sujetas a la regla Catch or Specify Requirement, como veremos más adelante. Para entender porqué, necesitamos revisar las tres categorias basicas de Excepciones, del cual solo una está sujeta a la regla mencionada.

Los tres tipos de Excepciones

El primer tipo de Excepción es la excepción verificada (Checked Exception). Estás son condiciones excepcionales que una aplicación bien diseñada debería anticiparse a ellas y recuperarse. Por ejemplo, suponga una aplicación que le solicita al usuario que ingrese el nombre de un archivo, para luego abrir el archivo pasandole el nombre ingresado al constructor de la clase java.io.FileReader. Normalmente, el usuario proporciona el nombre de un archivo que existe y es legible, entonces el constructor de la clase crea al objeto sin ningun problema y la ejecución de la aplicación continua con normalidad. Pero en ocasiones el usuario proporciona el nombre de un archivo que no existe, y el constructor lanza una excepción del tipo java.io.FileNotFoundException. Una aplicación bien escrita atrapará esta excepción y notificará el error al usuario, posiblemente solicitando el ingreso de un nombre de archivo correcto.

Las Excepciones Verificables (Checked Exceptions) están sujetas al principio Catch or Specify Requirement. Todas las excepciones son checked exceptions, excepto aquellas indicadas por Error, RuntimeException, y sus subclases.

El segundo tipo de excepciones es el tipo Error. Estas son aquellas condiciones excepcionales que son externas a la aplicación, y usualmente en donde la aplicación no puede anticiparse o recuperarse. Por ejemplo, supon que tenemos una aplicación que abre sin problemas un archivo para su lectura, pero no es posible leerlo porque hay un error de hardware o un error critico con el sistema de archivos. Este problema encontrado en la lectura lanzara un java.io.IOError. La aplicación puede atrapar esta excepción, con el fin de notificar al usuario del problema - pero tambien podría hacer sentido para el programa imprimir el stack trace (la secuencia de la llamada de los metodos) y salir.

Los errores no están sujetos al requisito de Atrapar o Especificar. Errores son aquellas excepciones indicadas por Error y sus subclases.

El tercer tipo de Excepciones son las excepciones en tiempo de ejecución (runtime exception en ingles). Estás son condiciones excepcionales que son internas a la aplicacion, y en las que usualmente la aplicacion no se puede anticipar o recuperar. Estas excepciones usualmente indican errores de programacion, errores logicos o por no usar correctamente una API. Por ejemplo, considera la aplicacion descrita anteriormente que pasa un nombre de archivo al constructor de la clase FileReader. Si un error de logica permite que una referencia null sea pasada al constructor, el constructor lanzará una excepción NullPointerException. La aplicación puede atrapar esta excepción, pero probablemente tenga mas sentido reparar la incidencia que causa que la excepción ocurra.

Las excepciones Runtime Exception no están sujetas al requerimiento Catch or Specify. Las excepciones Runtime Exception son aquellas indicadas por RuntimeException y sus subclases.

Errores y excepciones en tiempo de ejecución son categorizadas como excepciones no verificadas (unchecked exceptions).

Algunos programadores consideran el requerimiento Catch or Specify un defecto grave del mecanismo de las excepciones y lo dejan pasar usando excepciones no vericadas (unchecked exceptions) en lugar de excepciones verificadas (checked exceptions). En general, esto no es recomendado.

Ignorando el requerimiento Catch or Specify

La sección Excepciones no verificadas La controversia hablaremos acerca de cuando es apropiado el uso de excepciones no verificadas.

domingo, 9 de mayo de 2021

¿Qué es una Excepción en Java?

 Qué es una excepción?

El termino excepción hace a referencia a la frase "evento excepcional" o "evento inesperado." Una excepción es un evento excepcional que puede ocurrir durante la ejecución de un programa y que interrumpe el flujo normal de las instrucciones de nuestro programa.

Cuando curre un error dentro de un método, el método crea un objeto que encapsula este error y delega su manejo al sistema de ejecución. El objeto, llamado objeto de la excepción, contiene información acerca del error, incluyendo su tipo y el estado del programa cuando el error ocurrió. El crear un objeto de excepción y entregarlo al sistema de ejecución se conoce como Lanzar una excepción.

Después de que un método lanza una excepción, el sistema de ejecución intenta encontrar las instrucciones del programa que pueda manejarlo. Existe una lista ordenada donde se encuentran los métodos que han sido llamados, es en esta lista donde se buscará el método donde ocurrió el error y las instrucciones que puedan manejar este error. La lista de métodos es conocida como la pila de llamadas (ver la siguiente figura).

El sistema de ejecución busca en la pila de llamadas un método que contiene el bloque de código que puede manejar la excepción. Este bloque de código es llamado el manejador de la excepción. La búsqueda inicia con el método donde se origino el error y procede a recorrer la pila de llamadas en el orden inverso al que los métodos fueron llamados. Cuando un manejador apropiado es encontrado el sistema de ejecución le entrega el objeto y el control al manejador de dicha excepción. Un manejador de excepción es considerado apropiado si el tipo del objeto de excepción lanzado coincide con el tipo de excepción que puede ser manejado por el manejador.

Al hecho de elegir un manejador de excepción se le conoce como atrapar la excepción. Si el sistema de ejecución después de realizar una búsqueda exhaustiva de todos los métodos en la pila de llamadas y no encuentra un manejador adecuado para nuestra excepción, como se muestra en la siguiente figura, el sistema de ejecución y la propia aplicación pueden terminar su ejecución.

Usar excepciones para manejar errores tiene algunas ventajas sobre otras técnicas tradicionales de manejo de errores. Puedes conocer más sobre las ventajas de las Excepciones en esta entrada.

martes, 26 de noviembre de 2019

¿Cómo ser el mejor programador?

Llevo casi 10 años en esta industria, con altibajos, por momentos siento que soy buen programador pero los retos nunca terminan. Presentas una prueba por aquí otra por allá y en algunas te rechazan, entonces te vuelves a preguntar, ¿Qué tengo que hacer para ser un gran programador?, ¿Cómo se que lo estoy haciendo bien?

La mayoría de los que llevamos la programación en la sangre tenemos esa ansiedad por ser cada día mejores programadores, resolver problemas complejos, hacer maravillas, magia. Y hay que sobrellevar esta ansiedad porque si no terminará por consumirte. Por esta razón he creado una guía para ayudarte en este proceso de mejora.

Voy a darte una serie de consejos para que te conviertas en el mejor programador, desde luego, es pre condición que te guste la programación, que sientes pasión por ella  y que el desarrollo de software  sea tu camino.

1.- Empieza a programar desde muy joven.
Me hubiese encantado haber conocido esta ciencia cuando era niño, desde los 8-10 años. Las ciencias de la computación es un área muy extensa porque se apoya en otras áreas, como las matemáticas, física, lógica, teoría de conjuntos etc,. Por ello, insisto, si puedes empezar a programar cuando antes mejor.

2.- Encuentra un mentor
Un mentor te ayudará a estar motivado, te compartirá su experiencia, te ayudará a reducir años de aprendizaje. Hay mucho conocimiento que uno aprende a base de experimentar, analizar, observar y prueba/error. Pero experimentar consume mucho tiempo, por eso es mejor contar con un buen mentor que te ayude en el desarrollo de tu carrera.

4.- Domina el área de Físico-Matemático
La matemáticas y física te ayudan a desarrollar el pensamiento abstracto, el razonamiento y capacidad de solucionar problemas. Por ello, aprovecha estos cursos, no los desestimes, álgebra, trigonométrica, teoría de conjuntos, probabilidad, calculo integral, calculo diferencial y física.

5.- Domina las estructuras de datos
Tienes que dominar las estructuras de datos, arreglos, listas, colas, pilas, arboles, grafos.

6.- Algoritmos
Trata de conocer los algoritmos más famosos, algunos algoritmos son constantes, como búsqueda y ordenamiento. Pero año con año salen nuevos algoritmos, otros se mejoran y así. En Internet existen muchos algoritmos que puedes estudiar. O porque no, diseñar tus propios algoritmos. La otra habilidad que debes desarrollar es la de traducir la solución de un problema en un algoritmo. Hay que gente muy inteligente que logra encontrar la mejor solución a un problema, pero para programar la solución hay que definir el algoritmo. Si no eres muy inteligente para resolver problemas pero se te da bien definir el algoritmo, ahí tendrás un campo de trabajo.

7.- Desarrollar algunas habilidades
Para ser el mejor programador debes desarrollar las siguientes habilidades: observación, intuición.

8.- Lógica, Teoría de conjuntos, sucesiones y series

9.- Ingles
Los mejores artículos, tesis, e investigaciones relacionadas con las ciencias de la computación se encuentran en ingles. Por ello debes aprender Ingles.

10.- Practicar, practicar y practicar
Aprovechar el opensource, descarga código fuente, lee código fuente, debugea(depura) código fuente. En Internet hay muchas paginas donde puedes buscar problemas a resolver, practica mucho.,



domingo, 8 de septiembre de 2019

La complejidad del desarrollo de Software

Los programadores descubrimos rápidamente que diseñar y construir software es una tarea compleja y muy propensa a errores. Una y otra vez los proyectos de software fallan porque los equipos no están preparados para enfrentar la complejidad del software.

Como resultado, los proyectos no se terminan a tiempo, el costo sobrepasa lo presupuestado, y lo mas lamentable es que muchas veces no entregan valor al negocio.

Si miramos hacía atrás, el desarrollo de software como profesión tiene solo unas cuantas décadas de vida, la demanda ha ido creciendo, el software se hace presente cada día en varias áreas: educación, finanzas, salud, minería, petroleo, gastronomía, banca, seguridad, milicia, automatización, etc,.

Pero... ¿Por qué es tan complejo desarrollar software? Voy a mencionar los puntos que yo consideró que hacen complejo el software, trataré de ordenarlos lo más que pueda.

1.- Se está creando un Software Innovador, Visionario
El desarrollo de Software ha sido la punta de lanza de la innovación de los últimos 60 años. Al construir un Software nuevo uno se adentra en un terreno desconocido, hay limitaciones técnicas y no existe un marco teórico definido, no hay estado del arte. En este escenario no se tiene muy claro lo que se desea o se tiene muy claro lo que se desea pero la tecnología (algoritmos, poder de procesamiento, hardware) existente no satisface al 100%.

2.- No se hace uso de la Ingeniería de Software
xxxxxx

Hace 4 años decidí estudiar un TSU en desarrollo de Software, ha sido un viaje intenso que ya estoy a punto de concluir, en este viaje he conocido múltiples procesos de desarrollo de software y descubierto algunas buenas practicas.

He aprendido como los procesos de desarrollo han estado evolucionando constantemente, el software también evoluciona todo el tiempo. He descubierto también que el desarrollo de software es un proceso colaborativo, varias personas trabajan en diferentes partes del desarrollo del software con el fin de entregar algo que cubra las necesidades del cliente.
El proceso que hoy esta de moda es el Desarrollo Ágil, un proceso que nos permite entregar constantemente mejoras y requerimientos, hoy sabemos que la única constante en el software es el cambio. 

El desarrollo orientado a pruebas (TDD) es una de las buenas practicas que estoy empezando a poner en practica. Aquí una entrada que escribí Usar TDD con criterio


domingo, 21 de abril de 2019

La Arquitectura del Software

En la industria del desarrollo del Software, en las últimas décadas los conceptos de Arquitectura de Software y Arquitecto de Software ha cobrado relevancia. La ingeniería en Desarrollo de Software es una ingeniería joven y por ello busca similitudes en otras ingenierías, de ahí que haya adoptado los conceptos de la Arquitectura.

Comprender el concepto de la Arquitectura de Software, su importancia y el rol del Arquitecto de Software es fácil. Lo complejo es hacer Arquitectura, es complejo porque es un proceso creativo, abstracto y subjetivo.

¿Qué es la Arquitectura del Software?
La arquitectura del Software es el diseño fundamental de todo el sistema de software. Este diseño define los elementos que componen el sistema, que funciones realiza cada elemento, y como estos elementos se relacionan unos con otros. Es una representación de toda la estructura del sistema y de como cada uno de los elementos trabajan en conjunto.
La arquitectura del software forma parte del diseño del sistema, al definir una arquitectura de software se debe considerar los siguientes factores:

- EL propósito y objetivo del sistema,
- La evolución del sistema en el tiempo
- La audiencia y usuarios finales del sistema
- Las características que sean más relevantes para los usuarios, y
- El lugar o contexto donde el sistema estará funcionando.

Los elementos que componen el sistema dependerá del tipo de sistema y de la dimensión del sistema, así los elementos de un sistema pueden ser:

- Bases de datos
- Archivos de Configuración
- Modulos
- Librerias (jars, ddl, jni)
- APIs (commons, utils, persistencia, colecciones)
- Web Services (REST, SOAP)
- Otros sistemas, sistemas legados
- Clases e Interfaces

Una arquitectura de software debe ser documentada, evaluada y probada.

Referencias
MacCormack, A., Rusnak, J., & Baldwin, C. (2007). Exploring the duality between product and organizational architectures: A test of the “mirroring” hypothesis. [Working paper 08-039]. Boston, MA: Harvard Business School. Retrieved from http://www.hbs.edu/faculty/Publication%20Files/08-039_1861e507-1dc1-4602-85b8-90d71559d85b.pdf

Van der Linden, F. J., Schmid, K., & Rommes, E. (2007). Software product lines in action: The best industrial practice in product line engineering. Berlin, DE: Springer. [Books24x7 version]. Available from http://common.books24x7.com/toc.aspx?bookid=31005

lunes, 20 de agosto de 2018

Usar la metodología TDD con Criterio

Cuando aplicamos la metodología TDD (Test-Driven Development) a menudo caemos en los dos extremos, al comienzo del proyecto aplicamos TDD a casi todas nuestras clases y métodos. Luego, a medida que se acerca la fecha de entrega nos damos cuenta que estamos atrasados, nos abrumamos y dejamos de lado el TDD.

Como casi todo (o todo!!!) en la vida, la clave es el equilibrio, buscar el punto medio, y este punto medio nos lo da nuestro criterio.

¿Qué criterios aplicar?
Leyendo el blog de http://calenlegaspi.blogspot.com/ coincido con el, para encontrar el punto medio podemos hacernos las siguientes preguntas.

1.- ¿Que tan probable es que este escenario(clases y métodos) presente incidencias?

2.- ¿Qué tan critico es este escenario?

3.- Las clases son complejas, requiero retroalimentación

¿Que tan probable es que este escenario(clases y métodos) presente incidencias?
Obviamente, no deberíamos probar cosas tan simples como los getters y setters, referencias nulas, números positivos, etc. Estás validaciones se dan por hecho.

Si en una primera o en una segunda revisión (si señor, hay que hacer revisiones) del código del escenario te asegura que funcionará, entonces no necesitas una prueba unitaria.

Muy importante, tu puedes incrementar la cantidad de código que no necesiten de pruebas unitarias, esto lo consigues usando librerías de terceros de alta reputación, es código ya probado y con un madurez importante.

Es por eso que más pruebas no necesariamente significa una mejor calidad del software. De hecho, se obtiene más calidad de software si re-usas código probado a implementar tu propio código.

Las clases son complejas, requiero retroalimentación
Aplicar TDD me permite recibir retroalimentación de como trabajaría mi clase en tiempo de ejecución, que tan fácil y cómodo será su uso o integración y validar que la clase o clases cumplan con la funcionalidad esperada.

miércoles, 8 de agosto de 2018

Consejos para ser un mejor programador

En mi trayecto como desarrollador de software he ido mejorando, aprendiendo de mis errores, aprendiendo de otros programadores. Cuando vivo una experiencia que me marca, lo anoto para después repasarlo. Siempre con el espíritu de crecer profesionalmente.

Tengo tantas ideas, principios y hábitos, algunos en mi mente, otros escrito en alguna hoja guardada entre las paginas de algún libro. En esta entrada pretendo ir organizando estas ideas para compartirlas, recibir retro alimentación o consejos adicionales.

Esta entrada complementa a la entrada ¿Como ser el mejor programador?

1.- Aprende a leer la documentación
Si eres programador del lenguaje Java, generalmente las clases de las APIs más usadas están documentadas en Javadoc, Javadoc es un estandard de Java para documentar. Lo mas relevante de Javadoc es comprender como usar una clase. Cada método tiene una breve descripción, los parámetros que recibe, el objeto que retorna, las posibles excepciones y las reglas más relevantes de uso. Muchas veces usamos una clase sin leer su documentación, violamos algunas reglas de uso y esto genera errores en tiempo de ejecución. Corregir los errores en tiempo de ejecución es muy costoso por eso es mejor invertir un poco de tiempo en leer la documentación de las clases.

2.- Conoce tu herramienta
¿Netbeans o Eclipse? No importa cual sea tu IDE favorito, invierte tiempo en conocer tu IDE. Siempre pregúntate ¿Como puedo optimizar esta tarea?


3.- TDD y depuración de código
Aplica TDD cuando consideres necesario y depura tu código fuente. Aplicar TDD me permite recibir retroalimentación de como trabajaría mi clase en tiempo de ejecución, que tan fácil y cómodo será su uso o integración y validar que la clase o clases cumplan con la funcionalidad esperada. Mas sobre TDD en esta entrada Usar TDD con Criterio

4.- Enfoque, que tu mente esté en el presente.
Es común programar en automático, muchas veces no estamos enfocados al 100% en el código que estamos escribiendo. ¿Te imaginas lo que pasaría si un cirujano está distraído mientras realiza una operación? Bueno... pues hay similitud con nuestro trabajo, una condición o validación errónea puede llevar a que nuestro sistema tenga comportamientos no esperados. (complementar con el punto anterior)

5.- Prudencia y Comunicación
Cuando modificamos código existente debemos tener mucho cuidado con los cambios que introducimos, si dudamos de los cambios que deseamos aplicar es mejor consultar con el resto del equipo. No hay que guardarnos nada, conocer la opinión de otros nos ayuda a corregir o justificar nuestras decisiones.

6.- Buscar la solución más simple
Resolver un problema es un arte, es muy buena practica tener más de una solución y buscar la solución más simple. No porque sea la más simple será la menos eficiente, de hecho, encontrar la solución más simple requiere ingenio, destreza y esfuerzo mental. El tiempo usado en encontrar la solución más simple es una inversión, la solución será menos propensa a errores, fácil de modificar y fácil de depurar.

7.- Pedir Ayuda y saber pedir ayuda
Trabajamos en equipo, tenemos compañeros, apoyémonos en ellos. Muchas veces no preguntamos por no quedar como tontos y esto lo único que hace es afectar nuestra paz mental. Nadie debería juzgarte o criticarte por preguntar. Sin embargo, debes saber pedir ayuda, ordena tus ideas, usa herramientas de comunicación para expresar tus dudas, herramientas como diagramas, dibujos, tablas etc,. Debes ayudar a que los demás comprendan tus dudas para que así puedan ayudarte. Tampoco es que los demás sean más inteligentes o quizás... muchas veces es porque tienen más experiencia, llevan mas tiempo en este arte del desarrollo de software y tu debes aprovechar estos conocimientos.

8.- No Re Inventes la Rueda, Re usa código
Está bien que para practicar programes cosas 

domingo, 29 de julio de 2018

¿Como resolver una incidencia de software?

La complejidad de resolver una incidencia es relativa, depende de muchos factores y condiciones, ayuda mucho establecer en que situación nos encontramos, algunas de las razones de la complejidad son las siguientes:

  1. El desarrollador no conoce el código fuente del software, recién se está involucrando.
  2. La incidencia es muy ambigua, muchas veces esperamos que el error se haga evidente mediante un popup en la GUI, sin embargo, puede ser un error silencioso que solo se hace evidente en el resultado de la funcionalidad.
  3. La incidencia se presenta solo cuando el software es sometido a condiciones extremas, una gran cantidad de datos, muchas peticiones simultaneas o un entorno de red con mucho trafico.
  4. El desarrollador está experimentando con una tecnología nueva.
He establecido una serie de pasos que a mi me ayudan a resolver la incidencia de forma exitosa.


1.- Análisis del problema

Lo primero es comprender la incidencia desde el punto de vista del usuario, tener muy claro que es lo que el usuario percibe como incidencia, puede ser muy evidente como un popup con la descripción del error o puede ser muy difícil de comprender. Si la incidencia no es muy clara solicitaremos al usuario que nos narre en forma de historia que fue lo que pasó, hay que poner especial atención a los detalles.

Usualmente el Usuario reporta la incidencia en alguna herramienta o gestor de incidencias, debes leer detalladamente toda la información disponible, presta atención a cada detalle.
Si hay un popup con mensaje de error, este mensaje puede ser muy genérico, para obtener más detalle hay que revisar los archivos  logs de la aplicación y buscar más información sobre la incidencia.

Es importante que confirmes que la información entregada por el usuario sea coherente, que tenga sentido, muchas veces el usuario hace suposiciones sin sentido. Para ello te apoyaras en tus conocimientos sobre la aplicación, tu sentido común, tu lógica y por último, aprovechando la información de los logs.

1.1- Ponerse en Contexto (aplica con mayor relevancia si eres nuevo)
Si recién te has integrado al equipo de desarrollo y producto, lo primero será ponerte en contexto, aprender sobre la funcionalidad que presenta la incidencia, ¿Cuál es el objetivo?, ¿Qué datos de entrada se necesitan? ¿Qué resultados se esperan?
Familiarizarte con cada uno de los conceptos (jerga de palabras) que se manejan te ayudará a ser más productivo, ante la duda siempre pregunta.


2.- Replicar la incidencia
Una vez que tenemos muy claro en que consiste la incidencia, procedemos a replicarla en nuestro ambiente de desarrollo, este paso puede ser muy simple o muy complejo, puede tomar un par de minutos o incluso horas, dependerá de la complejidad de la incidencia.
Debes apoyarte del usuario que reportó la incidencia, si la complejidad es alta y tienes acceso en persona al usuario, lo mejor será que te muestre in situ como replicar el error. Si no tienes acceso en persona usa todas las herramientas disponibles a tu alcance: email, whatsapp, hangout, videos, etc,. Todo se vale.

Usualmente una incidencia se replica bajo ciertas condiciones, estás condiciones pueden ser los datos que el usuario utilizó, la forma (secuencia de pasos) en qué el usuario usó el sistema, el ambiente (configuración y hardware del sistema) donde el usuario usó el sistema.

Si la información proporcionada no es suficiente para replicar la incidencia y no podemos acceder al usuario que la reportó ni podemos acceder al ambiente donde se replica, lo único que nos queda es tratar de reconstruir los hechos mediante los logs de la aplicación. Buscaremos los datos que el usuario usó y realizaremos las actividades en la misma secuencia que el las realizó, nuestro objetivo es replicar la incidencia.

Si replicar la incidencia es complejo, te sugiero los siguientes pasos:

  1. Has una lista de todas las variables y escenarios que podrían afectar tu funcionalidad.
  2. Ordena la lista de acuerdo a la facilidad de probar
  3. Empieza por probar cada ítem de la lista del más simple al más complejo.


3.- Identificar los componentes GUI involucrados
Si la incidencia se reproduce en un ambiente GUI, debemos identificar el componente asociado a la funcionalidad de la incidencia. Usualmente el componente de partida será el botón por el que el usuario ejecuta la funcionalidad.
Sabemos que una funcionalidad puede involucrar un conjunto de componentes, debemos usar la observación, la astucia, la intuición y el sentido común para determinar que componentes son los que requieren ser revisados, así reducimos el espacio y conseguimos un mayor enfoque.

4.- Identificar las clases y métodos involucrados
Una vez identificado el botón u otro componente GUI que inicia la funcionalidad, vamos a identificar las clases y métodos involucrados.

5.- Resolver de forma aislada
Una vez que tenemos identificados las clases y métodos involucrados, vamos a debuggear de forma aislada cada uno de esos métodos, para ello como pre condición es necesario saber que hace cada método, cuales son los datos de entrada y que datos de salida se espera. Aconsejo debuggear porque así vamos leyendo cada linea de código fuente, y podemos identificar errores lógicos.
Si la incidencia no consiste en errores lógicos, debemos preguntarle al usuario que esperaba como resultado, posiblemente es un error de diseño y hay que buscar otro algoritmo.

6.- Probar la solución
Por ultimo, realizar las pruebas necesarias


sábado, 28 de julio de 2018

Que fue de Rational Software

Cuando estudiamos UML es inevitable leer sobre la empresa Rational Software, empresa que definió e impulso esta herramienta de modelado para el desarrollo de software, hasta llegar a ser un modelo estandard.
La empresa fue adquirida por IBM por la cuantiosa cifra de 2.1 billones de dolares, una cifra que  ni siquiera soy capaz de imaginar. La adquisición fue anunciada en Diciembre del 2002 y concretada el 21 de Febrero del 2003, casi dos meses duró el proceso.
Rational Software fue una empresa que desarrolló metodologías y herramientas para el desarrollo de software, pero lo mas valioso es que estás herramientas y modelos son un estandard y además son abiertas, es decir, están disponibles para el publico, al menos una parte de ellas.
Rational fue una empresa comprometida con la ingenieria de software, impulsó las mejores practicas y servicios para el desarrollo de las aplicaciones empresariales, la construcción de software y sistemas en general, incluyendo software embebido tales como software para telefonos y equipos mobiles.
Rational fue importante, IBM le puso el ojo y desembolsó tremenda cantidad de dinero. Para mi, el legado de Rational es que gracias a ellos ahora podemos desarrollar software de forma más rapida, segura, de calidad y software que está preparado para un entorno que cambia rapidamente.
Al momento de ser adquirida tenía clientes en al menos 89 ciudades, contaba con mas de 3400 empleados, y se estima que más de 600 000 desarrolladores de software usaban sus herramientas. Ahora Rational pertence a IBM y es participante de un mercado que se estima en mas de 15 billones de dolares.
La industria del desarrollo de software aun tiene para varios años más.
Se supone que Grady Booch fue fundador de Rational, pero oficialmente se reconoce a Mike Devlin, un veterano y ya retirado ingeniero en ciencias de la computación.

Referencias
https://www-03.ibm.com/press/us/en/pressrelease/314.wss


¿Como ser un mejor Líder?

Nunca fue mi intención asumir el rol de Líder Técnico, las responsabilidades de este rol son complejas y a mi me gusta la calma, la paz. Si he de enfrentar la adversidad quiero hacerlo solo, porque me conozco, se como motivarme, se como llevarme al limite, de hecho he sido mi propio líder.
Como cualquier otro apasionado por el Desarrollo de Software, tengo una visión, y quizás mi mayor anhelo es poder transmitir está visión al resto de mi equipo.

He aprendido que un líder se hace a través de sus acciones, de hechos y no de palabras, para mi en estos meses ha sido fundamental reconocer cuales son esas acciones, para redoblar esfuerzos, mejorar mi capacidad y motivar a mi equipo de trabajo.
Como siempre, escribo estás lineas, primeramente para organizar mis ideas, y para cumplir con una promesa que me hice años atrás: la de compartir mis experiencias con la nueva generación.

Mantener motivados a tu equipo de trabajo es un gran desafío, hacer que se pongan la camiseta de la empresa, que sientan cariño por la empresa y por el producto. Pero para lograrlo es necesario primeramente, ser un líder ejemplar, predicar con el ejemplo. En momentos de crisis la empresa tiene que ser guiada de forma correcta para mantener la esperanza y confianza en que tarde o temprano se logrará salir de este bache.

Un líder tiene que ser un referente para el resto del equipo, dirigir, motivar y exigir de la mejor manera para lograr un ambiente grato y alcanzar los objetivos propuestos.

Sin embargo, ocupar este puesto no es fácil, no ha sido fácil para mi, porque ante la adversidad la mayoría o todos se darán por vencidos, dirán que es muy difícil e incluso imposible y se quedarán de brazos cruzados, pero tu no, tu tienes que resolver, tu tienes que buscar la solución, tienes que confiar en ti, en tus capacidades y habilidades. Algunos días tendrás que quedarte hasta tarde, porque los problemas se tienen que solucionar, para el líder no existe la palabra imposible, ser líder requiere un enorme compromiso.

El bienestar laboral y emocional de tu equipo es una gran responsabilidad, debes cuidar que tu equipo te vea como un aliado que confíe en ti  y no como el malo del cuento, pero no debes ser tan suave como para que pasen encima de ti. Debes saber cuando ejercer presión, pero también conocer el limite de la presión que puedes ejercer.

En estos meses he aprendido mucho y puedo escribir los siguientes consejos, los escribo con el fin de no olvidarlos y echar mano de ellos cuando los necesite.

Poner el Ejemplo

El buen juez por su casa empieza, un buen líder es aquel que pone el ejemplo, un líder se compromete y cumple con sus compromisos en tiempo y forma. No se puede esperar que tu equipo sea comprometido si tu no lo eres. No se puede esperar que tu equipo aplique las buenas practicas y/o metodologías si tu no lo haces. No se puede esperar que tu equipo aplique Testing Unitario si tu no lo haces. No se puede esperar que tu equipo use las herramientas si tu las ignoras. Un primer paso es poner el ejemplo de estas y otras actividades.
El equipo tiene verte fuerte y seguro de ti mismo en aquellos momentos de adversidades, tienen que ver como tu estás seguro de que encontrarás la solución, que no hay imposibles para ti. Tienen que verte calmado y relajado aun en esas adversidades. ¿Como se consigue esto? Con experiencia y conocimientos.

Establecer Limites

Un líder tiene que reunirse periódicamente con su equipo para dejar claras las responsabilidades de cada uno y al mismo tiempo para conocer las capacidades de su equipo. Y aquí hay que ser bien detallado y preciso. ¿Cuantas horas le toma hacer cierta actividad?, ¿Que tan complejo era la tarea? ¿Que tecnologías se involucraron? ¿Que metodologías? ¿Qué se obtuvo como resultado?, etc,.  Hay que determinar de forma objetiva y verdadera la capacidad que tiene cada uno para poder alcanzar sus metas. Con esta información podrás organizarte mejor y asumir los compromisos en tiempo y forma.

Delegar Tareas

Desde luego, mas de una ocasión he pensado, mejor lo hago yo, porque me gusta que las cosas se hagan bien o un poco mejor que bien y en un dos por tres. Lo cierto es que el tiempo es escaso para nosotros los lideres. Delegar es una de las tareas más importantes, delegar implica confiar en tu equipo, pero la confianza se tiene que ir ganando, de a poco, y tu tienes que delegar paso a paso, primero tareas simples, para que tu equipo vaya cogiendo confianza y experiencia en ellos mismos, y así ir incrementando gradualmente la complejidad. Eso es lo ideal, pero sabemos que en la practica no todos es lineal, a veces hay calma y a veces hay tormentas.
Un líder que no delega no trabaja en equipo, trabajar en equipo es esencial para lograr el bienestar emocional y laboral del equipo, porque haces que se sientan parte de "algo", que sientan que también están contribuyendo en "algo".

Se un mentor
Tienes que ser alguien cercano a tu equipo, ayudarles a crecer, conocer sus fortalezas y debilidades. Poder detectar las fortalezas y debilidades de tu equipo requiere que desarrolles tu capacidad de observación. Sin embargo, si tu has crecido (profesionalmente) desde muy abajo, eso es muy bueno porque te permitirá conectar de una forma más real y sincera, tu pasaste por ahí y sabes lo que tu equipo piensa y siente.

Pida Ayuda

El desarrollo de software es complejo, no podemos abarcar todas las áreas, "nadie nace sabiendo todo " -decía mi abuela. Pedir ayuda no es malo, mostrar un poco de vulnerabilidad es bueno, así tu equipo sabrá que eres tan humano como ellos, que tienes debilidades técnicas pero que las compensas pidiendo ayuda. En ocasiones nos aturdimos y no logramos ver todo el panorama, siempre ayuda pedir la visión y/o percepción de otra persona, porque puede que ella vea ese pequeño detalle que tu estás pasando por alto.
También es bueno pedir ayuda a tu equipo cuando por falta de tiempo tu no puedes cumplir con tus compromisos, eso ayuda a fortalecer la confianza.

Seguiré escribiendo mas sobre estos temas a medida que vaya aprendiendo.