You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何调试Scala版MongoDB驱动代码?获取异常关联错误文档

打印Mongo解码错误的文档内容排查问题

问题场景

你的Mongo Codec配置代码:

object MongoCodecs {
  def Codecs: CodecRegistry = fromRegistries(
    fromProviders(
      
      createCodecProviderIgnoreNone[MyModel]
    ),
    DEFAULT_CODEC_REGISTRY
  )
}

读取文档时出现类型不匹配异常:

Error in block: readString can only be called when CurrentBSONType is STRING, not when CurrentBSONType is DOCUMENT., null, Stacktrace:
 org.bson.BsonInvalidOperationException: readString can only be called when CurrentBSONType is STRING, not when CurrentBSONType is DOCUMENT.
    at org.bson.AbstractBsonReader.verifyBSONType(AbstractBsonReader.java:689)
    at org.bson.AbstractBsonReader.checkPreconditions(AbstractBsonReader.java:721)
    at org.bson.AbstractBsonReader.readString(AbstractBsonReader.java:456)
    at org.bson.codecs.StringCodec.decode(StringCodec.java:80)
    at org.bson.codecs.StringCodec.decode(StringCodec.java:31)
    at org.mongodb.scala.bson.codecs.macrocodecs.MacroCodec.readDocument(MacroCodec.scala:232)
    at org.mongodb.scala.bson.codecs.macrocodecs.MacroCodec.readDocument$(MacroCodec.scala:225)
    at com.awesomecompany.MongoCodecs$$anon$30$MyModelMacroCodec$1.readDocument(MongoCodecs.scala:55)

解决方案:打印错误文档

可以通过自定义Codec包装类,在解码失败时捕获异常并读取当前的BSON文档,打印到日志中。

步骤1:实现日志包装Codec

import org.bson._
import org.bson.codecs._
import org.bson.codecs.configuration.CodecRegistries._
import org.mongodb.scala.bson.codecs.macrocodecs._
import org.slf4j.LoggerFactory

class LoggingCodecWrapper[T](underlying: Codec[T], logger: org.slf4j.Logger) extends Codec[T] {
  override def encode(writer: BsonWriter, value: T, encoderContext: EncoderContext): Unit = {
    underlying.encode(writer, value, encoderContext)
  }

  override def getEncoderClass: Class[T] = underlying.getEncoderClass

  override def decode(reader: BsonReader, decoderContext: DecoderContext): T = {
    try {
      underlying.decode(reader, decoderContext)
    } catch {
      case e: BsonInvalidOperationException =>
        // 读取并打印错误文档
        val errorDoc = BsonDocumentReader.readDocument(reader)
        logger.error(s"解码${getEncoderClass.getSimpleName}失败,错误文档内容:\n$errorDoc", e)
        throw e
    }
  }
}

步骤2:修改CodecRegistry配置

调整MongoCodecs对象,用包装类包裹原有的MacroCodec:

object MongoCodecs {
  private val logger = LoggerFactory.getLogger(MongoCodecs.getClass)

  def Codecs: CodecRegistry = fromRegistries(
    fromProviders(
      new CodecProvider {
        override def get[T](clazz: Class[T], registry: CodecRegistry): Codec[T] = {
          if (clazz == classOf[MyModel]) {
            val macroCodec = createCodecProviderIgnoreNone[MyModel].get(clazz, registry)
            new LoggingCodecWrapper(macroCodec, logger).asInstanceOf[Codec[T]]
          } else {
            null
          }
        }
      }
    ),
    DEFAULT_CODEC_REGISTRY
  )
}

注意事项

  • 确保项目引入SLF4J依赖(如slf4j-api和对应的实现,比如logback-classic)
  • BsonDocumentReader.readDocument(reader)会消耗当前Reader的内容,因此抛出异常后无法继续解码,这符合排查需求
  • 打印的文档会完整展示所有字段,能直接定位到哪个字符串字段对应了DOCUMENT类型的值

内容的提问来源于stack exchange,提问作者Capacytron

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.24 23:33:12