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>
No hay comentarios:
Publicar un comentario