Registro de traza en Java javierj@us.es / jjgrodriguez@gmail.com 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) 1 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 <info> GUI - Pulsado botón ejecutar. Source) at java.awt.lightweightdispatcher.dispatchevent(unknown Source) at java.awt.container.dispatcheventimpl(unknown <info> Parser Consulta Source) recibida TEO; at java.awt.window.dispatcheventimpl(unknown <info> Parser Consulta Source) válida TEO; at java.awt.component.dispatchevent(unknown Source) at java.awt.eventqueue.dispatchevent(unknown Source) at java.awt.eventdispatchthread.pumponeeventforhierarchy(unknown <info> SQLExec Ejecutando consulta Source) select * from TEO; at java.awt.eventdispatchthread.pumpeventsforhierarchy(unknown Source) at java.awt.eventdispatchthread.pumpevents(unknown Source) at java.awt.eventdispatchthread.pumpevents(unknown <debug> SQLExec Consulta Source) abortada at java.awt.eventdispatchthread.run(unknown Source) <info> SQLTranslator Consulta traducida a select * from TEO; <error> SQLExec Excepción Table not found: TEO in statement [SELECT * FROM Teo;] <info> GUI Consulta terminada. 2 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; public class PruebaLog { static Logger log; static { log = Logger.getLogger(PruebaLog.class.getName()); Aunque hay más, de momento es suficiente. Uno para todos. 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. public static void Intercambia(List l1, List l2) { 2. Object b1=null; 3. Object aux=null; 4. Collections.sort(l1); 5. Collections.sort(l2); 6. ListIterator i1=l1.listiterator(); 7. while (i1.hasnext()) { 8. b1=i1.next(); 9. int pos=collections.binarysearch(l2,b1); 10. if (pos<0) { 11. if (i1.hasnext()){ 12. aux=i1.next(); 13. if (b1.equals(aux)) { 14. i1.remove(); 15. l2.add(b1); 16. Collections.sort(l2); 17. } 18. } 19. } 20. } 21. } En qué líneas y con qué nivel se colocaría la traza? 4
Un log para Intercambiar Una solución 1. public static void Intercambia(List l1, List l2) { 2. Object b1=null; Object aux=null; 3. log.info("llamada a Intercambia(l1, l2);"); 4. if ((l1 == null) (l2==null)) { 5. log.error("lista nula:\nl1="+l1+"\nl2="+l2+"\noperación abortada."); 6. return; 0 [main] INFO IntercambiaConLog - LLamada a Intercambia(l1, l2); 7. } 0 [main] DEBUG IntercambiaConLog - Parßmetros: 8. Collections.sort(l1); l1=[1, Collections.sort(l2); 2, 2, 3] l2=[4, 5, 6] 9. log.debug("parámetros:\nl1="+l1+"\nl2="+l2); 0 [main] DEBUG IntercambiaConLog - Elemento procesado: 1 10. ListIterator i1=l1.listiterator(); 0 [main] DEBUG IntercambiaConLog - Elemento no encontrado en l2 11. while (i1.hasnext()) { 12. b1=i1.next(); 0 [main] DEBUG IntercambiaConLog - Elemento procesado: 2 13. log.debug("elemento0 procesado: [main] DEBUG "+b1); IntercambiaConLog - Elemento no encontrado en l2 14. int pos=collections.binarysearch(l2,b1); 0 [main] INFO IntercambiaConLog - Salida de Intercambia(l1, l2); 15. if (pos<0) { 16. if (i1.hasnext()) { 17. log.debug("elemento no encontrado en l2"); 18. aux=i1.next(); 19. if (b1.equals(aux)) 0 [main] INFO { IntercambiaConLog - LLamada a Intercambia(l1, l2); 20. log.debug("elemento 16 [main] ERROR repetido IntercambiaConLog encontrado en -l1"); Lista nula: 21. i1.remove(); l1=null l2.add(b1); l2=[3, 4, 2, 6, 4] 22. Collections.sort(l2); Operaci¾n abortada. 23. log.debug("nuevo valor de l2 ="+l2); 24. } 25. } 26. } else log.debug("elemento encontrado en l2"); 27. } 28. log.info("salida de Intercambia(l1, l2);"); 29. } 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 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 Nivel del log. Lista de appenders. 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
import org.apache.log4j.logger; public class PruebaLog { static Logger log; Como usarlo 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 log4j.logger.intercambiaconlog= DEBUG, stdout, logfile log4j.appender.logfile = org.apache.log4j.rollingfileappender log4j.appender.logfile.layout = org.apache.log4j.simplelayout log4j.appender.logfile.file=log.log log4j.appender.logfile.maxfilesize=100kb Ahora los mensajes irán tanto a stdout como a logfile. El appender guarda los mensajes en ficheros del tamaño indicado. Solo el nivel y el mensaje. Nombre del fichero Tamaño 7
Un fragmento del temporizador 1. public class Temporizador { 2. // Atributos Otro ejemplo 3. public Temporizador(int numpruebas) { 4. if (numpruebas>0){ this.numpruebas=numpruebas; tiempos= new long [numpruebas]; 5. } else { 6. throw new IllegalArgumentException(); 7. } 8. } Escribir la inicialización y los mensajes del log. 9. public Temporizador() { 10. this(1); 11. } 12. //. 13 } Un fragmento del temporizador 1. public class Temporizador { 2. // Atributos Otro ejemplo 3. public Temporizador(int numpruebas) { 4. log.debug("nuevo temporizador con "+numpruebas+" pruebas"); 5. if (numpruebas>0){ this.numpruebas=numpruebas; tiempos= new long [numpruebas]; 6. } else { 7. log.error("núm de pruebas importinvalido. org.apache.log4j.logger; Se lanza IllegalArgumentException."); 8. throw new IllegalArgumentException(); public class Temporizador { 9. } 10. } static Logger log; static { BasicConfigurator.configure(); //PropertyConfigurator.configure("log.prop"); 11. public Temporizador() { 12. this(1); log = 13. log.warn("creado temporizador con 1 sola prueba\n"+ Logger.getLogger(PruebaLog.class.getName()); 14. "Probablemente el tiempo de ejecución sea 0."); 15. } } 16. //. //.. 17. } } 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
Ejecutándo las pruebas desde ant Ant + Junit <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 > ant crear-distribución Crea un archivo ZIP con todo lo necesario. > compilar Primero compila todos los fuentes. > probar Después ejecuta las pruebas para asegurar que no hay ningún error. > Borrar o crear directorios Borra la antigua distribución o crea los directorios si no existen. > copiar-archivos Copia los archivos de la distribución > crear-javadoc Crea la documentación del código > empaquetar Empaqueta toda la distribución 13
FIN javierj@us.es / jjgrodriguez@gmail.com 14