lunes, 24 de octubre de 2011

Log con campos CLOB en Log4j

Recientemente he estado probando diferentes configuraciones del log4j, una de las cosas interesantes era guardar el log en base de datos. usando log4j no representa ningún problema, ya que podemos hacer uso del appender  JDBCAppender para inserciones sencillas.

El problema surge cuando queremos almacenar la traza de un error, y su tamaño por lo general tiene un tamaño considerable (muy superior a los 4000 caracteres que te permite un varchar2 de Oracle, por ejemplo). La solución obvia (en Oracle) es usar un CLOB (Character Large Object), pero actualmente Log4j no permite el uso de CLOB.

Buscando un poco por la red, encontremos algunas soluciones alternativas como la de Danko Mannhaupt, muy recomendada, la cuál permite el uso de CLOB si usamos "prepared statements". En mi caso, no planteamos usar esta solución, ya que implicaba ciertos cambios en el proyecto así como añadir librerías de terceros, etc. Además la estructura de la tabla en BBDD planteada no se ajustaba a lo que requeríamos.

En tal caso, optamos por hacerlo nosotros mismos. Tan solo es necesario heredar de la clase  JDBCAppender (la de Log4j),  y sobreescribir (override) el método execute, el cuál tiene como parámetro de entrada la SQL ya formada, por lo que sólo deberemos tratar la ristra y crear el PreparedStatement.

Veamos un ejemplo:

package com.fraguaDigital.log;

import java.io.StringReader;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import org.apache.log4j.jdbc.JDBCAppender;

public class CLobJDBCAppender extends JDBCAppender {

 @Override
 protected void execute(String sql) throws SQLException {
  Connection con = null;
  Statement stmt = null;
  try {
   con = getConnection();

   if (sql.contains("#")){
    int initPos = sql.indexOf("#");
    int lastPos = sql.lastIndexOf("#");
    String clobData = sql.substring(initPos+1, lastPos);
    StringReader stringReader = new StringReader(clobData);

    sql = sql.substring(0,initPos) +"?"+ sql.substring(lastPos+1);
    stmt = con.prepareStatement(sql);
    ((PreparedStatement)stmt).setCharacterStream(1, stringReader, clobData.length());          
    ((PreparedStatement)stmt).execute();
   }else{
    stmt = con.createStatement();
    stmt.executeUpdate(sql);         
   }
  } catch (SQLException e) {
   if (stmt != null)
    stmt.close();
   throw e;
  }

  stmt.close();
  closeConnection(con);
 }
}

En este caso usamos el carácter "#" para marcar el comienzo y el fin del campo que es CLOB. Esto es sólo un ejemplo, pero puede generalizarse fácilmente.

Por último, tendríamos que añadir  en la configuración del log4j que use nuestro appender y definir en la cadena sql el campo CLOB usando las "#".

<appender name="LOG_BBDD" class="com.fraguaDigital.log.CLobJDBCAppender">
 <param name="Threshold" value="WARN"/>
 <param name="driver" value="${jdbc.driverClassName}" />
 <param name="URL" value="${jdbc.url}" />
 <param name="user" value="${jdbc.username}" />
 <param name="password" value="${jdbc.password}" />
 <layout class="org.apache.log4j.PatternLayout">
  <param name="ConversionPattern"
   value="INSERT INTO tabla (FECHA, PRIORIDAD, MENSAJE, CLASE, STACKTRACE, USUARIO) VALUES ('%d', '%p', '%m%n', '%C.%M(%L)', #%throwable#)" />
 </layout> 
</appender>

sábado, 21 de mayo de 2011

Anidar contenedores en Flex

     Cuando tenemos componentes anidados dentro de otros componentes a veces queremos que el componente ocupe el 100% del padre, por tanto definimos su ancho y alto al 100% (hasta ahí todo normal), para que al realizar el calculo de las dimensiones (método measure) se ajuste. Aún así, cuando usamos contenedores anidado podemos ver que siempre deja un pequeño espacio entre el borde del padre y el del contenedor hijo, por lo que creo es debido al padding, pero incluso modificado los valores del padding en el estilo (tag style o en el css) no conseguía que el contenedor hijo ocupara realmente el 100. Quedaba algo así:
     La solución que encontré a este problema es utilizar la propiedad clipContent. Establecido el valor de clipContent a true en el contenedor interior (contenedor 2) indicamos que  el contenido del hijo se puede extender fuera de sus limites, y clipContent a false en el contenedor padre (Contenedor 1), de forma que los componentes que se encuentren dentro del contenedor 2 podrán extenderse fuera de este pero no fuera del padre. Por tanto no tendremos la sensación de que los contenedores están identados unos dentro de otros. Quedando así:
Nota: El pequeño borde rojo alrededor de la segunda imagen lo he dejado para mostrar que el contenedor 1 sigue estando en el displayList.

jueves, 27 de enero de 2011

java.lang.OutOfMemoryError: PermGen

En algunas ocasiones tenemos problemas con el espacio de memoria permanente en nuestras aplicaciones Java. Durante la ejecución d cualquier método o servicio la aplicación lanza una excepción java.lang.OutOfMemoryError: PermGen. Esto sucede porque el espacio de memoria Permanente (no es el Heap que se usa  para asignar los objetos), que se usa para colocar contenido generado por la aplicación, como las clases que carga el ClassLoader o las cadenas internas (String.intern), que se usan para optimizar el manejo de las String, se llena y se queda sin espacio. El contenido cargado en este área de memoria no se libera (por defecto).

Sucede habitualmente durante los reinicios en caliente de los servidores de aplicaciones, ya que se producen fugas de memoria al reiniciar el servidor sin apagarlo, es decir como no se apaga no se libera la memoria permanente usada, y tras múltiples reinicios se llena. La solución obvia  en este caso es reiniciar.

También puede suceder por otras circunstancias, como que realmente se esté llenando el espacio de memoria permanente. La solución trivial es aumentar el tamaño de este espacio de memoria, para ello modificaremos las opciones de la Máquina Virtual de Java (JVM) usando la variable de entorno JAVA_OPTS aumentaremos el tamaño máximo con:

 -XX:MaxPermSize=256m

Esta solución puede solucionar el problema, si colocamos el tamaño adecuado, pero si aun así el espacio es insuficiente tan sólo lo retrasaremos. Para ello deberíamos saber cuánta memoria esta usando nuestra aplicación. Para saber cuánta memoria usa la aplcacion debemos añadir en JAVA_OPTS lo siguiente:

            -verbose:gc -XX:+PrintGCDetails

Con los valores anteriores indicamos a la JVM que se muestre los datos de recolector de basura y que muestre los detalles, algo así:

[Full GC [CMS: 168674K->168674K(379016K), 0.9549626 secs] 168688K->168674K(395336K), [CMS Perm : 72024K->72024K(122292K)], 0.9556050 secs]

El texto indicado tiene el siguiente patrón:

[CMS Perm : Memoria Antes(Kb)->Memoria después(Kb)(Memoria en uso(Kb))]

Con lo cuál veremos, cada vez que se ejecute el recolector de basura la memoria en uso, y sabremos que tamaño debemos poner al espacio PermGen.

Una solución mejor es permitir  el recolector de basura que elimine el contenido de la memoria PermGen que no esté en uso, hay muchas clases que se cargan y se quedan ahí sin usarlas más (esto sucede con asiduidad si usamos frameworks como Hibernate o Spring). Para ello debemos añadir a la variable JAVA_OPTS lo siguiente:

  • -XX:+UseConcMarkSweepGC --> Política del garbageCollector, para que trabaje de forma concurrente si hay múltiples procesadores.
  • -XX:+CMSPermGenSweepingEnabled --> Se habilita el barrido por el espacio de memoria permanente (PermGen).
  • -XX:+CMSClassUnloadingEnabled --> Se permite descargar clases que no se usen.


NOTA IMPORTANTE: Si estamos usando Maven para lanzar nuestro servidor de aplicaciones la variable de entrono que debemos modificar es MAVE_OPTS en lugar de JAVA_OPTS.