【问题标题】:Hibernate trace values of statement parameters except blobs除 blob 外的语句参数的休眠跟踪值
【发布时间】:2019-01-09 23:45:54
【问题描述】:

为了跟踪休眠 SQL 语句的参数值,常见的 log4j 参数化如下所示:

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

这会产生如下的日志输出:

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]

这对于分析应用程序路径在运行时是如何执行的非常有用。

问题是,BasicBinder 还记录了 LOB 参数值的整个字符串表示形式(如 byte[]),这是非常无用的:

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, ...

整个 blob 的字符串表示会打印到日志文件中,这对我来说很烦人。

有没有办法在 Hibernate 或 log4j 中抑制/缩短 LOB 值的日志输出,同时仍显示其他语句参数的值?

是否可以在 log4j 中设置最大日志语句大小?

【问题讨论】:

    标签: java hibernate logging


    【解决方案1】:

    已经为 Hibernate 5.2.3 修复了一个问题 HHH-11097,它也应该可以解决您的问题:

    this commit 中,BlobTypeDescriptor(和其他)会覆盖extractLoggableRepresentation

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

    现在覆盖的默认实现(导致您的问题)如下所示:

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

    这应该会从您的日志中删除那些大行。

    如果您使用的是最新版本的 hibernate,您可能会使用物化 blob/原始字节数组 (byte[])。对此负责的类型描述符是PrimitiveByteArrayTypeDescriptor,它疯狂地实现了extractLoggableRepresentation 方法,如下所示:

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

    我在这种情况下看到的唯一解决方案是

    • Report this as a bug
    • 防止使用具体化的 blob。
    • 通过实现自定义UserType来使用解决方法

    自定义UserType 必须实现nullSafeSet 并将自定义JavaTypeDescriptor 传递给执行绑定和日志记录的活页夹:

    @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);
    }
    

    自定义JavaTypeDescriptor 只是扩展PrimitiveByteArrayTypeDescriptor 并覆盖有问题的extractLoggableRepresentation 方法:

    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 + "]";
            }
        }
    }
    

    【讨论】:

    • 感谢您的努力,@Selaron。如何使用用户类型?只是将它用作实体中的字段类型?可以提供样品吗?
    • @andy 您可以在自定义名称下注册UserType,或将属性映射到完全限定的类名称:@Type( type = "your.custom.ByteArrayType" )。详情请参阅Hibernate Custom Type chapter
    • 自定义UserType扩展了哪种类型?
    • 它实现了接口org.hibernate.usertype.UserType。它只是管理如何从 SQL ResultSet 接收 byte[] 数组或将其作为参数绑定到 PreparedStatement。
    • 我认为这个答案中最有用的提示是:使用当前的 Hibernate 版本
    【解决方案2】:

    我知道这是一个老问题,但我今天遇到了这个问题,并找到了一种方法来正常记录所有类型,同时避免使用 log4j2 过滤器的 blob 类型,特别是 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>
    

    也许这对某人有用。

    【讨论】:

    • 创建问题时,我使用的是 log4j。但与此同时,我们正在使用 log4j2。所以这也是一个可能的解决方案。谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-10-11
    • 2015-02-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多