Registro de traza en Java [email protected] / [email protected] ¿…y qué es la traza? Veremos dentro de poco como crear el objeto “log”. public int suma(int a, int b) { log.debug(“Entrando en suma”); int sum; sum = a + b; log.info(“Resultado de la suma:”+sum); log.debug(“Saliendo de suma”); return sum; } <debug> Entrando en suma <info> Resultado de la suma: X <debug> Saliendo de suma 1 Utilidad de la traza Ayuda a encontrar errores. Ayuda a depurar programas (multihilo, distribuidos, etc…) Permite mejorar el rendimiento. ¿Cuál es más útil para detectar lo que ha pasado? java.sql.SQLException: Table not found: TEO in statement [SELECT * FROM Teo;] at org.hsqldb.Trace.getError(Unknown Source) at org.hsqldb.jdbcResultSet.<init>(Unknown Source) at org.hsqldb.jdbcConnection.executeStandalone(Unknown Source) at org.hsqldb.jdbcConnection.execute(Unknown Source) at org.hsqldb.jdbcStatement.fetchResult(Unknown Source) at org.hsqldb.jdbcStatement.executeQuery(Unknown Source) at org.rquery.helppers.Metadata.GetFieldList(Metadata.java:115) at org.rquery.analex.ra.SQLBuilderAR.buildRelation(SQLBuilderAR.java:69) at org.rquery.analex.ra.ParserLLAR.generate(ParserLLAR.java:204) at org.rquery.analex.ra.ParserLLAR.translateQuery(ParserLLAR.java:99) at org.rquery.analex.AnalexDecorator.translateQuery(AnalexDecorator.java:36) at org.rquery.gui.RQueryAdvancedGUI$EjecutarAction.doQuery(RQueryAdvancedGUI.java:776) at org.rquery.gui.RQueryAdvancedGUI$EjecutarAction.actionPerformed(RQueryAdvancedGUI.java:755) at javax.swing.AbstractButton.fireActionPerformed(Unknown Source) at javax.swing.AbstractButton$ForwardActionEvents.actionPerformed(Unknown Source) at javax.swing.DefaultButtonModel.fireActionPerformed(Unknown Source) at javax.swing.DefaultButtonModel.setPressed(Unknown Source) at javax.swing.plaf.basic.BasicButtonListener.mouseReleased(Unknown Source) at java.awt.AWTEventMulticaster.mouseReleased(Unknown Source) at java.awt.Component.processMouseEvent(Unknown Source) at java.awt.Component.processEvent(Unknown Source) at java.awt.Container.processEvent(Unknown Source) at java.awt.Component.dispatchEventImpl(Unknown Source) at java.awt.Container.dispatchEventImpl(Unknown Source) at java.awt.Component.dispatchEvent(Unknown Source) at java.awt.LightweightDispatcher.retargetMouseEvent(Unknown Source) at java.awt.LightweightDispatcher.processMouseEvent(Unknown Source) <info> GUI - Pulsado botón ejecutar. at java.awt.LightweightDispatcher.dispatchEvent(Unknown Source) <info> Parser – Consulta recibida ‘TEO;’ at java.awt.Container.dispatchEventImpl(Unknown Source) at java.awt.Window.dispatchEventImpl(Unknown Source) válida ‘TEO;’ <info> Parser – Consulta at java.awt.Component.dispatchEvent(Unknown Source) <info> SQLTranslator – Consulta traducida a ‘select * from TEO;‘ at java.awt.EventQueue.dispatchEvent(Unknown Source) <info> SQLExec – Ejecutando consulta ‘select * from TEO;‘ at java.awt.EventDispatchThread.pumpOneEventForHierarchy(Unknown Source) at java.awt.EventDispatchThread.pumpEventsForHierarchy(Unknown Source) <error> SQLExec – Excepción Table not found: TEO in statement at java.awt.EventDispatchThread.pumpEvents(Unknown Source) <debug> SQLExec – Consulta at java.awt.EventDispatchThread.pumpEvents(Unknown Source) abortada at java.awt.EventDispatchThread.run(Unknown Source) <info> GUI – Consulta terminada. 1 2 [SELECT * FROM Teo;] 2 Características de Log4j Habilitar o desabilitar logs. Priorizar mensajes. Redirección de la salida. Formato de los mensajes. Configuración por código o archivos externos. Organización jerárquiza. Creando un log import org.apache.log4j.Logger; Aunque hay más, de momento es suficiente. public class PruebaLog { Uno para todos…. static Logger log; static { log = Logger.getLogger(PruebaLog.class.getName()); El nombre de un log es el nombre de la clase y de los paquetes donde está. Si no existe lo crea y si ya se creo devuelve una referencia } } Todo log tiene una configuración por defecto que podemos cambiar desde el código o desde un archivo externo. 3 Niveles de prioridad DEBUG: Mensajes de depuración. INFO: Mensajes similares al modo "verbose" en otras aplicaciones. WARN: Mensajes de alerta sobre eventos que se desea mantener constancia, pero que no afectan el funcionamiento del programa. ERROR: Mensajes de error de la aplicación que se desea guardar, estos eventos afectan al programa pero lo dejan seguir funcionando. FATAL: Mensajes críticos del sistema, generalmente después de guardar el mensaje el programa abortará. (Solo para el archivo de configuración): ALL: este es el nivel más bajo posible, habilita todos los logs. OFF: este es el nivel más alto posible, deshabilita todos los logs. FATAL < ERROR < WARN < INFO < DEBUG Un log para Intercambiar 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. public static void Intercambia(List l1, List l2) { Object b1=null; Object aux=null; Collections.sort(l1); Collections.sort(l2); ListIterator i1=l1.listIterator(); while (i1.hasNext()) { b1=i1.next(); int pos=Collections.binarySearch(l2,b1); if (pos<0) { if (i1.hasNext()){ aux=i1.next(); if (b1.equals(aux)) { i1.remove(); l2.add(b1); Collections.sort(l2); } } } } } ¿En qué líneas y con qué nivel se colocaría la traza? 4 Un log para Intercambiar Una solución 1. 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. 18. 19. 20. 21. 22. 23. 24. 25. 26. 27. 28. 29. public static void Intercambia(List l1, List l2) { Object b1=null; Object aux=null; log.info("LLamada a Intercambia(l1, l2);"); if ((l1 == null) || (l2==null)) { log.error("Lista nula:\nl1="+l1+"\nl2="+l2+"\nOperación abortada."); 0 [main] INFO IntercambiaConLog - LLamada a Intercambia(l1, l2); return; } 0 [main] DEBUG IntercambiaConLog - Parßmetros: Collections.sort(l1);l1=[1, Collections.sort(l2); 2, 2, 3]|l2=[4, 5, 6] log.debug("Parámetros:\nl1="+l1+"\nl2="+l2); 0 [main] DEBUG IntercambiaConLog - Elemento procesado: 1 ListIterator i1=l1.listIterator(); 0 [main] DEBUG IntercambiaConLog - Elemento no encontrado en l2 while (i1.hasNext()) { 0 [main] DEBUG IntercambiaConLog - Elemento procesado: 2 b1=i1.next(); [main] DEBUG IntercambiaConLog - Elemento no encontrado en l2 log.debug("Elemento0 procesado: "+b1); int pos=Collections.binarySearch(l2,b1); 0 [main] INFO IntercambiaConLog - Salida de Intercambia(l1, l2); if (pos<0) { if (i1.hasNext()) { log.debug("Elemento no encontrado en l2"); aux=i1.next(); 0 [main] INFO {IntercambiaConLog - LLamada a Intercambia(l1, l2); if (b1.equals(aux)) log.debug("Elemento repetido encontrado en - l1"); 16 [main] ERROR IntercambiaConLog Lista nula: i1.remove(); l2.add(b1); l1=null| l2=[3, 4, 2, 6, 4] Collections.sort(l2); Operaci¾n abortada. log.debug("Nuevo valor de l2 ="+l2); } } } else log.debug("Elemento encontrado en l2"); } log.info("Salida de Intercambia(l1, l2);"); } Cambio del nivel del log. 0 [main] INFO IntercambiaConLog - LLamada a Intercambia(l1, l2); 0 [main] DEBUG IntercambiaConLog - Parßmetros: l1=[1, 2, 2, 3]|l2=[4, 5, 6] 0 [main] DEBUG IntercambiaConLog - Elemento procesado: 1 0 [main] DEBUG IntercambiaConLog - Elemento no encontrado en l2 0 [main] DEBUG IntercambiaConLog - Elemento procesado: 2 0 [main] DEBUG IntercambiaConLog - Elemento no encontrado en l2 0 [main] INFO IntercambiaConLog - Salida de Intercambia(l1, l2); 0 [main] INFO IntercambiaConLog - LLamada a Intercambia(l1, l2); 0 [main] INFO IntercambiaConLog - Salida de Intercambia(l1, l2); log.setLevel(Level.INFO); 5 Conceptos Dos conceptos importantes: Appender: indica que hacer con la información que recibe. Layout: indica el formato de la información. Ejemplo anterior Appender: mostrarlo por salida estándar. Layout: todo lo que añadía almensaje. Vamos a ver estos conceptos junto con el archivo de propiedades El archivo de propiedades Nombre del log. log4j.logger.IntercambiaConLog=DEBUG, stdout Nivel del log. log4j.appender.stdout = org.apache.log4j.ConsoleAppender Lista de appenders. log4j.appender.stdout.layout = org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern = %5p (%F:%L) - %m%n El appender “stdout” manda el mensaje a la consola. El layout de “stdout” sige un patrón. Si no indicamos el patrón se muestra solo el mensaje. Un posible patrón: %p: nivel del mensaje. %F: nombre del fichero. %L: Núm. de línea. %m: Mensaje. 6 Como usarlo import org.apache.log4j.Logger; public class PruebaLog { static Logger log; static { PropertyConfigurator.configure("log.prop"); log = Logger.getLogger(PruebaLog.class.getName()); } } INFO (IntercambiaConLog.java:31) - LLamada a Intercambia(l1, l2); DEBUG (IntercambiaConLog.java:39) - Parßmetros: l1=[1, 2, 2, 3]|l2=[4, 5, 6] DEBUG (IntercambiaConLog.java:44) - Elemento procesado: 1 DEBUG (IntercambiaConLog.java:50) - Elemento no encontrado en l2 DEBUG (IntercambiaConLog.java:44) - Elemento procesado: 2 DEBUG (IntercambiaConLog.java:50) - Elemento no encontrado en l2 INFO (IntercambiaConLog.java:65) - Salida de Intercambia(l1, l2); Otro appender Ahora los mensajes irán tanto a “stdout” como a “logfile”. log4j.logger.IntercambiaConLog= DEBUG, stdout, logfile log4j.appender.logfile = org.apache.log4j.RollingFileAppender log4j.appender.logfile.layout = org.apache.log4j.SimpleLayout El appender guarda los mensajes en ficheros del tamaño indicado. Solo el nivel y el mensaje. log4j.appender.logfile.File=log.log Nombre del fichero log4j.appender.logfile.MaxFileSize=100KB Tamaño 7 Otro ejemplo Un fragmento del temporizador 1. public class Temporizador { 2. // Atributos… 3. 4. public Temporizador(int numPruebas) { if (numPruebas>0){ this.numPruebas=numPruebas; tiempos= new long [numPruebas]; 5. 6. 7. 8. } else { throw new IllegalArgumentException(); } } 9. 10. 11. public Temporizador() { this(1); } 12. // …. 13 Escribir la inicialización y los mensajes del log. } Otro ejemplo Un fragmento del temporizador 1. public class Temporizador { 2. 3. 4. 5. 6. 7. 8. 9. 10. 11. 12. 13. 14. 15. 16. 17. // Atributos… public Temporizador(int numPruebas) { log.debug("Nuevo temporizador con "+numPruebas+" pruebas"); if (numPruebas>0){ this.numPruebas=numPruebas; tiempos= new long [numPruebas]; } else { log.error("Núm de pruebas Se lanza importinvalido. org.apache.log4j.Logger; IllegalArgumentException."); throw new IllegalArgumentException(); public class Temporizador { } static Logger log; } static { BasicConfigurator.configure(); public Temporizador() { //PropertyConfigurator.configure("log.prop"); this(1); log = log.warn("Creado temporizadorLogger.getLogger(PruebaLog.class.getName()); con 1 sola prueba\n"+ "Probablemente el tiempo de ejecución sea 0."); } } // …. //….. } } 8 El problema de los temporizadores Escribir un archivo de propiedades para este problema. El problema de los temporizadores # Log padre de todos los demás log4j.rootLogger=DEBUG, stdout ## Logs log4j.logger.Temporizador=DEBUG, stdout log4j.logger.Busqueda=DEBUG, stdout ## Estas dos ya nos las dan hechas, por lo que es probable que funcionen log4j.logger.BusquedaBinaria=ERROR, busquedas log4j.logger.BusquedaLineal=ERROR, busquedas # Este appener va a la consola - Salida por pantalla log4j.appender.stdout=org.apache.log4j.ConsoleAppender log4j.appender.stdout.layout=org.apache.log4j.PatternLayout log4j.appender.stdout.layout.ConversionPattern=%5p (%F:%L) - %m%n # Appender de las búsquedas ya implementadas log4j.appender.busquedas=org.apache.log4j.FileAppender log4j.appender.busquedas.File=busquedas.log # Mostramos el número de línea, el archivo y el mensaje log4j.appender.busquedas.layout=org.apache.log4j.PatternLayout log4j.appender.busquedas.layout.ConversionPattern= (%L) - [%F] %m%n 9 Ant ¿Qué es Ant? Una versión evolucionada de “make”. http://jakarta.apache.org/ant/index.html Ant busca automatiza la ejecución de tareas. Las tareas se guardan en un archivo XML. 10 Algunos ejemplos de tareas automatizables. Crear la estructura de carpetas de nuestro proyecto. Compilación incremental Generar la documentación javadoc. Generar la versión de distribución. Un ejemplo de ejecución I. Tareas. build.xml Nombre por defecto. <?xml version="1.0" encoding="UTF-8"?> <project name="PruebaAnt" basedir="."> <target name="init" description="o Crea la estructura de directorios"> <mkdir dir="bin"> </mkdir> <mkdir dir="src"> </mkdir> <mkdir dir="lib"> </mkdir> </target> </project> 11 Más tareas. build.xml Compila todos los .java excepto las pruebas. <?xml version="1.0" encoding="UTF-8"?> <project name="PruebaAnt" basedir=".“ default="Compile"> <target name="compile" description="o Compila el codigo"> <javac destdir="bin" deprecation="true“ debug="true“ optimize="false" excludes="**/Test*"> <src> <pathelement location="src"> </pathelement> </src> <classpath> <fileset dir="lib"> <include name="*.jar"> </include> </fileset> </classpath> </javac> </target> </project> Documentación sobre las tareas En la documentación de ant se pueden encontrar todas las tareas y sus atributos. 12 Ant + Junit Ejecutándo las pruebas desde ant <target name="test" description="o Ejecuta las pruebas"> <junit printsummary="yes"> <!-- Por defecto el resultado siempre se envia a un archivo. --> <formatter type="plain" usefile="false"/> <batchtest fork="yes"> <fileset dir="."> <include name="*Test*.class"/> </fileset> </batchtest> </junit> </target> Dependencias entre tareas Crea un archivo ZIP con todo lo necesario. > ant crear-distribución Primero compila todos los fuentes. > compilar Después ejecuta las pruebas para asegurar que no hay ningún error. > probar > Borrar o crear directorios > copiar-archivos > crear-javadoc > empaquetar Borra la antigua distribución o crea los directorios si no existen. Copia los archivos de la distribución Crea la documentación del código Empaqueta toda la distribución 13 FIN [email protected] / [email protected] 14