andy
andy

Reputation: 1105

Hibernate trace values of statement parameters except blobs

To trace parameter values of hibernate SQL statements, a common log4j parametrization looks like that:

<logger name="org.hibernate.SQL">
  <level value="debug" />
</logger>
<logger name="org.hibernate.type.descriptor.sql.BasicBinder">
  <level value="trace" />
</logger>

This yields log outputs like that:

2019-01-10 00:10:29,349 [main] DEBUG  SqlStatementLogger.logStatement(SqlStatementLogger.java:92) - select land0_.fk_land as fk_land1_24_0_ from land land0_ where land0_.fk_land=?
2019-01-10 00:10:29,349 [main] TRACE  BasicBinder.bind(BasicBinder.java:65) - binding parameter [1] as [BIGINT] - [27]

This is very useful to analyze how the application path was executed at runtime.

The problem is, that BasicBinder also logs the whole string representation of values of LOB parameters (like byte[]) which is very unuseful:

2019-01-07 13:28:45,466 [wwsservices-catalina-exec-10] TRACE   org.hibernate.type.descriptor.sql.BasicBinder: binding parameter [2] as [BLOB] - [[37, 80, 68, 70, 45, 49, 46, 52, ...

The string representation of the whole blob is printed to the log file which is very annoying to me.

Is there a way to suppress / shorten log output of LOB values in Hibernate or log4j while still displaying values of other statement parameters?

Is there a possibility to set maximum log statement size in log4j?

Upvotes: 6

Views: 1621

Answers (2)

iodog
iodog

Reputation: 21

I know this is an old question but I was having this problem today and found a way to log all types normally while avoiding the blob type using log4j2 filters, specifically a RegexFilter

 <AsyncLogger name="org.hibernate.type.descriptor.sql" level="trace" additivity="false">
    <RegexFilter regex=".*[BLOB].*" onMatch="DENY" onMismatch="NEUTRAL"/>
    <AppenderRef ref="Console"/>
    <AppenderRef ref="File" />
 </AsyncLogger>

Maybe this will be usefull to someone.

Upvotes: 2

Selaron
Selaron

Reputation: 6184

There has been an issue HHH-11097 fixed for Hibernate 5.2.3 which should also solve your problem:

In this commit, the BlobTypeDescriptor(and others) gets an override for extractLoggableRepresentation:

@Override
public String extractLoggableRepresentation(Blob value) {
    return value == null ? "null" : "BLOB{...}";
}

The default implementation (causing your issue) now overridden looks like this:

@Override
public String extractLoggableRepresentation(T value) {
    return (value == null) ? "null" : value.toString();
}

This should remove those giant lines from your log.

If you are using a recent version of hibernate, you are likely using materialized blobs / primitive byte arrays (byte[]). The type descriptor responsible for this is PrimitiveByteArrayTypeDescriptor which madly implements the extractLoggableRepresentation method like this:

@Override
public String extractLoggableRepresentation(byte[] value) {
    return (value == null) ? super.extractLoggableRepresentation( null ) : Arrays.toString( value );
}

The only solutions I see in this case are

  • Report this as a bug
  • Prevent usage of materialized blobs.
  • Use a workaround by implementing a custom UserType:

The custom UserType must implement the nullSafeSet and pass a custom JavaTypeDescriptor to the binder which does the binding and logging:

@Override
public void nullSafeSet(PreparedStatement st, Object value, int index,
        final SharedSessionContractImplementor session) throws HibernateException, SQLException {

    // Simply do what 
    // org.hibernate.type.AbstractStandardBasicType.nullSafeSet(PreparedStatement, Object, int, WrapperOptions)
    // does, but using a custom descriptor.

    session.remapSqlTypeDescriptor(MaterializedBlobType.INSTANCE.getSqlTypeDescriptor())
            .getBinder(CustomPrimitiveByteArrayTypeDescriptor.INSTANCE)
            .bind(st, (byte[]) value, index, session);
}

The custom JavaTypeDescriptor simply extends the PrimitiveByteArrayTypeDescriptor and overrides the problematic extractLoggableRepresentation method:

public class CustomPrimitiveByteArrayTypeDescriptor extends PrimitiveByteArrayTypeDescriptor {
    public static final CustomPrimitiveByteArrayTypeDescriptor INSTANCE = new CustomPrimitiveByteArrayTypeDescriptor();

    @Override
    public String extractLoggableRepresentation(byte[] value) {
        if (null == value) {
            return super.extractLoggableRepresentation(value);
        } else {
            return "byte[" + value.length + "]";
        }
    }
}

Upvotes: 6

Related Questions