如何调试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
相关产品推荐
相关产品推荐

