聊点技术|秒级根因定位可能吗?博睿数据将不可能变为可能

2023-11-02

10月20日,数智融,ONE向新——博睿数据2023秋季产品发布会圆满落幕,全新一代一体化智能可观测平台Bonree ONE 2023秋季正式版焕新发布,重点

升级了数据采集、全局拓扑、数据分析、会话回放等多个功能模块,为组织提供了更加轻盈、有序、精准的超智能运维体验。

企业微信截图_5f935383-d37f-4bcb-a424-7f37709e8a43-1698913862575

本文作者:

博睿数据AI研发负责人-丁锐

博睿数据Alert研发负责人-焦帅婷

随着数字化时代的来临,各家国企央企,政府单位,数据中心,互联网企业等都采用了越来越复杂的应用系统,导致内部应用系统呈现指数级增长,系统之间的调用关系错综复杂,其依赖关系越来越紧密,某一个系统或应用发生异常,会导致一系列关联应用出现问题,进而导致不可预期的运维事故。

在某个运维故障发生时,运维人员从发现故障到解决故障的整个过程可以大致分为以下几个部分:

· 感知故障发生:运维人员从监控数据或告警系统中收到某些告警数据,或从监控大屏上发现某些指标出现异常,指标走势突增突降,或者收到某些用户投诉等,运维人员感知到某故障发生。

· 告警降噪收敛:某一个故障有可能引起多个关联的应用系统发生异常,故而这些关联系统接二连三的发出告警,在短时间内可能产生大量的告警,造成告警风暴,此时通过告警降噪和收敛策略,可以将这些时间关联的或内容相似的告警进行收敛,避免产生大量通知,最终将收敛之后的告警以一定方式通知给运维人员。

· 故障根因定位:运维人员在收到告警后,会查找各种数据源来分析故障的产生原因,或通过对比原始指标数据,调用链数据,日志数据来分析底层的运行逻辑,排除其他干扰项等,或借助AI分析工具自动化地将多种不同来源的数据整合后,摸清相关的拓扑结构,清晰明了地分析出故障的根因所在。

· 故障解决恢复:一旦找到故障的根本原因,运维人员通过一定方式来恢复系统,比如重启系统,修改配置,减少流量等,这些方式可以通过手动进行,也可以通过自动化脚本来进行故障自愈,使得整个业务系统恢复正常。

目前市场上的大部分产品只满足了上述前两个部分,即发出告警和一定程度的告警收敛,而Bonree ONE则从故障发生到自动根因定位都实现了,通过综合分析告警、日志、性能和配置数据,Bonree ONE能够自动发现、聚合并同步处理异常,结合博睿数据自主研发的智能化探针或集成第三方监控源,可以自动进行故障传播拓扑图展示,自动对每一个故障进行根因分析,且整个根因分析过程可以在5秒之内完成,真正实现了业界首屈一指的秒级根因定位能力,可以大大缩短运维人员进行故障排查的时间,提高应用系统的可用性。

根因定位的难点在哪?

在运维事故发生时,如何准确地定位出故障的根因常常是一个具有挑战性的任务。特别是在以下场景中,比如:在复杂的系统架构中,故障可能发生在不同的组件或层级上,例如数据库、网络、应用程序等。这些组件的故障可能是由环境状态的变化引起的,例如配置错误、资源瓶颈等,也有可能是因为代码内部错误,或者是调用的第三方服务发生异常,或者数据库连接超时导致报错等。

在分布式系统中,可能会有多台服务器、多个组件以及各种各样的服务之间的通信。在这种环境下,定位问题可能会涉及到多个层面和组件之间的相互作用,这可能会导致跨越不同服务器的调试、日志收集和跟踪成为具有挑战性的任务。

在微服务架构中,应用程序通常由许多小型服务组成。当一个事故发生时,可能需要同时检查许多微服务,以确定哪些服务可能会对问题负责。此外,这些微服务可能分布在不同的服务器上,对诊断和修复造成困难。


博睿数据的杀手锏:两阶段分析法

针对以上根因分析的难点,博睿数据在业界首次提出了两阶段分析法的定位策略,第一步进行初因定位,用于在多个故障实体之间,根据拓扑连接关系,定位到是哪个实体发生了真正的故障。第二步进行深度分析,展示出该故障实体上的哪些指标发生何种异常,或者哪些调用链发生超时,或者数据库的哪些sql语句发生异常,或者哪个接口发生了4xx或5xx的异常等。

通过自创的两阶段分析法,博睿数据首次将根因定位过程拆分为粗细两个粒度,并同时给用户展现出每个粒度的定位结果。在实际客户场景下,博睿数据自创的两阶段分析法,其综合准确性可以达到85%以上,而且从发现问题产生告警,到给出初因结果,整个过程在5秒之内完成,真正实现了秒级根因定位的能力。

· 初因定位-聚焦故障实体

下图是博睿数据从指标异常检测开始,到发现异常,触发告警,告警收敛得到故障,通知到运维人员的流程图。从图中可以看出,进行AI收敛和分析时,采用了多种收敛方法,比如AI文本相似收敛,AI时域收敛,AI根因收敛等,这些收敛得到的结果再结合下方的无监督知识图谱技术方案,即可得到初步的根因结果。


无监督知识图谱是指在构建知识图谱时,不需要手工标注和指导,而是使用自动化的方法从大量数据中发现和提取知识。这种方法利用自然语言处理和信息系统技术,自动进行数据抽取和关联,使得知识图谱可以包含更加丰富的实体、属性和关系等元素,建立过程主要依赖于算法的发现和规律的挖掘,与传统知识图谱相比,它没有手工标注的负担和人为的限制,因此可以处理大量文本数据、进行更加高效的知识发现。

图3示意的是博睿数据自主研发的探针自动收集并上报的各种实体和实体之间的关联关系,将这些实体和关系存储在专用图数据库中,即可自动构建出无监督知识图谱体系。


使用AI故障收敛算法对一段时间内(比如30min或1h等)的告警数据进行综合分析,将这些告警数据收敛到一颗或多颗故障树中。其收敛算法背后的逻辑是,遍历一段时间内的所有告警,依次判断这些告警与原有故障树是否拓扑可达,此处的拓扑可达被定义为:在N跳(比如N=2等)内从一个告警实体节点是否可以按照一定顺序到达另外一个告警实体节点,如果可以到达,则认定为拓扑可达,则该告警可以收敛到已存在的故障树中,如果不是拓扑可达,则需要重新建立一颗新的故障树,比如以下是某一个时间段内的所有告警:

image-1698914660748

假设N=2,且对某时间段内的所有告警按照时间顺序排序后,进行逐一遍历,最终形成的两颗故障树如下图所示:

图4的黄色数字表示告警id,这些告警均可以根据其告警对象之间是否关联在一起,是否可以在一定程度内拓扑可达,来判断这些告警是否应该被合并到一颗故障树中。如图所示,表1的告警数据经过无监督知识图谱的收敛算法之后,可以得到两颗不相关的故障树,即表示这6条告警数据,被收敛到2个故障场景中。

在实际运维业务中,一颗故障树表示一个具体的故障场景,表示一个由某些节点异常导致的其他关联节点也发生异常的真实场景,每一颗故障树包含有多个告警,实现了告警按照无监督知识图谱进行收敛的效果。在故障发生时,这种依据无监督知识图谱的收敛方案,可以实现秒级合并到原有故障树的效果。

针对每一个故障场景,系统能够通过快速根因定位算法计算出故障实体,即最终给出的初因。在多种初因计算方法中,博睿数据采用了一种近似逻辑回归的算法,计算出该故障树中每个实体点的故障概率,获取最大概率的实体点,将其作为最终初因推荐出来。

这种根因算法主要计算出每个实体节点作为根因的可能性,这种可能性由三部分组成,一是该实体节点被多少个其他节点所调用,调用数量越多,代表该节点越重要,一旦发生问题,其引起的影响面会更广,其成为根因的可能性更大。二是该实体节点调用了多少个其他节点,调用的越多,代表该节点的重要性越高,其成为根因的可能性更大。三是该实体节点上发生的告警数量越多,代表该实体节点上的问题越多,因此其成为根因节点的可能性增加。

系统通过这三个组分及其对应的权重计算出每个节点的得分,判断该节点是根因节点的可能性,最终将得分最高的节点作为初因推荐出来。这种逻辑回归近似算法计算简单,逻辑明确,可以在非常短的时间内计算出最终结果,故而真正实现了秒级根因定位结果的计算,实现了精准快速的根因定位。

· 深度分析-揭示核心根因

博睿数据的深度分析是在知识图谱定位的问题初因的基础上对根因结果开展更进一步的深入分析,指导运维人员快速解决问题。通过初因定位,我们可以缩小问题范围,将注意力集中在可能出问题的节点上。但从传统运维角度来说,原因定位到节点粒度往往是不够,为了降低系统故障带来的影响,运维人员往往需要更深层次、更细粒度的结果作为依据来对症下药,使系统能够在尽可能短的时间内恢复正常。

过去,分析问题更深层次的原因需要通过人工方式进行,运维人员在该阶段排查故障根因往往需要经历艰辛的过程,需要查看系统日志、监控指标,了解系统状态,必要时还要协调研发测试等多部门协助定位。而随着自动化运维的发展,系统的自动监控和信息收集日趋完善,Metric、Trace、Log都在Bonree One平台体系中被无缝集成和支持,如何把这些数据同问题根因定位关联起来,正是博睿数据根因深度分析的诀窍所在。关联分析不仅仅是罗列数据,这三种要素每种单独来看都有其自身价值,从运维角度来说,这些数据的真正价值在于在平台能否把这些数据智能关联打通,自动分析其内在逻辑,能自动给出问题更深层次原因。

对于运维和客户,我们希望能基于典型几大场景快速准确帮用户定位到深层次根因,能够像人脑一样,通过分析Metric、Trace、Log等数据后进行判断,对于问题能快速深度分析,避免长时间复杂的人工分析。对于典型故障场景,博睿数据依靠自身强大的数据整合能力和AI算法引擎,为用户提供了最佳定位实践。

 定位效果展示 

· 错误类

在错误类故障场景中,往往错误已经发生,指标、日志、调用链信息等也都已各自产生。 传统人工分析往往要先检索日志,根据日志查看调用链,如果此时还定位不到问题根本原因,则进行各业务指标的分析与判断,查看各业务指标是否有异常,响应时间是否升高、错误次数是否同比增长,是否是业务上量等。博睿数据把这些过程通过AI自适应整合,默认典型错误类指标报警规则,告警发生并定位到初因节点后,点击深度分析,则可触发问题根因深度分。对于错误类场景,基于日志、调用链、指标及博睿数据独家创新的异常中台数据分析引擎,进行数据实时快速分析,能将问题根因定位到代码级别、Sql级别等。对于正常发布、变更或部署导致的告警情况,智能清洗出对应事件,挖掘出深层次导致异常的原因,如服务重启、环境变量变更等,并给出操作建议。


· 缓慢类

在缓慢类故障场景中,发生缓慢时,已经影响到了业务和客户。前端客户告急,客户访问龟速,业务大量流失。传统运维人工分析方法往往先定位到入口业务,由入口业务相关人员向下层层级联排查到问题时往往故障时长已远超容忍范围。借助博睿数据 根因深度分析,对于缓慢类场景自动发现复杂服务之间调用关系,对于初因节点,深度分析根据故障实体类型,自动关联错误、Trace、日志、慢调用等信息,缓慢原因定位值Sql级别或者代码级别。并智能关联告警时间段内变更事件,还原整个异常现场,根据对应数据给出最佳操作建议。

· 资源类

在资源类故障场景中,目前主要分为CPU和内存两大类,经过对众多一线开发运维人员的调研,博睿数据根据实际排查过程及经验整理出深度分析逻辑。对于CPU类场景,博睿数据深度分析自动关联出所属主机上占用CPU最高的Top5进程,并给出进程命令行、进程占用内存趋势图等信息。智能关联告警时间段内变更事件,更大面积还原出告警现场。对于内存类场景,则给出内存占用Top5进程并给出命令行、告警时间段内趋势图等信息,智能关联告警时间段内变更事件,给出深层次建议。

 效果对比 

博睿数据研发的基于无监督知识图谱的两阶段根因分析法,相对于其他传统的根因定位方式,具有非常明显的优势:

image-1698914637758

除此之外,博睿数据开发的两阶段根因分析法,还具有其他优势:

· 从整体分析流程上来看,告警产生,降噪收敛,根因分析三个环节可以一次性秒级完成。

· 无监督知识图谱方案决定了AI算法无需人工打标,免去了根因定位过程中打标的麻烦。

· 计算速度快,所需资源少,2个3C3G的实例资源就能够高可用起跑,在版本优化后,资源使用上,CPU仅占用约9.4核,相对前一版19.7核的节省了50%,内存占用仅5.2g,相对于前一版的46.2g节省了90%。

· 支持多数据融合,支持第三方数据和博睿数据数据的融合,比如zabbix, prometheus, skywalking, pinpoint多源数据一体化融合。

如下是具体数据:

image


文章标签

根因分析

相关文章

FreeMarker template error (DEBUG mode; use RETHROW in production!): The string doesn't match the expected date/time/date-time format. The string to parse was: "2024年3月5日". The expected format was: "MMM d, y". The nested reason given follows: Unparseable date: "2024年3月5日" ---- FTL stack trace ("~" means nesting-related): - Failed at: #if "2024年3月5日"?date lt item.createTi... [in template "themes/halo_quickstarter/post_newsDetail.ftl" at line 106, column 25] ---- Java stack trace (for programmers): ---- freemarker.core._TemplateModelException: [... Exception message was already printed; see it above ...] at freemarker.core.BuiltInsForMultipleTypes$dateBI$DateParser.parse(BuiltInsForMultipleTypes.java:220) at freemarker.core.BuiltInsForMultipleTypes$dateBI$DateParser.getAsDateModel(BuiltInsForMultipleTypes.java:190) at freemarker.core.BuiltInsForMultipleTypes$dateBI$DateParser.getAsDate(BuiltInsForMultipleTypes.java:197) at freemarker.core.EvalUtil.modelToDate(EvalUtil.java:86) at freemarker.core.EvalUtil.compare(EvalUtil.java:270) at freemarker.core.EvalUtil.compare(EvalUtil.java:115) at freemarker.core.ComparisonExpression.evalToBoolean(ComparisonExpression.java:78) at freemarker.core.IfBlock.accept(IfBlock.java:49) at freemarker.core.Environment.visit(Environment.java:370) at freemarker.core.IteratorBlock$IterationContext.executedNestedContentForCollOrSeqListing(IteratorBlock.java:321) at freemarker.core.IteratorBlock$IterationContext.executeNestedContent(IteratorBlock.java:271) at freemarker.core.IteratorBlock$IterationContext.accept(IteratorBlock.java:244) at freemarker.core.Environment.visitIteratorBlock(Environment.java:644) at freemarker.core.IteratorBlock.acceptWithResult(IteratorBlock.java:108) at freemarker.core.IteratorBlock.accept(IteratorBlock.java:94) at freemarker.core.Environment.visit(Environment.java:334) at freemarker.core.Environment.visit(Environment.java:340) at freemarker.core.Environment.visit(Environment.java:340) at freemarker.core.Environment.process(Environment.java:313) at freemarker.template.Template.process(Template.java:383) at org.springframework.web.servlet.view.freemarker.FreeMarkerView.processTemplate(FreeMarkerView.java:391) at org.springframework.web.servlet.view.freemarker.FreeMarkerView.doRender(FreeMarkerView.java:304) at org.springframework.web.servlet.view.freemarker.FreeMarkerView.renderMergedTemplateModel(FreeMarkerView.java:255) at org.springframework.web.servlet.view.AbstractTemplateView.renderMergedOutputModel(AbstractTemplateView.java:179) at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:316) at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1401) at org.springframework.web.servlet.DispatcherServlet.processDispatchResult(DispatcherServlet.java:1145) at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:1084) at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:963) at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:1006) at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:898) at javax.servlet.http.HttpServlet.service(HttpServlet.java:497) at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:883) at javax.servlet.http.HttpServlet.service(HttpServlet.java:584) at org.eclipse.jetty.servlet.ServletHolder.handle(ServletHolder.java:799) at org.eclipse.jetty.servlet.ServletHandler$ChainEnd.doFilter(ServletHandler.java:1631) at org.eclipse.jetty.websocket.server.WebSocketUpgradeFilter.doFilter(WebSocketUpgradeFilter.java:230) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at run.halo.app.cas.UserInfoFilter.doFilter(UserInfoFilter.java:37) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at org.jasig.cas.client.util.AssertionThreadLocalFilter.doFilter(AssertionThreadLocalFilter.java:54) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at org.jasig.cas.client.util.HttpServletRequestWrapperFilter.doFilter(HttpServletRequestWrapperFilter.java:75) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at run.halo.app.cas.MyAbstractTicketValidationFilter.doFilter(MyAbstractTicketValidationFilter.java:215) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at run.halo.app.cas.MyAuthenticationNoLoginFilter.doFilter(MyAuthenticationNoLoginFilter.java:57) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:102) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at run.halo.app.security.filter.ContentFilter.doAuthenticate(ContentFilter.java:69) at run.halo.app.security.filter.AbstractAuthenticationFilter.doFilterInternal(AbstractAuthenticationFilter.java:229) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:100) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at org.springframework.web.filter.FormContentFilter.doFilterInternal(FormContentFilter.java:93) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at run.halo.app.filter.CorsFilter.doFilter(CorsFilter.java:53) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:96) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:201) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at run.halo.app.filter.LogFilter.doFilterInternal(LogFilter.java:40) at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:117) at org.eclipse.jetty.servlet.FilterHolder.doFilter(FilterHolder.java:193) at org.eclipse.jetty.servlet.ServletHandler$Chain.doFilter(ServletHandler.java:1601) at org.eclipse.jetty.servlet.ServletHandler.doHandle(ServletHandler.java:548) at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:143) at org.eclipse.jetty.security.SecurityHandler.handle(SecurityHandler.java:600) at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:235) at org.eclipse.jetty.server.session.SessionHandler.doHandle(SessionHandler.java:1624) at org.eclipse.jetty.server.handler.ScopedHandler.nextHandle(ScopedHandler.java:233) at org.eclipse.jetty.server.handler.ContextHandler.doHandle(ContextHandler.java:1440) at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:188) at org.eclipse.jetty.servlet.ServletHandler.doScope(ServletHandler.java:501) at org.eclipse.jetty.server.session.SessionHandler.doScope(SessionHandler.java:1594) at org.eclipse.jetty.server.handler.ScopedHandler.nextScope(ScopedHandler.java:186) at org.eclipse.jetty.server.handler.ContextHandler.doScope(ContextHandler.java:1355) at org.eclipse.jetty.server.handler.ScopedHandler.handle(ScopedHandler.java:141) at org.eclipse.jetty.server.handler.HandlerWrapper.handle(HandlerWrapper.java:127) at org.eclipse.jetty.server.Server.handle(Server.java:516) at org.eclipse.jetty.server.HttpChannel.lambda$handle$1(HttpChannel.java:487) at org.eclipse.jetty.server.HttpChannel.dispatch(HttpChannel.java:732) at org.eclipse.jetty.server.HttpChannel.handle(HttpChannel.java:479) at org.eclipse.jetty.server.HttpConnection.onFillable(HttpConnection.java:277) at org.eclipse.jetty.io.AbstractConnection$ReadCallback.succeeded(AbstractConnection.java:311) at org.eclipse.jetty.io.FillInterest.fillable(FillInterest.java:105) at org.eclipse.jetty.io.ChannelEndPoint$1.run(ChannelEndPoint.java:104) at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.runTask(EatWhatYouKill.java:338) at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.doProduce(EatWhatYouKill.java:315) at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.tryProduce(EatWhatYouKill.java:173) at org.eclipse.jetty.util.thread.strategy.EatWhatYouKill.run(EatWhatYouKill.java:131) at org.eclipse.jetty.util.thread.ReservedThreadExecutor$ReservedThread.run(ReservedThreadExecutor.java:409) at org.eclipse.jetty.util.thread.QueuedThreadPool.runJob(QueuedThreadPool.java:883) at org.eclipse.jetty.util.thread.QueuedThreadPool$Runner.run(QueuedThreadPool.java:1034) at java.base/java.lang.Thread.run(Thread.java:834) Caused by: freemarker.core.UnparsableValueException: Unparseable date: "2024年3月5日" at freemarker.core.JavaTemplateDateFormat.parse(JavaTemplateDateFormat.java:51) at freemarker.core.JavaTemplateDateFormat.parse(JavaTemplateDateFormat.java:33) at freemarker.core.BuiltInsForMultipleTypes$dateBI$DateParser.parse(BuiltInsForMultipleTypes.java:213) ... 118 more Caused by: java.text.ParseException: Unparseable date: "2024年3月5日" at java.base/java.text.DateFormat.parse(DateFormat.java:395) at freemarker.core.JavaTemplateDateFormat.parse(JavaTemplateDateFormat.java:49) ... 120 more